diff --git a/.clang-format b/.clang-format index ccd4b5c73..558c0d0ad 100644 --- a/.clang-format +++ b/.clang-format @@ -118,7 +118,7 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 -AttributeMacros: [SQLITE_ORM_CPP_LIKELY, SQLITE_ORM_CPP_UNLIKELY] +AttributeMacros: [SQLITE_ORM_CPP_LIKELY, SQLITE_ORM_CPP_UNLIKELY, SQLITE_ORM_EXPORT] StatementMacros: - __pragma - _Pragma diff --git a/.vscode/settings.json b/.vscode/settings.json index 7216b3ba8..9a81556b5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -74,6 +74,19 @@ "type_traits": "cpp", "utility": "cpp", "__config": "cpp", - "any": "cpp" + "any": "cpp", + "__threading_support": "cpp", + "condition_variable": "cpp", + "complex": "cpp", + "thread": "cpp", + "future": "cpp", + "fstream": "cpp", + "execution": "cpp", + "iostream": "cpp", + "numbers": "cpp", + "queue": "cpp", + "semaphore": "cpp", + "stack": "cpp", + "unordered_set": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a56294f29..9fb4d5f9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,14 @@ set(PACKAGE_VERSION ${sqlite_orm_VERSION}) project("sqlite_orm" VERSION ${PACKAGE_VERSION}) # Handling C++ standard version to use +option(SQLITE_ORM_ENABLE_CXX_23 "Enable C++ 23" OFF) option(SQLITE_ORM_ENABLE_CXX_20 "Enable C++ 20" OFF) option(SQLITE_ORM_ENABLE_CXX_17 "Enable C++ 17" OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(SQLITE_ORM_ENABLE_CXX_20) +if(SQLITE_ORM_ENABLE_CXX_23) + set(CMAKE_CXX_STANDARD 23) + message(STATUS "SQLITE_ORM: Build with C++23 features") +elseif(SQLITE_ORM_ENABLE_CXX_20) set(CMAKE_CXX_STANDARD 20) message(STATUS "SQLITE_ORM: Build with C++20 features") elseif(SQLITE_ORM_ENABLE_CXX_17) @@ -51,6 +55,17 @@ target_sources(sqlite_orm INTERFACE $) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(sqlite_orm INTERFACE + -Wreorder + -Werror=reorder + ) +elseif(MSVC) + target_compile_options(sqlite_orm INTERFACE + /we5038 + ) +endif() + include(ucm) if (MSVC) diff --git a/README.md b/README.md index 850aeb62a..bd21f2398 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ SQLite ORM light header only library for modern C++. Please read the license pre * **No raw string queries** * **Intuitive syntax** * **Comfortable interface - one code line per single query** -* **Built with modern C++14/C++17/C++20 features (no macros and external scripts)** +* **Built with modern C++17 functionality (or higher) - no macros and external scripts** * **CRUD support** * **Pure select query support** * **Prepared statements support** @@ -801,7 +801,7 @@ If you want to use the lib directly with Make or something else, just set the in # Requirements -* C++14 compatible compiler (not C++11 cause of templated lambdas in the lib). +* C++17 compatible compiler. * 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 diff --git a/appveyor.yml b/appveyor.yml index efe9a995a..1f0a07715 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,27 +20,26 @@ configuration: environment: appveyor_yml_disable_ps_linux: true matrix: - - job_name: clang, C++14 - appveyor_build_worker_image: Ubuntu - CC: clang - CXX: clang++ - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + - job_name: Visual Studio 2022, x64, C++23 + appveyor_build_worker_image: Visual Studio 2022 + platform: x64 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" + + - job_name: clang, C++23 + appveyor_build_worker_image: Ubuntu2204 + CC: clang-18 + CXX: clang++-18 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" cmake_build_parallel: "" - - job_name: gcc, C++14 - appveyor_build_worker_image: Ubuntu + - job_name: gcc, C++23 + appveyor_build_worker_image: Ubuntu2204 CC: gcc CXX: g++ - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" # gcc was stuck with a parallel build cmake_build_parallel: "" - # Representative for C++14 - - job_name: Visual Studio 2017, x64, C++14 - appveyor_build_worker_image: Visual Studio 2017 - platform: x64 - SQLITE_ORM_CXX_STANDARD: "" - - job_name: clang, C++17 appveyor_build_worker_image: Ubuntu CC: clang @@ -72,8 +71,9 @@ environment: SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" cmake_build_parallel: "" - - job_name: Visual Studio 2022, x64, C++17 - appveyor_build_worker_image: Visual Studio 2022 + # Representative for C++17 + - job_name: Visual Studio 2017, x64, C++17 + appveyor_build_worker_image: Visual Studio 2017 platform: x64 SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" @@ -108,7 +108,7 @@ for: install: - |- cd C:\Tools\vcpkg - git fetch --tags && git checkout 2025.01.13 + git fetch --tags && git checkout 2025.03.19 cd %APPVEYOR_BUILD_FOLDER% C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics C:\Tools\vcpkg\vcpkg integrate install @@ -132,6 +132,7 @@ for: matrix: only: - appveyor_build_worker_image: Ubuntu + - appveyor_build_worker_image: Ubuntu2204 init: - |- echo $appveyor_build_worker_image @@ -141,7 +142,7 @@ for: install: - |- pushd $HOME/vcpkg - git fetch --tags && git checkout 2025.01.13 + git fetch --tags && git checkout 2025.03.19 popd $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets @@ -169,7 +170,7 @@ for: # using custom vcpkg triplets for building and linking dynamic dependent libraries install: - |- - git clone --depth 1 --branch 2025.01.13 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + git clone --depth 1 --branch 2025.03.19 https://github.com/microsoft/vcpkg.git $HOME/vcpkg $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 282243910..bf8f4d6e5 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -6,13 +6,13 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.2.1 + GIT_TAG v3.8.0 ) else() FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.2.1 + GIT_TAG v3.8.0 # prefer find_package() over building from source FIND_PACKAGE_ARGS 3 CONFIG ) diff --git a/dev/alias.h b/dev/alias.h index c3a1154ea..902e36ec4 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string @@ -7,12 +8,14 @@ #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #endif +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl/conditional.h" #include "functional/cstring_literal.h" #include "type_traits.h" #include "alias_traits.h" +#include "field_of.h" #include "table_type_of.h" #include "tags.h" #include "column_pointer.h" @@ -185,7 +188,9 @@ namespace sqlite_orm { inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Using a column pointer, create a column reference to an aliased table column. * @@ -247,7 +252,7 @@ namespace sqlite_orm { requires (!orm_cte_moniker>) constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = decltype(als); + using A = std::remove_const_t; using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -343,7 +348,7 @@ namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t{std::move(expression)}; + return internal::as_t, E>{std::move(expression)}; } /** @@ -375,7 +380,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder{}; + return internal::alias_holder>{}; } #endif diff --git a/dev/alias_traits.h b/dev/alias_traits.h index abdb2d389..8612c1875 100644 --- a/dev/alias_traits.h +++ b/dev/alias_traits.h @@ -1,20 +1,24 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif +#endif #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "table_reference.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ struct alias_tag {}; +} +namespace sqlite_orm { namespace internal { template @@ -65,7 +69,9 @@ namespace sqlite_orm { template using is_cte_moniker = polyfill::bool_constant>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept orm_alias = std::derived_from; diff --git a/dev/arg_values.h b/dev/arg_values.h index 0e8260790..2fb92ea4c 100644 --- a/dev/arg_values.h +++ b/dev/arg_values.h @@ -4,7 +4,7 @@ #include "row_extractor.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ @@ -98,9 +98,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index a89e7a2d8..63b4a9f63 100644 --- a/dev/arithmetic_tag.h +++ b/dev/arithmetic_tag.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral +#endif #include "functional/mpl/conditional.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper classes used by statement_binder and row_extractor. diff --git a/dev/ast/excluded.h b/dev/ast/excluded.h index faea0a340..69bc40ae2 100644 --- a/dev/ast/excluded.h +++ b/dev/ast/excluded.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move +#endif namespace sqlite_orm { namespace internal { @@ -12,7 +14,9 @@ namespace sqlite_orm { expression_type expression; }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::excluded_t excluded(T expression) { return {std::move(expression)}; diff --git a/dev/ast/exists.h b/dev/ast/exists.h index b6d2a1cfd..5efb10cc6 100644 --- a/dev/ast/exists.h +++ b/dev/ast/exists.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move +#endif #include "../tags.h" @@ -17,7 +19,9 @@ namespace sqlite_orm { exists_t(expression_type expression_) : expression(std::move(expression_)) {} }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * EXISTS(condition). * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), diff --git a/dev/ast/group_by.h b/dev/ast/group_by.h index 966dc1550..9be1b015b 100644 --- a/dev/ast/group_by.h +++ b/dev/ast/group_by.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move +#endif #include "../functional/cxx_type_traits_polyfill.h" @@ -37,7 +39,9 @@ namespace sqlite_orm { using is_group_by = polyfill::disjunction, polyfill::is_specialization_of>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * GROUP BY column. * Example: storage.get_all(group_by(&Employee::name)) diff --git a/dev/ast/into.h b/dev/ast/into.h index d2bba6a2e..2e961be25 100644 --- a/dev/ast/into.h +++ b/dev/ast/into.h @@ -13,7 +13,9 @@ namespace sqlite_orm { template using is_into = polyfill::is_specialization_of; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::into_t into() { return {}; diff --git a/dev/ast/match.h b/dev/ast/match.h index b2a65c70f..2059c38c1 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,6 +1,8 @@ #pragma once -#include // std::move +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include +#endif namespace sqlite_orm { namespace internal { @@ -11,11 +13,11 @@ namespace sqlite_orm { using argument_type = X; argument_type argument; - - match_t(argument_type argument) : argument(std::move(argument)) {} }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; diff --git a/dev/ast/rank.h b/dev/ast/rank.h index e71ec8723..28387d0a6 100644 --- a/dev/ast/rank.h +++ b/dev/ast/rank.h @@ -4,7 +4,9 @@ namespace sqlite_orm { namespace internal { struct rank_t {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rank_t rank() { return {}; } diff --git a/dev/ast/set.h b/dev/ast/set.h index b997e4c5a..aa9c20846 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string #include // std::vector #include // std::stringstream #include // std::false_type, std::true_type +#endif #include "../tuple_helper/tuple_traits.h" #include "../table_name_collector.h" @@ -85,7 +87,9 @@ namespace sqlite_orm { template struct is_dynamic_set> : std::true_type {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) diff --git a/dev/ast/special_keywords.h b/dev/ast/special_keywords.h index f553d123b..efbcb82a2 100644 --- a/dev/ast/special_keywords.h +++ b/dev/ast/special_keywords.h @@ -6,7 +6,9 @@ namespace sqlite_orm { struct current_date_t {}; struct current_timestamp_t {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::current_time_t current_time() { return {}; } diff --git a/dev/ast/upsert_clause.h b/dev/ast/upsert_clause.h index 287abaace..e877d8a35 100644 --- a/dev/ast/upsert_clause.h +++ b/dev/ast/upsert_clause.h @@ -1,9 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move #endif +#endif #include "../functional/cxx_type_traits_polyfill.h" @@ -51,7 +53,9 @@ namespace sqlite_orm { template using is_upsert_clause = polyfill::bool_constant>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3024000 /** * ON CONFLICT upsert clause builder function. diff --git a/dev/ast/where.h b/dev/ast/where.h index c18d936fc..3100d1716 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move +#endif #include "../functional/cxx_type_traits_polyfill.h" #include "../serialize_result_type.h" @@ -35,7 +37,9 @@ namespace sqlite_orm { template struct is_where : polyfill::bool_constant> {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * WHERE clause. Use it to add WHERE conditions wherever you like. * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index b48debde8..85e376f6f 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -1,8 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_invocable #include // std::vector #include // std::reference_wrapper +#endif +#include "functional/cxx_functional_polyfill.h" // std::is_invocable #include "tuple_helper/tuple_iteration.h" #include "type_traits.h" #include "conditions.h" @@ -34,7 +38,7 @@ namespace sqlite_orm { * ast_iterator is used in finding literals to be bound to * a statement, and to collect table names. * - * Note that not all leaves of the expression tree are always visited: + * Note that not all leaves of the expression tree are visited: * Column expressions can be more complex, but are passed as a whole to the callable. * Examples are `column_pointer<>` and `alias_column_t<>`. * @@ -50,17 +54,26 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T& t, L& lambda) const { - lambda(t); + void operator()(const node_type& leaf, L& lambda) const { + lambda(leaf); } }; /** - * Simplified API + * Simplified API. + * + * @param lambda Callable invoked with each leaf expression. + * The callable can opt in to be invoked for a node expression. */ template void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; + + // possibly invoke lambda with node itself + if constexpr (polyfill::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } + iterator(t, lambda); } @@ -112,7 +125,6 @@ namespace sqlite_orm { template void operator()(const node_type& expression, L& lambda) const { - lambda(expression); iterate_ast(expression.argument0, lambda); iterate_ast(expression.argument1, lambda); iterate_ast(expression.argument2, lambda); @@ -701,7 +713,17 @@ namespace sqlite_orm { template void operator()(const node_type& node, L& lambda) const { - iterate_ast(node.expression, lambda); + iterate_ast(node._expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = multi_order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; diff --git a/dev/backup.h b/dev/backup.h index 562632208..a2fc533ea 100644 --- a/dev/backup.h +++ b/dev/backup.h @@ -1,10 +1,12 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error #include // std::string #include #include // std::move, std::exchange +#endif #include "error_code.h" #include "connection_holder.h" diff --git a/dev/carray.h b/dev/carray.h index a16d50f9b..f352cda76 100644 --- a/dev/carray.h +++ b/dev/carray.h @@ -6,6 +6,7 @@ * Hence we make it only available for compilers supporting inline variables. */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #include // std::move @@ -14,12 +15,13 @@ #endif #endif #endif +#endif #include "pointer_value.h" #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; diff --git a/dev/column_expression.h b/dev/column_expression.h index e0d099257..af4c41439 100644 --- a/dev/column_expression.h +++ b/dev/column_expression.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper +#endif #include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_transformer.h" diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 519713132..3b5dfc658 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -1,11 +1,13 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string #include // std::vector #include // std::reference_wrapper #include #include // std::move +#endif #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_iteration.h" @@ -34,7 +36,7 @@ namespace sqlite_orm { table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + quote_identifier(column.name)); } else if (qualified) { @@ -46,7 +48,7 @@ namespace sqlite_orm { }); } else { collectedExpressions.reserve(collectedExpressions.size() + 1); - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.skip_table_name) { const basic_table& table = pick_table>(context.db_objects); @@ -98,8 +100,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -112,8 +113,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; diff --git a/dev/column_pointer.h b/dev/column_pointer.h index dcbe11340..393fed7d6 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -1,9 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move +#endif -#include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "table_reference.h" @@ -40,7 +41,9 @@ namespace sqlite_orm { struct alias_holder; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicitly refer to a column, used in contexts * where the automatic object mapping deduction needs to be overridden. @@ -110,7 +113,7 @@ namespace sqlite_orm { * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ template = true> - constexpr auto column(F field) { + constexpr auto column([[maybe_unused]] F field) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); @@ -123,7 +126,6 @@ namespace sqlite_orm { } else { return column_pointer{std::move(field)}; } - (void)field; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES diff --git a/dev/column_result.h b/dev/column_result.h index 9244d0a28..7848e6330 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl.h" @@ -129,8 +131,8 @@ namespace sqlite_orm { }; template - struct column_result_t { - using type = nullptr_t; + struct column_result_t { + using type = std::nullptr_t; }; template diff --git a/dev/conditions.h b/dev/conditions.h index 880336a56..da0b77f53 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const #include // std::vector @@ -7,9 +8,10 @@ #include // std::move, std::forward #include // std::stringstream #include // std::flush +#endif #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "type_traits.h" #include "collate_argument.h" #include "constraints.h" @@ -140,7 +142,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; @@ -368,10 +370,6 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif }; /** @@ -434,15 +432,8 @@ namespace sqlite_orm { }; struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; - - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified }; struct order_by_string { @@ -459,19 +450,19 @@ namespace sqlite_orm { using expression_type = O; using self = order_by_t; - expression_type expression; + expression_type _expression; - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + order_by_t(expression_type expression) : order_by_base(), _expression(std::move(expression)) {} self asc() const { auto res = *this; - res.asc_desc = 1; + res._order = 1; return res; } self desc() const { auto res = *this; - res.asc_desc = -1; + res._order = -1; return res; } @@ -522,8 +513,8 @@ namespace sqlite_orm { struct dynamic_order_by_entry_t : order_by_base { std::string name; - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + dynamic_order_by_entry_t(decltype(name) name_, std::string collate_argument_, int asc_desc_) : + order_by_base{std::move(collate_argument_), asc_desc_}, name(std::move(name_)) {} }; /** @@ -538,13 +529,11 @@ namespace sqlite_orm { dynamic_order_by_t(const context_t& context_) : context(context_) {} template - void push_back(order_by_t order_by) { + void push_back(order_by_t orderBy) { auto newContext = this->context; newContext.skip_table_name = false; - auto columnName = serialize(order_by.expression, newContext); - this->entries.emplace_back(std::move(columnName), - order_by.asc_desc, - std::move(order_by._collate_argument)); + auto columnName = serialize(orderBy._expression, newContext); + this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } const_iterator begin() const { @@ -822,7 +811,9 @@ namespace sqlite_orm { template using is_constrained_join = polyfill::is_detected; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicit FROM function. Usage: * `storage.select(&User::id, from());` diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 11e6dd0a7..a3ea30dd5 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -1,50 +1,94 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#include // std::function #include // std::string +#endif #include "error_code.h" +#include "vfs_name.h" +#include "db_open_mode.h" +#include "storage_options.h" namespace sqlite_orm { - namespace internal { struct connection_holder { + connection_holder(std::string filename, + std::function didOpenDb, + const connection_control& options = {}) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), + open_mode(options.open_mode) {} + + connection_holder(const connection_holder&) = delete; - connection_holder(std::string filename_) : filename(std::move(filename_)) {} + connection_holder(const connection_holder& other, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, vfs_name(other.vfs_name), + open_mode{other.open_mode} {} void retain() { - if (1 == ++this->_retain_count) { - auto rc = sqlite3_open(this->filename.c_str(), &this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // first one opens the connection. + // we presume that the connection is opened once in a single-threaded context [also open forever]. + // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. + if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { + int open_flags = internal::db_open_mode_to_int_flags(this->open_mode); +#if SQLITE_VERSION_NUMBER >= 3037002 + open_flags |= SQLITE_OPEN_EXRESCODE; +#endif + + const int rc = + sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, this->vfs_name.c_str()); + + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { + throw_translated_sqlite_error(rc); + } + + if (_didOpenDb) { + _didOpenDb(this->db); } } } void release() { - if (0 == --this->_retain_count) { - auto rc = sqlite3_close(this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // last one closes the connection. + // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. + if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close_v2(this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { + throw_translated_sqlite_error(this->db); + } else { + this->db = nullptr; } } } sqlite3* get() const { + // note: ensuring a valid DB handle was already memory ordered with `retain()` return this->db; } + /** + * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. + */ int retain_count() const { - return this->_retain_count; + return _retainCount.load(std::memory_order_relaxed); } - const std::string filename; - protected: sqlite3* db = nullptr; - std::atomic_int _retain_count{}; + + private: + std::atomic_int _retainCount{}; + + private: + const std::function _didOpenDb; + + public: + const std::string filename; + const std::string vfs_name; + const db_open_mode open_mode; }; struct connection_ref { diff --git a/dev/constraints.h b/dev/constraints.h index 7eb4a1cda..50c09794a 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream #include // std::string #include // std::tuple +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl.h" @@ -14,9 +16,10 @@ #include "type_traits.h" #include "collate_argument.h" #include "error_code.h" +#include "field_of.h" #include "table_type_of.h" #include "type_printer.h" -#include "column_pointer.h" +#include "table_reference.h" namespace sqlite_orm { @@ -50,9 +53,6 @@ namespace sqlite_orm { const primary_key_type& as_base() const { return *this; } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} -#endif }; /** @@ -62,59 +62,58 @@ namespace sqlite_orm { */ template struct primary_key_t : primary_key_base { - using self = primary_key_t; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple; columns_tuple columns; - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + constexpr primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} - self asc() const { + constexpr primary_key_t asc() const { auto res = *this; res.options.asc_option = order_by::ascending; return res; } - self desc() const { + constexpr primary_key_t desc() const { auto res = *this; res.options.asc_option = order_by::descending; return res; } - primary_key_with_autoincrement autoincrement() const { + constexpr primary_key_with_autoincrement autoincrement() const { return {*this}; } - self on_conflict_rollback() const { + constexpr primary_key_t on_conflict_rollback() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::rollback; return res; } - self on_conflict_abort() const { + constexpr primary_key_t on_conflict_abort() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::abort; return res; } - self on_conflict_fail() const { + constexpr primary_key_t on_conflict_fail() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::fail; return res; } - self on_conflict_ignore() const { + constexpr primary_key_t on_conflict_ignore() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::ignore; return res; } - self on_conflict_replace() const { + constexpr primary_key_t on_conflict_replace() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::replace; @@ -188,9 +187,8 @@ namespace sqlite_orm { * FOREIGN KEY constraint class. * Cs are columns which has foreign key * Rs are column which C references to - * Available in SQLite 3.6.19 or higher + * Available since SQLite 3.6.19 */ - template struct foreign_key_t; @@ -302,8 +300,8 @@ namespace sqlite_orm { return res; } - operator bool() const { - return this->_action != foreign_key_action::none; + explicit operator bool() const { + return _action != foreign_key_action::none; } }; @@ -316,7 +314,6 @@ namespace sqlite_orm { struct foreign_key_t, std::tuple> { using columns_type = std::tuple; using references_type = std::tuple; - using self = foreign_key_t; /** * Holds obect type of all referenced columns. @@ -331,28 +328,23 @@ namespace sqlite_orm { columns_type columns; references_type references; - on_update_delete_t on_update; - on_update_delete_t on_delete; + on_update_delete_t on_update; + on_update_delete_t on_delete; static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + static_assert(!std::is_same::value, "All columns must have the same mapped type"); + static_assert(!std::is_same::value, "All references must have the same mapped type"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self& other) : + foreign_key_t(const foreign_key_t& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self& operator=(const self& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + foreign_key_t& operator=(const foreign_key_t&) = delete; }; template @@ -362,33 +354,49 @@ namespace sqlite_orm { } /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + * Cs can be a class member pointer or column pointer + * Available since SQLite 3.6.19 */ template struct foreign_key_intermediate_t { using tuple_type = std::tuple; - tuple_type columns; + tuple_type _columns; + /** + * Specify one or more target fields, which can either be pointers to class members or column pointers. + */ template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; + foreign_key_t> references(Rs... refs) { + return {std::move(_columns), {std::forward(refs)...}}; + } + + /** + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + foreign_key_t> references(F Base::*... refs) { + static_assert(polyfill::conjunction...>::value, + "Referenced fields must be from explicitly specified derived class"); + return {std::move(_columns), {refs...}}; } - template - foreign_key_t, std::tuple...>> references(Rs... refs) { - return {std::move(this->columns), {sqlite_orm::column(refs)...}}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + auto references(F Base::*... refs) { + return this->references>(refs...); } +#endif }; #endif struct collate_constraint_t { collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif operator std::string() const { return "COLLATE " + this->string_from_collate_argument(this->argument); @@ -425,10 +433,6 @@ namespace sqlite_orm { bool full = true; storage_type storage = storage_type::not_specified; #endif - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif }; #if SQLITE_VERSION_NUMBER >= 3031000 @@ -513,7 +517,9 @@ namespace sqlite_orm { check_if_is_type>, T>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3031000 template internal::generated_always_t generated_always_as(T expression) { @@ -528,13 +534,37 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. + * Available since SQLite 3.6.19 */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { return {{std::forward(columns)...}}; } + + /** + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + * Available since SQLite 3.6.19 + */ + template + internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { + static_assert(polyfill::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + * Available since SQLite 3.6.19 + */ + template + auto foreign_key(F Base::*... columns) { + return foreign_key>(columns...); + } +#endif #endif /** @@ -604,17 +634,39 @@ namespace sqlite_orm { #endif /** - * PRIMARY KEY table constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. */ template - internal::primary_key_t primary_key(Cs... cs) { + constexpr internal::primary_key_t primary_key(Cs... cs) { return {{std::forward(cs)...}}; } /** - * PRIMARY KEY column constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + constexpr internal::primary_key_t primary_key(F Base::*... columns) { + static_assert(polyfill::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + constexpr auto primary_key(F Base::*... columns) { + return primary_key>(columns...); + } +#endif + + /** + * PRIMARY KEY column constraint builder function (used at a single column). */ - inline internal::primary_key_t<> primary_key() { + inline constexpr internal::primary_key_t<> primary_key() { return {{}}; } diff --git a/dev/core_functions.h b/dev/core_functions.h index eeefd42b5..9c01d65af 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -1,14 +1,16 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if #include // std::unique_ptr #include // std::vector +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl/conditional.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tuple_helper/tuple_traits.h" #include "conditions.h" #include "serialize_result_type.h" @@ -18,10 +20,6 @@ #include "ast/into.h" namespace sqlite_orm { - - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - namespace internal { template @@ -48,7 +46,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; @@ -622,17 +620,19 @@ namespace sqlite_orm { using argument1_type = Y; using argument2_type = Z; - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; - - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} + argument0_type argument0{}; + argument1_type argument1{}; + argument2_type argument2{}; }; } +} -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS +SQLITE_ORM_EXPORT namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS /** * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos * @@ -1697,7 +1697,7 @@ namespace sqlite_orm { /** * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) /** * NULLIF(X,Y) using common return type of X and Y */ diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index a122a395a..37a8e1ee8 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -1,11 +1,13 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include #include // std::reference_wrapper #include #endif +#endif #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" @@ -157,8 +159,9 @@ namespace sqlite_orm { std::vector columnNames = get_cte_column_names(sel.col, context); // 2. override column names from cte expression - if (size_t n = std::tuple_size_v) { - if (n != columnNames.size()) { + constexpr size_t nExplicitColumns = std::tuple_size_v; + if constexpr (nExplicitColumns > 0) { + if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } @@ -195,11 +198,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index 1f104a01b..6b6b1434d 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -9,6 +10,7 @@ #include // std::ignore #include #endif +#endif #include "functional/cstring_literal.h" #include "alias.h" @@ -63,7 +65,9 @@ namespace sqlite_orm { #endif }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline namespace literals { /** * cte_moniker<'n'> from a numeric literal. diff --git a/dev/cte_storage.h b/dev/cte_storage.h index 9709c3ac7..adbf02355 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -1,11 +1,13 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include +#include // std::remove_const #include #include #include #endif +#endif #include "tuple_helper/tuple_fy.h" #include "table_type_of.h" @@ -55,13 +57,16 @@ namespace sqlite_orm { typename SubselectColRefs, typename FinalColRefs, typename Result> - using create_cte_mapper_t = - typename create_cte_mapper:: - type; + using create_cte_mapper_t = typename create_cte_mapper, + ExplicitColRefs, + Expression, + SubselectColRefs, + FinalColRefs, + Result>::type; // aliased column expressions, explicit or implicitly numbered template = true> - static auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { + auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { using object_type = aliased_field, F>; return sqlite_orm::make_column<>(std::move(name), &object_type::field); @@ -69,8 +74,7 @@ namespace sqlite_orm { // F O::* template = true> - static auto make_cte_column(std::string name, const ColRef& finalColRef) { - using object_type = table_type_of_t; + auto make_cte_column(std::string name, const ColRef& finalColRef) { using column_type = column_t; return column_type{std::move(name), finalColRef, empty_setter{}}; diff --git a/dev/cte_types.h b/dev/cte_types.h index c7d5cbfc7..25efbe693 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -1,12 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include #include #endif +#endif -#include "functional/cxx_core_features.h" -#include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_fy.h" #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) diff --git a/dev/db_open_mode.h b/dev/db_open_mode.h new file mode 100644 index 000000000..cf3cef8e6 --- /dev/null +++ b/dev/db_open_mode.h @@ -0,0 +1,26 @@ +#pragma once + +SQLITE_ORM_EXPORT namespace sqlite_orm { + + enum class db_open_mode { + default_ = 0, + create_readwrite = 0, + readonly = 1, + }; +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + namespace internal { + constexpr int db_open_mode_to_int_flags(db_open_mode open) { + + switch (open) { + case db_open_mode::readonly: + return SQLITE_OPEN_READONLY; + case db_open_mode::create_readwrite: + return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; + }; + + return -1; + } + } +} diff --git a/dev/default_value_extractor.h b/dev/default_value_extractor.h index 927e1e53a..0b38587cb 100644 --- a/dev/default_value_extractor.h +++ b/dev/default_value_extractor.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif #include "constraints.h" #include "serializer_context.h" diff --git a/dev/eponymous_vtabs/dbstat.h b/dev/eponymous_vtabs/dbstat.h index 3c56b44ba..538ad60fe 100644 --- a/dev/eponymous_vtabs/dbstat.h +++ b/dev/eponymous_vtabs/dbstat.h @@ -1,14 +1,16 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string #endif +#endif #include "../schema/column.h" #include "../schema/table.h" #include "../column_pointer.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { std::string name; diff --git a/dev/error_code.h b/dev/error_code.h index a76fd9264..c53268fae 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -1,13 +1,15 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error #include // std::string #include #include // std::ostringstream #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @@ -39,6 +41,7 @@ namespace sqlite_orm { index_is_out_of_bounds, value_is_null, no_tables_specified, + empty_range, }; } @@ -50,7 +53,7 @@ namespace std { struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { class orm_error_category : public std::error_category { public: @@ -110,8 +113,8 @@ namespace sqlite_orm { return "SQLite error"; } - std::string message(int c) const override final { - return sqlite3_errstr(c); + std::string message(int ev) const override final { + return sqlite3_errstr(ev); } }; diff --git a/dev/expression.h b/dev/expression.h index 39a86aec3..edef774c5 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if #include // std::move, std::forward, std::declval +#endif #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" @@ -34,7 +36,7 @@ namespace sqlite_orm { return {this->value, std::move(r)}; } - assign_t operator=(nullptr_t) const { + assign_t operator=(std::nullptr_t) const { return {this->value, nullptr}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -90,7 +92,9 @@ namespace sqlite_orm { template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or * `storage.update(set(c(&User::name) = "Dua Lipa")); diff --git a/dev/expression_object_type.h b/dev/expression_object_type.h index 087602e3b..def363183 100644 --- a/dev/expression_object_type.h +++ b/dev/expression_object_type.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper +#endif #include "type_traits.h" #include "prepared_statement.h" diff --git a/dev/field_of.h b/dev/field_of.h new file mode 100644 index 000000000..c00869819 --- /dev/null +++ b/dev/field_of.h @@ -0,0 +1,57 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::enable_if, std::is_convertible, std::bool_constant +#endif + +#include "member_traits/member_traits.h" + +namespace sqlite_orm { + namespace internal { + template + struct column_pointer; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; + + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + + template + struct is_field_of : polyfill::bool_constant> {}; + + /* + * Compare unrelated fields, like from completely different class types or an empty setter. + */ + template + constexpr bool compare_fields(const L&, const R&) { + return false; + } + + /* + * Compare related fields, either from the same class types or also if the pointer-to-member is of a base class. + */ + template::value || is_field_of::value, bool> = true> + constexpr bool compare_fields(F O1::* lhs, F O2::* rhs) { + return lhs == rhs; + } + } +} diff --git a/dev/field_printer.h b/dev/field_printer.h index f9971c62d..4ba6e2763 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream #include // std::vector @@ -9,12 +10,13 @@ #include // std::codecvt_utf8_utf16 #endif #include "functional/cxx_optional.h" +#endif #include "functional/cxx_type_traits_polyfill.h" #include "is_std_ptr.h" #include "type_traits.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Is used to print members mapped to objects in storage_t::dump member function. @@ -22,7 +24,9 @@ namespace sqlite_orm { */ template struct field_printer; +} +namespace sqlite_orm { namespace internal { /* * Implementation note: the technique of indirect expression testing is because @@ -118,8 +122,8 @@ namespace sqlite_orm { }; #endif // SQLITE_ORM_OMITS_CODECVT template<> - struct field_printer { - std::string operator()(const nullptr_t&) const { + struct field_printer { + std::string operator()(const std::nullptr_t&) const { return "NULL"; } }; @@ -142,7 +146,7 @@ namespace sqlite_orm { if (t) { return field_printer()(*t); } else { - return field_printer{}(nullptr); + return field_printer{}(nullptr); } } }; diff --git a/dev/function.h b/dev/function.h index cffff9732..c92563b6b 100644 --- a/dev/function.h +++ b/dev/function.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include // std::copy_constructible @@ -7,6 +8,7 @@ #include // std::tuple, std::tuple_size, std::tuple_element #include // std::min, std::copy_n #include // std::move, std::forward +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/cstring_literal.h" @@ -60,7 +62,9 @@ namespace sqlite_orm { template struct function; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). */ @@ -127,7 +131,9 @@ namespace sqlite_orm { quotedF.callable(); }; #endif +} +namespace sqlite_orm { namespace internal { template struct callable_arguments_impl; @@ -239,10 +245,10 @@ namespace sqlite_orm { // Always allow binding nullptr to a pointer argument template - constexpr bool is_same_pvt_v> = true; + constexpr bool is_same_pvt_v> = true; // Always allow binding nullptr to a pointer argument template - constexpr bool is_same_pvt_v, pointer_binding, void> = true; + constexpr bool is_same_pvt_v, pointer_binding, void> = true; template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_data_type() { @@ -251,7 +257,7 @@ namespace sqlite_orm { return valid; } -#if __cplusplus >= 201703L // C++17 or later +#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { constexpr bool valid = Binding == PointerArg; @@ -300,7 +306,6 @@ namespace sqlite_orm { using func_param_type = std::tuple_element_t; using call_arg_type = unpacked_arg_t>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr bool valid = validate_pointer_value_type, unpacked_arg_t>>( @@ -308,14 +313,6 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); return validate_pointer_value_types(polyfill::index_constant{}) && valid; -#else - return validate_pointer_value_types(polyfill::index_constant{}) && - validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); -#endif } /* @@ -323,11 +320,7 @@ namespace sqlite_orm { * but other call argument types are not checked against the parameter types of the function. */ template -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED SQLITE_ORM_CONSTEVAL void check_function_call() { -#else - void check_function_call() { -#endif using call_args_tuple = std::tuple; using function_params_tuple = typename callable_arguments::args_tuple; constexpr size_t callArgsCount = std::tuple_size::value; @@ -432,7 +425,7 @@ namespace sqlite_orm { template struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; + constexpr quoted_function_builder(const char (&cstr)[N]) : cstring_literal{cstr} {} /* * From a freestanding function, possibly overloaded. @@ -488,7 +481,9 @@ namespace sqlite_orm { }; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Call a user-defined function. * * Note: Currently the number of call arguments is checked and whether the types of pointer values match, diff --git a/dev/functional/always_default.h b/dev/functional/always_default.h new file mode 100644 index 000000000..47f00ea61 --- /dev/null +++ b/dev/functional/always_default.h @@ -0,0 +1,24 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + /* + * Function object whose variadic call operator always returns the default constructed value of its template parameter type. + */ + template + struct always_default_of { + template + constexpr R operator()(Args&&...) const { + return R(); + } + + using is_transparent = int; + }; + + template + constexpr always_default_of always_default{}; + } + +} diff --git a/dev/functional/config.h b/dev/functional/config.h index 4b98290eb..3c75a91f1 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -2,14 +2,14 @@ #include "cxx_universal.h" -#if SQLITE_ORM_HAS_INCLUDE() -#include +#ifdef BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_EXPORT export +#else +#define SQLITE_ORM_EXPORT #endif -#ifdef SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 constexpr -#else -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 +#if SQLITE_ORM_HAS_INCLUDE() +#include #endif #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED @@ -18,12 +18,6 @@ #define SQLITE_ORM_INLINE_VAR #endif -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_IF constexpr -#else -#define SQLITE_ORM_CONSTEXPR_IF -#endif - #if __cpp_lib_constexpr_functional >= 201907L #define SQLITE_ORM_CONSTEXPR_CPP20 constexpr #else @@ -76,8 +70,15 @@ #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) #define SQLITE_ORM_WITH_CTE + +#if defined(_WIN32) +#define SQLITE_ORM_WIN +#elif defined(__APPLE__) +#define SQLITE_ORM_APPLE +#define SQLITE_ORM_UNIX +#elif defined(__unix__) || defined(__unix) || defined(__linux__) || defined(__FreeBSD__) +#define SQLITE_ORM_UNIX #endif // define the inline namespace "literals" so that it is available even if it was not introduced by a feature diff --git a/dev/functional/cstring_literal.h b/dev/functional/cstring_literal.h index 3521ca5cb..b14904213 100644 --- a/dev/functional/cstring_literal.h +++ b/dev/functional/cstring_literal.h @@ -1,15 +1,17 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence #include // std::copy_n #endif +#endif #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { - /* - * Wraps a C string of fixed size. - * Its main purpose is to enable the user-defined string literal operator template. + /* + * Wraps a C string of fixed size. + * Its main purpose is to enable the user-defined string literal operator template. */ template struct cstring_literal { diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h new file mode 100644 index 000000000..08b2c8e63 --- /dev/null +++ b/dev/functional/cxx_check_prerequisites.h @@ -0,0 +1,17 @@ +#pragma once + +/* + * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. + */ + +// note: Before the C++17 language standard was made the baseline, the library had workarounds for these specific missing C++14 language features, +// so they are kept here as explicit checks for reference. +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L +#error A fully C++17-compliant compiler is required. +#endif + +#if (__cpp_noexcept_function_type < 201510L) || \ + (__cpp_fold_expressions < 201603L || __cpp_constexpr < 201603L || __cpp_aggregate_bases < 201603L) || \ + (__cpp_if_constexpr < 201606L) +#error A fully C++17-compliant compiler is required. +#endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 86da3dcb3..1afdc17a6 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -1,7 +1,7 @@ #pragma once /* - * This header detects core C++ language features on which sqlite_orm depends. + * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h */ @@ -17,38 +17,10 @@ #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_nsdmi >= 201304L -#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED -#endif - -#if __cpp_constexpr >= 201304L -#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED -#endif - -#if __cpp_noexcept_function_type >= 201510L -#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED -#endif - -#if __cpp_aggregate_bases >= 201603L -#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED -#endif - -#if __cpp_fold_expressions >= 201603L -#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED -#endif - -#if __cpp_constexpr >= 201603L -#define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#endif - #if __cpp_range_based_for >= 201603L #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif -#if __cpp_if_constexpr >= 201606L -#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#endif - #if __cpp_inline_variables >= 201606L #define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #endif @@ -57,9 +29,12 @@ #define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED #endif +#if __cpp_deduction_guides >= 201703L +#define SQLITE_ORM_CTAD_SUPPORTED +#endif + #if __cpp_generic_lambdas >= 201707L #define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED -#else #endif #if __cpp_init_captures >= 201803L @@ -96,4 +71,5 @@ #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED #endif diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 3b9fdc8fa..ee4bf09fc 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -1,10 +1,14 @@ #pragma once +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include #if __cpp_lib_invoke < 201411L #include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer #endif #include // std::forward +#endif #include "cxx_type_traits_polyfill.h" #include "../member_traits/member_traits.h" @@ -74,6 +78,29 @@ namespace sqlite_orm { return std::forward(callable)(std::forward(args)...); } #endif + +#if __cpp_lib_is_invocable >= 201703L + using std::is_invocable; +#else + template + struct is_invocable_impl : std::false_type {}; + +#if __cplusplus >= 201703L + template + struct is_invocable_impl()...))>, Ts...> + : std::true_type {}; +#else + template + struct is_invocable_impl< + polyfill::void_t>>()( + std::declval()...))>, + Callable, + Args...> : std::true_type {}; +#endif + + template + struct is_invocable : is_invocable_impl::type {}; +#endif } } diff --git a/dev/functional/cxx_optional.h b/dev/functional/cxx_optional.h index 627a4fb58..7d64060e0 100644 --- a/dev/functional/cxx_optional.h +++ b/dev/functional/cxx_optional.h @@ -2,9 +2,13 @@ #include "cxx_core_features.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #if SQLITE_ORM_HAS_INCLUDE() #include #endif +#endif #if __cpp_lib_optional >= 201606L #define SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/dev/functional/cxx_string_view.h b/dev/functional/cxx_string_view.h index 9fedcce7c..b33f1290c 100644 --- a/dev/functional/cxx_string_view.h +++ b/dev/functional/cxx_string_view.h @@ -2,9 +2,13 @@ #include "cxx_core_features.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #if SQLITE_ORM_HAS_INCLUDE() #include #endif +#endif #if __cpp_lib_string_view >= 201606L #define SQLITE_ORM_STRING_VIEW_SUPPORTED diff --git a/dev/functional/cxx_tuple_polyfill.h b/dev/functional/cxx_tuple_polyfill.h index b795e8656..ebfcb6dcd 100644 --- a/dev/functional/cxx_tuple_polyfill.h +++ b/dev/functional/cxx_tuple_polyfill.h @@ -1,9 +1,13 @@ #pragma once +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include // std::apply; std::tuple_size #if __cpp_lib_apply < 201603L #include // std::forward, std::index_sequence, std::make_index_sequence #endif +#endif #include "../functional/cxx_functional_polyfill.h" // std::invoke diff --git a/dev/functional/cxx_type_traits_polyfill.h b/dev/functional/cxx_type_traits_polyfill.h index 31d99e444..2a45c458d 100644 --- a/dev/functional/cxx_type_traits_polyfill.h +++ b/dev/functional/cxx_type_traits_polyfill.h @@ -1,5 +1,10 @@ #pragma once + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include +#endif #include "mpl/conditional.h" diff --git a/dev/functional/cxx_universal.h b/dev/functional/cxx_universal.h index dd2b4dc20..85f1682cd 100644 --- a/dev/functional/cxx_universal.h +++ b/dev/functional/cxx_universal.h @@ -3,18 +3,23 @@ /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations - * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - ::size_t, ::ptrdiff_t, std::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks + * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations -#include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early - -// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, -// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). -// actually it should be available when including stddef.h +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // sqlite_orm is using ::size_t, ::ptrdiff_t, std::nullptr_t everywhere, pull it in early +// earlier libcxx versions didn't make std::nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/std::nullptr_t#Notes). using std::nullptr_t; +// Further note on the use of nullptr_t: +// msvc 14.40 has problems finding `::nullptr_t` within sqlite_orm when consuming sqlite_orm as a named module. +// Hence, sqlite_orm is internally using `std::nullptr_t` instead. +#endif +#include "cxx_check_prerequisites.h" #include "cxx_core_features.h" #include "cxx_compiler_quirks.h" diff --git a/dev/functional/function_traits.h b/dev/functional/function_traits.h index c814eaf25..4a44d3978 100644 --- a/dev/functional/function_traits.h +++ b/dev/functional/function_traits.h @@ -51,7 +51,6 @@ namespace sqlite_orm { using signature_type = R(Args...) const; }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct function_traits : function_traits { using signature_type = R(Args...) noexcept; @@ -61,7 +60,6 @@ namespace sqlite_orm { struct function_traits : function_traits { using signature_type = R(Args...) const noexcept; }; -#endif /* * Pick signature of function pointer diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index 3abb4a2e2..f15a50baa 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -1,20 +1,22 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence +#endif namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) - /** - * Get the index value of an `index_sequence` at a specific position. + /** + * Get the index value of an `index_sequence` at a specific position. */ template SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } -#elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - /** - * Get the index value of an `index_sequence` at a specific position. +#else + /** + * Get the index value of an `index_sequence` at a specific position. */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { @@ -29,16 +31,6 @@ namespace sqlite_orm { (void)((result = Idx, i++ == Pos) || ...); return result; } -#else - /** - * Get the index value of an `index_sequence` at a specific position. - * `Pos` must always be `0`. - */ - template - SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { - static_assert(Pos == 0, ""); - return I; - } #endif template diff --git a/dev/functional/is_base_template_of.h b/dev/functional/is_base_template_of.h new file mode 100644 index 000000000..14f350a74 --- /dev/null +++ b/dev/functional/is_base_template_of.h @@ -0,0 +1,40 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::true_type, std::false_type, std::declval +#endif + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_template_of_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); + }; + + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); +#else + template class Base, typename... Ts> + std::true_type is_base_template_of_impl(const Base&); + + template class Base> + std::false_type is_base_template_of_impl(...); + + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); +#endif + + template class Base, typename T> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; + } +} diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 4c02b1244..ba2bd3bcf 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -28,11 +28,9 @@ * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED #include -#else -#include #endif #include "cxx_type_traits_polyfill.h" @@ -48,7 +46,7 @@ namespace sqlite_orm { * Determines whether a class template has a nested metafunction `fn`. * * Implementation note: the technique of specialiazing on the inline variable must come first because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR]. */ template SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; @@ -334,7 +332,6 @@ namespace sqlite_orm { using bind_front_higherorder_fn = bind_front::quote_fn, quote_fn, Bound...>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr size_t find_first_true_helper(std::initializer_list values) { size_t i = 0; for (auto first = values.begin(); first != values.end() && !*first; ++first) { @@ -350,17 +347,6 @@ namespace sqlite_orm { } return n; } -#else - template - constexpr size_t find_first_true_helper(const std::array& values, size_t i = 0) { - return i == N || values[i] ? 0 : 1 + find_first_true_helper(values, i + 1); - } - - template - constexpr size_t count_true_helper(const std::array& values, size_t i = 0) { - return i == N ? 0 : values[i] + count_true_helper(values, i + 1); - } -#endif /* * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, @@ -377,11 +363,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = find_first_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + find_first_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -407,11 +390,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + count_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -437,12 +417,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = - static_cast(count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({TraitQ::template fn>::value...})); + static constexpr size_t value = static_cast( + count_true_helper({TraitQ::template fn>::value...})); using type = polyfill::bool_constant; }; @@ -477,7 +453,7 @@ namespace sqlite_orm { * Commonly used named abbreviation for `check_if`. */ template - using check_if_is_type = mpl::bind_front_fn; + using check_if_is_type = check_if; /* * Quoted trait metafunction that checks if a type's template matches the specified template @@ -487,6 +463,18 @@ namespace sqlite_orm { using check_if_is_template = mpl::pass_extracted_fn_to>>; + /* + * Quoted trait metafunction that checks if a type names a nested type determined by `Op`. + */ + template class Op> + using check_if_names = mpl::bind_front_higherorder_fn; + + /* + * Quoted trait metafunction that checks if a type does not name a nested type determined by `Op`. + */ + template class Op> + using check_if_lacks = mpl::not_>; + /* * Quoted metafunction that finds the index of the given type in a tuple. */ diff --git a/dev/functional/static_magic.h b/dev/functional/static_magic.h deleted file mode 100644 index a033067a0..000000000 --- a/dev/functional/static_magic.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant -#endif -#include // std::forward - -namespace sqlite_orm { - - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { - - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function - template - struct empty_callable_t { - template - R operator()(Args&&...) const { - return R(); - } - }; - template - constexpr empty_callable_t empty_callable{}; - -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return std::forward(falseFn); - } - } - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } - } - - template - void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { - if constexpr (B) { - lambda(std::forward(args)...); - } - } -#else - template - decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { - return std::forward(trueFn); - } - - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } - - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); - } - - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); - } - - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } -#endif - } - -} diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 73f0adf37..89a90305f 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -1,17 +1,18 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get +#endif #include "functional/cxx_type_traits_polyfill.h" -#include "functional/static_magic.h" #include "type_traits.h" #include "prepared_statement.h" #include "ast_iterator.h" #include "node_tuple.h" #include "expression_object_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template auto& get(internal::prepared_statement_t>& statement) { @@ -133,16 +134,13 @@ namespace sqlite_orm { const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = &n; - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = &node; + } + } } }); return internal::get_ref(*result); @@ -159,16 +157,13 @@ namespace sqlite_orm { internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = const_cast>(&n); - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = const_cast(&node); + } + } } }); return internal::get_ref(*result); diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index 370deec55..4ce4792bf 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -4,9 +4,10 @@ */ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique +#endif -#include "../functional/static_magic.h" #include "../tuple_helper/tuple_traits.h" #include "../default_value_extractor.h" #include "../schema/column.h" @@ -19,12 +20,10 @@ namespace sqlite_orm { static constexpr size_t default_op_index = find_tuple_template::value; std::unique_ptr value; - call_if_constexpr::value>( - [&value](auto& constraints) { - value = - std::make_unique(serialize_default_value(std::get(constraints))); - }, - this->constraints); + if constexpr (default_op_index != std::tuple_size::value) { + value = std::make_unique( + serialize_default_value(std::get(this->constraints))); + } return value; } diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index cdb820b59..2f76658ea 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -4,11 +4,13 @@ */ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream #include // std::flush #include // std::reference_wrapper, std::cref #include // std::find_if, std::ranges::find +#endif #include "../type_traits.h" #include "../sqlite_schema_table.h" @@ -20,18 +22,25 @@ namespace sqlite_orm { namespace internal { - template template> - sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { - if (std::is_same, sqlite_master>::value) { - return sync_schema_result::already_in_sync; - } + sync_schema_result storage_t::sync_table([[maybe_unused]] const Table& table, + [[maybe_unused]] sqlite3* db, + [[maybe_unused]] bool preserve) { + if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB - if (std::is_same, dbstat>::value) { + std::is_same, dbstat>::value || +#endif + std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; + } else { + return this->sync_regular_table(table, db, preserve); } -#endif // SQLITE_ENABLE_DBSTAT_VTAB + } + + template + template> + sync_schema_result storage_t::sync_regular_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; @@ -126,7 +135,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), @@ -140,12 +149,16 @@ namespace sqlite_orm { } }); - std::stringstream ss; - ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" - << streaming_identifiers(columnNames) << ") " - << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " + << streaming_identifier(sourceTableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } } } diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index c474df26c..ae8dd0a59 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -4,9 +4,11 @@ */ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move #include // std::find_if, std::ranges::find +#endif #include "../tuple_helper/tuple_filter.h" #include "../type_traits.h" @@ -43,19 +45,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } diff --git a/dev/indexed_column.h b/dev/indexed_column.h index daa36d488..f7d471219 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#endif #include "ast/where.h" @@ -13,14 +15,9 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} -#endif - - column_type column_or_expression; + column_type _column_or_expression; std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified indexed_column_t collate(std::string name) { auto res = std::move(*this); @@ -56,7 +53,9 @@ namespace sqlite_orm { return std::move(col); } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) diff --git a/dev/is_base_of_template.h b/dev/is_base_of_template.h deleted file mode 100644 index 788bb3f0b..000000000 --- a/dev/is_base_of_template.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include // std::true_type, std::false_type, std::declval - -namespace sqlite_orm { - - namespace internal { - - /* - * This is because of bug in MSVC, for more information, please visit - * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 - */ -#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION - template class Base> - struct is_base_of_template_impl { - template - static constexpr std::true_type test(const Base&); - - static constexpr std::false_type test(...); - }; - - template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); -#else - template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); - - template class C> - std::false_type is_base_of_template_impl(...); - - template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); -#endif - - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; - } -} diff --git a/dev/is_std_ptr.h b/dev/is_std_ptr.h index 3570df780..998cbdef8 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -1,9 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). diff --git a/dev/journal_mode.h b/dev/journal_mode.h index 1c8c21424..fe799b8d1 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif #include "serialize_result_type.h" @@ -14,7 +16,7 @@ #undef DELETE #endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Caps case because of: @@ -31,7 +33,9 @@ namespace sqlite_orm { WAL = 4, OFF = 5, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& journal_mode_to_string(journal_mode value) { @@ -59,7 +63,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/limit_accessor.h b/dev/limit_accessor.h index e87adbf82..e52f84488 100644 --- a/dev/limit_accessor.h +++ b/dev/limit_accessor.h @@ -1,9 +1,11 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map #include // std::function #include // std::shared_ptr +#endif #include "connection_holder.h" diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 55ba6ac1f..00c575e4b 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -1,19 +1,23 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif #include "serialize_result_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& locking_mode_to_string(locking_mode value) { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED @@ -33,7 +37,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/mapped_iterator.h b/dev/mapped_iterator.h index e35c0d6d1..9f527eea2 100644 --- a/dev/mapped_iterator.h +++ b/dev/mapped_iterator.h @@ -1,11 +1,13 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared #include // std::move #include // std::input_iterator_tag #include // std::system_error #include // std::bind +#endif #include "statement_finalizer.h" #include "error_code.h" diff --git a/dev/mapped_type_proxy.h b/dev/mapped_type_proxy.h index b18eea0e6..a043daf65 100644 --- a/dev/mapped_type_proxy.h +++ b/dev/mapped_type_proxy.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const +#endif #include "type_traits.h" #include "table_reference.h" diff --git a/dev/mapped_view.h b/dev/mapped_view.h index 45f90c1cb..db76673eb 100644 --- a/dev/mapped_view.h +++ b/dev/mapped_view.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move +#endif #include "row_extractor.h" #include "mapped_iterator.h" @@ -49,12 +51,14 @@ namespace sqlite_orm { mapped_iterator begin() { using context_t = serializer_context; + auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); return {dbObjects, std::move(stmt)}; } diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index eba948877..a2b90baef 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type +#endif #include "../functional/cxx_type_traits_polyfill.h" @@ -32,13 +34,11 @@ namespace sqlite_orm { template struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; -#endif // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) template @@ -53,10 +53,8 @@ namespace sqlite_orm { template struct setter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct setter_field_type : polyfill::remove_cvref {}; -#endif template struct is_getter : std::false_type {}; @@ -88,5 +86,21 @@ namespace sqlite_orm { template using member_object_type_t = typename member_object_type::type; + + /* + * Casts the class type of a pointer-to-member from a base class to the specified derived class. + */ + template + constexpr F O::* as_field_of(F Base::* f) { + return f; + } + + /* + * Metafunction that casts the class type of a pointer-to-member from a base class to the specified derived class. + * note (implementation): go through `member_field_type_t<>` instead of `decltype(as_field_of())` because of + * older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + */ + template + using as_field_of_t = member_field_type_t O::*; } } diff --git a/dev/node_tuple.h b/dev/node_tuple.h index f86ccd330..1d5a9c518 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple #include // std::pair #include // std::reference_wrapper #include "functional/cxx_optional.h" +#endif #include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_filter.h" @@ -103,6 +105,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; + template struct node_tuple, void> : node_tuple {}; diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h index 86aae7f40..6b191cf9e 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -1,10 +1,11 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer #include // std::move +#endif -#include "functional/static_magic.h" #include "member_traits/member_traits.h" #include "table_reference.h" #include "row_extractor.h" @@ -18,11 +19,6 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; int columnIndex = -1; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_from_column_builder_base(sqlite3_stmt* stmt, int columnIndex = -1) : - stmt{stmt}, columnIndex{columnIndex} {} -#endif }; /** @@ -41,13 +37,11 @@ namespace sqlite_orm { void operator()(const column_field& column) { const auto rowExtractor = row_value_extractor>(); auto value = rowExtractor.extract(this->stmt, ++this->columnIndex); - static_if::value>( - [&value, &object = this->object](const auto& column) { - object.*column.member_pointer = std::move(value); - }, - [&value, &object = this->object](const auto& column) { - (object.*column.setter)(std::move(value)); - })(column); + if constexpr (std::is_member_object_pointer::value) { + object.*column.member_pointer = std::move(value); + } else { + (object.*column.setter)(std::move(value)); + }; } }; diff --git a/dev/operators.h b/dev/operators.h index d656a0ec9..eea071679 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move +#endif #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tags.h" #include "serialize_result_type.h" @@ -24,7 +26,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; @@ -209,7 +211,9 @@ namespace sqlite_orm { template struct is_assign_t> : public std::true_type {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index bc2d418ac..a2fb6f1e4 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -1,7 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include #include // std::string #include // std::stringstream +#include // std::exchange +#endif namespace sqlite_orm { @@ -16,6 +20,20 @@ namespace sqlite_orm { return serializer(t, context); } + inline void seralize_collate(std::ostream& ss, const order_by_base& orderBy) { + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch (orderBy._order) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + } + template struct order_by_serializer, void> { using statement_type = order_by_t; @@ -26,18 +44,8 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; - ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; - } - switch (orderBy.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } + ss << serialize(orderBy._expression, newContext); + seralize_collate(ss, orderBy); return ss.str(); } }; @@ -50,29 +58,21 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - int index = 0; + static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; const dynamic_order_by_entry_t& entry: orderBy) { + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#else + bool first = true; for (const dynamic_order_by_entry_t& entry: orderBy) { - if (index > 0) { - ss << ", "; - } - - ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; - } - switch (entry.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - ++index; - }; + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#endif return ss.str(); } }; - } } diff --git a/dev/pointer_value.h b/dev/pointer_value.h index dd4a9b26f..f44017030 100644 --- a/dev/pointer_value.h +++ b/dev/pointer_value.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include #include @@ -8,21 +9,26 @@ #include #endif #endif +#endif #include "functional/cstring_literal.h" #include "xdestroy_handling.h" #if SQLITE_VERSION_NUMBER >= 3020000 namespace sqlite_orm { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES template struct pointer_type { using value_type = const char[sizeof...(C) + 1]; static inline constexpr value_type value = {C..., '\0'}; }; +#endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline namespace literals { template [[nodiscard]] consteval auto operator"" _pointer_type() { @@ -184,7 +190,7 @@ namespace sqlite_orm { #endif } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Wrap a pointer, its type and its deleter function for binding it to a statement. * diff --git a/dev/pragma.h b/dev/pragma.h index b0f35f8ab..947537eeb 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -1,13 +1,14 @@ #pragma once #include -#include // atoi +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::function #include // std::shared_ptr #include // std::vector #include #include // std::flush +#endif #include "error_code.h" #include "row_extractor.h" @@ -21,6 +22,7 @@ namespace sqlite_orm { namespace internal { struct storage_base; + struct sqlite_executor; template int getPragmaCallback(void* data, int argc, char** argv, char** x) { @@ -33,8 +35,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } @@ -42,7 +43,8 @@ namespace sqlite_orm { struct pragma_t { using get_connection_t = std::function; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + pragma_t(get_connection_t get_connection_, const sqlite_executor& executor) : + get_connection(std::move(get_connection_)), executor(executor) {} std::vector module_list() { return this->get_pragma>("module_list"); @@ -156,13 +158,17 @@ namespace sqlite_orm { auto connection = this->get_connection(); std::vector result; - std::ostringstream ss; - ss << "PRAGMA " - "table_xinfo(" - << streaming_identifier(tableName) << ")" << std::flush; - perform_exec( + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -192,14 +198,18 @@ namespace sqlite_orm { std::vector table_info(const std::string& tableName) const { auto connection = this->get_connection(); - std::ostringstream ss; - ss << "PRAGMA " - "table_info(" - << streaming_identifier(tableName) << ")" << std::flush; + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } std::vector result; - perform_exec( + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -225,12 +235,14 @@ namespace sqlite_orm { int synchronous_ = -1; signed char journal_mode_ = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; + const sqlite_executor& executor; template T get_pragma(const std::string& name) { auto connection = this->get_connection(); T result; - perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + const std::string sql = "PRAGMA " + name; + this->executor.perform_exec(connection.get(), sql, getPragmaCallback, &result); return result; } @@ -257,12 +269,13 @@ namespace sqlite_orm { this->set_pragma_impl(ss.str(), db); } - void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if (db == nullptr) { - db = con.get(); + void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { + if (db) { + this->executor.perform_void_exec(db, sql.data()); + } else { + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } - perform_void_exec(db, query); } }; } diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index e3dfdebd4..a828f3ea5 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -1,12 +1,14 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::iterator_traits #include // std::string #include // std::integral_constant, std::declval #include // std::move, std::forward, std::pair #include // std::tuple +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" @@ -27,10 +29,6 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} -#endif - ~prepared_statement_base() { sqlite3_finalize(this->stmt); } @@ -313,16 +311,14 @@ namespace sqlite_orm { struct insert_constraint { conflict_action action = conflict_action::abort; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - insert_constraint(conflict_action action) : action{action} {} -#endif }; template using is_insert_constraint = std::is_same; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::insert_constraint or_rollback() { return {internal::conflict_action::rollback}; } diff --git a/dev/result_set_iterator.h b/dev/result_set_iterator.h index 32a5f7742..ef4e35deb 100644 --- a/dev/result_set_iterator.h +++ b/dev/result_set_iterator.h @@ -1,9 +1,11 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper +#endif #include "statement_finalizer.h" #include "row_extractor.h" diff --git a/dev/result_set_view.h b/dev/result_set_view.h index 776e021c8..652d13f48 100644 --- a/dev/result_set_view.h +++ b/dev/result_set_view.h @@ -1,12 +1,14 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref #include // std::reference_wrapper #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) && \ defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) #include // std::ranges::view_interface #endif +#endif #include "functional/cxx_type_traits_polyfill.h" #include "row_extractor.h" @@ -49,11 +51,13 @@ namespace sqlite_orm::internal { using select_type = polyfill::detected_or_t; using column_result_type = column_result_of_t; using context_t = serializer_context; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression, conditional_binder{stmt.get()}); // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 757344e1c..297ce30a8 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -1,9 +1,10 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll -#include // strlen +#include // ::atof, ::atoi, ::atoll +#include // ::strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -17,9 +18,9 @@ #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif +#endif #include "functional/cxx_functional_polyfill.h" -#include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" #include "column_result_proxy.h" #include "arithmetic_tag.h" @@ -30,7 +31,7 @@ #include "is_std_ptr.h" #include "type_traits.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. @@ -81,7 +82,9 @@ namespace sqlite_orm { { extractor.extract(value) } -> std::same_as; }; #endif +} +namespace sqlite_orm { namespace internal { /* * Make a row extractor to be used for casting SQL column text to a C++ typed value. @@ -116,7 +119,9 @@ namespace sqlite_orm { return {}; } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template int extract_single_value(void* data, int argc, char** argv, char**) { auto& res = *(R*)data; @@ -126,7 +131,9 @@ namespace sqlite_orm { } return 0; } +} +namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3020000 /** * Specialization for the 'pointer-passing interface'. @@ -370,16 +377,16 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<> - struct row_extractor { - nullptr_t extract(const char* /*columnText*/) const { + struct row_extractor { + std::nullptr_t extract(const char* /*columnText*/) const { return nullptr; } - nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + std::nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { return nullptr; } - nullptr_t extract(sqlite3_value*) const { + std::nullptr_t extract(sqlite3_value*) const { return nullptr; } }; @@ -465,7 +472,6 @@ namespace sqlite_orm { template struct struct_extractor; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED /* * Returns a value-based row extractor for an unmapped type, * returns a structure extractor for a table reference, tuple or named struct. @@ -479,34 +485,6 @@ namespace sqlite_orm { return row_value_extractor(); } } -#else - /* - * Overload for an unmapped type returns a common row extractor. - */ - template< - class R, - class DBOs, - std::enable_if_t, - polyfill::is_specialization_of, - is_table_reference>>::value, - bool> = true> - auto make_row_extractor(const DBOs& /*dbObjects*/) { - return row_value_extractor(); - } - - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. - */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif /** * Specialization for a tuple of top-level column results. diff --git a/dev/rowid.h b/dev/rowid.h index a99fe6d51..d77bed8c9 100644 --- a/dev/rowid.h +++ b/dev/rowid.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif namespace sqlite_orm { @@ -39,7 +41,9 @@ namespace sqlite_orm { }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rowid_t rowid() { return {}; } diff --git a/dev/schema/column.h b/dev/schema/column.h index c3967dd83..ed567bee0 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -1,10 +1,12 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string #include // std::unique_ptr #include // std::is_same, std::is_member_object_pointer #include // std::move +#endif #include "../functional/cxx_type_traits_polyfill.h" #include "../tuple_helper/tuple_traits.h" @@ -95,13 +97,7 @@ namespace sqlite_orm { * It is a composition of orthogonal information stored in different base classes. */ template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; + struct column_t : column_identifier, column_field, column_constraints {}; template struct column_field_expression { @@ -141,7 +137,9 @@ namespace sqlite_orm { constraints_type_t, filter_tuple_sequence_t>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Factory function for a column definition from a member object pointer of the object to be mapped. */ diff --git a/dev/schema/index.h b/dev/schema/index.h index 63c6846ea..b6bc92710 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string #include // std::forward +#endif #include "../tuple_helper/tuple_traits.h" #include "../indexed_column.h" @@ -15,10 +17,6 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif }; template @@ -27,15 +25,12 @@ namespace sqlite_orm { using object_type = void; using table_mapped_type = T; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - index_t(std::string name_, bool unique_, elements_type elements_) : - index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} -#endif - elements_type elements; }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::index_t()))...> make_index(std::string name, Cols... cols) { diff --git a/dev/schema/table.h b/dev/schema/table.h index 5e4ca9bf7..25edddcad 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -1,14 +1,15 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type #include // std::vector #include // std::tuple_element #include // std::forward, std::move +#endif #include "../functional/cxx_type_traits_polyfill.h" #include "../functional/cxx_functional_polyfill.h" -#include "../functional/static_magic.h" #include "../functional/mpl.h" #include "../functional/index_sequence_util.h" #include "../tuple_helper/tuple_filter.h" @@ -16,7 +17,7 @@ #include "../tuple_helper/tuple_iteration.h" #include "../tuple_helper/tuple_transformer.h" #include "../member_traits/member_traits.h" -#include "../typed_comparator.h" +#include "../field_of.h" #include "../type_traits.h" #include "../alias_traits.h" #include "../constraints.h" @@ -86,11 +87,6 @@ namespace sqlite_orm { elements_type elements; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif - table_t without_rowid() const { return {this->name, this->elements}; } @@ -141,7 +137,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_any(column.setter, memberPointer)) { + if (compare_fields(column.setter, memberPointer)) { res = &polyfill::invoke(column.member_pointer, object); } })); @@ -149,7 +145,7 @@ namespace sqlite_orm { } const basic_generated_always::storage_type* - find_column_generated_storage_type(const std::string& name) const { + find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { const basic_generated_always::storage_type* result = nullptr; #if SQLITE_VERSION_NUMBER >= 3031000 iterate_tuple(this->elements, @@ -164,8 +160,6 @@ namespace sqlite_orm { constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); result = &std::get(column.constraints).storage; }); -#else - (void)name; #endif return result; } @@ -190,7 +184,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { using pkcol_index_sequence = col_index_sequence_with; - if (pkcol_index_sequence::size() > 0) { + if constexpr (pkcol_index_sequence::size() > 0) { return create_from_tuple>(this->elements, pkcol_index_sequence{}, &column_identifier::name); @@ -229,14 +223,16 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template = true> - const std::string* find_column_name(M m) const { - const std::string* res = nullptr; + const std::string* find_column_name(M memberPointer) const { using field_type = member_field_type_t; + + const std::string* res = nullptr; iterate_tuple(this->elements, col_index_sequence_with_field_type{}, - [&res, m](auto& c) { - if (compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { - res = &c.name; + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; } }); return res; @@ -310,11 +306,6 @@ namespace sqlite_orm { module_details_type module_details; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - virtual_table_t(std::string name, module_details_type module_details) : - basic_table{std::move(name)}, module_details{std::move(module_details)} {} -#endif - /** * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. @@ -341,6 +332,11 @@ namespace sqlite_orm { void for_each_column(L&& lambda) const { this->module_details.for_each_column(lambda); } + + template = true> + const std::string* find_column_name(MP memberPointer) const { + return this->module_details.find_column_name(memberPointer); + } }; template @@ -386,6 +382,22 @@ namespace sqlite_orm { using col_index_sequence = filter_tuple_sequence_t; iterate_tuple(this->columns, col_index_sequence{}, lambda); } + + template = true> + const std::string* find_column_name(M memberPointer) const { + using field_type = member_field_type_t; + + const std::string* res = nullptr; + iterate_tuple(this->columns, + col_index_sequence_with_field_type{}, + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } }; #endif @@ -400,8 +412,8 @@ namespace sqlite_orm { check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_any(memberPointer, column.member_pointer) || - compare_any(memberPointer, column.setter)) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { res = true; } }); @@ -415,7 +427,9 @@ namespace sqlite_orm { return false; } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3009000 template>::object_type> internal::using_fts5_t using_fts5(Cs... columns) { diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index 5ec53e38b..2e6ecebd9 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -1,9 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #include #include +#endif #include "../optional_container.h" @@ -70,11 +72,6 @@ namespace sqlite_orm { * Statements of the triggers (to be executed when the trigger fires) */ elements_type elements; - -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - trigger_t(std::string name, T trigger_base, elements_type statements) : - base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} -#endif }; /** @@ -203,10 +200,6 @@ namespace sqlite_orm { type_t type = type_t::ignore; std::string message; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} -#endif }; template @@ -222,8 +215,10 @@ namespace sqlite_orm { expression_type expression; }; - } // NAMESPACE internal + } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NEW.expression function used within TRIGGER expressions */ diff --git a/dev/select_constraints.h b/dev/select_constraints.h index d023745c5..2a77bf774 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif @@ -7,10 +8,11 @@ #include // std::string #include // std::move #include // std::tuple, std::get, std::tuple_size +#endif #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_transformer.h" #include "tuple_helper/tuple_iteration.h" @@ -88,10 +90,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -112,10 +110,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -135,11 +129,6 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif }; template @@ -165,7 +154,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; @@ -173,10 +162,6 @@ namespace sqlite_orm { struct union_base { bool all = false; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif - operator std::string() const { if (!this->all) { return "UNION"; @@ -285,30 +270,30 @@ namespace sqlite_orm { template struct cte_builder { - ExplicitCols explicitColumns; + ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #endif }; @@ -339,10 +324,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -350,10 +331,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -432,7 +409,9 @@ namespace sqlite_orm { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template internal::as_optional_t as_optional(T value) { @@ -603,8 +582,8 @@ namespace sqlite_orm { static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - using builder_type = - cte_builder, decay_explicit_column_t>>; + using builder_type = cte_builder, + transform_tuple_t, decay_explicit_column_t>>; return builder_type{{std::move(explicitColumns)...}}; } #endif diff --git a/dev/serialize_result_type.h b/dev/serialize_result_type.h index ac5f606b8..937992cb3 100644 --- a/dev/serialize_result_type.h +++ b/dev/serialize_result_type.h @@ -1,18 +1,22 @@ #pragma once #include "functional/cxx_string_view.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // std::string #endif +#endif namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED using serialize_result_type = std::string_view; using serialize_arg_type = std::string_view; + using string_constant_type = std::string_view; #else using serialize_result_type = std::string; using serialize_arg_type = const std::string&; + using string_constant_type = const char*; #endif } } diff --git a/dev/serializing_util.h b/dev/serializing_util.h index 4c538316a..f1fa3be42 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -1,11 +1,13 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include #include #include #include #include // std::exchange, std::tuple_size, std::make_index_sequence +#endif #include "functional/cxx_type_traits_polyfill.h" // std::remove_cvref, polyfill::is_detected #include "functional/cxx_functional_polyfill.h" @@ -237,9 +239,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } @@ -417,7 +425,7 @@ namespace sqlite_orm { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; - if SQLITE_ORM_CONSTEXPR_IF (!hasExplicitNullableConstraint) { + if constexpr (!hasExplicitNullableConstraint) { if (isNotNull) { ss << " NOT NULL"; } else { diff --git a/dev/sqlite_schema_table.h b/dev/sqlite_schema_table.h index 9dfda257c..1dd48dab5 100644 --- a/dev/sqlite_schema_table.h +++ b/dev/sqlite_schema_table.h @@ -1,13 +1,15 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif #include "schema/column.h" #include "schema/table.h" #include "column_pointer.h" #include "alias.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. * diff --git a/dev/statement_binder.h b/dev/statement_binder.h index a96f60f22..338c4b6eb 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -1,6 +1,7 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence #include // std::default_delete #include // std::string, std::wstring @@ -14,6 +15,7 @@ #include // std::wstring_convert #include // std::codecvt_utf8_utf16 #endif +#endif #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" @@ -25,14 +27,16 @@ #include "xdestroy_handling.h" #include "pointer_value.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. */ template struct statement_binder; +} +namespace sqlite_orm { namespace internal { /* * Implementation note: the technique of indirect expression testing is because @@ -200,15 +204,15 @@ namespace sqlite_orm { #endif /** - * Specialization for nullptr_t. + * Specialization for std::nullptr_t. */ template<> - struct statement_binder { - int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { return sqlite3_bind_null(stmt, index); } - void result(sqlite3_context* context, const nullptr_t&) const { + void result(sqlite3_context* context, const std::nullptr_t&) const { sqlite3_result_null(context); } }; @@ -240,7 +244,7 @@ namespace sqlite_orm { if (value) { return statement_binder().bind(stmt, index, *value); } else { - return statement_binder().bind(stmt, index, nullptr); + return statement_binder().bind(stmt, index, nullptr); } } }; @@ -296,7 +300,7 @@ namespace sqlite_orm { void operator()(const T& t) { int rc = statement_binder{}.bind(this->stmt, this->index++, t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -333,24 +337,16 @@ namespace sqlite_orm { } private: -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } -#else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; - } -#endif template void bind(const T& t, size_t idx) const { int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } diff --git a/dev/statement_finalizer.h b/dev/statement_finalizer.h index d94950080..1de4575ff 100644 --- a/dev/statement_finalizer.h +++ b/dev/statement_finalizer.h @@ -1,10 +1,12 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::integral_constant +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 7fd20144b..ada6b1bb7 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1,5 +1,6 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream #include // std::string @@ -11,11 +12,13 @@ #include #include #include // std::list +#endif #include "functional/cxx_string_view.h" #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" // std::remove_cvref, std::disjunction #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke +#include "functional/always_default.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" #include "ast/upsert_clause.h" @@ -138,7 +141,7 @@ namespace sqlite_orm { template std::string do_serialize(const pointer_binding&) const { // always serialize null (security reasons) - return field_printer{}(nullptr); + return field_printer{}(nullptr); } #endif }; @@ -157,7 +160,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } return ss.str(); @@ -325,18 +328,18 @@ namespace sqlite_orm { ss << "ON CONFLICT"; iterate_tuple(statement.target_args, [&ss, &context](auto& value) { using value_type = polyfill::remove_cvref_t; - auto needParenthesis = std::is_member_pointer::value; + constexpr bool needParenthesis = std::is_member_pointer::value; ss << ' '; - if (needParenthesis) { + if constexpr (needParenthesis) { ss << '('; } ss << serialize(value, context); - if (needParenthesis) { + if constexpr (needParenthesis) { ss << ')'; } }); ss << ' ' << "DO"; - if (std::tuple_size::value == 0) { + if constexpr (std::tuple_size::value == 0) { ss << " NOTHING"; } else { auto updateContext = context; @@ -729,11 +732,11 @@ namespace sqlite_orm { std::stringstream ss; ss << expression.serialize(); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.argument), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -755,11 +758,11 @@ namespace sqlite_orm { std::stringstream ss; ss << static_cast(expression) << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.c), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -784,19 +787,19 @@ namespace sqlite_orm { is_binary_operator>::value; std::stringstream ss; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << "("; } ss << serialize(statement.lhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << ")"; } ss << " " << statement.serialize() << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << "("; } ss << serialize(statement.rhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << ")"; } return ss.str(); @@ -847,13 +850,13 @@ namespace sqlite_orm { ss << "NOT IN"; } ss << " "; - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << '('; } auto newContext = context; newContext.use_parentheses = true; ss << serialize(statement.argument, newContext); - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << ')'; } return ss.str(); @@ -900,7 +903,7 @@ namespace sqlite_orm { using args_type = std::tuple; constexpr bool theOnlySelect = std::tuple_size::value == 1 && is_select>::value; - if (!theOnlySelect) { + if constexpr (!theOnlySelect) { ss << "("; } ss << streaming_expressions_tuple(statement.argument, context); @@ -1046,8 +1049,8 @@ namespace sqlite_orm { ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); } using columns_tuple = typename statement_type::columns_tuple; - const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + constexpr size_t columnsCount = std::tuple_size::value; + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -1064,7 +1067,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); @@ -1269,7 +1272,7 @@ namespace sqlite_orm { << streaming_non_generated_column_names(table) << ")" << " VALUES (" << streaming_field_values_excluding(check_if{}, - empty_callable, // don't exclude + always_default, // don't exclude context, get_ref(statement.object)) << ")"; @@ -1516,7 +1519,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if (is_insert_raw::value) { + if constexpr (is_insert_raw::value) { ss << "INSERT"; } else { ss << "REPLACE"; @@ -1524,12 +1527,12 @@ namespace sqlite_orm { iterate_tuple(statement.args, [&context, &ss](auto& value) { using value_type = polyfill::remove_cvref_t; ss << ' '; - if (is_columns::value) { + if constexpr (is_columns::value) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if (is_values::value || is_select::value) { + } else if constexpr (is_values::value || is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -1689,12 +1692,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } @@ -1778,23 +1788,22 @@ namespace sqlite_orm { subCtx.use_parentheses = true; std::stringstream ss; - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << "("; } ss << "SELECT "; - call_if_constexpr>( + if constexpr (is_rowset_deduplicator_v) { // note: make use of implicit to-string conversion - [&ss](std::string keyword) { - ss << keyword << ' '; - }, - sel.col); + std::string keyword = sel.col; + ss << keyword << ' '; + } } ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; - if (!hasExplicitFrom) { + if constexpr (!hasExplicitFrom) { auto tableNames = collect_table_names(sel, context); using joins_index_sequence = filter_tuple_sequence_t; // deduplicate table names of constrained join statements @@ -1811,7 +1820,7 @@ namespace sqlite_orm { } } ss << streaming_conditions_tuple(sel.conditions, context); - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -1827,20 +1836,18 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << serialize(statement.column_or_expression, context); + ss << serialize(statement._column_or_expression, context); if (!statement._collation_name.empty()) { ss << " COLLATE " << statement._collation_name; } if (statement._order) { switch (statement._order) { - case -1: - ss << " DESC"; - break; case 1: ss << " ASC"; break; - default: - throw std::system_error{orm_error_code::incorrect_order}; + case -1: + ss << " DESC"; + break; } } return ss.str(); @@ -1896,7 +1903,7 @@ namespace sqlite_orm { std::string whereString; iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { using value_type = polyfill::remove_cvref_t; - if (!is_where::value) { + if constexpr (!is_where::value) { auto newContext = context; newContext.use_parentheses = false; auto whereString = serialize(value, newContext); @@ -2084,7 +2091,7 @@ namespace sqlite_orm { ss << " BEGIN "; iterate_tuple(statement.elements, [&ss, &context](auto& element) { using element_type = polyfill::remove_cvref_t; - if (is_select::value) { + if constexpr (is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(element, newContext); @@ -2236,8 +2243,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; - if (HO) { - if (OI) { + if constexpr (HO) { + if constexpr (OI) { limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); diff --git a/dev/storage.h b/dev/storage.h index 144283a97..b82c1f4ce 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1,6 +1,7 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr/shared_ptr, std::make_unique #include // std::system_error #include // std::string @@ -13,11 +14,11 @@ #include // std::tuple_size, std::tuple, std::make_tuple, std::tie #include // std::forward, std::pair #include // std::for_each, std::ranges::for_each +#endif #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" -#include "functional/static_magic.h" #include "functional/mpl.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_filter.h" @@ -79,21 +80,91 @@ namespace sqlite_orm { polyfill::void_t().prepare(std::declval()))>>> = true; + template + decltype(auto) storage_opt_or_default(OptionsTpl& options) { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + if constexpr (tuple_has_type::value) { + return std::move(std::get(options)); + } else { + return Opt{}; + } +#else + return Opt{}; +#endif + } + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + /* + * AST iteration callable that matches function call node expressions. + * * Throws a `orm_error_code::function_not_found` exception + * if an application-defined scalar or aggregate function was not registered. + * * Throws a `sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)` if a named collation function was not registered. + */ + struct udf_presence_checker { + const std::list& _scalarFunctions; + const std::list& _aggregateFunctions; + const std::map& _collatingFunctions; + + // examine `function_call` node expressions + template + void operator()(polyfill::bool_constant, const function_call& udfCall) const { + auto&& name = udfCall.name(); + SQLITE_ORM_CPP_UNLIKELY { + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + } + } + + // examine `named_collate` node expressions + void operator()(polyfill::bool_constant, const named_collate_base& collateCall) const { + if (_collatingFunctions.find(collateCall.name) == _collatingFunctions.end()) SQLITE_ORM_CPP_UNLIKELY { +#if SQLITE_VERSION_NUMBER >= 3008008 + throw std::system_error{sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ), std::string(collateCall.name)}; +#else + throw std::system_error{sqlite_errc(SQLITE_ERROR), std::string(collateCall.name)}; +#endif + } + } + + // swallow leaf expressions + template + void operator()(const T&) const {} + + static bool _contains(const std::list& functions, const std::string_view& name) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + auto it = std::ranges::find(functions, name, &udf_proxy::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { + return udfProxy.name == name; + }); +#endif + return it != functions.end(); + } + }; +#endif + /** * 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 self_type = storage_t; using db_objects_type = db_objects_tuple; /** * @param filename database filename. * @param dbObjects db_objects_tuple */ - storage_t(std::string filename, db_objects_type dbObjects) : - storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + template + storage_t(std::string filename, db_objects_type dbObjects, OptionsTpl options) : + storage_base{std::move(filename), + storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), + foreign_keys_count()}, + db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; @@ -112,7 +183,7 @@ namespace sqlite_orm { * * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. */ - friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + friend const db_objects_type& obtain_db_objects(const self_type& storage) noexcept { return storage.db_objects; } @@ -122,8 +193,8 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - std::string sql = serializer.serialize(table, context, tableName); - perform_void_exec(db, std::move(sql)); + const std::string sql = serializer.serialize(table, context, tableName); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -139,10 +210,14 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " - << streaming_identifier(columnName) << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } #endif @@ -189,9 +264,8 @@ namespace sqlite_orm { template void assert_updatable_type() const { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - using Table = storage_pick_table_t; - using elements_type = elements_type_t; + using table_type = storage_pick_table_t; + using elements_type = elements_type_t; using col_index_sequence = filter_tuple_sequence_t; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; @@ -205,7 +279,6 @@ namespace sqlite_orm { static_assert( nonPrimaryKeysColumnsCount > 0, "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); -#endif } template, class... Args> - mapped_view iterate(Args&&... args) { + mapped_view iterate(Args&&... args) { this->assert_mapped_type(); - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + auto connection = this->get_connection(); + return {*this, std::move(connection), std::forward(args)...}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -275,8 +348,8 @@ namespace sqlite_orm { requires (is_select_v) #endif result_set_view, db_objects_type> iterate(with_t expression) { - auto con = this->get_connection(); - return {this->db_objects, std::move(con), std::move(expression)}; + auto connection = this->get_connection(); + return {this->db_objects, std::move(connection), std::move(expression)}; } #endif #endif @@ -779,17 +852,15 @@ namespace sqlite_orm { std::enable_if_t::value && !is_mapped::value, bool> = true> std::string dump(E&& expression, bool parametrized = false) const { - static_assert(is_preparable_v, "Expression must be a high-level statement"); + static_assert(is_preparable_v, "Expression must be a high-level statement"); - decltype(auto) e2 = static_if::value>( - [](auto expression) -> auto { - expression.highest_level = true; - return expression; - }, - [](const auto& expression) -> decltype(auto) { - return (expression); - })(std::forward(expression)); - return this->dump_highest_level(e2, parametrized); + if constexpr (is_select::value) { + auto e2 = std::forward(expression); + e2.highest_level = true; + return this->dump_highest_level(e2, parametrized); + } else { + return this->dump_highest_level(expression, parametrized); + } } /** @@ -1110,46 +1181,56 @@ namespace sqlite_orm { template sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(virtualTable, context); - perform_void_exec(db, query); + const auto sql = serialize(virtualTable, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(index, context); - perform_void_exec(db, query); + const auto sql = serialize(index, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; // TODO Change accordingly using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; - auto query = serialize(trigger, context); - perform_void_exec(db, query); + const auto sql = serialize(trigger, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template = true> sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); + template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { using context_t = serializer_context; context_t context{this->db_objects}; - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " + << serialize(column, context) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } template @@ -1159,8 +1240,9 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); + std::vector res; - perform_steps( + this->executor.perform_steps( stmt, [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { res.push_back(rowExtractor.extract(stmt, 0)); @@ -1172,7 +1254,9 @@ namespace sqlite_orm { template std::string dump_highest_level(E&& expression, bool parametrized) const { const auto& exprDBOs = db_objects_for_expression(this->db_objects, expression); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() @@ -1182,16 +1266,24 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + iterate_ast( + statement, + udf_presence_checker{this->scalarFunctions, this->aggregateFunctions, this->collatingFunctions}); +#endif + const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto con = this->get_connection(); - std::string sql = serialize(statement, context); - sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); - return prepared_statement_t{std::forward(statement), stmt, con}; + auto conection = this->get_connection(); + const std::string sql = serialize(statement, context); + sqlite3_stmt* stmt = prepare_stmt(conection.get(), sql); + return prepared_statement_t{std::forward(statement), stmt, std::move(conection)}; } public: @@ -1359,6 +1451,9 @@ namespace sqlite_orm { using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -1366,6 +1461,9 @@ namespace sqlite_orm { prepared_statement_t prepare(E statement) { using object_type = expression_object_type_t; this->assert_mapped_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -1380,7 +1478,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1393,7 +1501,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #endif @@ -1401,9 +1519,23 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template int64 execute(const prepared_statement_t>& statement) { using object_type = statement_object_type_t; @@ -1415,7 +1547,17 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1434,31 +1576,42 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + if constexpr (is_replace_range::value) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - + } else { + const object_type& object = get_object(statement.expression); + processObject(object); + }; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template, is_insert_range>::value, bool> = true> int64 execute(const prepared_statement_t& statement) { @@ -1469,6 +1622,7 @@ namespace sqlite_orm { auto processObject = [&table = this->get_table(), bindValue = field_value_binder{stmt}](auto& object) mutable { using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + table.template for_each_column_excluding< mpl::conjunction>, mpl::disjunction_fn>>( @@ -1479,29 +1633,36 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + if constexpr (is_insert_range::value) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - + } else { + const object_type& object = get_object(statement.expression); + processObject(object); + } + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1509,7 +1670,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -1532,7 +1703,17 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -1542,11 +1723,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return res; } @@ -1558,10 +1749,20 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -1574,17 +1775,37 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } + const auto stepRes = sqlite3_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } switch (stepRes) { case SQLITE_ROW: { T res; @@ -1596,7 +1817,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::not_found}; } break; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } #endif @@ -1606,7 +1827,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -1615,7 +1846,17 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1642,17 +1883,17 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { O obj; object_from_column_builder builder{obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -1663,17 +1904,17 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_unique(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -1685,30 +1926,65 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_optional(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template + using dbo_index_sequence = filter_tuple_sequence_t::template fn>; + + template + using opt_index_sequence = filter_tuple_sequence_t::template fn>; + + template + storage_t make_storage(std::string filename, std::tuple dbObjects, OptionsTpl options) { + return {std::move(filename), std::move(dbObjects), std::move(options)}; + } +#endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_CTAD_SUPPORTED /* - * Factory function for a storage, from a database file and a bunch of database object definitions. + * Factory function for a storage instance, from a database file, a set of database object definitions + * and option storage options like connection control options and an 'on open' callback. + * + * E.g. + * auto storage = make_storage("", connection_control{.open_forever = true}, on_open([](sqlite3* db) {})); + */ + template + auto make_storage(std::string filename, Spec... specifications) { + using namespace ::sqlite_orm::internal; + + std::tuple specTuple{std::forward(specifications)...}; + return internal::make_storage( + std::move(filename), + create_from_tuple(std::move(specTuple), dbo_index_sequence{}), + create_from_tuple(std::move(specTuple), opt_index_sequence{})); + } +#else + /* + * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { - return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; } +#endif /** * sqlite3_threadsafe() interface. diff --git a/dev/storage_base.h b/dev/storage_base.h index 49c60124e..6e9109a53 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -1,6 +1,7 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front @@ -13,8 +14,9 @@ #include // std::list #include // std::make_unique, std::unique_ptr #include // std::map -#include // std::is_same +#include // std::is_same, std::is_aggregate #include // std::find_if, std::ranges::find +#endif #include "functional/cxx_tuple_polyfill.h" // std::apply #include "tuple_helper/tuple_iteration.h" @@ -32,9 +34,9 @@ #include "udf_proxy.h" #include "serializing_util.h" #include "table_info.h" +#include "storage_options.h" namespace sqlite_orm { - namespace internal { struct storage_base { @@ -113,7 +115,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_vacuum.html */ void vacuum() { - perform_void_exec(this->get_connection().get(), "VACUUM"); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), "VACUUM"); } /** @@ -122,7 +125,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, false); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, false); } /** @@ -131,22 +135,28 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table_if_exists(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, true); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, true); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { - this->rename_table(this->get_connection().get(), from, to); + auto connection = this->get_connection(); + this->rename_table(connection.get(), from, to); } protected: void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " + << streaming_identifier(newName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } /** @@ -155,18 +165,22 @@ namespace sqlite_orm { * @return true if table with a given name exists in db, false otherwise. */ bool table_exists(const std::string& tableName) { - auto con = this->get_connection(); - return this->table_exists(con.get(), tableName); + auto connection = this->get_connection(); + return this->table_exists(connection.get(), tableName); } bool table_exists(sqlite3* db, const std::string& tableName) const { bool result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") - << " AND name = " << quote_string_literal(tableName) << std::flush; - perform_exec( + std::string sql; + { + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( db, - ss.str(), + sql, [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if (argc) { @@ -193,26 +207,26 @@ namespace sqlite_orm { * sqlite3_changes function. */ int changes() { - auto con = this->get_connection(); - return sqlite3_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_changes(connection.get()); } /** * sqlite3_total_changes function. */ int total_changes() { - auto con = this->get_connection(); - return sqlite3_total_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_total_changes(connection.get()); } int64 last_insert_rowid() { - auto con = this->get_connection(); - return sqlite3_last_insert_rowid(con.get()); + auto connection = this->get_connection(); + return sqlite3_last_insert_rowid(connection.get()); } int busy_timeout(int ms) { - auto con = this->get_connection(); - return sqlite3_busy_timeout(con.get(), ms); + auto connection = this->get_connection(); + return sqlite3_busy_timeout(connection.get(), ms); } /** @@ -222,24 +236,27 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function& f) { + bool transaction(const std::function& function) { + if (!function) { + return false; + } auto guard = this->transaction_guard(); - return guard.commit_on_destroy = f(); + return guard.commit_on_destroy = function(); } std::string current_time() { - auto con = this->get_connection(); - return this->current_time(con.get()); + auto connection = this->get_connection(); + return this->current_time(connection.get()); } std::string current_date() { - auto con = this->get_connection(); - return this->current_date(con.get()); + auto connection = this->get_connection(); + return this->current_date(connection.get()); } std::string current_timestamp() { - auto con = this->get_connection(); - return this->current_timestamp(con.get()); + auto connection = this->get_connection(); + return this->current_timestamp(connection.get()); } #if SQLITE_VERSION_NUMBER >= 3007010 @@ -250,8 +267,8 @@ namespace sqlite_orm { * in 3.7.10 https://sqlite.org/changes.html */ int db_release_memory() { - auto con = this->get_connection(); - return sqlite3_db_release_memory(con.get()); + auto connection = this->get_connection(); + return sqlite3_db_release_memory(connection.get()); } #endif @@ -261,11 +278,12 @@ namespace sqlite_orm { * @return Returns list of tables in database. */ std::vector table_names() { - auto con = this->get_connection(); - std::vector tableNames; using data_t = std::vector; - perform_exec( - con.get(), + + auto connection = this->get_connection(); + data_t tableNames; + this->executor.perform_exec( + connection.get(), "SELECT name FROM sqlite_master WHERE type='table'", [](void* data, int argc, char** argv, char** /*columnName*/) -> int { auto& tableNames_ = *(data_t*)data; @@ -287,20 +305,19 @@ namespace sqlite_orm { * needed and closes when it is not needed. This function breaks this rule. In memory storage always * keeps connection opened so calling this for in-memory storage changes nothing. * Note about multithreading: in multithreading context avoiding using this function for not in-memory - * storage may lead to data races. If you have data races in such a configuration try to call `open_forever` + * storage may lead to data races. If you have data races in such a configuration try to call `open_forever()` * before accessing your storage - it may fix data races. */ void open_forever() { - this->isOpenedForever = true; - this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); + if (!this->isOpenedForever) { + this->isOpenedForever = true; + this->connection->retain(); } } /** * Create an application-defined scalar SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -343,7 +360,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -358,15 +375,16 @@ namespace sqlite_orm { /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * If `quotedF` contains a freestanding function, stateless lambda or stateless function object, * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; * otherwise, it repeatedly uses a copy of the contained function object, assuming possible side effects. */ - template + template + requires (orm_quoted_scalar_function) void create_scalar_function() { - using Sig = auto_udf_type_t; + using Sig = auto_udf_type_t<(quotedF)>; using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; constexpr auto argsCount = std::is_same>::value @@ -399,7 +417,7 @@ namespace sqlite_orm { /** * Create an application-defined aggregate SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -448,7 +466,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined aggregate function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -463,7 +481,7 @@ namespace sqlite_orm { /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -475,7 +493,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -484,9 +502,10 @@ namespace sqlite_orm { /** * Delete a quoted scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ - template + template + requires (orm_quoted_scalar_function) void delete_scalar_function() { this->delete_function_impl(quotedF.name(), this->scalarFunctions); } @@ -494,7 +513,7 @@ namespace sqlite_orm { /** * Delete aggregate function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_aggregate_function() { @@ -537,7 +556,7 @@ namespace sqlite_orm { function, functionExists ? collate_callback : nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -571,7 +590,7 @@ namespace sqlite_orm { void commit() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "COMMIT"); + this->executor.perform_void_exec(db, "COMMIT"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -580,7 +599,7 @@ namespace sqlite_orm { void rollback() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "ROLLBACK"); + this->executor.perform_void_exec(db, "ROLLBACK"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -608,7 +627,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -618,7 +637,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } @@ -639,13 +658,35 @@ namespace sqlite_orm { return this->connection->retain_count() > 0; } + /** + * Return the name of the VFS object used by the database connection. + */ + const std::string& vfs_name() const { + return this->connection->vfs_name; + } + + /** + * Return the current open_mode for this storage object. + */ + db_open_mode open_mode() const { + return this->connection->open_mode; + } + + /** + * Return true if this database object is opened in a readonly state. + */ + bool db_readonly() { + auto con = this->get_connection(); + return static_cast(sqlite3_db_readonly(con.get(), "main")); + } + /* * returning false when there is a transaction in place * otherwise true; function is not const because it has to call get_connection() */ bool get_autocommit() { - auto con = this->get_connection(); - return sqlite3_get_autocommit(con.get()); + auto connection = this->get_connection(); + return sqlite3_get_autocommit(connection.get()); } int busy_handler(std::function handler) { @@ -662,26 +703,43 @@ namespace sqlite_orm { } protected: - storage_base(std::string filename, int foreignKeysCount) : - pragma(std::bind(&storage_base::get_connection, this)), + storage_base(std::string filename, + connection_control connectionCtrl, + on_open_spec onOpenSpec, + will_run_query_spec willRunQuerySpec, + did_run_query_spec didRunQuerySpec, + int foreignKeysCount) : + on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), - inMemory(filename.empty() || filename == ":memory:"), - connection(std::make_unique(std::move(filename))), - cachedForeignKeysCount(foreignKeysCount) { + inMemory(filename.empty() || filename == ":memory:"), isOpenedForever{connectionCtrl.open_forever}, + connection(std::make_unique( + std::move(filename), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), + connectionCtrl)), + cachedForeignKeysCount(foreignKeysCount), + executor{std::move(willRunQuerySpec.willRunQuery), std::move(didRunQuerySpec.didRunQuery)} { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } storage_base(const storage_base& other) : - on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), - connection(std::make_unique(other.connection->filename)), - cachedForeignKeysCount(other.cachedForeignKeysCount) { + isOpenedForever{other.isOpenedForever}, + connection(std::make_unique( + *other.connection, + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), + cachedForeignKeysCount(other.cachedForeignKeysCount), + executor{std::move(other.executor.will_run_query), std::move(other.executor.did_run_query)} { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } @@ -694,46 +752,44 @@ namespace sqlite_orm { } } - void begin_transaction_internal(const std::string& query) { + void begin_transaction_internal(const std::string& sql) { this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } sqlite3* db = this->connection->get(); - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql.data()); } connection_ref get_connection() { connection_ref res{*this->connection}; - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } return res; } #if SQLITE_VERSION_NUMBER >= 3006019 void foreign_keys(sqlite3* db, bool value) { - std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } bool foreign_keys(sqlite3* db) { bool result = false; - perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + this->executor.perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } - #endif - void on_open_internal(sqlite3* db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if (this->cachedForeignKeysCount) { this->foreign_keys(db, true); } #endif + if (this->pragma.synchronous_ != -1) { - this->pragma.synchronous(this->pragma.synchronous_); + this->pragma.set_pragma("synchronous", this->pragma.synchronous_, db); } if (this->pragma.journal_mode_ != -1) { @@ -743,7 +799,7 @@ namespace sqlite_orm { for (auto& p: this->collatingFunctions) { int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -777,7 +833,7 @@ namespace sqlite_orm { : int(std::tuple_size::value); using is_stateless = std::is_empty; auto udfMemorySpace = preallocate_udf_memory(); - if SQLITE_ORM_CONSTEXPR_IF (is_stateless::value) { + if constexpr (is_stateless::value) { constructAt(udfMemorySpace.first); } this->scalarFunctions.emplace_back( @@ -850,10 +906,10 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else - auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { return udfProxy.name == name; }); #endif @@ -870,7 +926,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } it = functions.erase(it); @@ -880,29 +936,29 @@ namespace sqlite_orm { } static void try_to_create_scalar_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - udfProxy.func, - nullptr, - nullptr, - nullptr); + const int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + udfProxy.func, + nullptr, + nullptr, + nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } static void try_to_create_aggregate_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - udfProxy.func, - aggregate_function_final_callback); + const int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + udfProxy.func, + aggregate_function_final_callback); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -910,50 +966,64 @@ namespace sqlite_orm { std::string current_time(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); return result; } std::string current_date(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); return result; } std::string current_timestamp(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); return result; } void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { - std::stringstream ss; - ss << "DROP TABLE"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TABLE"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << streaming_identifier(tableName) << std::flush; + sql = ss.str(); } - ss << ' ' << streaming_identifier(tableName) << std::flush; - perform_void_exec(db, ss.str()); + this->executor.perform_void_exec(db, sql.data()); } void drop_index_internal(const std::string& indexName, bool ifExists) { - std::stringstream ss; - ss << "DROP INDEX"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP INDEX"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(indexName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { - std::stringstream ss; - ss << "DROP TRIGGER"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TRIGGER"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(triggerName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } static int @@ -985,7 +1055,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { @@ -993,8 +1063,8 @@ namespace sqlite_orm { }); #endif if (dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = + table_xinfo& dbColumnInfo = *dbColumnInfoIt; + bool columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && @@ -1005,8 +1075,7 @@ namespace sqlite_orm { break; } dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); + storageTableInfo.erase(storageTableInfo.begin() + storageColumnInfoIndex); --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); @@ -1023,6 +1092,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; + const sqlite_executor executor; }; } } diff --git a/dev/storage_impl.h b/dev/storage_impl.h index bc40b6389..27e51c72e 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -1,8 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif -#include "functional/static_magic.h" #include "functional/index_sequence_util.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_filter.h" @@ -22,21 +23,22 @@ namespace sqlite_orm { using tables_index_sequence = filter_tuple_sequence_t; template = true> - int foreign_keys_count(const DBOs& dbObjects) { + constexpr int foreign_keys_count() { int res = 0; - iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.template count_of(); + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_type::template count_of(); }); return res; } template> decltype(auto) lookup_table_name(const DBOs& dbObjects) { - return static_if::value>( - [](const auto& dbObjects) -> const std::string& { - return pick_table(dbObjects).name; - }, - empty_callable)(dbObjects); + if constexpr (is_mapped::value) { + return (pick_table(dbObjects).name); + } else { + return std::string{}; + } } /** @@ -68,17 +70,15 @@ namespace sqlite_orm { constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; + using cte_fields_type = typename cte_mapper_type_t::fields_type; // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; + return &aliased_field>::field; } #endif @@ -102,14 +102,13 @@ namespace sqlite_orm { constexpr decltype(auto) find_column_name(const DBOs& dboObjects, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; using column_index_sequence = filter_tuple_sequence_t, is_column>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `table_t<>::find_column_name()` mechanism; diff --git a/dev/storage_lookup.h b/dev/storage_lookup.h index a4f097529..2388db0c2 100644 --- a/dev/storage_lookup.h +++ b/dev/storage_lookup.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void #include #include // std::index_sequence, std::make_index_sequence +#endif #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" diff --git a/dev/storage_options.h b/dev/storage_options.h new file mode 100644 index 000000000..8b6081909 --- /dev/null +++ b/dev/storage_options.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif + +#include "serialize_result_type.h" + +namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; + + struct on_open_spec { + using storage_opt_tag = int; + + std::function onOpen; + }; + struct will_run_query_spec { + using storage_opt_tag = int; + + std::function willRunQuery; + }; + struct did_run_query_spec { + using storage_opt_tag = int; + + std::function didRunQuery; + }; + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + std::string vfs_name{default_vfs_name}; + db_open_mode open_mode = db_open_mode::default_; + + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif + inline internal::will_run_query_spec + will_run_query(std::function willRunQuery) { + return {std::move(willRunQuery)}; + } + + inline internal::did_run_query_spec did_run_query(std::function didRunQuery) { + return {std::move(didRunQuery)}; + } +} diff --git a/dev/storage_traits.h b/dev/storage_traits.h index 4fc1f33ed..7794d59f5 100644 --- a/dev/storage_traits.h +++ b/dev/storage_traits.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple +#endif #include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_filter.h" diff --git a/dev/sync_schema_result.h b/dev/sync_schema_result.h index 5de9a6db9..0f0e59c8f 100644 --- a/dev/sync_schema_result.h +++ b/dev/sync_schema_result.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class sync_schema_result { diff --git a/dev/table_info.h b/dev/table_info.h index ac0118412..21a7a95bd 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -1,8 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#include // std::move +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { struct table_info { int cid = 0; @@ -12,7 +15,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -33,7 +36,7 @@ namespace sqlite_orm { int pk = 0; int hidden = 0; // different than 0 => generated_always_as() - TODO verify -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_xinfo(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h index c5d7990fa..567d54141 100644 --- a/dev/table_name_collector.h +++ b/dev/table_name_collector.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string #include // std::pair, std::move +#endif #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" @@ -28,8 +30,6 @@ namespace sqlite_orm { const db_objects_type& db_objects; - table_name_collector() = default; - table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} template @@ -88,7 +88,7 @@ namespace sqlite_orm { } template - void operator()(const highlight_t&) { + void operator()(polyfill::bool_constant, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; diff --git a/dev/table_reference.h b/dev/table_reference.h index c464f15b0..ac792d7e2 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -1,13 +1,15 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity +#endif #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { - /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. + /* + * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. */ template struct table_reference : polyfill::type_identity {}; @@ -33,12 +35,14 @@ namespace sqlite_orm { template struct is_table_reference : polyfill::bool_constant> {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. - * - * A concrete table reference has the following traits: - * - specialization of `table_reference`, whose `type` typename references a mapped object. + /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. + * + * A concrete table reference has the following traits: + * - specialization of `table_reference`, whose `type` typename references a mapped object. */ template concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; diff --git a/dev/table_type_of.h b/dev/table_type_of.h index 52604d788..a3123dcf7 100644 --- a/dev/table_type_of.h +++ b/dev/table_type_of.h @@ -1,9 +1,6 @@ #pragma once -#include // std::enable_if, std::is_convertible - namespace sqlite_orm { - namespace internal { template @@ -41,26 +38,5 @@ namespace sqlite_orm { template using table_type_of_t = typename table_type_of::type; - - /* - * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. - * - * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. - * E.g. - * regular: `alias_column>(column(&Base::field))` - * short: `alias_column>(&Base::field)` - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - - /* - * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_field_of_v::value>> = true; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; } } diff --git a/dev/transaction_guard.h b/dev/transaction_guard.h index 2065e4fb8..380886478 100644 --- a/dev/transaction_guard.h +++ b/dev/transaction_guard.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move +#endif #include "connection_holder.h" diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index c0bd482b1..b0c651142 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -1,6 +1,11 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::same_as +#endif +#endif namespace sqlite_orm { namespace internal { @@ -13,6 +18,15 @@ namespace sqlite_orm { using type = void; }; + template + using same_or_void_t = typename same_or_void::type; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template... Rest> + struct same_or_void { + using type = A; + }; +#else template struct same_or_void { using type = A; @@ -23,11 +37,9 @@ namespace sqlite_orm { using type = A; }; - template - using same_or_void_t = typename same_or_void::type; - template struct same_or_void : same_or_void {}; +#endif template struct common_type_of; diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index d28485ebb..116c6dfb0 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element +#endif #include "../functional/mpl/conditional.h" #include "../functional/index_sequence_util.h" diff --git a/dev/tuple_helper/tuple_fy.h b/dev/tuple_helper/tuple_fy.h index 3e2ec3b7d..ceaee0124 100644 --- a/dev/tuple_helper/tuple_fy.h +++ b/dev/tuple_helper/tuple_fy.h @@ -1,6 +1,8 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#endif namespace sqlite_orm { diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index 8859abe08..4697658ed 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -1,38 +1,29 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence #include // std::forward, std::move +#endif namespace sqlite_orm { namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED + [[maybe_unused]] int sink; +#else + [[maybe_unused]] int sink = 0; +#endif // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { (lambda(std::get(tpl)), ...); } } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF (reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - } - } -#endif template constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, @@ -40,28 +31,18 @@ namespace sqlite_orm { std::forward(lambda)); } -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif + template - void iterate_tuple(L&& lambda) { + constexpr void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } template class Base, class L> struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif template decltype(auto) operator()(const Base& object) { return L::operator()(object); diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index b96ba0f7b..994688597 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -1,7 +1,9 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get +#endif #include "../functional/cxx_type_traits_polyfill.h" #include "../functional/cxx_functional_polyfill.h" @@ -26,8 +28,6 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; - // note: applying a combiner like `plus_fold_integrals` needs fold expressions -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. * @@ -93,7 +93,6 @@ namespace sqlite_orm { IdxSeq{}, project_nested_tuple_size{}, std::integral_constant{})); -#endif template constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { @@ -101,7 +100,7 @@ namespace sqlite_orm { } /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. + * Like `std::make_from_tuple()`, but using a projection on the tuple elements. */ template constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { @@ -110,5 +109,23 @@ namespace sqlite_orm { std::make_index_sequence>::value>{}, std::forward(project)); } + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template class R, class Tpl, size_t... Idx, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + /* + * Similar to `create_from_tuple()`, but the result type is specified as a template class. + */ + template class R, class Tpl, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } +#endif } } diff --git a/dev/type_is_nullable.h b/dev/type_is_nullable.h index 9f161c110..1847f697f 100644 --- a/dev/type_is_nullable.h +++ b/dev/type_is_nullable.h @@ -1,12 +1,14 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr +#endif #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types diff --git a/dev/type_printer.h b/dev/type_printer.h index 79df36b03..fe3c38779 100644 --- a/dev/type_printer.h +++ b/dev/type_printer.h @@ -1,15 +1,17 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector +#endif #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "is_std_ptr.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) diff --git a/dev/type_traits.h b/dev/type_traits.h index b4baa5d6a..f4e9a3103 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -1,11 +1,13 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L #include // std::reference_wrapper #else #include // std::reference_wrapper #endif +#endif #include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" @@ -112,8 +114,8 @@ namespace sqlite_orm { template using udf_type_t = typename T::udf_type; - template - using auto_udf_type_t = typename decltype(a)::udf_type; + template + using auto_udf_type_t = typename std::remove_reference_t::udf_type; #endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -136,7 +138,9 @@ namespace sqlite_orm { concept stateless = std::is_empty_v; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template concept orm_names_type = requires { typename T::type; }; diff --git a/dev/typed_comparator.h b/dev/typed_comparator.h deleted file mode 100644 index d911c1b7e..000000000 --- a/dev/typed_comparator.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -namespace sqlite_orm { - - namespace internal { - - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; - } - } -} diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index bd14a5023..47ed39916 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -1,28 +1,30 @@ #pragma once #include -#include // assert macro +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr #include // std::string #include // std::function #include // std::move, std::pair +#endif #include "error_code.h" namespace sqlite_orm { namespace internal { - /* - * Returns properly allocated memory space for the specified application-defined function object - * paired with an accompanying deallocation function. + /* + * Returns properly allocated memory space for the specified application-defined function object + * paired with an accompanying deallocation function. */ template std::pair preallocate_udf_memory() { std::allocator allocator; using traits = std::allocator_traits; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -31,18 +33,18 @@ namespace sqlite_orm { return {traits::allocate(allocator, 1), deallocate}; } - /* - * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. + /* + * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. */ template std::pair obtain_udf_allocator() { - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto allocate = []() { + constexpr auto allocate = []() { std::allocator allocator; using traits = std::allocator_traits; return (void*)traits::allocate(allocator, 1); }; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -51,8 +53,8 @@ namespace sqlite_orm { return {allocate, deallocate}; } - /* - * A deleter that only destroys the application-defined function object. + /* + * A deleter that only destroys the application-defined function object. */ struct udf_destruct_only_deleter { template @@ -63,12 +65,12 @@ namespace sqlite_orm { } }; - /* - * Stores type-erased information in relation to an application-defined scalar or aggregate function object: - * - name and argument count - * - function dispatch (step, final) - * - either preallocated memory with a possibly a priori constructed function object [scalar], - * - or memory allocation/deallocation functions [aggregate] + /* + * Stores type-erased information in relation to an application-defined scalar or aggregate function object: + * - name and argument count + * - function dispatch (step, final) + * - either preallocated memory with a possibly a priori constructed function object [scalar], + * - or memory allocation/deallocation functions [aggregate] */ struct udf_proxy { using sqlite_func_t = void (*)(sqlite3_context* context, int argsCount, sqlite3_value** values); @@ -140,19 +142,16 @@ namespace sqlite_orm { }; // safety net of doing a triple check at runtime - inline void assert_args_count(const udf_proxy* proxy, int argsCount) { + inline void assert_args_count([[maybe_unused]] const udf_proxy* proxy, [[maybe_unused]] int argsCount) { assert((proxy->argumentsCount == -1) || (proxy->argumentsCount == argsCount || /*check fin call*/ argsCount == -1)); - (void)proxy; - (void)argsCount; } // safety net of doing a triple check at runtime - inline void proxy_assert_args_count(sqlite3_context* context, int argsCount) { + inline void proxy_assert_args_count([[maybe_unused]] sqlite3_context* context, int argsCount) { udf_proxy* proxy; assert((proxy = static_cast(sqlite3_user_data(context))) != nullptr); assert_args_count(proxy, argsCount); - (void)context; } // note: may throw `std::bad_alloc` in case memory space for the aggregate function object cannot be allocated diff --git a/dev/util.h b/dev/util.h index e4ed10689..8d12cdd0f 100644 --- a/dev/util.h +++ b/dev/util.h @@ -1,12 +1,19 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#include // std::function +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string_view +#endif +#endif #include "error_code.h" +#include "serialize_result_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. @@ -36,7 +43,7 @@ namespace sqlite_orm { */ inline std::string quote_blob_literal(std::string v) { constexpr char quoteChar = '\''; - return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + return std::string{'x', quoteChar} + std::move(v) + quoteChar; } /** @@ -47,54 +54,10 @@ namespace sqlite_orm { constexpr char quoteChar = '"'; return quoteChar + sql_escape(std::move(identifier), quoteChar) + quoteChar; } +} +namespace sqlite_orm { namespace internal { - // Wrapper to reduce boiler-plate code - inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { - sqlite3_reset(stmt); - return stmt; - } - - // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { - sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - return stmt; - } - - inline void perform_void_exec(sqlite3* db, const std::string& query) { - int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const char* query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - int rc = sqlite3_exec(db, query, callback, user_data, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - return perform_exec(db, query.c_str(), callback, user_data); - } - - template - void perform_step(sqlite3_stmt* stmt) { - int rc = sqlite3_step(stmt); - if (rc != expected) { - throw_translated_sqlite_error(stmt); - } - } template void perform_step(sqlite3_stmt* stmt, L&& lambda) { @@ -103,28 +66,101 @@ namespace sqlite_orm { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(stmt); + struct sqlite_executor { + std::function will_run_query; + std::function did_run_query; + + inline void perform_void_exec(sqlite3* db, const char* sql) const { + if (this->will_run_query) { + this->will_run_query(sql); + } + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); + } + if (this->did_run_query) { + this->did_run_query(sql); + } + } + + inline void perform_exec(sqlite3* db, + const char* sql, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + if (this->will_run_query) { + this->will_run_query(sql); + } + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); + } + if (this->did_run_query) { + this->did_run_query(sql); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + return perform_exec(db, query.data(), callback, user_data); + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + const char* sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + if (this->did_run_query) { + this->did_run_query(sql); + } + return; + default: { + throw_translated_sqlite_error(stmt); + } } } - } while (rc != SQLITE_DONE); + } + }; + + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { + sqlite3_stmt* stmt; + const int rc = sqlite3_prepare_v2(db, query.data(), query.size(), &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + return stmt; + } + + template + void perform_step(sqlite3_stmt* stmt) { + const int rc = sqlite3_step(stmt); + if (rc != expected) { + throw_translated_sqlite_error(rc); + } } } } diff --git a/dev/values.h b/dev/values.h index 02d347255..07891788b 100644 --- a/dev/values.h +++ b/dev/values.h @@ -1,8 +1,10 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple #include // std::forward, std::move +#endif #include "functional/cxx_type_traits_polyfill.h" @@ -29,7 +31,9 @@ namespace sqlite_orm { }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::values_t values(Args... args) { return {{std::forward(args)...}}; diff --git a/dev/values_to_tuple.h b/dev/values_to_tuple.h index 83c5be4cf..e6e4be907 100644 --- a/dev/values_to_tuple.h +++ b/dev/values_to_tuple.h @@ -1,8 +1,10 @@ #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element +#endif #include "functional/cxx_functional_polyfill.h" #include "type_traits.h" diff --git a/dev/vfs_name.h b/dev/vfs_name.h new file mode 100644 index 000000000..3dda86f1b --- /dev/null +++ b/dev/vfs_name.h @@ -0,0 +1,23 @@ +#pragma once + +#include "serialize_result_type.h" + +SQLITE_ORM_EXPORT namespace sqlite_orm { + +#ifdef SQLITE_ORM_UNIX + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_posix_vfs_name = unix_vfs_name; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; +#ifdef SQLITE_ORM_APPLE + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = unix_vfs_name; +#endif + +#ifdef SQLITE_ORM_WIN + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_vfs_name = "win32"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_longpath_vfs_name = "win32-longpath"; + + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = win32_vfs_name; +#endif +} diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index 0865080c9..c659d29c5 100644 --- a/dev/xdestroy_handling.h +++ b/dev/xdestroy_handling.h @@ -1,13 +1,15 @@ #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif +#endif #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; @@ -172,7 +174,7 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** diff --git a/examples/any.cpp b/examples/any.cpp index 4355766fd..4d6f30bb0 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -3,7 +3,7 @@ * It is a bit more complex than the other examples, because it * uses custom row_extractor and statement_binder. * Please note that this implementation is not the one and only - * option of implementation of std:any bindong to sqlite_orm. + * option of implementation of `std:any` binding to sqlite_orm. * It is just an example of how to use std::any as a mapped type. * Implementation is based on 5 types SQLite supports: * NULL, INTEGER, REAL, TEXT, BLOB. @@ -14,9 +14,19 @@ * BLOB is mapped to std::vector. */ #include +#ifdef __has_include +#if __has_include() #include +#endif +#endif + +#if __cpp_lib_any >= 201606L +#define ENABLE_THIS_EXAMPLE +#endif + +#ifdef ENABLE_THIS_EXAMPLE #include -#include +#include // std::remove using namespace sqlite_orm; using std::cout; @@ -128,14 +138,16 @@ namespace sqlite_orm { } }; } +#endif int main() { +#ifdef ENABLE_THIS_EXAMPLE struct Value { int id = 0; std::any value; }; auto filename = "any.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("test", make_column("id", &Value::id, primary_key()), make_column("value", &Value::value))); @@ -151,5 +163,6 @@ int main() { cout << storage.dump(test) << endl; } cout << endl; +#endif return 0; } diff --git a/examples/blob.cpp b/examples/blob.cpp index 279ef842d..3ad94cf6d 100644 --- a/examples/blob.cpp +++ b/examples/blob.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index fbd29219f..ab8b57967 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -16,11 +16,6 @@ struct Rect { int y = 0; int width = 0; int height = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Rect() = default; - Rect(int x, int y, int width, int height) : x{x}, y{y}, width{width}, height{height} {} -#endif }; bool operator==(const Rect& lhs, const Rect& rhs) { @@ -30,11 +25,6 @@ bool operator==(const Rect& lhs, const Rect& rhs) { struct Zone { int id = 0; Rect rect; // this member will be mapped as BLOB column - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Zone() = default; - Zone(int id, Rect rect) : id{id}, rect{rect} {} -#endif }; bool operator==(const Zone& lhs, const Zone& rhs) { diff --git a/examples/case.cpp b/examples/case.cpp index d40810afe..4249fbd85 100644 --- a/examples/case.cpp +++ b/examples/case.cpp @@ -15,12 +15,6 @@ int main() { std::string name; std::string email; float marks = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Student() {} - Student(int id, std::string name, std::string email, float marks) : - id{id}, name{std::move(name)}, email{std::move(email)}, marks{marks} {} -#endif }; auto storage = make_storage({}, diff --git a/examples/check.cpp b/examples/check.cpp index 7233862ff..0ed10c880 100644 --- a/examples/check.cpp +++ b/examples/check.cpp @@ -13,13 +13,6 @@ int main() { std::string lastName; std::string email; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() {} - Contact(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, - phone{std::move(phone)} {} -#endif }; struct Product { @@ -27,12 +20,6 @@ int main() { std::string name; float listPrice = 0; float discount = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() {} - Product(int id, std::string name, float listPrice, float discount) : - id{id}, name{std::move(name)}, listPrice{listPrice}, discount{discount} {} -#endif }; auto storage = make_storage(":memory:", diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index 8307b18a9..f5c14ac97 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -10,7 +10,8 @@ #endif #ifdef ENABLE_THIS_EXAMPLE -#include +#include +#include // std::remove #include #include #include @@ -30,8 +31,7 @@ // also we need transform functions to make string from enum.. static std::string sysDaysToString(std::chrono::sys_days pt) { - auto r = std::format("{:%F}", pt); - return r; + return std::format("{:%F}", pt); } /** @@ -149,7 +149,7 @@ struct Person { int main(int, char**) { #ifdef ENABLE_THIS_EXAMPLE const std::string db_name = "sys_days.sqlite"; - ::remove(db_name.c_str()); + std::remove(db_name.c_str()); auto storage = make_storage(db_name, make_table("Persons", diff --git a/examples/column_aliases.cpp b/examples/column_aliases.cpp index 8fdb55cc1..4c6f43f98 100644 --- a/examples/column_aliases.cpp +++ b/examples/column_aliases.cpp @@ -1,6 +1,5 @@ #include #include -#include using std::cout; using std::endl; @@ -13,12 +12,6 @@ void marvel_hero_ordered_by_o_pos() { std::string name; std::string abilities; short points = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MarvelHero() {} - MarvelHero(int id, std::string name, std::string abilities, short points) : - id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} -#endif }; auto storage = make_storage("", diff --git a/examples/composite_key.cpp b/examples/composite_key.cpp index 345734d46..30a0c9a65 100644 --- a/examples/composite_key.cpp +++ b/examples/composite_key.cpp @@ -2,7 +2,6 @@ * This example shows you how to create a storage with a tablw with a composite primary key * and another table woth foreign key to first table's primary compisite key. */ -#include #include #include diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index ce6dc4149..162a4fe98 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include using std::cout; using std::endl; @@ -10,12 +10,6 @@ struct MarvelHero { std::string name; std::string abilities; short points = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MarvelHero() {} - MarvelHero(int id, std::string name, std::string abilities, short points) : - id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} -#endif }; struct Contact { @@ -23,12 +17,6 @@ struct Contact { std::string firstName; std::string lastName; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() {} - Contact(int id, std::string firstName, std::string lastName, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} -#endif }; struct Customer { @@ -45,27 +33,6 @@ struct Customer { std::unique_ptr fax; std::string email; int supportRepId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Customer() {} - Customer(int id, - std::string firstName, - std::string lastName, - std::string company, - std::string address, - std::string city, - std::string state, - std::string country, - std::string postalCode, - std::string phone, - std::unique_ptr fax, - std::string email, - int supportRepId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, - address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, - postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, - supportRepId{supportRepId} {} -#endif }; int main(int, char** argv) { diff --git a/examples/custom_aliases.cpp b/examples/custom_aliases.cpp index fcd44189f..e44797e7a 100644 --- a/examples/custom_aliases.cpp +++ b/examples/custom_aliases.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include @@ -17,23 +17,12 @@ struct Employee { int age = 0; std::string address; float salary = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() {} - Employee(int id, std::string name, int age, std::string address, float salary) : - id{id}, name{std::move(name)}, age{age}, address{std::move(address)}, salary{salary} {} -#endif }; struct Department { int id = 0; std::string dept; int empId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Department() {} - Department(int id, std::string dept, int empId) : id{id}, dept{std::move(dept)}, empId{empId} {} -#endif }; using namespace sqlite_orm; diff --git a/examples/enum_binding.cpp b/examples/enum_binding.cpp index 70984f9a4..14bfbc380 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/except_intersection.cpp b/examples/except_intersection.cpp index 7b3f3bd65..928649d0f 100644 --- a/examples/except_intersection.cpp +++ b/examples/except_intersection.cpp @@ -11,11 +11,6 @@ using std::endl; struct DeptMaster { int deptId = 0; std::string deptName; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - DeptMaster() = default; - DeptMaster(int deptId, std::string deptName) : deptId{deptId}, deptName{std::move(deptName)} {} -#endif }; struct EmpMaster { @@ -24,16 +19,6 @@ struct EmpMaster { std::string lastName; long salary; decltype(DeptMaster::deptId) deptId; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - EmpMaster() = default; - EmpMaster(int empId, - std::string firstName, - std::string lastName, - long salary, - decltype(DeptMaster::deptId) deptId) : - empId{empId}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} -#endif }; int main() { diff --git a/examples/foreign_key.cpp b/examples/foreign_key.cpp index ec7c92e9e..24f230499 100644 --- a/examples/foreign_key.cpp +++ b/examples/foreign_key.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #if SQLITE_VERSION_NUMBER >= 3006019 diff --git a/examples/generated_column.cpp b/examples/generated_column.cpp index 7626cec3f..c56d4c5c7 100644 --- a/examples/generated_column.cpp +++ b/examples/generated_column.cpp @@ -23,12 +23,6 @@ int main() { int quantity = 0; float price = 0; float totalValue = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() {} - Product(int id, std::string name, int quantity, float price, float totalValue = 0.f) : - id{id}, name{std::move(name)}, quantity{quantity}, price{price}, totalValue{totalValue} {} -#endif }; auto storage = make_storage({}, make_table("products", diff --git a/examples/in_memory.cpp b/examples/in_memory.cpp index 5799f967a..f10ccb3f4 100644 --- a/examples/in_memory.cpp +++ b/examples/in_memory.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include using namespace sqlite_orm; diff --git a/examples/iteration.cpp b/examples/iteration.cpp index a1dcc1e8e..b57e0b5bf 100644 --- a/examples/iteration.cpp +++ b/examples/iteration.cpp @@ -1,7 +1,9 @@ - -#include +#include +#include +#include #include #include +#include using std::cout; using std::endl; diff --git a/examples/prepared_statement.cpp b/examples/prepared_statement.cpp index f06c4ed44..6ba752b43 100644 --- a/examples/prepared_statement.cpp +++ b/examples/prepared_statement.cpp @@ -18,36 +18,18 @@ struct Doctor { int doctor_id = 0; std::string doctor_name; std::string degree; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Doctor() = default; - Doctor(int doctor_id, std::string doctor_name, std::string degree) : - doctor_id{doctor_id}, doctor_name{std::move(doctor_name)}, degree{std::move(degree)} {} -#endif }; struct Speciality { int spl_id = 0; std::string spl_descrip; int doctor_id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Speciality() = default; - Speciality(int spl_id, std::string spl_descrip, int doctor_id) : - spl_id{spl_id}, spl_descrip{std::move(spl_descrip)}, doctor_id{doctor_id} {} -#endif }; struct Visit { int doctor_id = 0; std::string patient_name; std::string vdate; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int doctor_id, std::string patient_name, std::string vdate) : - doctor_id{doctor_id}, patient_name{std::move(patient_name)}, vdate{std::move(vdate)} {} -#endif }; int main() { diff --git a/examples/private_class_members.cpp b/examples/private_class_members.cpp index 73d10a4ad..a071c4378 100644 --- a/examples/private_class_members.cpp +++ b/examples/private_class_members.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/self_join.cpp b/examples/self_join.cpp index 8d103a9df..3ea98c1ec 100644 --- a/examples/self_join.cpp +++ b/examples/self_join.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #if SQLITE_VERSION_NUMBER >= 3006019 #define ENABLE_THIS_EXAMPLE diff --git a/examples/triggers.cpp b/examples/triggers.cpp index 24810bf9e..3dd8972c9 100644 --- a/examples/triggers.cpp +++ b/examples/triggers.cpp @@ -16,13 +16,6 @@ struct Lead { std::string lastName; std::string email; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Lead() = default; - Lead(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, - phone{std::move(phone)} {} -#endif }; struct LeadLog { diff --git a/examples/union.cpp b/examples/union.cpp index 68b229917..2bc0d4a61 100644 --- a/examples/union.cpp +++ b/examples/union.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include diff --git a/examples/user_defined_functions.cpp b/examples/user_defined_functions.cpp index d9ee12ee7..062d809bb 100644 --- a/examples/user_defined_functions.cpp +++ b/examples/user_defined_functions.cpp @@ -121,11 +121,6 @@ int main() { int a = 0; int b = 0; int c = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Table() = default; - Table(int a, int b, int c) : a{a}, b{b}, c{c} {} -#endif }; auto storage = make_storage( diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c92f4cb5b..129ec5139 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -16,23 +16,45 @@ __pragma(push_macro("max")) /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations - * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - ::size_t, ::ptrdiff_t, std::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks + * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations -#include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early - -// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, -// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). -// actually it should be available when including stddef.h +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // sqlite_orm is using ::size_t, ::ptrdiff_t, std::nullptr_t everywhere, pull it in early +// earlier libcxx versions didn't make std::nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/std::nullptr_t#Notes). using std::nullptr_t; +// Further note on the use of nullptr_t: +// msvc 14.40 has problems finding `::nullptr_t` within sqlite_orm when consuming sqlite_orm as a named module. +// Hence, sqlite_orm is internally using `std::nullptr_t` instead. +#endif + +// #include "cxx_check_prerequisites.h" + +/* + * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. + */ + +// note: Before the C++17 language standard was made the baseline, the library had workarounds for these specific missing C++14 language features, +// so they are kept here as explicit checks for reference. +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L +#error A fully C++17-compliant compiler is required. +#endif + +#if (__cpp_noexcept_function_type < 201510L) || \ + (__cpp_fold_expressions < 201603L || __cpp_constexpr < 201603L || __cpp_aggregate_bases < 201603L) || \ + (__cpp_if_constexpr < 201606L) +#error A fully C++17-compliant compiler is required. +#endif // #include "cxx_core_features.h" /* - * This header detects core C++ language features on which sqlite_orm depends. + * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h */ @@ -48,38 +70,10 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_nsdmi >= 201304L -#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED -#endif - -#if __cpp_constexpr >= 201304L -#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED -#endif - -#if __cpp_noexcept_function_type >= 201510L -#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED -#endif - -#if __cpp_aggregate_bases >= 201603L -#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED -#endif - -#if __cpp_fold_expressions >= 201603L -#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED -#endif - -#if __cpp_constexpr >= 201603L -#define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#endif - #if __cpp_range_based_for >= 201603L #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif -#if __cpp_if_constexpr >= 201606L -#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#endif - #if __cpp_inline_variables >= 201606L #define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #endif @@ -88,9 +82,12 @@ using std::nullptr_t; #define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED #endif +#if __cpp_deduction_guides >= 201703L +#define SQLITE_ORM_CTAD_SUPPORTED +#endif + #if __cpp_generic_lambdas >= 201707L #define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED -#else #endif #if __cpp_init_captures >= 201803L @@ -127,6 +124,7 @@ using std::nullptr_t; #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED #endif // #include "cxx_compiler_quirks.h" @@ -194,14 +192,14 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif -#if SQLITE_ORM_HAS_INCLUDE() -#include +#ifdef BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_EXPORT export +#else +#define SQLITE_ORM_EXPORT #endif -#ifdef SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 constexpr -#else -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 +#if SQLITE_ORM_HAS_INCLUDE() +#include #endif #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED @@ -210,12 +208,6 @@ using std::nullptr_t; #define SQLITE_ORM_INLINE_VAR #endif -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_IF constexpr -#else -#define SQLITE_ORM_CONSTEXPR_IF -#endif - #if __cpp_lib_constexpr_functional >= 201907L #define SQLITE_ORM_CONSTEXPR_CPP20 constexpr #else @@ -268,8 +260,15 @@ using std::nullptr_t; #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) #define SQLITE_ORM_WITH_CTE + +#if defined(_WIN32) +#define SQLITE_ORM_WIN +#elif defined(__APPLE__) +#define SQLITE_ORM_APPLE +#define SQLITE_ORM_UNIX +#elif defined(__unix__) || defined(__unix) || defined(__linux__) || defined(__FreeBSD__) +#define SQLITE_ORM_UNIX #endif // define the inline namespace "literals" so that it is available even if it was not introduced by a feature @@ -279,6 +278,7 @@ namespace sqlite_orm { #pragma once #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr/shared_ptr, std::make_unique #include // std::system_error #include // std::string @@ -291,13 +291,18 @@ namespace sqlite_orm { #include // std::tuple_size, std::tuple, std::make_tuple, std::tie #include // std::forward, std::pair #include // std::for_each, std::ranges::for_each +#endif // #include "functional/cxx_optional.h" // #include "cxx_core_features.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #if SQLITE_ORM_HAS_INCLUDE() #include #endif +#endif #if __cpp_lib_optional >= 201606L #define SQLITE_ORM_OPTIONAL_SUPPORTED @@ -305,7 +310,11 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include +#endif // #include "mpl/conditional.h" @@ -492,17 +501,23 @@ namespace sqlite_orm { // #include "functional/cxx_functional_polyfill.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include #if __cpp_lib_invoke < 201411L #include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer #endif #include // std::forward +#endif // #include "cxx_type_traits_polyfill.h" // #include "../member_traits/member_traits.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -534,13 +549,11 @@ namespace sqlite_orm { template struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; -#endif // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) template @@ -555,10 +568,8 @@ namespace sqlite_orm { template struct setter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct setter_field_type : polyfill::remove_cvref {}; -#endif template struct is_getter : std::false_type {}; @@ -590,6 +601,22 @@ namespace sqlite_orm { template using member_object_type_t = typename member_object_type::type; + + /* + * Casts the class type of a pointer-to-member from a base class to the specified derived class. + */ + template + constexpr F O::* as_field_of(F Base::* f) { + return f; + } + + /* + * Metafunction that casts the class type of a pointer-to-member from a base class to the specified derived class. + * note (implementation): go through `member_field_type_t<>` instead of `decltype(as_field_of())` because of + * older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + */ + template + using as_field_of_t = member_field_type_t O::*; } } @@ -658,90 +685,33 @@ namespace sqlite_orm { return std::forward(callable)(std::forward(args)...); } #endif - } - } - - namespace polyfill = internal::polyfill; -} - -// #include "functional/static_magic.h" - -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant -#endif -#include // std::forward - -namespace sqlite_orm { - - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { - - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function - template - struct empty_callable_t { - template - R operator()(Args&&...) const { - return R(); - } - }; - template - constexpr empty_callable_t empty_callable{}; - -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return std::forward(falseFn); - } - } - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } - } - template - void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { - if constexpr (B) { - lambda(std::forward(args)...); - } - } +#if __cpp_lib_is_invocable >= 201703L + using std::is_invocable; #else - template - decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { - return std::forward(trueFn); - } + template + struct is_invocable_impl : std::false_type {}; - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } - - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); - } - - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); - } +#if __cplusplus >= 201703L + template + struct is_invocable_impl()...))>, Ts...> + : std::true_type {}; +#else + template + struct is_invocable_impl< + polyfill::void_t>>()( + std::declval()...))>, + Callable, + Args...> : std::true_type {}; +#endif - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } + template + struct is_invocable : is_invocable_impl::type {}; #endif + } } + namespace polyfill = internal::polyfill; } // #include "functional/mpl.h" @@ -774,11 +744,9 @@ namespace sqlite_orm { * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED #include -#else -#include #endif // #include "cxx_type_traits_polyfill.h" @@ -795,7 +763,7 @@ namespace sqlite_orm { * Determines whether a class template has a nested metafunction `fn`. * * Implementation note: the technique of specialiazing on the inline variable must come first because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR]. */ template SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; @@ -1081,7 +1049,6 @@ namespace sqlite_orm { using bind_front_higherorder_fn = bind_front::quote_fn, quote_fn, Bound...>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr size_t find_first_true_helper(std::initializer_list values) { size_t i = 0; for (auto first = values.begin(); first != values.end() && !*first; ++first) { @@ -1097,17 +1064,6 @@ namespace sqlite_orm { } return n; } -#else - template - constexpr size_t find_first_true_helper(const std::array& values, size_t i = 0) { - return i == N || values[i] ? 0 : 1 + find_first_true_helper(values, i + 1); - } - - template - constexpr size_t count_true_helper(const std::array& values, size_t i = 0) { - return i == N ? 0 : values[i] + count_true_helper(values, i + 1); - } -#endif /* * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, @@ -1124,11 +1080,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = find_first_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + find_first_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -1154,11 +1107,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + count_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -1184,12 +1134,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = - static_cast(count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({TraitQ::template fn>::value...})); + static constexpr size_t value = static_cast( + count_true_helper({TraitQ::template fn>::value...})); using type = polyfill::bool_constant; }; @@ -1224,7 +1170,7 @@ namespace sqlite_orm { * Commonly used named abbreviation for `check_if`. */ template - using check_if_is_type = mpl::bind_front_fn; + using check_if_is_type = check_if; /* * Quoted trait metafunction that checks if a type's template matches the specified template @@ -1234,6 +1180,18 @@ namespace sqlite_orm { using check_if_is_template = mpl::pass_extracted_fn_to>>; + /* + * Quoted trait metafunction that checks if a type names a nested type determined by `Op`. + */ + template class Op> + using check_if_names = mpl::bind_front_higherorder_fn; + + /* + * Quoted trait metafunction that checks if a type does not name a nested type determined by `Op`. + */ + template class Op> + using check_if_lacks = mpl::not_>; + /* * Quoted metafunction that finds the index of the given type in a tuple. */ @@ -1353,14 +1311,18 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element +#endif // #include "../functional/mpl/conditional.h" // #include "../functional/index_sequence_util.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence +#endif namespace sqlite_orm { namespace internal { @@ -1372,7 +1334,7 @@ namespace sqlite_orm { SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } -#elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +#else /** * Get the index value of an `index_sequence` at a specific position. */ @@ -1389,16 +1351,6 @@ namespace sqlite_orm { (void)((result = Idx, i++ == Pos) || ...); return result; } -#else - /** - * Get the index value of an `index_sequence` at a specific position. - * `Pos` must always be `0`. - */ - template - SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { - static_assert(Pos == 0, ""); - return I; - } #endif template @@ -1509,8 +1461,10 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -1537,8 +1491,6 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; - // note: applying a combiner like `plus_fold_integrals` needs fold expressions -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. * @@ -1604,7 +1556,6 @@ namespace sqlite_orm { IdxSeq{}, project_nested_tuple_size{}, std::integral_constant{})); -#endif template constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { @@ -1612,7 +1563,7 @@ namespace sqlite_orm { } /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. + * Like `std::make_from_tuple()`, but using a projection on the tuple elements. */ template constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { @@ -1621,44 +1572,53 @@ namespace sqlite_orm { std::make_index_sequence>::value>{}, std::forward(project)); } + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template class R, class Tpl, size_t... Idx, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + /* + * Similar to `create_from_tuple()`, but the result type is specified as a template class. + */ + template class R, class Tpl, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } +#endif } } // #include "tuple_helper/tuple_iteration.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence #include // std::forward, std::move +#endif namespace sqlite_orm { namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED + [[maybe_unused]] int sink; +#else + [[maybe_unused]] int sink = 0; +#endif // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { (lambda(std::get(tpl)), ...); } } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF (reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - } - } -#endif template constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, @@ -1666,28 +1626,18 @@ namespace sqlite_orm { std::forward(lambda)); } -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif + template - void iterate_tuple(L&& lambda) { + constexpr void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } template class Base, class L> struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif template decltype(auto) operator()(const Base& object) { return L::operator()(object); @@ -1714,12 +1664,14 @@ namespace sqlite_orm { // #include "type_traits.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L #include // std::reference_wrapper #else #include // std::reference_wrapper #endif +#endif // #include "functional/cxx_core_features.h" @@ -1827,8 +1779,8 @@ namespace sqlite_orm { template using udf_type_t = typename T::udf_type; - template - using auto_udf_type_t = typename decltype(a)::udf_type; + template + using auto_udf_type_t = typename std::remove_reference_t::udf_type; #endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1851,7 +1803,9 @@ namespace sqlite_orm { concept stateless = std::is_empty_v; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template concept orm_names_type = requires { typename T::type; }; @@ -1860,6 +1814,7 @@ namespace sqlite_orm { // #include "alias.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string @@ -1867,6 +1822,7 @@ namespace sqlite_orm { #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -1874,10 +1830,12 @@ namespace sqlite_orm { // #include "functional/cstring_literal.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence #include // std::copy_n #endif +#endif #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { @@ -1909,10 +1867,12 @@ namespace sqlite_orm::internal { // #include "alias_traits.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -1920,7 +1880,9 @@ namespace sqlite_orm::internal { // #include "table_reference.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -1953,7 +1915,9 @@ namespace sqlite_orm { template struct is_table_reference : polyfill::bool_constant> {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. * @@ -1965,12 +1929,14 @@ namespace sqlite_orm { #endif } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ struct alias_tag {}; +} +namespace sqlite_orm { namespace internal { template @@ -2021,7 +1987,9 @@ namespace sqlite_orm { template using is_cte_moniker = polyfill::bool_constant>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept orm_alias = std::derived_from; @@ -2079,12 +2047,67 @@ namespace sqlite_orm { #endif } -// #include "table_type_of.h" +// #include "field_of.h" -#include // std::enable_if, std::is_convertible +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::enable_if, std::is_convertible, std::bool_constant +#endif + +// #include "member_traits/member_traits.h" namespace sqlite_orm { + namespace internal { + template + struct column_pointer; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; + + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + + template + struct is_field_of : polyfill::bool_constant> {}; + + /* + * Compare unrelated fields, like from completely different class types or an empty setter. + */ + template + constexpr bool compare_fields(const L&, const R&) { + return false; + } + + /* + * Compare related fields, either from the same class types or also if the pointer-to-member is of a base class. + */ + template::value || is_field_of::value, bool> = true> + constexpr bool compare_fields(F O1::* lhs, F O2::* rhs) { + return lhs == rhs; + } + } +} + +// #include "table_type_of.h" +namespace sqlite_orm { namespace internal { template @@ -2122,27 +2145,6 @@ namespace sqlite_orm { template using table_type_of_t = typename table_type_of::type; - - /* - * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. - * - * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. - * E.g. - * regular: `alias_column>(column(&Base::field))` - * short: `alias_column>(&Base::field)` - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - - /* - * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_field_of_v::value>> = true; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; } } @@ -2177,10 +2179,10 @@ namespace sqlite_orm { // #include "column_pointer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move - -// #include "functional/cxx_core_features.h" +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -2222,7 +2224,9 @@ namespace sqlite_orm { struct alias_holder; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicitly refer to a column, used in contexts * where the automatic object mapping deduction needs to be overridden. @@ -2292,7 +2296,7 @@ namespace sqlite_orm { * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ template = true> - constexpr auto column(F field) { + constexpr auto column([[maybe_unused]] F field) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); @@ -2305,7 +2309,6 @@ namespace sqlite_orm { } else { return column_pointer{std::move(field)}; } - (void)field; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -2511,7 +2514,9 @@ namespace sqlite_orm { inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Using a column pointer, create a column reference to an aliased table column. * @@ -2573,7 +2578,7 @@ namespace sqlite_orm { requires (!orm_cte_moniker>) constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = decltype(als); + using A = std::remove_const_t; using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -2669,7 +2674,7 @@ namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t{std::move(expression)}; + return internal::as_t, E>{std::move(expression)}; } /** @@ -2701,7 +2706,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder{}; + return internal::alias_holder>{}; } #endif @@ -2820,13 +2825,15 @@ namespace sqlite_orm { // #include "error_code.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error #include // std::string #include #include // std::ostringstream #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @@ -2858,6 +2865,7 @@ namespace sqlite_orm { index_is_out_of_bounds, value_is_null, no_tables_specified, + empty_range, }; } @@ -2869,7 +2877,7 @@ namespace std { struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { class orm_error_category : public std::error_category { public: @@ -2929,8 +2937,8 @@ namespace sqlite_orm { return "SQLite error"; } - std::string message(int c) const override final { - return sqlite3_errstr(c); + std::string message(int ev) const override final { + return sqlite3_errstr(ev); } }; @@ -2989,9 +2997,11 @@ namespace sqlite_orm { // #include "type_printer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector +#endif // #include "functional/cxx_optional.h" // #include "functional/cxx_type_traits_polyfill.h" @@ -3000,10 +3010,12 @@ namespace sqlite_orm { // #include "is_std_ptr.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). @@ -3030,7 +3042,7 @@ namespace sqlite_orm { }; } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) @@ -3104,11 +3116,13 @@ namespace sqlite_orm { // #include "constraints.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream #include // std::string #include // std::tuple +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -3116,9 +3130,14 @@ namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type - -namespace sqlite_orm { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::same_as +#endif +#endif + +namespace sqlite_orm { namespace internal { /** @@ -3129,6 +3148,15 @@ namespace sqlite_orm { using type = void; }; + template + using same_or_void_t = typename same_or_void::type; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template... Rest> + struct same_or_void { + using type = A; + }; +#else template struct same_or_void { using type = A; @@ -3139,11 +3167,9 @@ namespace sqlite_orm { using type = A; }; - template - using same_or_void_t = typename same_or_void::type; - template struct same_or_void : same_or_void {}; +#endif template struct common_type_of; @@ -3183,11 +3209,13 @@ namespace sqlite_orm { // #include "error_code.h" +// #include "field_of.h" + // #include "table_type_of.h" // #include "type_printer.h" -// #include "column_pointer.h" +// #include "table_reference.h" namespace sqlite_orm { @@ -3221,9 +3249,6 @@ namespace sqlite_orm { const primary_key_type& as_base() const { return *this; } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} -#endif }; /** @@ -3233,59 +3258,58 @@ namespace sqlite_orm { */ template struct primary_key_t : primary_key_base { - using self = primary_key_t; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple; columns_tuple columns; - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + constexpr primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} - self asc() const { + constexpr primary_key_t asc() const { auto res = *this; res.options.asc_option = order_by::ascending; return res; } - self desc() const { + constexpr primary_key_t desc() const { auto res = *this; res.options.asc_option = order_by::descending; return res; } - primary_key_with_autoincrement autoincrement() const { + constexpr primary_key_with_autoincrement autoincrement() const { return {*this}; } - self on_conflict_rollback() const { + constexpr primary_key_t on_conflict_rollback() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::rollback; return res; } - self on_conflict_abort() const { + constexpr primary_key_t on_conflict_abort() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::abort; return res; } - self on_conflict_fail() const { + constexpr primary_key_t on_conflict_fail() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::fail; return res; } - self on_conflict_ignore() const { + constexpr primary_key_t on_conflict_ignore() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::ignore; return res; } - self on_conflict_replace() const { + constexpr primary_key_t on_conflict_replace() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::replace; @@ -3359,9 +3383,8 @@ namespace sqlite_orm { * FOREIGN KEY constraint class. * Cs are columns which has foreign key * Rs are column which C references to - * Available in SQLite 3.6.19 or higher + * Available since SQLite 3.6.19 */ - template struct foreign_key_t; @@ -3473,8 +3496,8 @@ namespace sqlite_orm { return res; } - operator bool() const { - return this->_action != foreign_key_action::none; + explicit operator bool() const { + return _action != foreign_key_action::none; } }; @@ -3487,7 +3510,6 @@ namespace sqlite_orm { struct foreign_key_t, std::tuple> { using columns_type = std::tuple; using references_type = std::tuple; - using self = foreign_key_t; /** * Holds obect type of all referenced columns. @@ -3502,28 +3524,23 @@ namespace sqlite_orm { columns_type columns; references_type references; - on_update_delete_t on_update; - on_update_delete_t on_delete; + on_update_delete_t on_update; + on_update_delete_t on_delete; static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + static_assert(!std::is_same::value, "All columns must have the same mapped type"); + static_assert(!std::is_same::value, "All references must have the same mapped type"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self& other) : + foreign_key_t(const foreign_key_t& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self& operator=(const self& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + foreign_key_t& operator=(const foreign_key_t&) = delete; }; template @@ -3533,33 +3550,49 @@ namespace sqlite_orm { } /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + * Cs can be a class member pointer or column pointer + * Available since SQLite 3.6.19 */ template struct foreign_key_intermediate_t { using tuple_type = std::tuple; - tuple_type columns; + tuple_type _columns; + /** + * Specify one or more target fields, which can either be pointers to class members or column pointers. + */ template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; + foreign_key_t> references(Rs... refs) { + return {std::move(_columns), {std::forward(refs)...}}; } - template - foreign_key_t, std::tuple...>> references(Rs... refs) { - return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + /** + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + foreign_key_t> references(F Base::*... refs) { + static_assert(polyfill::conjunction...>::value, + "Referenced fields must be from explicitly specified derived class"); + return {std::move(_columns), {refs...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + auto references(F Base::*... refs) { + return this->references>(refs...); } +#endif }; #endif struct collate_constraint_t { collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif operator std::string() const { return "COLLATE " + this->string_from_collate_argument(this->argument); @@ -3596,10 +3629,6 @@ namespace sqlite_orm { bool full = true; storage_type storage = storage_type::not_specified; #endif - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif }; #if SQLITE_VERSION_NUMBER >= 3031000 @@ -3684,7 +3713,9 @@ namespace sqlite_orm { check_if_is_type>, T>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3031000 template internal::generated_always_t generated_always_as(T expression) { @@ -3699,13 +3730,37 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. + * Available since SQLite 3.6.19 */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { return {{std::forward(columns)...}}; } + + /** + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + * Available since SQLite 3.6.19 + */ + template + internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { + static_assert(polyfill::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + * Available since SQLite 3.6.19 + */ + template + auto foreign_key(F Base::*... columns) { + return foreign_key>(columns...); + } +#endif #endif /** @@ -3775,17 +3830,39 @@ namespace sqlite_orm { #endif /** - * PRIMARY KEY table constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. */ template - internal::primary_key_t primary_key(Cs... cs) { + constexpr internal::primary_key_t primary_key(Cs... cs) { return {{std::forward(cs)...}}; } /** - * PRIMARY KEY column constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ - inline internal::primary_key_t<> primary_key() { + template + constexpr internal::primary_key_t primary_key(F Base::*... columns) { + static_assert(polyfill::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. + */ + template + constexpr auto primary_key(F Base::*... columns) { + return primary_key>(columns...); + } +#endif + + /** + * PRIMARY KEY column constraint builder function (used at a single column). + */ + inline constexpr internal::primary_key_t<> primary_key() { return {{}}; } @@ -3822,6 +3899,7 @@ namespace sqlite_orm { // #include "field_printer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream #include // std::vector @@ -3832,13 +3910,15 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" +#endif + // #include "functional/cxx_type_traits_polyfill.h" // #include "is_std_ptr.h" // #include "type_traits.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Is used to print members mapped to objects in storage_t::dump member function. @@ -3846,7 +3926,9 @@ namespace sqlite_orm { */ template struct field_printer; +} +namespace sqlite_orm { namespace internal { /* * Implementation note: the technique of indirect expression testing is because @@ -3942,8 +4024,8 @@ namespace sqlite_orm { }; #endif // SQLITE_ORM_OMITS_CODECVT template<> - struct field_printer { - std::string operator()(const nullptr_t&) const { + struct field_printer { + std::string operator()(const std::nullptr_t&) const { return "NULL"; } }; @@ -3966,7 +4048,7 @@ namespace sqlite_orm { if (t) { return field_printer()(*t); } else { - return field_printer{}(nullptr); + return field_printer{}(nullptr); } } }; @@ -3992,7 +4074,9 @@ namespace sqlite_orm { // #include "rowid.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif namespace sqlite_orm { @@ -4031,7 +4115,9 @@ namespace sqlite_orm { }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rowid_t rowid() { return {}; } @@ -4062,14 +4148,18 @@ namespace sqlite_orm { // #include "operators.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move +#endif // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval +#endif namespace sqlite_orm { @@ -4081,28 +4171,28 @@ namespace sqlite_orm { */ #ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION template class Base> - struct is_base_of_template_impl { + struct is_base_template_of_impl { template static constexpr std::true_type test(const Base&); static constexpr std::false_type test(...); }; - template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); #else - template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); + template class Base, typename... Ts> + std::true_type is_base_template_of_impl(const Base&); - template class C> - std::false_type is_base_of_template_impl(...); + template class Base> + std::false_type is_base_template_of_impl(...); - template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); #endif - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + template class Base, typename T> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; } } @@ -4114,26 +4204,34 @@ namespace sqlite_orm { // #include "cxx_core_features.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #if SQLITE_ORM_HAS_INCLUDE() #include #endif +#endif #if __cpp_lib_string_view >= 201606L #define SQLITE_ORM_STRING_VIEW_SUPPORTED #endif +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // std::string #endif +#endif namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED using serialize_result_type = std::string_view; using serialize_arg_type = std::string_view; + using string_constant_type = std::string_view; #else using serialize_result_type = std::string; using serialize_arg_type = const std::string&; + using string_constant_type = const char*; #endif } } @@ -4154,7 +4252,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; @@ -4339,7 +4437,9 @@ namespace sqlite_orm { template struct is_assign_t> : public std::true_type {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users @@ -4429,6 +4529,7 @@ namespace sqlite_orm { // #include "select_constraints.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif @@ -4436,11 +4537,12 @@ namespace sqlite_orm { #include // std::string #include // std::move #include // std::tuple, std::get, std::tuple_size +#endif // #include "functional/cxx_optional.h" // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "tuple_helper/tuple_traits.h" @@ -4484,8 +4586,10 @@ namespace sqlite_orm { // #include "ast/where.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -4520,7 +4624,9 @@ namespace sqlite_orm { template struct is_where : polyfill::bool_constant> {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * WHERE clause. Use it to add WHERE conditions wherever you like. * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc @@ -4538,9 +4644,11 @@ namespace sqlite_orm { // #include "ast/group_by.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -4575,7 +4683,9 @@ namespace sqlite_orm { using is_group_by = polyfill::disjunction, polyfill::is_specialization_of>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * GROUP BY column. * Example: storage.get_all(group_by(&Employee::name)) @@ -4588,22 +4698,25 @@ namespace sqlite_orm { // #include "core_functions.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if #include // std::unique_ptr #include // std::vector +#endif // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl/conditional.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "tuple_helper/tuple_traits.h" // #include "conditions.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const #include // std::vector @@ -4611,10 +4724,11 @@ namespace sqlite_orm { #include // std::move, std::forward #include // std::stringstream #include // std::flush +#endif // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "type_traits.h" @@ -4673,9 +4787,11 @@ namespace sqlite_orm { // #include "expression.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if #include // std::move, std::forward, std::declval +#endif // #include "functional/cxx_optional.h" // #include "functional/cxx_type_traits_polyfill.h" @@ -4709,7 +4825,7 @@ namespace sqlite_orm { return {this->value, std::move(r)}; } - assign_t operator=(nullptr_t) const { + assign_t operator=(std::nullptr_t) const { return {this->value, nullptr}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4765,7 +4881,9 @@ namespace sqlite_orm { template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or * `storage.update(set(c(&User::name) = "Dua Lipa")); @@ -4915,7 +5033,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; @@ -5143,10 +5261,6 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif }; /** @@ -5209,15 +5323,8 @@ namespace sqlite_orm { }; struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; - - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified }; struct order_by_string { @@ -5234,19 +5341,19 @@ namespace sqlite_orm { using expression_type = O; using self = order_by_t; - expression_type expression; + expression_type _expression; - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + order_by_t(expression_type expression) : order_by_base(), _expression(std::move(expression)) {} self asc() const { auto res = *this; - res.asc_desc = 1; + res._order = 1; return res; } self desc() const { auto res = *this; - res.asc_desc = -1; + res._order = -1; return res; } @@ -5297,8 +5404,8 @@ namespace sqlite_orm { struct dynamic_order_by_entry_t : order_by_base { std::string name; - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + dynamic_order_by_entry_t(decltype(name) name_, std::string collate_argument_, int asc_desc_) : + order_by_base{std::move(collate_argument_), asc_desc_}, name(std::move(name_)) {} }; /** @@ -5313,13 +5420,11 @@ namespace sqlite_orm { dynamic_order_by_t(const context_t& context_) : context(context_) {} template - void push_back(order_by_t order_by) { + void push_back(order_by_t orderBy) { auto newContext = this->context; newContext.skip_table_name = false; - auto columnName = serialize(order_by.expression, newContext); - this->entries.emplace_back(std::move(columnName), - order_by.asc_desc, - std::move(order_by._collate_argument)); + auto columnName = serialize(orderBy._expression, newContext); + this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } const_iterator begin() const { @@ -5597,7 +5702,9 @@ namespace sqlite_orm { template using is_constrained_join = polyfill::is_detected; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicit FROM function. Usage: * `storage.select(&User::id, from());` @@ -6086,7 +6193,9 @@ namespace sqlite_orm { template using is_into = polyfill::is_specialization_of; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::into_t into() { return {}; @@ -6094,10 +6203,6 @@ namespace sqlite_orm { } namespace sqlite_orm { - - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - namespace internal { template @@ -6124,7 +6229,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; @@ -6698,17 +6803,19 @@ namespace sqlite_orm { using argument1_type = Y; using argument2_type = Z; - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; - - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} + argument0_type argument0{}; + argument1_type argument1{}; + argument2_type argument2{}; }; } +} -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS +SQLITE_ORM_EXPORT namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS /** * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos * @@ -7773,7 +7880,7 @@ namespace sqlite_orm { /** * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) /** * NULLIF(X,Y) using common return type of X and Y */ @@ -8243,6 +8350,7 @@ namespace sqlite_orm { // #include "cte_moniker.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -8252,6 +8360,7 @@ namespace sqlite_orm { #include // std::ignore #include #endif +#endif // #include "functional/cstring_literal.h" @@ -8307,7 +8416,9 @@ namespace sqlite_orm { #endif }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline namespace literals { /** * cte_moniker<'n'> from a numeric literal. @@ -8334,11 +8445,13 @@ namespace sqlite_orm { // #include "schema/column.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string #include // std::unique_ptr #include // std::is_same, std::is_member_object_pointer #include // std::move +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -8352,13 +8465,15 @@ namespace sqlite_orm { // #include "../type_is_nullable.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr +#endif // #include "functional/cxx_optional.h" // #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types @@ -8474,13 +8589,7 @@ namespace sqlite_orm { * It is a composition of orthogonal information stored in different base classes. */ template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; + struct column_t : column_identifier, column_field, column_constraints {}; template struct column_field_expression { @@ -8520,7 +8629,9 @@ namespace sqlite_orm { constraints_type_t, filter_tuple_sequence_t>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Factory function for a column definition from a member object pointer of the object to be mapped. */ @@ -8640,10 +8751,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -8664,10 +8771,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -8687,11 +8790,6 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif }; template @@ -8717,7 +8815,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; @@ -8725,10 +8823,6 @@ namespace sqlite_orm { struct union_base { bool all = false; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif - operator std::string() const { if (!this->all) { return "UNION"; @@ -8837,30 +8931,30 @@ namespace sqlite_orm { template struct cte_builder { - ExplicitCols explicitColumns; + ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #endif }; @@ -8891,10 +8985,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -8902,10 +8992,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -8984,7 +9070,9 @@ namespace sqlite_orm { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template internal::as_optional_t as_optional(T value) { @@ -9155,8 +9243,8 @@ namespace sqlite_orm { static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - using builder_type = - cte_builder, decay_explicit_column_t>>; + using builder_type = cte_builder, + transform_tuple_t, decay_explicit_column_t>>; return builder_type{{std::move(explicitColumns)...}}; } #endif @@ -9366,6 +9454,7 @@ namespace sqlite_orm { // #include "statement_binder.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence #include // std::default_delete #include // std::string, std::wstring @@ -9380,6 +9469,7 @@ namespace sqlite_orm { #include // std::wstring_convert #include // std::codecvt_utf8_utf16 #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -9395,11 +9485,13 @@ namespace sqlite_orm { // #include "arithmetic_tag.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral +#endif // #include "functional/mpl/conditional.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper classes used by statement_binder and row_extractor. @@ -9419,14 +9511,16 @@ namespace sqlite_orm { // #include "xdestroy_handling.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; @@ -9591,7 +9685,7 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @@ -9669,6 +9763,7 @@ namespace sqlite_orm { // #include "pointer_value.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include #include @@ -9677,6 +9772,7 @@ namespace sqlite_orm { #include #endif #endif +#endif // #include "functional/cstring_literal.h" @@ -9684,15 +9780,19 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3020000 namespace sqlite_orm { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES template struct pointer_type { using value_type = const char[sizeof...(C) + 1]; static inline constexpr value_type value = {C..., '\0'}; }; +#endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline namespace literals { template [[nodiscard]] consteval auto operator"" _pointer_type() { @@ -9854,7 +9954,7 @@ namespace sqlite_orm { #endif } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Wrap a pointer, its type and its deleter function for binding it to a statement. * @@ -9967,14 +10067,16 @@ namespace sqlite_orm { } #endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. */ template struct statement_binder; +} +namespace sqlite_orm { namespace internal { /* * Implementation note: the technique of indirect expression testing is because @@ -10142,15 +10244,15 @@ namespace sqlite_orm { #endif /** - * Specialization for nullptr_t. + * Specialization for std::nullptr_t. */ template<> - struct statement_binder { - int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { return sqlite3_bind_null(stmt, index); } - void result(sqlite3_context* context, const nullptr_t&) const { + void result(sqlite3_context* context, const std::nullptr_t&) const { sqlite3_result_null(context); } }; @@ -10182,7 +10284,7 @@ namespace sqlite_orm { if (value) { return statement_binder().bind(stmt, index, *value); } else { - return statement_binder().bind(stmt, index, nullptr); + return statement_binder().bind(stmt, index, nullptr); } } }; @@ -10238,7 +10340,7 @@ namespace sqlite_orm { void operator()(const T& t) { int rc = statement_binder{}.bind(this->stmt, this->index++, t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -10275,24 +10377,16 @@ namespace sqlite_orm { } private: -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } -#else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; - } -#endif template void bind(const T& t, size_t idx) const { int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -10312,8 +10406,10 @@ namespace sqlite_orm { // #include "column_result.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -10323,7 +10419,9 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#endif namespace sqlite_orm { @@ -10355,7 +10453,9 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const +#endif // #include "type_traits.h" @@ -10442,14 +10542,11 @@ namespace sqlite_orm { // #include "cte_types.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include #include #endif - -// #include "functional/cxx_core_features.h" - -// #include "functional/cxx_type_traits_polyfill.h" +#endif // #include "tuple_helper/tuple_fy.h" @@ -10506,7 +10603,9 @@ namespace sqlite_orm { // #include "storage_traits.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -10518,9 +10617,11 @@ namespace sqlite_orm { // #include "storage_lookup.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void #include #include // std::index_sequence, std::make_index_sequence +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -10722,6 +10823,7 @@ namespace sqlite_orm { // #include "function.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include // std::copy_constructible @@ -10729,6 +10831,7 @@ namespace sqlite_orm { #include // std::tuple, std::tuple_size, std::tuple_element #include // std::min, std::copy_n #include // std::move, std::forward +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -10788,7 +10891,6 @@ namespace sqlite_orm { using signature_type = R(Args...) const; }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct function_traits : function_traits { using signature_type = R(Args...) noexcept; @@ -10798,7 +10900,6 @@ namespace sqlite_orm { struct function_traits : function_traits { using signature_type = R(Args...) const noexcept; }; -#endif /* * Pick signature of function pointer @@ -10870,7 +10971,9 @@ namespace sqlite_orm { template struct function; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). */ @@ -10937,7 +11040,9 @@ namespace sqlite_orm { quotedF.callable(); }; #endif +} +namespace sqlite_orm { namespace internal { template struct callable_arguments_impl; @@ -11049,10 +11154,10 @@ namespace sqlite_orm { // Always allow binding nullptr to a pointer argument template - constexpr bool is_same_pvt_v> = true; + constexpr bool is_same_pvt_v> = true; // Always allow binding nullptr to a pointer argument template - constexpr bool is_same_pvt_v, pointer_binding, void> = true; + constexpr bool is_same_pvt_v, pointer_binding, void> = true; template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_data_type() { @@ -11061,7 +11166,7 @@ namespace sqlite_orm { return valid; } -#if __cplusplus >= 201703L // C++17 or later +#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { constexpr bool valid = Binding == PointerArg; @@ -11110,7 +11215,6 @@ namespace sqlite_orm { using func_param_type = std::tuple_element_t; using call_arg_type = unpacked_arg_t>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr bool valid = validate_pointer_value_type, unpacked_arg_t>>( @@ -11118,14 +11222,6 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); return validate_pointer_value_types(polyfill::index_constant{}) && valid; -#else - return validate_pointer_value_types(polyfill::index_constant{}) && - validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); -#endif } /* @@ -11133,11 +11229,7 @@ namespace sqlite_orm { * but other call argument types are not checked against the parameter types of the function. */ template -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED SQLITE_ORM_CONSTEVAL void check_function_call() { -#else - void check_function_call() { -#endif using call_args_tuple = std::tuple; using function_params_tuple = typename callable_arguments::args_tuple; constexpr size_t callArgsCount = std::tuple_size::value; @@ -11242,7 +11334,7 @@ namespace sqlite_orm { template struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; + constexpr quoted_function_builder(const char (&cstr)[N]) : cstring_literal{cstr} {} /* * From a freestanding function, possibly overloaded. @@ -11298,7 +11390,9 @@ namespace sqlite_orm { }; #endif } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Call a user-defined function. * * Note: Currently the number of call arguments is checked and whether the types of pointer values match, @@ -11369,7 +11463,9 @@ namespace sqlite_orm { struct current_date_t {}; struct current_timestamp_t {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::current_time_t current_time() { return {}; } @@ -11488,8 +11584,8 @@ namespace sqlite_orm { }; template - struct column_result_t { - using type = nullptr_t; + struct column_result_t { + using type = std::nullptr_t; }; template @@ -11711,9 +11807,11 @@ namespace sqlite_orm { // #include "sync_schema_result.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class sync_schema_result { @@ -11773,9 +11871,12 @@ namespace sqlite_orm { // #include "table_info.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#include // std::move +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { struct table_info { int cid = 0; @@ -11785,7 +11886,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -11806,7 +11907,7 @@ namespace sqlite_orm { int pk = 0; int hidden = 0; // different than 0 => generated_always_as() - TODO verify -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_xinfo(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -11822,9 +11923,9 @@ namespace sqlite_orm { // #include "storage_impl.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string - -// #include "functional/static_magic.h" +#endif // #include "functional/index_sequence_util.h" @@ -11844,18 +11945,18 @@ namespace sqlite_orm { // #include "schema/table.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type #include // std::vector #include // std::tuple_element #include // std::forward, std::move +#endif // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/cxx_functional_polyfill.h" -// #include "../functional/static_magic.h" - // #include "../functional/mpl.h" // #include "../functional/index_sequence_util.h" @@ -11870,22 +11971,7 @@ namespace sqlite_orm { // #include "../member_traits/member_traits.h" -// #include "../typed_comparator.h" - -namespace sqlite_orm { - - namespace internal { - - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; - } - } -} +// #include "../field_of.h" // #include "../type_traits.h" @@ -11897,16 +11983,20 @@ namespace sqlite_orm { // #include "index.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string #include // std::forward +#endif // #include "../tuple_helper/tuple_traits.h" // #include "../indexed_column.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#endif // #include "ast/where.h" @@ -11918,14 +12008,9 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} -#endif - - column_type column_or_expression; + column_type _column_or_expression; std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified indexed_column_t collate(std::string name) { auto res = std::move(*this); @@ -11961,7 +12046,9 @@ namespace sqlite_orm { return std::move(col); } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) @@ -11981,10 +12068,6 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif }; template @@ -11993,15 +12076,12 @@ namespace sqlite_orm { using object_type = void; using table_mapped_type = T; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - index_t(std::string name_, bool unique_, elements_type elements_) : - index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} -#endif - elements_type elements; }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::index_t()))...> make_index(std::string name, Cols... cols) { @@ -12099,11 +12179,6 @@ namespace sqlite_orm { elements_type elements; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif - table_t without_rowid() const { return {this->name, this->elements}; } @@ -12154,7 +12229,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_any(column.setter, memberPointer)) { + if (compare_fields(column.setter, memberPointer)) { res = &polyfill::invoke(column.member_pointer, object); } })); @@ -12162,7 +12237,7 @@ namespace sqlite_orm { } const basic_generated_always::storage_type* - find_column_generated_storage_type(const std::string& name) const { + find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { const basic_generated_always::storage_type* result = nullptr; #if SQLITE_VERSION_NUMBER >= 3031000 iterate_tuple(this->elements, @@ -12177,8 +12252,6 @@ namespace sqlite_orm { constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); result = &std::get(column.constraints).storage; }); -#else - (void)name; #endif return result; } @@ -12203,7 +12276,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { using pkcol_index_sequence = col_index_sequence_with; - if (pkcol_index_sequence::size() > 0) { + if constexpr (pkcol_index_sequence::size() > 0) { return create_from_tuple>(this->elements, pkcol_index_sequence{}, &column_identifier::name); @@ -12242,14 +12315,16 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template = true> - const std::string* find_column_name(M m) const { - const std::string* res = nullptr; + const std::string* find_column_name(M memberPointer) const { using field_type = member_field_type_t; + + const std::string* res = nullptr; iterate_tuple(this->elements, col_index_sequence_with_field_type{}, - [&res, m](auto& c) { - if (compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { - res = &c.name; + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; } }); return res; @@ -12323,11 +12398,6 @@ namespace sqlite_orm { module_details_type module_details; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - virtual_table_t(std::string name, module_details_type module_details) : - basic_table{std::move(name)}, module_details{std::move(module_details)} {} -#endif - /** * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. @@ -12354,6 +12424,11 @@ namespace sqlite_orm { void for_each_column(L&& lambda) const { this->module_details.for_each_column(lambda); } + + template = true> + const std::string* find_column_name(MP memberPointer) const { + return this->module_details.find_column_name(memberPointer); + } }; template @@ -12399,6 +12474,22 @@ namespace sqlite_orm { using col_index_sequence = filter_tuple_sequence_t; iterate_tuple(this->columns, col_index_sequence{}, lambda); } + + template = true> + const std::string* find_column_name(M memberPointer) const { + using field_type = member_field_type_t; + + const std::string* res = nullptr; + iterate_tuple(this->columns, + col_index_sequence_with_field_type{}, + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } }; #endif @@ -12413,8 +12504,8 @@ namespace sqlite_orm { check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_any(memberPointer, column.member_pointer) || - compare_any(memberPointer, column.setter)) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { res = true; } }); @@ -12428,7 +12519,9 @@ namespace sqlite_orm { return false; } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3009000 template>::object_type> internal::using_fts5_t using_fts5(Cs... columns) { @@ -12503,21 +12596,22 @@ namespace sqlite_orm { using tables_index_sequence = filter_tuple_sequence_t; template = true> - int foreign_keys_count(const DBOs& dbObjects) { + constexpr int foreign_keys_count() { int res = 0; - iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.template count_of(); + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_type::template count_of(); }); return res; } template> decltype(auto) lookup_table_name(const DBOs& dbObjects) { - return static_if::value>( - [](const auto& dbObjects) -> const std::string& { - return pick_table(dbObjects).name; - }, - empty_callable)(dbObjects); + if constexpr (is_mapped::value) { + return (pick_table(dbObjects).name); + } else { + return std::string{}; + } } /** @@ -12549,17 +12643,15 @@ namespace sqlite_orm { constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; + using cte_fields_type = typename cte_mapper_type_t::fields_type; // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; + return &aliased_field>::field; } #endif @@ -12583,14 +12675,13 @@ namespace sqlite_orm { constexpr decltype(auto) find_column_name(const DBOs& dboObjects, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; using column_index_sequence = filter_tuple_sequence_t, is_column>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `table_t<>::find_column_name()` mechanism; @@ -12606,11 +12697,13 @@ namespace sqlite_orm { // #include "journal_mode.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif // #include "serialize_result_type.h" @@ -12620,7 +12713,7 @@ namespace sqlite_orm { #undef DELETE #endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Caps case because of: @@ -12637,7 +12730,9 @@ namespace sqlite_orm { WAL = 4, OFF = 5, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& journal_mode_to_string(journal_mode value) { @@ -12665,7 +12760,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -12694,15 +12789,18 @@ namespace sqlite_orm { // #include "mapped_view.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move +#endif // #include "row_extractor.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll -#include // strlen +#include // ::atof, ::atoi, ::atoll +#include // ::strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -12716,11 +12814,10 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif +#endif // #include "functional/cxx_functional_polyfill.h" -// #include "functional/static_magic.h" - // #include "tuple_helper/tuple_transformer.h" // #include "column_result_proxy.h" @@ -12733,20 +12830,24 @@ namespace sqlite_orm { // #include "locking_mode.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif // #include "serialize_result_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& locking_mode_to_string(locking_mode value) { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED @@ -12766,7 +12867,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -12794,7 +12895,7 @@ namespace sqlite_orm { // #include "type_traits.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. @@ -12845,7 +12946,9 @@ namespace sqlite_orm { { extractor.extract(value) } -> std::same_as; }; #endif +} +namespace sqlite_orm { namespace internal { /* * Make a row extractor to be used for casting SQL column text to a C++ typed value. @@ -12880,7 +12983,9 @@ namespace sqlite_orm { return {}; } } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template int extract_single_value(void* data, int argc, char** argv, char**) { auto& res = *(R*)data; @@ -12890,7 +12995,9 @@ namespace sqlite_orm { } return 0; } +} +namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3020000 /** * Specialization for the 'pointer-passing interface'. @@ -13134,16 +13241,16 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<> - struct row_extractor { - nullptr_t extract(const char* /*columnText*/) const { + struct row_extractor { + std::nullptr_t extract(const char* /*columnText*/) const { return nullptr; } - nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + std::nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { return nullptr; } - nullptr_t extract(sqlite3_value*) const { + std::nullptr_t extract(sqlite3_value*) const { return nullptr; } }; @@ -13229,7 +13336,6 @@ namespace sqlite_orm { template struct struct_extractor; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED /* * Returns a value-based row extractor for an unmapped type, * returns a structure extractor for a table reference, tuple or named struct. @@ -13243,34 +13349,6 @@ namespace sqlite_orm { return row_value_extractor(); } } -#else - /* - * Overload for an unmapped type returns a common row extractor. - */ - template< - class R, - class DBOs, - std::enable_if_t, - polyfill::is_specialization_of, - is_table_reference>>::value, - bool> = true> - auto make_row_extractor(const DBOs& /*dbObjects*/) { - return row_value_extractor(); - } - - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. - */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif /** * Specialization for a tuple of top-level column results. @@ -13346,19 +13424,23 @@ namespace sqlite_orm { // #include "mapped_iterator.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared #include // std::move #include // std::input_iterator_tag #include // std::system_error #include // std::bind +#endif // #include "statement_finalizer.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::integral_constant +#endif -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor @@ -13372,10 +13454,10 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer #include // std::move - -// #include "functional/static_magic.h" +#endif // #include "member_traits/member_traits.h" @@ -13394,11 +13476,6 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; int columnIndex = -1; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_from_column_builder_base(sqlite3_stmt* stmt, int columnIndex = -1) : - stmt{stmt}, columnIndex{columnIndex} {} -#endif }; /** @@ -13417,13 +13494,11 @@ namespace sqlite_orm { void operator()(const column_field& column) { const auto rowExtractor = row_value_extractor>(); auto value = rowExtractor.extract(this->stmt, ++this->columnIndex); - static_if::value>( - [&value, &object = this->object](const auto& column) { - object.*column.member_pointer = std::move(value); - }, - [&value, &object = this->object](const auto& column) { - (object.*column.setter)(std::move(value)); - })(column); + if constexpr (std::is_member_object_pointer::value) { + object.*column.member_pointer = std::move(value); + } else { + (object.*column.setter)(std::move(value)); + }; } }; @@ -13463,12 +13538,20 @@ namespace sqlite_orm { // #include "util.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#include // std::function +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string_view +#endif +#endif // #include "error_code.h" -namespace sqlite_orm { +// #include "serialize_result_type.h" + +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. @@ -13498,7 +13581,7 @@ namespace sqlite_orm { */ inline std::string quote_blob_literal(std::string v) { constexpr char quoteChar = '\''; - return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + return std::string{'x', quoteChar} + std::move(v) + quoteChar; } /** @@ -13509,54 +13592,10 @@ namespace sqlite_orm { constexpr char quoteChar = '"'; return quoteChar + sql_escape(std::move(identifier), quoteChar) + quoteChar; } +} +namespace sqlite_orm { namespace internal { - // Wrapper to reduce boiler-plate code - inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { - sqlite3_reset(stmt); - return stmt; - } - - // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { - sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - return stmt; - } - - inline void perform_void_exec(sqlite3* db, const std::string& query) { - int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const char* query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - int rc = sqlite3_exec(db, query, callback, user_data, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - return perform_exec(db, query.c_str(), callback, user_data); - } - - template - void perform_step(sqlite3_stmt* stmt) { - int rc = sqlite3_step(stmt); - if (rc != expected) { - throw_translated_sqlite_error(stmt); - } - } template void perform_step(sqlite3_stmt* stmt, L&& lambda) { @@ -13565,28 +13604,101 @@ namespace sqlite_orm { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(stmt); + struct sqlite_executor { + std::function will_run_query; + std::function did_run_query; + + inline void perform_void_exec(sqlite3* db, const char* sql) const { + if (this->will_run_query) { + this->will_run_query(sql); + } + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); + } + if (this->did_run_query) { + this->did_run_query(sql); + } + } + + inline void perform_exec(sqlite3* db, + const char* sql, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + if (this->will_run_query) { + this->will_run_query(sql); + } + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); + } + if (this->did_run_query) { + this->did_run_query(sql); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + return perform_exec(db, query.data(), callback, user_data); + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + const char* sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + if (this->did_run_query) { + this->did_run_query(sql); + } + return; + default: { + throw_translated_sqlite_error(stmt); + } } } - } while (rc != SQLITE_DONE); + } + }; + + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { + sqlite3_stmt* stmt; + const int rc = sqlite3_prepare_v2(db, query.data(), query.size(), &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + return stmt; + } + + template + void perform_step(sqlite3_stmt* stmt) { + const int rc = sqlite3_step(stmt); + if (rc != expected) { + throw_translated_sqlite_error(rc); + } } } } @@ -13695,88 +13807,260 @@ namespace sqlite_orm { } } -// #include "ast_iterator.h" +// #include "ast_iterator.h" + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_invocable +#include // std::vector +#include // std::reference_wrapper +#endif + +// #include "functional/cxx_functional_polyfill.h" +// std::is_invocable +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "conditions.h" + +// #include "alias.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "core_functions.h" + +// #include "prepared_statement.h" + +#include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::unique_ptr +#include // std::iterator_traits +#include // std::string +#include // std::integral_constant, std::declval +#include // std::move, std::forward, std::pair +#include // std::tuple +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "connection_holder.h" + +#include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include +#include // std::function +#include // std::string +#endif + +// #include "error_code.h" + +// #include "vfs_name.h" + +// #include "serialize_result_type.h" + +SQLITE_ORM_EXPORT namespace sqlite_orm { + +#ifdef SQLITE_ORM_UNIX + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_posix_vfs_name = unix_vfs_name; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; +#ifdef SQLITE_ORM_APPLE + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = unix_vfs_name; +#endif + +#ifdef SQLITE_ORM_WIN + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_vfs_name = "win32"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_longpath_vfs_name = "win32-longpath"; + + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = win32_vfs_name; +#endif +} + +// #include "db_open_mode.h" + +SQLITE_ORM_EXPORT namespace sqlite_orm { -#include // std::vector -#include // std::reference_wrapper + enum class db_open_mode { + default_ = 0, + create_readwrite = 0, + readonly = 1, + }; +} -// #include "tuple_helper/tuple_iteration.h" +SQLITE_ORM_EXPORT namespace sqlite_orm { + namespace internal { + constexpr int db_open_mode_to_int_flags(db_open_mode open) { -// #include "type_traits.h" + switch (open) { + case db_open_mode::readonly: + return SQLITE_OPEN_READONLY; + case db_open_mode::create_readwrite: + return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; + }; -// #include "conditions.h" + return -1; + } + } +} -// #include "alias.h" +// #include "storage_options.h" -// #include "select_constraints.h" +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif -// #include "operators.h" +// #include "serialize_result_type.h" -// #include "core_functions.h" +namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; -// #include "prepared_statement.h" + struct on_open_spec { + using storage_opt_tag = int; -#include -#include // std::unique_ptr -#include // std::iterator_traits -#include // std::string -#include // std::integral_constant, std::declval -#include // std::move, std::forward, std::pair -#include // std::tuple + std::function onOpen; + }; + struct will_run_query_spec { + using storage_opt_tag = int; -// #include "functional/cxx_type_traits_polyfill.h" + std::function willRunQuery; + }; + struct did_run_query_spec { + using storage_opt_tag = int; -// #include "functional/cxx_functional_polyfill.h" + std::function didRunQuery; + }; + } +} -// #include "tuple_helper/tuple_traits.h" +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + std::string vfs_name{default_vfs_name}; + db_open_mode open_mode = db_open_mode::default_; -// #include "connection_holder.h" + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif -#include -#include -#include // std::string +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif + inline internal::will_run_query_spec + will_run_query(std::function willRunQuery) { + return {std::move(willRunQuery)}; + } -// #include "error_code.h" + inline internal::did_run_query_spec did_run_query(std::function didRunQuery) { + return {std::move(didRunQuery)}; + } +} namespace sqlite_orm { - namespace internal { struct connection_holder { + connection_holder(std::string filename, + std::function didOpenDb, + const connection_control& options = {}) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), + open_mode(options.open_mode) {} + + connection_holder(const connection_holder&) = delete; - connection_holder(std::string filename_) : filename(std::move(filename_)) {} + connection_holder(const connection_holder& other, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, vfs_name(other.vfs_name), + open_mode{other.open_mode} {} void retain() { - if (1 == ++this->_retain_count) { - auto rc = sqlite3_open(this->filename.c_str(), &this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // first one opens the connection. + // we presume that the connection is opened once in a single-threaded context [also open forever]. + // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. + if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { + int open_flags = internal::db_open_mode_to_int_flags(this->open_mode); +#if SQLITE_VERSION_NUMBER >= 3037002 + open_flags |= SQLITE_OPEN_EXRESCODE; +#endif + + const int rc = + sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, this->vfs_name.c_str()); + + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { + throw_translated_sqlite_error(rc); + } + + if (_didOpenDb) { + _didOpenDb(this->db); } } } void release() { - if (0 == --this->_retain_count) { - auto rc = sqlite3_close(this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // last one closes the connection. + // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. + if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close_v2(this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { + throw_translated_sqlite_error(this->db); + } else { + this->db = nullptr; } } } sqlite3* get() const { + // note: ensuring a valid DB handle was already memory ordered with `retain()` return this->db; } + /** + * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. + */ int retain_count() const { - return this->_retain_count; + return _retainCount.load(std::memory_order_relaxed); } - const std::string filename; - protected: sqlite3* db = nullptr; - std::atomic_int _retain_count{}; + + private: + std::atomic_int _retainCount{}; + + private: + const std::function _didOpenDb; + + public: + const std::string filename; + const std::string vfs_name; + const db_open_mode open_mode; }; struct connection_ref { @@ -13817,9 +14101,11 @@ namespace sqlite_orm { // #include "values.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple #include // std::forward, std::move +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -13846,7 +14132,9 @@ namespace sqlite_orm { }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::values_t values(Args... args) { return {{std::forward(args)...}}; @@ -13864,10 +14152,12 @@ namespace sqlite_orm { // #include "ast/upsert_clause.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move #endif +#endif // #include "../functional/cxx_type_traits_polyfill.h" @@ -13915,7 +14205,9 @@ namespace sqlite_orm { template using is_upsert_clause = polyfill::bool_constant>; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3024000 /** * ON CONFLICT upsert clause builder function. @@ -13938,19 +14230,23 @@ namespace sqlite_orm { // #include "ast/set.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string #include // std::vector #include // std::stringstream #include // std::false_type, std::true_type +#endif // #include "../tuple_helper/tuple_traits.h" // #include "../table_name_collector.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string #include // std::pair, std::move +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -13982,8 +14278,6 @@ namespace sqlite_orm { const db_objects_type& db_objects; - table_name_collector() = default; - table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} template @@ -14042,7 +14336,7 @@ namespace sqlite_orm { } template - void operator()(const highlight_t&) { + void operator()(polyfill::bool_constant, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; @@ -14132,7 +14426,9 @@ namespace sqlite_orm { template struct is_dynamic_set> : std::true_type {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) @@ -14164,10 +14460,6 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} -#endif - ~prepared_statement_base() { sqlite3_finalize(this->stmt); } @@ -14450,16 +14742,14 @@ namespace sqlite_orm { struct insert_constraint { conflict_action action = conflict_action::abort; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - insert_constraint(conflict_action action) : action{action} {} -#endif }; template using is_insert_constraint = std::is_same; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::insert_constraint or_rollback() { return {internal::conflict_action::rollback}; } @@ -14969,7 +15259,9 @@ namespace sqlite_orm { // #include "ast/excluded.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move +#endif namespace sqlite_orm { namespace internal { @@ -14981,7 +15273,9 @@ namespace sqlite_orm { expression_type expression; }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::excluded_t excluded(T expression) { return {std::move(expression)}; @@ -14998,7 +15292,9 @@ namespace sqlite_orm { // #include "ast/exists.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move +#endif // #include "../tags.h" @@ -15015,7 +15311,9 @@ namespace sqlite_orm { exists_t(expression_type expression_) : expression(std::move(expression_)) {} }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * EXISTS(condition). * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), @@ -15034,7 +15332,9 @@ namespace sqlite_orm { // #include "ast/match.h" -#include // std::move +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include +#endif namespace sqlite_orm { namespace internal { @@ -15045,11 +15345,11 @@ namespace sqlite_orm { using argument_type = X; argument_type argument; - - match_t(argument_type argument) : argument(std::move(argument)) {} }; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; @@ -15068,7 +15368,7 @@ namespace sqlite_orm { * ast_iterator is used in finding literals to be bound to * a statement, and to collect table names. * - * Note that not all leaves of the expression tree are always visited: + * Note that not all leaves of the expression tree are visited: * Column expressions can be more complex, but are passed as a whole to the callable. * Examples are `column_pointer<>` and `alias_column_t<>`. * @@ -15084,17 +15384,26 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T& t, L& lambda) const { - lambda(t); + void operator()(const node_type& leaf, L& lambda) const { + lambda(leaf); } }; /** - * Simplified API + * Simplified API. + * + * @param lambda Callable invoked with each leaf expression. + * The callable can opt in to be invoked for a node expression. */ template void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; + + // possibly invoke lambda with node itself + if constexpr (polyfill::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } + iterator(t, lambda); } @@ -15146,7 +15455,6 @@ namespace sqlite_orm { template void operator()(const node_type& expression, L& lambda) const { - lambda(expression); iterate_ast(expression.argument0, lambda); iterate_ast(expression.argument1, lambda); iterate_ast(expression.argument2, lambda); @@ -15735,7 +16043,17 @@ namespace sqlite_orm { template void operator()(const node_type& node, L& lambda) const { - iterate_ast(node.expression, lambda); + iterate_ast(node._expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = multi_order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; @@ -15797,12 +16115,14 @@ namespace sqlite_orm { mapped_iterator begin() { using context_t = serializer_context; + auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); return {dbObjects, std::move(stmt)}; } @@ -15822,12 +16142,14 @@ inline constexpr bool std::ranges::enable_borrowed_range +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref #include // std::reference_wrapper #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) && \ defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) #include // std::ranges::view_interface #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -15836,9 +16158,11 @@ inline constexpr bool std::ranges::enable_borrowed_range +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper +#endif // #include "statement_finalizer.h" @@ -15967,11 +16291,13 @@ namespace sqlite_orm::internal { using select_type = polyfill::detected_or_t; using column_result_type = column_result_of_t; using context_t = serializer_context; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression, conditional_binder{stmt.get()}); // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; @@ -16002,6 +16328,7 @@ inline constexpr bool std::ranges::enable_borrowed_range +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front @@ -16014,15 +16341,20 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::list #include // std::make_unique, std::unique_ptr #include // std::map -#include // std::is_same +#include // std::is_same, std::is_aggregate #include // std::find_if, std::ranges::find +#endif // #include "functional/cxx_tuple_polyfill.h" +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else #include // std::apply; std::tuple_size #if __cpp_lib_apply < 201603L #include // std::forward, std::index_sequence, std::make_index_sequence #endif +#endif // #include "../functional/cxx_functional_polyfill.h" // std::invoke @@ -16057,13 +16389,14 @@ namespace sqlite_orm { // #include "pragma.h" #include -#include // atoi +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::function #include // std::shared_ptr #include // std::vector #include #include // std::flush +#endif // #include "error_code.h" @@ -16079,12 +16412,14 @@ namespace sqlite_orm { // #include "serializing_util.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include #include #include #include #include // std::exchange, std::tuple_size, std::make_index_sequence +#endif // #include "functional/cxx_type_traits_polyfill.h" // std::remove_cvref, polyfill::is_detected @@ -16324,9 +16659,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } @@ -16504,7 +16845,7 @@ namespace sqlite_orm { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; - if SQLITE_ORM_CONSTEXPR_IF (!hasExplicitNullableConstraint) { + if constexpr (!hasExplicitNullableConstraint) { if (isNotNull) { ss << " NOT NULL"; } else { @@ -16522,6 +16863,7 @@ namespace sqlite_orm { namespace internal { struct storage_base; + struct sqlite_executor; template int getPragmaCallback(void* data, int argc, char** argv, char** x) { @@ -16534,8 +16876,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } @@ -16543,7 +16884,8 @@ namespace sqlite_orm { struct pragma_t { using get_connection_t = std::function; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + pragma_t(get_connection_t get_connection_, const sqlite_executor& executor) : + get_connection(std::move(get_connection_)), executor(executor) {} std::vector module_list() { return this->get_pragma>("module_list"); @@ -16657,13 +16999,17 @@ namespace sqlite_orm { auto connection = this->get_connection(); std::vector result; - std::ostringstream ss; - ss << "PRAGMA " - "table_xinfo(" - << streaming_identifier(tableName) << ")" << std::flush; - perform_exec( + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -16693,14 +17039,18 @@ namespace sqlite_orm { std::vector table_info(const std::string& tableName) const { auto connection = this->get_connection(); - std::ostringstream ss; - ss << "PRAGMA " - "table_info(" - << streaming_identifier(tableName) << ")" << std::flush; + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } std::vector result; - perform_exec( + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -16726,12 +17076,14 @@ namespace sqlite_orm { int synchronous_ = -1; signed char journal_mode_ = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; + const sqlite_executor& executor; template T get_pragma(const std::string& name) { auto connection = this->get_connection(); T result; - perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + const std::string sql = "PRAGMA " + name; + this->executor.perform_exec(connection.get(), sql, getPragmaCallback, &result); return result; } @@ -16758,12 +17110,13 @@ namespace sqlite_orm { this->set_pragma_impl(ss.str(), db); } - void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if (db == nullptr) { - db = con.get(); + void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { + if (db) { + this->executor.perform_void_exec(db, sql.data()); + } else { + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } - perform_void_exec(db, query); } }; } @@ -16772,9 +17125,11 @@ namespace sqlite_orm { // #include "limit_accessor.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map #include // std::function #include // std::shared_ptr +#endif // #include "connection_holder.h" @@ -16911,8 +17266,10 @@ namespace sqlite_orm { // #include "transaction_guard.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move +#endif // #include "connection_holder.h" @@ -16997,10 +17354,12 @@ namespace sqlite_orm { // #include "backup.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error #include // std::string #include #include // std::move, std::exchange +#endif // #include "error_code.h" @@ -17074,8 +17433,10 @@ namespace sqlite_orm { // #include "values_to_tuple.h" #include +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element +#endif // #include "functional/cxx_functional_polyfill.h" @@ -17089,7 +17450,7 @@ namespace sqlite_orm { // #include "row_extractor.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ @@ -17183,9 +17544,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; @@ -17272,13 +17635,15 @@ namespace sqlite_orm { // #include "udf_proxy.h" #include -#include // assert macro +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr #include // std::string #include // std::function #include // std::move, std::pair +#endif // #include "error_code.h" @@ -17293,7 +17658,7 @@ namespace sqlite_orm { std::allocator allocator; using traits = std::allocator_traits; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -17307,13 +17672,13 @@ namespace sqlite_orm { */ template std::pair obtain_udf_allocator() { - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto allocate = []() { + constexpr auto allocate = []() { std::allocator allocator; using traits = std::allocator_traits; return (void*)traits::allocate(allocator, 1); }; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -17411,19 +17776,16 @@ namespace sqlite_orm { }; // safety net of doing a triple check at runtime - inline void assert_args_count(const udf_proxy* proxy, int argsCount) { + inline void assert_args_count([[maybe_unused]] const udf_proxy* proxy, [[maybe_unused]] int argsCount) { assert((proxy->argumentsCount == -1) || (proxy->argumentsCount == argsCount || /*check fin call*/ argsCount == -1)); - (void)proxy; - (void)argsCount; } // safety net of doing a triple check at runtime - inline void proxy_assert_args_count(sqlite3_context* context, int argsCount) { + inline void proxy_assert_args_count([[maybe_unused]] sqlite3_context* context, int argsCount) { udf_proxy* proxy; assert((proxy = static_cast(sqlite3_user_data(context))) != nullptr); assert_args_count(proxy, argsCount); - (void)context; } // note: may throw `std::bad_alloc` in case memory space for the aggregate function object cannot be allocated @@ -17505,8 +17867,9 @@ namespace sqlite_orm { // #include "table_info.h" -namespace sqlite_orm { +// #include "storage_options.h" +namespace sqlite_orm { namespace internal { struct storage_base { @@ -17585,7 +17948,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_vacuum.html */ void vacuum() { - perform_void_exec(this->get_connection().get(), "VACUUM"); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), "VACUUM"); } /** @@ -17594,7 +17958,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, false); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, false); } /** @@ -17603,22 +17968,28 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table_if_exists(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, true); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, true); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { - this->rename_table(this->get_connection().get(), from, to); + auto connection = this->get_connection(); + this->rename_table(connection.get(), from, to); } protected: void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " + << streaming_identifier(newName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } /** @@ -17627,18 +17998,22 @@ namespace sqlite_orm { * @return true if table with a given name exists in db, false otherwise. */ bool table_exists(const std::string& tableName) { - auto con = this->get_connection(); - return this->table_exists(con.get(), tableName); + auto connection = this->get_connection(); + return this->table_exists(connection.get(), tableName); } bool table_exists(sqlite3* db, const std::string& tableName) const { bool result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") - << " AND name = " << quote_string_literal(tableName) << std::flush; - perform_exec( + std::string sql; + { + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( db, - ss.str(), + sql, [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if (argc) { @@ -17665,26 +18040,26 @@ namespace sqlite_orm { * sqlite3_changes function. */ int changes() { - auto con = this->get_connection(); - return sqlite3_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_changes(connection.get()); } /** * sqlite3_total_changes function. */ int total_changes() { - auto con = this->get_connection(); - return sqlite3_total_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_total_changes(connection.get()); } int64 last_insert_rowid() { - auto con = this->get_connection(); - return sqlite3_last_insert_rowid(con.get()); + auto connection = this->get_connection(); + return sqlite3_last_insert_rowid(connection.get()); } int busy_timeout(int ms) { - auto con = this->get_connection(); - return sqlite3_busy_timeout(con.get(), ms); + auto connection = this->get_connection(); + return sqlite3_busy_timeout(connection.get(), ms); } /** @@ -17694,24 +18069,27 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function& f) { + bool transaction(const std::function& function) { + if (!function) { + return false; + } auto guard = this->transaction_guard(); - return guard.commit_on_destroy = f(); + return guard.commit_on_destroy = function(); } std::string current_time() { - auto con = this->get_connection(); - return this->current_time(con.get()); + auto connection = this->get_connection(); + return this->current_time(connection.get()); } std::string current_date() { - auto con = this->get_connection(); - return this->current_date(con.get()); + auto connection = this->get_connection(); + return this->current_date(connection.get()); } std::string current_timestamp() { - auto con = this->get_connection(); - return this->current_timestamp(con.get()); + auto connection = this->get_connection(); + return this->current_timestamp(connection.get()); } #if SQLITE_VERSION_NUMBER >= 3007010 @@ -17722,8 +18100,8 @@ namespace sqlite_orm { * in 3.7.10 https://sqlite.org/changes.html */ int db_release_memory() { - auto con = this->get_connection(); - return sqlite3_db_release_memory(con.get()); + auto connection = this->get_connection(); + return sqlite3_db_release_memory(connection.get()); } #endif @@ -17733,11 +18111,12 @@ namespace sqlite_orm { * @return Returns list of tables in database. */ std::vector table_names() { - auto con = this->get_connection(); - std::vector tableNames; using data_t = std::vector; - perform_exec( - con.get(), + + auto connection = this->get_connection(); + data_t tableNames; + this->executor.perform_exec( + connection.get(), "SELECT name FROM sqlite_master WHERE type='table'", [](void* data, int argc, char** argv, char** /*columnName*/) -> int { auto& tableNames_ = *(data_t*)data; @@ -17759,20 +18138,19 @@ namespace sqlite_orm { * needed and closes when it is not needed. This function breaks this rule. In memory storage always * keeps connection opened so calling this for in-memory storage changes nothing. * Note about multithreading: in multithreading context avoiding using this function for not in-memory - * storage may lead to data races. If you have data races in such a configuration try to call `open_forever` + * storage may lead to data races. If you have data races in such a configuration try to call `open_forever()` * before accessing your storage - it may fix data races. */ void open_forever() { - this->isOpenedForever = true; - this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); + if (!this->isOpenedForever) { + this->isOpenedForever = true; + this->connection->retain(); } } /** * Create an application-defined scalar SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -17815,7 +18193,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -17830,15 +18208,16 @@ namespace sqlite_orm { /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * If `quotedF` contains a freestanding function, stateless lambda or stateless function object, * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; * otherwise, it repeatedly uses a copy of the contained function object, assuming possible side effects. */ - template + template + requires (orm_quoted_scalar_function) void create_scalar_function() { - using Sig = auto_udf_type_t; + using Sig = auto_udf_type_t<(quotedF)>; using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; constexpr auto argsCount = std::is_same>::value @@ -17871,7 +18250,7 @@ namespace sqlite_orm { /** * Create an application-defined aggregate SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -17920,7 +18299,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined aggregate function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -17935,7 +18314,7 @@ namespace sqlite_orm { /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -17947,7 +18326,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -17956,9 +18335,10 @@ namespace sqlite_orm { /** * Delete a quoted scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ - template + template + requires (orm_quoted_scalar_function) void delete_scalar_function() { this->delete_function_impl(quotedF.name(), this->scalarFunctions); } @@ -17966,7 +18346,7 @@ namespace sqlite_orm { /** * Delete aggregate function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_aggregate_function() { @@ -18009,7 +18389,7 @@ namespace sqlite_orm { function, functionExists ? collate_callback : nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -18043,7 +18423,7 @@ namespace sqlite_orm { void commit() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "COMMIT"); + this->executor.perform_void_exec(db, "COMMIT"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -18052,7 +18432,7 @@ namespace sqlite_orm { void rollback() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "ROLLBACK"); + this->executor.perform_void_exec(db, "ROLLBACK"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -18080,7 +18460,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -18090,7 +18470,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } @@ -18111,13 +18491,35 @@ namespace sqlite_orm { return this->connection->retain_count() > 0; } + /** + * Return the name of the VFS object used by the database connection. + */ + const std::string& vfs_name() const { + return this->connection->vfs_name; + } + + /** + * Return the current open_mode for this storage object. + */ + db_open_mode open_mode() const { + return this->connection->open_mode; + } + + /** + * Return true if this database object is opened in a readonly state. + */ + bool db_readonly() { + auto con = this->get_connection(); + return static_cast(sqlite3_db_readonly(con.get(), "main")); + } + /* * returning false when there is a transaction in place * otherwise true; function is not const because it has to call get_connection() */ bool get_autocommit() { - auto con = this->get_connection(); - return sqlite3_get_autocommit(con.get()); + auto connection = this->get_connection(); + return sqlite3_get_autocommit(connection.get()); } int busy_handler(std::function handler) { @@ -18134,26 +18536,43 @@ namespace sqlite_orm { } protected: - storage_base(std::string filename, int foreignKeysCount) : - pragma(std::bind(&storage_base::get_connection, this)), + storage_base(std::string filename, + connection_control connectionCtrl, + on_open_spec onOpenSpec, + will_run_query_spec willRunQuerySpec, + did_run_query_spec didRunQuerySpec, + int foreignKeysCount) : + on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), - inMemory(filename.empty() || filename == ":memory:"), - connection(std::make_unique(std::move(filename))), - cachedForeignKeysCount(foreignKeysCount) { + inMemory(filename.empty() || filename == ":memory:"), isOpenedForever{connectionCtrl.open_forever}, + connection(std::make_unique( + std::move(filename), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), + connectionCtrl)), + cachedForeignKeysCount(foreignKeysCount), + executor{std::move(willRunQuerySpec.willRunQuery), std::move(didRunQuerySpec.didRunQuery)} { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } storage_base(const storage_base& other) : - on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), - connection(std::make_unique(other.connection->filename)), - cachedForeignKeysCount(other.cachedForeignKeysCount) { + isOpenedForever{other.isOpenedForever}, + connection(std::make_unique( + *other.connection, + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), + cachedForeignKeysCount(other.cachedForeignKeysCount), + executor{std::move(other.executor.will_run_query), std::move(other.executor.did_run_query)} { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } @@ -18166,46 +18585,44 @@ namespace sqlite_orm { } } - void begin_transaction_internal(const std::string& query) { + void begin_transaction_internal(const std::string& sql) { this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } sqlite3* db = this->connection->get(); - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql.data()); } connection_ref get_connection() { connection_ref res{*this->connection}; - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } return res; } #if SQLITE_VERSION_NUMBER >= 3006019 void foreign_keys(sqlite3* db, bool value) { - std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } bool foreign_keys(sqlite3* db) { bool result = false; - perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + this->executor.perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } - #endif - void on_open_internal(sqlite3* db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if (this->cachedForeignKeysCount) { this->foreign_keys(db, true); } #endif + if (this->pragma.synchronous_ != -1) { - this->pragma.synchronous(this->pragma.synchronous_); + this->pragma.set_pragma("synchronous", this->pragma.synchronous_, db); } if (this->pragma.journal_mode_ != -1) { @@ -18215,7 +18632,7 @@ namespace sqlite_orm { for (auto& p: this->collatingFunctions) { int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -18249,7 +18666,7 @@ namespace sqlite_orm { : int(std::tuple_size::value); using is_stateless = std::is_empty; auto udfMemorySpace = preallocate_udf_memory(); - if SQLITE_ORM_CONSTEXPR_IF (is_stateless::value) { + if constexpr (is_stateless::value) { constructAt(udfMemorySpace.first); } this->scalarFunctions.emplace_back( @@ -18322,10 +18739,10 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else - auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { return udfProxy.name == name; }); #endif @@ -18342,7 +18759,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } it = functions.erase(it); @@ -18352,29 +18769,29 @@ namespace sqlite_orm { } static void try_to_create_scalar_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - udfProxy.func, - nullptr, - nullptr, - nullptr); + const int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + udfProxy.func, + nullptr, + nullptr, + nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } static void try_to_create_aggregate_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - udfProxy.func, - aggregate_function_final_callback); + const int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + udfProxy.func, + aggregate_function_final_callback); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -18382,50 +18799,64 @@ namespace sqlite_orm { std::string current_time(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); return result; } std::string current_date(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); return result; } std::string current_timestamp(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); return result; } void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { - std::stringstream ss; - ss << "DROP TABLE"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TABLE"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << streaming_identifier(tableName) << std::flush; + sql = ss.str(); } - ss << ' ' << streaming_identifier(tableName) << std::flush; - perform_void_exec(db, ss.str()); + this->executor.perform_void_exec(db, sql.data()); } void drop_index_internal(const std::string& indexName, bool ifExists) { - std::stringstream ss; - ss << "DROP INDEX"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP INDEX"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(indexName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { - std::stringstream ss; - ss << "DROP TRIGGER"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TRIGGER"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(triggerName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } static int @@ -18457,7 +18888,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { @@ -18465,8 +18896,8 @@ namespace sqlite_orm { }); #endif if (dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = + table_xinfo& dbColumnInfo = *dbColumnInfoIt; + bool columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && @@ -18477,8 +18908,7 @@ namespace sqlite_orm { break; } dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); + storageTableInfo.erase(storageTableInfo.begin() + storageColumnInfoIndex); --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); @@ -18495,6 +18925,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; + const sqlite_executor executor; }; } } @@ -18503,8 +18934,10 @@ namespace sqlite_orm { // #include "expression_object_type.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper +#endif // #include "type_traits.h" @@ -18620,6 +19053,7 @@ namespace sqlite_orm { // #include "statement_serializer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream #include // std::string @@ -18631,6 +19065,7 @@ namespace sqlite_orm { #include #include #include // std::list +#endif // #include "functional/cxx_string_view.h" // #include "functional/cxx_optional.h" @@ -18639,6 +19074,31 @@ namespace sqlite_orm { // std::remove_cvref, std::disjunction // #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke +// #include "functional/always_default.h" + +namespace sqlite_orm { + + namespace internal { + + /* + * Function object whose variadic call operator always returns the default constructed value of its template parameter type. + */ + template + struct always_default_of { + template + constexpr R operator()(Args&&...) const { + return R(); + } + + using is_transparent = int; + }; + + template + constexpr always_default_of always_default{}; + } + +} + // #include "functional/mpl.h" // #include "tuple_helper/tuple_filter.h" @@ -18659,7 +19119,9 @@ namespace sqlite_orm { namespace internal { struct rank_t {}; } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rank_t rank() { return {}; } @@ -18695,12 +19157,14 @@ namespace sqlite_orm { // #include "column_names_getter.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string #include // std::vector #include // std::reference_wrapper #include #include // std::move +#endif // #include "tuple_helper/tuple_traits.h" @@ -18737,7 +19201,7 @@ namespace sqlite_orm { table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + quote_identifier(column.name)); } else if (qualified) { @@ -18749,7 +19213,7 @@ namespace sqlite_orm { }); } else { collectedExpressions.reserve(collectedExpressions.size() + 1); - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.skip_table_name) { const basic_table& table = pick_table>(context.db_objects); @@ -18801,8 +19265,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -18815,8 +19278,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -18835,12 +19297,14 @@ namespace sqlite_orm { // #include "cte_column_names_collector.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include #include // std::reference_wrapper #include #endif +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -18998,8 +19462,9 @@ namespace sqlite_orm { std::vector columnNames = get_cte_column_names(sel.col, context); // 2. override column names from cte expression - if (size_t n = std::tuple_size_v) { - if (n != columnNames.size()) { + constexpr size_t nExplicitColumns = std::tuple_size_v; + if constexpr (nExplicitColumns > 0) { + if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } @@ -19036,11 +19501,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; @@ -19051,8 +19525,12 @@ namespace sqlite_orm { // #include "order_by_serializer.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include #include // std::string #include // std::stringstream +#include // std::exchange +#endif namespace sqlite_orm { @@ -19067,6 +19545,20 @@ namespace sqlite_orm { return serializer(t, context); } + inline void seralize_collate(std::ostream& ss, const order_by_base& orderBy) { + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch (orderBy._order) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + } + template struct order_by_serializer, void> { using statement_type = order_by_t; @@ -19077,18 +19569,8 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; - ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; - } - switch (orderBy.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } + ss << serialize(orderBy._expression, newContext); + seralize_collate(ss, orderBy); return ss.str(); } }; @@ -19101,30 +19583,22 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - int index = 0; + static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; const dynamic_order_by_entry_t& entry: orderBy) { + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#else + bool first = true; for (const dynamic_order_by_entry_t& entry: orderBy) { - if (index > 0) { - ss << ", "; - } - - ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; - } - switch (entry.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - ++index; - }; + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#endif return ss.str(); } }; - } } @@ -19144,10 +19618,12 @@ namespace sqlite_orm { // #include "schema/triggers.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #include #include +#endif // #include "../optional_container.h" @@ -19214,11 +19690,6 @@ namespace sqlite_orm { * Statements of the triggers (to be executed when the trigger fires) */ elements_type elements; - -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - trigger_t(std::string name, T trigger_base, elements_type statements) : - base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} -#endif }; /** @@ -19347,10 +19818,6 @@ namespace sqlite_orm { type_t type = type_t::ignore; std::string message; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} -#endif }; template @@ -19366,8 +19833,10 @@ namespace sqlite_orm { expression_type expression; }; - } // NAMESPACE internal + } +} +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NEW.expression function used within TRIGGER expressions */ @@ -19522,7 +19991,7 @@ namespace sqlite_orm { template std::string do_serialize(const pointer_binding&) const { // always serialize null (security reasons) - return field_printer{}(nullptr); + return field_printer{}(nullptr); } #endif }; @@ -19541,7 +20010,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } return ss.str(); @@ -19709,18 +20178,18 @@ namespace sqlite_orm { ss << "ON CONFLICT"; iterate_tuple(statement.target_args, [&ss, &context](auto& value) { using value_type = polyfill::remove_cvref_t; - auto needParenthesis = std::is_member_pointer::value; + constexpr bool needParenthesis = std::is_member_pointer::value; ss << ' '; - if (needParenthesis) { + if constexpr (needParenthesis) { ss << '('; } ss << serialize(value, context); - if (needParenthesis) { + if constexpr (needParenthesis) { ss << ')'; } }); ss << ' ' << "DO"; - if (std::tuple_size::value == 0) { + if constexpr (std::tuple_size::value == 0) { ss << " NOTHING"; } else { auto updateContext = context; @@ -20113,11 +20582,11 @@ namespace sqlite_orm { std::stringstream ss; ss << expression.serialize(); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.argument), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -20139,11 +20608,11 @@ namespace sqlite_orm { std::stringstream ss; ss << static_cast(expression) << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.c), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -20168,19 +20637,19 @@ namespace sqlite_orm { is_binary_operator>::value; std::stringstream ss; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << "("; } ss << serialize(statement.lhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << ")"; } ss << " " << statement.serialize() << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << "("; } ss << serialize(statement.rhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << ")"; } return ss.str(); @@ -20231,13 +20700,13 @@ namespace sqlite_orm { ss << "NOT IN"; } ss << " "; - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << '('; } auto newContext = context; newContext.use_parentheses = true; ss << serialize(statement.argument, newContext); - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << ')'; } return ss.str(); @@ -20284,7 +20753,7 @@ namespace sqlite_orm { using args_type = std::tuple; constexpr bool theOnlySelect = std::tuple_size::value == 1 && is_select>::value; - if (!theOnlySelect) { + if constexpr (!theOnlySelect) { ss << "("; } ss << streaming_expressions_tuple(statement.argument, context); @@ -20430,8 +20899,8 @@ namespace sqlite_orm { ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); } using columns_tuple = typename statement_type::columns_tuple; - const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + constexpr size_t columnsCount = std::tuple_size::value; + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -20448,7 +20917,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); @@ -20653,7 +21122,7 @@ namespace sqlite_orm { << streaming_non_generated_column_names(table) << ")" << " VALUES (" << streaming_field_values_excluding(check_if{}, - empty_callable, // don't exclude + always_default, // don't exclude context, get_ref(statement.object)) << ")"; @@ -20900,7 +21369,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if (is_insert_raw::value) { + if constexpr (is_insert_raw::value) { ss << "INSERT"; } else { ss << "REPLACE"; @@ -20908,12 +21377,12 @@ namespace sqlite_orm { iterate_tuple(statement.args, [&context, &ss](auto& value) { using value_type = polyfill::remove_cvref_t; ss << ' '; - if (is_columns::value) { + if constexpr (is_columns::value) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if (is_values::value || is_select::value) { + } else if constexpr (is_values::value || is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -21073,12 +21542,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } @@ -21162,23 +21638,22 @@ namespace sqlite_orm { subCtx.use_parentheses = true; std::stringstream ss; - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << "("; } ss << "SELECT "; - call_if_constexpr>( + if constexpr (is_rowset_deduplicator_v) { // note: make use of implicit to-string conversion - [&ss](std::string keyword) { - ss << keyword << ' '; - }, - sel.col); + std::string keyword = sel.col; + ss << keyword << ' '; + } } ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; - if (!hasExplicitFrom) { + if constexpr (!hasExplicitFrom) { auto tableNames = collect_table_names(sel, context); using joins_index_sequence = filter_tuple_sequence_t; // deduplicate table names of constrained join statements @@ -21195,7 +21670,7 @@ namespace sqlite_orm { } } ss << streaming_conditions_tuple(sel.conditions, context); - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -21211,20 +21686,18 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << serialize(statement.column_or_expression, context); + ss << serialize(statement._column_or_expression, context); if (!statement._collation_name.empty()) { ss << " COLLATE " << statement._collation_name; } if (statement._order) { switch (statement._order) { - case -1: - ss << " DESC"; - break; case 1: ss << " ASC"; break; - default: - throw std::system_error{orm_error_code::incorrect_order}; + case -1: + ss << " DESC"; + break; } } return ss.str(); @@ -21280,7 +21753,7 @@ namespace sqlite_orm { std::string whereString; iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { using value_type = polyfill::remove_cvref_t; - if (!is_where::value) { + if constexpr (!is_where::value) { auto newContext = context; newContext.use_parentheses = false; auto whereString = serialize(value, newContext); @@ -21468,7 +21941,7 @@ namespace sqlite_orm { ss << " BEGIN "; iterate_tuple(statement.elements, [&ss, &context](auto& element) { using element_type = polyfill::remove_cvref_t; - if (is_select::value) { + if constexpr (is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(element, newContext); @@ -21620,8 +22093,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; - if (HO) { - if (OI) { + if constexpr (HO) { + if constexpr (OI) { limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); @@ -21739,12 +22212,14 @@ namespace sqlite_orm { // #include "cte_storage.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include +#include // std::remove_const #include #include #include #endif +#endif // #include "tuple_helper/tuple_fy.h" @@ -21764,9 +22239,11 @@ namespace sqlite_orm { // #include "column_expression.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper +#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -21915,13 +22392,16 @@ namespace sqlite_orm { typename SubselectColRefs, typename FinalColRefs, typename Result> - using create_cte_mapper_t = - typename create_cte_mapper:: - type; + using create_cte_mapper_t = typename create_cte_mapper, + ExplicitColRefs, + Expression, + SubselectColRefs, + FinalColRefs, + Result>::type; // aliased column expressions, explicit or implicitly numbered template = true> - static auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { + auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { using object_type = aliased_field, F>; return sqlite_orm::make_column<>(std::move(name), &object_type::field); @@ -21929,8 +22409,7 @@ namespace sqlite_orm { // F O::* template = true> - static auto make_cte_column(std::string name, const ColRef& finalColRef) { - using object_type = table_type_of_t; + auto make_cte_column(std::string name, const ColRef& finalColRef) { using column_type = column_t; return column_type{std::move(name), finalColRef, empty_setter{}}; @@ -22208,21 +22687,91 @@ namespace sqlite_orm { polyfill::void_t().prepare(std::declval()))>>> = true; + template + decltype(auto) storage_opt_or_default(OptionsTpl& options) { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + if constexpr (tuple_has_type::value) { + return std::move(std::get(options)); + } else { + return Opt{}; + } +#else + return Opt{}; +#endif + } + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + /* + * AST iteration callable that matches function call node expressions. + * * Throws a `orm_error_code::function_not_found` exception + * if an application-defined scalar or aggregate function was not registered. + * * Throws a `sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)` if a named collation function was not registered. + */ + struct udf_presence_checker { + const std::list& _scalarFunctions; + const std::list& _aggregateFunctions; + const std::map& _collatingFunctions; + + // examine `function_call` node expressions + template + void operator()(polyfill::bool_constant, const function_call& udfCall) const { + auto&& name = udfCall.name(); + SQLITE_ORM_CPP_UNLIKELY { + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + } + } + + // examine `named_collate` node expressions + void operator()(polyfill::bool_constant, const named_collate_base& collateCall) const { + if (_collatingFunctions.find(collateCall.name) == _collatingFunctions.end()) SQLITE_ORM_CPP_UNLIKELY { +#if SQLITE_VERSION_NUMBER >= 3008008 + throw std::system_error{sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ), std::string(collateCall.name)}; +#else + throw std::system_error{sqlite_errc(SQLITE_ERROR), std::string(collateCall.name)}; +#endif + } + } + + // swallow leaf expressions + template + void operator()(const T&) const {} + + static bool _contains(const std::list& functions, const std::string_view& name) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + auto it = std::ranges::find(functions, name, &udf_proxy::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { + return udfProxy.name == name; + }); +#endif + return it != functions.end(); + } + }; +#endif + /** * 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 self_type = storage_t; using db_objects_type = db_objects_tuple; /** * @param filename database filename. * @param dbObjects db_objects_tuple */ - storage_t(std::string filename, db_objects_type dbObjects) : - storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + template + storage_t(std::string filename, db_objects_type dbObjects, OptionsTpl options) : + storage_base{std::move(filename), + storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), + foreign_keys_count()}, + db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; @@ -22241,7 +22790,7 @@ namespace sqlite_orm { * * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. */ - friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + friend const db_objects_type& obtain_db_objects(const self_type& storage) noexcept { return storage.db_objects; } @@ -22251,8 +22800,8 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - std::string sql = serializer.serialize(table, context, tableName); - perform_void_exec(db, std::move(sql)); + const std::string sql = serializer.serialize(table, context, tableName); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -22268,10 +22817,14 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " - << streaming_identifier(columnName) << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } #endif @@ -22318,9 +22871,8 @@ namespace sqlite_orm { template void assert_updatable_type() const { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - using Table = storage_pick_table_t; - using elements_type = elements_type_t
; + using table_type = storage_pick_table_t; + using elements_type = elements_type_t; using col_index_sequence = filter_tuple_sequence_t; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; @@ -22334,7 +22886,6 @@ namespace sqlite_orm { static_assert( nonPrimaryKeysColumnsCount > 0, "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); -#endif } template, class... Args> - mapped_view iterate(Args&&... args) { + mapped_view iterate(Args&&... args) { this->assert_mapped_type(); - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + auto connection = this->get_connection(); + return {*this, std::move(connection), std::forward(args)...}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -22404,8 +22955,8 @@ namespace sqlite_orm { requires (is_select_v) #endif result_set_view, db_objects_type> iterate(with_t expression) { - auto con = this->get_connection(); - return {this->db_objects, std::move(con), std::move(expression)}; + auto connection = this->get_connection(); + return {this->db_objects, std::move(connection), std::move(expression)}; } #endif #endif @@ -22908,17 +23459,15 @@ namespace sqlite_orm { std::enable_if_t::value && !is_mapped::value, bool> = true> std::string dump(E&& expression, bool parametrized = false) const { - static_assert(is_preparable_v, "Expression must be a high-level statement"); + static_assert(is_preparable_v, "Expression must be a high-level statement"); - decltype(auto) e2 = static_if::value>( - [](auto expression) -> auto { - expression.highest_level = true; - return expression; - }, - [](const auto& expression) -> decltype(auto) { - return (expression); - })(std::forward(expression)); - return this->dump_highest_level(e2, parametrized); + if constexpr (is_select::value) { + auto e2 = std::forward(expression); + e2.highest_level = true; + return this->dump_highest_level(e2, parametrized); + } else { + return this->dump_highest_level(expression, parametrized); + } } /** @@ -23239,46 +23788,56 @@ namespace sqlite_orm { template sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(virtualTable, context); - perform_void_exec(db, query); + const auto sql = serialize(virtualTable, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(index, context); - perform_void_exec(db, query); + const auto sql = serialize(index, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; // TODO Change accordingly using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; - auto query = serialize(trigger, context); - perform_void_exec(db, query); + const auto sql = serialize(trigger, context); + this->executor.perform_void_exec(db, sql.data()); return res; } template = true> sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); + template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { using context_t = serializer_context; context_t context{this->db_objects}; - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " + << serialize(column, context) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } template @@ -23288,8 +23847,9 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); + std::vector res; - perform_steps( + this->executor.perform_steps( stmt, [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { res.push_back(rowExtractor.extract(stmt, 0)); @@ -23301,7 +23861,9 @@ namespace sqlite_orm { template std::string dump_highest_level(E&& expression, bool parametrized) const { const auto& exprDBOs = db_objects_for_expression(this->db_objects, expression); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() @@ -23311,16 +23873,24 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + iterate_ast( + statement, + udf_presence_checker{this->scalarFunctions, this->aggregateFunctions, this->collatingFunctions}); +#endif + const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto con = this->get_connection(); - std::string sql = serialize(statement, context); - sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); - return prepared_statement_t{std::forward(statement), stmt, con}; + auto conection = this->get_connection(); + const std::string sql = serialize(statement, context); + sqlite3_stmt* stmt = prepare_stmt(conection.get(), sql); + return prepared_statement_t{std::forward(statement), stmt, std::move(conection)}; } public: @@ -23488,6 +24058,9 @@ namespace sqlite_orm { using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -23495,6 +24068,9 @@ namespace sqlite_orm { prepared_statement_t prepare(E statement) { using object_type = expression_object_type_t; this->assert_mapped_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -23509,7 +24085,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -23522,7 +24108,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #endif @@ -23530,9 +24126,23 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template int64 execute(const prepared_statement_t>& statement) { using object_type = statement_object_type_t; @@ -23544,7 +24154,17 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -23563,31 +24183,42 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + if constexpr (is_replace_range::value) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - + } else { + const object_type& object = get_object(statement.expression); + processObject(object); + }; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template, is_insert_range>::value, bool> = true> int64 execute(const prepared_statement_t& statement) { @@ -23598,6 +24229,7 @@ namespace sqlite_orm { auto processObject = [&table = this->get_table(), bindValue = field_value_binder{stmt}](auto& object) mutable { using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + table.template for_each_column_excluding< mpl::conjunction>, mpl::disjunction_fn>>( @@ -23608,29 +24240,36 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + if constexpr (is_insert_range::value) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - + } else { + const object_type& object = get_object(statement.expression); + processObject(object); + } + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -23638,7 +24277,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -23661,7 +24310,17 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -23671,11 +24330,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return res; } @@ -23687,10 +24356,20 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -23703,17 +24382,37 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } + const auto stepRes = sqlite3_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } switch (stepRes) { case SQLITE_ROW: { T res; @@ -23725,7 +24424,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::not_found}; } break; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } #endif @@ -23735,7 +24434,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } template @@ -23744,7 +24453,17 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); + std::string sql; + if (this->executor.will_run_query || this->executor.did_run_query) { + sql = statement.sql(); + } + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } perform_step(stmt); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -23771,17 +24490,17 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { O obj; object_from_column_builder builder{obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -23792,17 +24511,17 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_unique(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -23814,30 +24533,65 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_optional(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template + using dbo_index_sequence = filter_tuple_sequence_t::template fn>; + + template + using opt_index_sequence = filter_tuple_sequence_t::template fn>; + + template + storage_t make_storage(std::string filename, std::tuple dbObjects, OptionsTpl options) { + return {std::move(filename), std::move(dbObjects), std::move(options)}; + } +#endif } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /* + * Factory function for a storage instance, from a database file, a set of database object definitions + * and option storage options like connection control options and an 'on open' callback. + * + * E.g. + * auto storage = make_storage("", connection_control{.open_forever = true}, on_open([](sqlite3* db) {})); + */ + template + auto make_storage(std::string filename, Spec... specifications) { + using namespace ::sqlite_orm::internal; + std::tuple specTuple{std::forward(specifications)...}; + return internal::make_storage( + std::move(filename), + create_from_tuple(std::move(specTuple), dbo_index_sequence{}), + create_from_tuple(std::move(specTuple), opt_index_sequence{})); + } +#else /* - * Factory function for a storage, from a database file and a bunch of database object definitions. + * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { - return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; } +#endif /** * sqlite3_threadsafe() interface. @@ -23858,15 +24612,17 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique - -// #include "../functional/static_magic.h" +#endif // #include "../tuple_helper/tuple_traits.h" // #include "../default_value_extractor.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif // #include "constraints.h" @@ -23905,12 +24661,10 @@ namespace sqlite_orm { static constexpr size_t default_op_index = find_tuple_template::value; std::unique_ptr value; - call_if_constexpr::value>( - [&value](auto& constraints) { - value = - std::make_unique(serialize_default_value(std::get(constraints))); - }, - this->constraints); + if constexpr (default_op_index != std::tuple_size::value) { + value = std::make_unique( + serialize_default_value(std::get(this->constraints))); + } return value; } @@ -23923,9 +24677,11 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move #include // std::find_if, std::ranges::find +#endif // #include "../tuple_helper/tuple_filter.h" @@ -23966,19 +24722,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } @@ -23991,17 +24752,21 @@ namespace sqlite_orm { * e.g. usage of the dbstat table. */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream #include // std::flush #include // std::reference_wrapper, std::cref #include // std::find_if, std::ranges::find +#endif // #include "../type_traits.h" // #include "../sqlite_schema_table.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string +#endif // #include "schema/column.h" @@ -24011,7 +24776,7 @@ namespace sqlite_orm { // #include "alias.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. * @@ -24050,9 +24815,11 @@ namespace sqlite_orm { // #include "../eponymous_vtabs/dbstat.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string #endif +#endif // #include "../schema/column.h" @@ -24060,7 +24827,7 @@ namespace sqlite_orm { // #include "../column_pointer.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { std::string name; @@ -24105,18 +24872,25 @@ namespace sqlite_orm { namespace sqlite_orm { namespace internal { - template template> - sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { - if (std::is_same, sqlite_master>::value) { - return sync_schema_result::already_in_sync; - } + sync_schema_result storage_t::sync_table([[maybe_unused]] const Table& table, + [[maybe_unused]] sqlite3* db, + [[maybe_unused]] bool preserve) { + if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB - if (std::is_same, dbstat>::value) { + std::is_same, dbstat>::value || +#endif + std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; + } else { + return this->sync_regular_table(table, db, preserve); } -#endif // SQLITE_ENABLE_DBSTAT_VTAB + } + + template + template> + sync_schema_result storage_t::sync_regular_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; @@ -24211,7 +24985,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), @@ -24225,25 +24999,29 @@ namespace sqlite_orm { } }); - std::stringstream ss; - ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" - << streaming_identifiers(columnNames) << ") " - << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " + << streaming_identifier(sourceTableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql.data()); } } } #pragma once +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get +#endif // #include "functional/cxx_type_traits_polyfill.h" -// #include "functional/static_magic.h" - // #include "type_traits.h" // #include "prepared_statement.h" @@ -24252,12 +25030,15 @@ namespace sqlite_orm { // #include "node_tuple.h" +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple #include // std::pair #include // std::reference_wrapper // #include "functional/cxx_optional.h" +#endif + // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_filter.h" @@ -24370,6 +25151,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; + template struct node_tuple, void> : node_tuple {}; @@ -24526,7 +25310,7 @@ namespace sqlite_orm { // #include "expression_object_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template auto& get(internal::prepared_statement_t>& statement) { @@ -24648,16 +25432,13 @@ namespace sqlite_orm { const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = &n; - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = &node; + } + } } }); return internal::get_ref(*result); @@ -24674,16 +25455,13 @@ namespace sqlite_orm { internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = const_cast>(&n); - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = const_cast(&node); + } + } } }); return internal::get_ref(*result); @@ -24697,6 +25475,7 @@ namespace sqlite_orm { * Hence we make it only available for compilers supporting inline variables. */ +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #include // std::move @@ -24705,12 +25484,13 @@ namespace sqlite_orm { #endif #endif #endif +#endif // #include "pointer_value.h" #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; diff --git a/modules/sqlite_orm.cppm b/modules/sqlite_orm.cppm new file mode 100644 index 000000000..fb3929fae --- /dev/null +++ b/modules/sqlite_orm.cppm @@ -0,0 +1,24 @@ +// In a module-file, the optional `module;` must appear first; see [cpp.pre]. +module; + +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +// Including assert.h when using `import std;` doesn't work with msvc (as it leads to multiple defined symbols) (see sqlite_orm.ixx), hence we include it here +#include // assert macro +#endif + +export module sqlite_orm; + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +import std.compat; +#endif + +#pragma clang diagnostic push +// warning: '#include ' attaches the declarations to the named module 'sqlite_orm', which is not usually intended; consider moving that directive before the module declaration +#pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" + +#include + +#pragma clang diagnostic pop diff --git a/modules/sqlite_orm.cxx b/modules/sqlite_orm.cxx new file mode 100644 index 000000000..7a116389b --- /dev/null +++ b/modules/sqlite_orm.cxx @@ -0,0 +1,18 @@ +// In a module-file, the optional `module;` must appear first; see [cpp.pre]. +module; + +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +// Including assert.h when using `import std;` doesn't work with msvc (as it leads to multiple defined symbols) (see sqlite_orm.ixx), hence we include it here +#include // assert macro +#endif + +export module sqlite_orm; + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +import std.compat; +#endif + +#include diff --git a/modules/sqlite_orm.ixx b/modules/sqlite_orm.ixx new file mode 100644 index 000000000..3c830e5a7 --- /dev/null +++ b/modules/sqlite_orm.ixx @@ -0,0 +1,23 @@ +// In a module-file, the optional `module;` must appear first; see [cpp.pre]. +module; + +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +// Including assert.h when using `import std;` below doesn't work (as it leads to multiple defined symbols), hence we include it here +#include // assert macro +#endif + +export module sqlite_orm; + +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +import std.compat; +#endif + +#pragma warning(push) +#pragma warning(disable : 5244) // '#include ' in the purview of module 'sqlite_orm' appears erroneous. + +#include + +#pragma warning(pop) diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 045194261..317938b9c 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -26,7 +26,22 @@ install(TARGETS sqlite_orm INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/include/sqlite_orm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/sqlite_orm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# install C++ named module interface unit file and possible various accompanying files produced by CMake. +# note that there is no convention for the install location of module source files at the time of writing. +# the module source file will get installed to subfolder "${SQLITE_ORM_INSTALL_CMAKEDIR}/src" following Craig Scott's [writeup](https://crascit.com/2024/04/04/cxx-modules-cmake-shared-libraries/#h-installing-shared-libraries-with-c-20-modules). +# also see @starball's detailed stackoverflow [answer](https://stackoverflow.com/a/78209338/279251). +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28.2) + install(TARGETS sqlite_orm + FILE_SET CXX_MODULES + DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR}/src + ) + install(EXPORT SqliteOrmTargets + DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR} + CXX_MODULES_DIRECTORY . + ) +endif() install(EXPORT SqliteOrmTargets DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 768721a83..fc6fb4cac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,8 +5,48 @@ FetchContent_MakeAvailable(Catch2) option(SQLITE_ORM_OMITS_CODECVT "Omits codec testing" OFF) +include(ucm) + + +# Consuming the standard library as C++ named module is a C++23 standard library feature and additionally requires support from build systems. +# At the time of adding support for sqlite_orm as a C++ named module, CMake has still only experimental features to +# effect the provision of the the C++ Standard Library Modules [see `CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`]. +# +# However, Visual C++ has an option that instructs MSBuild to automatically provision the standard modules, +# and this option is ON by default when using the `/std:c++latest` compiler switch: +# "Build ISO C++23 Standard Library Modules", MSBuild property `BuildStlModules`. +if(SQLITE_ORM_ENABLE_CXX_23 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28.2 AND MSVC AND MSVC_VERSION GREATER_EQUAL 1930) + add_executable(module_tests + named_module/named_module.cpp + ) + + target_sources(module_tests + PRIVATE FILE_SET all_modules TYPE CXX_MODULES + BASE_DIRS ../modules + FILES ../modules/sqlite_orm.ixx + ) + + target_compile_features(module_tests PUBLIC cxx_std_23) + + if(SQLITE_ORM_OMITS_CODECVT) + message(STATUS "SQLITE_ORM_OMITS_CODECVT is enabled") + target_compile_definitions(module_tests PRIVATE SQLITE_ORM_OMITS_CODECVT=1) + endif() + + target_precompile_headers(module_tests PRIVATE + ) + + # note: sqlite3 already linked in top-level CMakeLists + target_link_libraries(module_tests PRIVATE sqlite_orm) + + add_test(NAME "module_tests" + COMMAND module_tests + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endif() + # Glob all .cpp files recursively in the current directory file(GLOB_RECURSE UNIT_TEST_SOURCES "*.cpp") +ucm_remove_directories(named_module FROM UNIT_TEST_SOURCES) add_executable(unit_tests ${UNIT_TEST_SOURCES}) @@ -29,8 +69,6 @@ if (MSVC) target_compile_options(unit_tests PUBLIC # warning-level 4 /W4 - # C4127: conditional expression is constant - /wd4127 # C4456: declaration of 'symbol' hides previous local declaration /wd4456 # C4458: declaration of 'symbol' hides class member diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index c56580bfd..3e197f8ca 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -10,16 +10,45 @@ using internal::column_alias; using internal::column_pointer; using internal::iterate_ast; +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +template +struct overloaded : L... { + using L::operator()...; +}; +#if __cpp_deduction_guides < 201907L +template +overloaded(L...) -> overloaded; +#endif +#endif + TEST_CASE("ast_iterator") { struct User { int id = 0; std::string name; }; + std::vector typeIndexes; decltype(typeIndexes) expected; - auto lambda = [&typeIndexes](auto& value) { + const auto lambda = [&typeIndexes](auto& value) { typeIndexes.push_back(typeid(value)); }; +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED + const auto nodeLambda = overloaded{ + [&typeIndexes](polyfill::bool_constant, + const internal::function_call& udfCall) { + typeIndexes.push_back(typeid(udfCall.name)); + }, + [&typeIndexes](polyfill::bool_constant, const internal::named_collate_base& collateCall) { + typeIndexes.push_back(typeid(collateCall)); + }, + [&typeIndexes](polyfill::bool_constant, + const internal::highlight_t&) { + typeIndexes.push_back(typeid(T)); + }, + // swallow leaf expressions + [](auto&) {}}; +#endif + SECTION("bindables") { auto node = select(1); expected.push_back(typeid(int)); @@ -183,6 +212,22 @@ TEST_CASE("ast_iterator") { auto node = order_by(1); iterate_ast(node, lambda); } + SECTION("normal") { + auto node = order_by(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("multi, single") { + auto node = multi_order_by(order_by(&User::id)); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("multi, several") { + auto node = multi_order_by(order_by(&User::id), order_by(&User::name)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + iterate_ast(node, lambda); + } SECTION("sole column alias") { auto node = order_by(get()); iterate_ast(node, lambda); @@ -395,11 +440,25 @@ TEST_CASE("ast_iterator") { #endif SECTION("highlight") { auto expression = highlight(0, std::string(""), std::string("")); - expected.push_back(typeid(expression)); expected.push_back(typeid(int)); expected.push_back(typeid(std::string)); expected.push_back(typeid(std::string)); iterate_ast(expression, lambda); } +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED + SECTION("node expressions") { + struct Func { + static const char* name(); + const std::string& operator()(const std::string& value) const; + }; + auto expression1 = func(&User::name); + auto expression2 = is_equal("", "").collate(""); + auto expression3 = highlight(0, std::string(""), std::string("")); + expected.assign({typeid(internal::udf_holder), typeid(internal::named_collate_base), typeid(User)}); + iterate_ast(expression1, nodeLambda); + iterate_ast(expression2, nodeLambda); + iterate_ast(expression3, nodeLambda); + } +#endif REQUIRE(typeIndexes == expected); } diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index b935e3e82..dc8215c66 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -1,6 +1,6 @@ #include #include -#include // remove +#include // std::remove using namespace sqlite_orm; @@ -9,18 +9,17 @@ namespace { int id = 0; std::string name; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } #endif }; - bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } - struct MarvelHero { - int id; + int id = 0; std::string name; std::string abilities; }; @@ -47,7 +46,7 @@ TEST_CASE("backup") { 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()); + std::remove(backupFilename.c_str()); auto storage2 = makeStorage(backupFilename); auto storage = makeStorage(""); storage.sync_schema(); @@ -87,7 +86,7 @@ TEST_CASE("backup") { REQUIRE_THAT(rowsFromBackup, UnorderedEquals(expectedRows)); } SECTION("from") { - ::remove(backupFilename.c_str()); + std::remove(backupFilename.c_str()); auto storage = makeStorage(backupFilename); storage.sync_schema(); storage.replace(User{1, "Sharon"}); @@ -138,16 +137,16 @@ TEST_CASE("Backup crash") { storage.remove_all(); // --- Insert values - storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); - storage.insert(MarvelHero{-1, "Thor", "Storm god"}); - storage.insert(MarvelHero{-1, "Vision", "Min Stone"}); - storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield"}); - storage.insert(MarvelHero{-1, "Hulk", "Strength"}); - storage.insert(MarvelHero{-1, "Star Lord", "Humor"}); - storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman"}); - storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye"}); - storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow"}); - storage.insert(MarvelHero{-1, "Groot", "I am Groot!"}); + storage.insert(MarvelHero{0, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); + storage.insert(MarvelHero{0, "Thor", "Storm god"}); + storage.insert(MarvelHero{0, "Vision", "Min Stone"}); + storage.insert(MarvelHero{0, "Captain America", "Vibranium shield"}); + storage.insert(MarvelHero{0, "Hulk", "Strength"}); + storage.insert(MarvelHero{0, "Star Lord", "Humor"}); + storage.insert(MarvelHero{0, "Peter Parker", "Spiderman"}); + storage.insert(MarvelHero{0, "Clint Barton", "Hawkeye"}); + storage.insert(MarvelHero{0, "Natasha Romanoff", "Black widow"}); + storage.insert(MarvelHero{0, "Groot", "I am Groot!"}); REQUIRE(storage.count() == 10); // --- Create backup file name and verify that the file does not exist diff --git a/tests/built_in_functions_tests/core_functions_tests.cpp b/tests/built_in_functions_tests/core_functions_tests.cpp index ad47b5f6a..a86b363a3 100644 --- a/tests/built_in_functions_tests/core_functions_tests.cpp +++ b/tests/built_in_functions_tests/core_functions_tests.cpp @@ -8,11 +8,6 @@ TEST_CASE("substr") { std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Test() = default; - Test(std::string text, int x, int y) : text{std::move(text)}, x{x}, y{y} {} -#endif }; auto storage = make_storage( {}, @@ -51,11 +46,6 @@ TEST_CASE("substr") { TEST_CASE("zeroblob") { struct Test { int value = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Test() = default; - Test(int value) : value{value} {} -#endif }; auto storage = make_storage({}, make_table("test", make_column("value", &Test::value))); @@ -180,12 +170,6 @@ TEST_CASE("quote") { std::string name; int managerId = 0; int locationId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Department() = default; - Department(int id, std::string name, int managerId, int locationId) : - id{id}, name{std::move(name)}, managerId{managerId}, locationId{locationId} {} -#endif }; auto storage = make_storage({}, make_table("departments", @@ -264,12 +248,6 @@ TEST_CASE("instr") { std::string firstName; std::string lastName; std::string address; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() = default; - Employee(int id, std::string firstName, std::string lastName, std::string address) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, address{std::move(address)} {} -#endif }; struct sw : alias_tag { @@ -336,12 +314,6 @@ namespace replace_func_local { std::string firstName; std::string lastName; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() = default; - Contact(int id, std::string firstName, std::string lastName, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} -#endif }; bool operator==(const Contact& lhs, const Contact& rhs) { @@ -487,7 +459,7 @@ TEST_CASE("nullif") { REQUIRE(rows.size() == 1); REQUIRE(rows[0] == true); } -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) SECTION("common return type") { auto rows = storage.select(&Foo::field, where(nullif(&Foo::field, false))); REQUIRE(rows.size() == 1); @@ -524,27 +496,6 @@ TEST_CASE("ifnull") { std::unique_ptr fax; std::string email; int supportRepId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Customer() = default; - Customer(int id, - std::string firstName, - std::string lastName, - std::string company, - std::string address, - std::string city, - std::string state, - std::string country, - std::string postalCode, - std::string phone, - decltype(fax) fax, - std::string email, - int supportRepId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, - address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, - postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, - supportRepId{supportRepId} {} -#endif }; auto storage = make_storage({}, make_table("customers", diff --git a/tests/constraints/composite_key.cpp b/tests/constraints/composite_key.cpp index 91d9b06f0..1641dc63d 100644 --- a/tests/constraints/composite_key.cpp +++ b/tests/constraints/composite_key.cpp @@ -8,11 +8,6 @@ TEST_CASE("Composite key") { int year = 0; int month = 0; int amount = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Record() = default; - Record(int year, int month, int amount) : year{year}, month{month}, amount{amount} {} -#endif }; auto recordsTableName = "records"; diff --git a/tests/constraints/default.cpp b/tests/constraints/default.cpp index d35c3347c..14979883d 100644 --- a/tests/constraints/default.cpp +++ b/tests/constraints/default.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove using namespace sqlite_orm; @@ -14,7 +15,7 @@ TEST_CASE("Default value") { auto filename = "test_db.sqlite"; - ::remove(filename); + std::remove(filename); auto storage1 = make_storage(filename, make_table("User", diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index b4fd703ba..39e14e795 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -2,6 +2,8 @@ #include #include // std::is_same +#include // std::uint8_t +#include // std::time_t, std::time #include "../static_tests/static_tests_storage_traits.h" @@ -22,8 +24,8 @@ TEST_CASE("Foreign key") { int id; std::unique_ptr location; std::unique_ptr user; - int visited_at; - uint8_t mark; + std::time_t visited_at; + std::uint8_t mark; }; // this case didn't compile on linux until `typedef constraints_type` was added to `foreign_key_t` @@ -45,8 +47,8 @@ TEST_CASE("Foreign key") { using namespace sqlite_orm::internal::storage_traits; using Storage = decltype(storage); - STATIC_REQUIRE(storage_foreign_keys_count::value == 1); - STATIC_REQUIRE(storage_foreign_keys_count::value == 0); + STATIC_REQUIRE(storage_foreign_keys_target_count() == 1); + STATIC_REQUIRE(storage_foreign_keys_target_count() == 0); using LocationFks = storage_fk_references::type; STATIC_REQUIRE(std::is_same>::value); @@ -56,8 +58,8 @@ TEST_CASE("Foreign key") { } storage.sync_schema(); - int fromDate = int(std::time(nullptr)); - int toDate = int(std::time(nullptr)); + std::time_t fromDate = std::time(nullptr); + std::time_t toDate = 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 index dd7a81be9..dd78c499e 100644 --- a/tests/constraints/unique.cpp +++ b/tests/constraints/unique.cpp @@ -1,42 +1,30 @@ #include #include +#include "../catch_matchers.h" using namespace sqlite_orm; TEST_CASE("Unique") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3037002 + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); +#else + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct Contact { int id = 0; std::string firstName; std::string lastName; std::string email; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() = default; - Contact(int id, std::string firstName, std::string lastName, std::string email) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)} {} -#endif }; struct Shape { int id = 0; std::string backgroundColor; std::string foregroundColor; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Shape() = default; - Shape(int id, std::string backgroundColor, std::string foregroundColor) : - id{id}, backgroundColor{std::move(backgroundColor)}, foregroundColor{std::move(foregroundColor)} {} -#endif }; struct List { int id = 0; std::unique_ptr email; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - List() = default; - List(int id, decltype(email) email) : id{id}, email{std::move(email)} {} -#endif }; auto storage = make_storage( @@ -56,12 +44,13 @@ TEST_CASE("Unique") { storage.insert(Contact{0, "John", "Doe", "john.doe@gmail.com"}); - REQUIRE_THROWS_WITH(storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}), - ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}), + std::system_error, + uniqueExceptionMatcher); storage.insert(Shape{0, "red", "green"}); storage.insert(Shape{0, "red", "blue"}); - REQUIRE_THROWS_WITH(storage.insert(Shape{0, "red", "green"}), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Shape{0, "red", "green"}), std::system_error, uniqueExceptionMatcher); std::vector lists(2); REQUIRE_NOTHROW(storage.insert_range(lists.begin(), lists.end())); diff --git a/tests/filename.cpp b/tests/filename.cpp index e779ab836..de4f5559f 100644 --- a/tests/filename.cpp +++ b/tests/filename.cpp @@ -4,15 +4,15 @@ using namespace sqlite_orm; TEST_CASE("filename") { - { + SECTION("empty") { auto storage = make_storage(""); REQUIRE(storage.filename() == ""); } - { + SECTION("memory") { auto storage = make_storage(":memory:"); REQUIRE(storage.filename() == ":memory:"); } - { + SECTION("file name") { auto storage = make_storage("myDatabase.sqlite"); REQUIRE(storage.filename() == "myDatabase.sqlite"); } diff --git a/tests/get_all_custom_containers.cpp b/tests/get_all_custom_containers.cpp index ec7da2e97..7bd85e72b 100644 --- a/tests/get_all_custom_containers.cpp +++ b/tests/get_all_custom_containers.cpp @@ -10,11 +10,6 @@ namespace { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Comparator { diff --git a/tests/iterate.cpp b/tests/iterate.cpp index 8e70e4e56..5b4521006 100644 --- a/tests/iterate.cpp +++ b/tests/iterate.cpp @@ -6,12 +6,16 @@ using namespace sqlite_orm; TEST_CASE("Iterate mapped") { struct Test { - int64_t id; + int64_t id = 0; std::vector key; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Test&) const = default; +#else bool operator==(const Test& rhs) const { return this->id == rhs.id && this->key == rhs.key; } +#endif }; auto db = @@ -63,7 +67,7 @@ TEST_CASE("Iterate mapped") { #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) TEST_CASE("Iterate select statement") { struct Test { - int64_t id; + int64_t id = 0; std::vector key; bool operator==(const Test&) const = default; diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp new file mode 100644 index 000000000..a6c1724e7 --- /dev/null +++ b/tests/logger_tests.cpp @@ -0,0 +1,1430 @@ +#include +#include + +using namespace sqlite_orm; + +struct WillLogsCollector { + static std::vector logs; + + void operator()(const std::string_view log) { + this->logs.push_back(std::string(log)); + } +}; + +std::vector WillLogsCollector::logs; + +struct DidLogsCollector { + static std::vector logs; + + void operator()(const std::string_view log) { + this->logs.push_back(std::string(log)); + } +}; + +std::vector DidLogsCollector::logs; + +TEST_CASE("logger") { + using Logs = std::vector; + using Callback = std::function; + + Logs expectedWillLogs; + Logs expectedDidLogs; + + auto runRequire = [&expectedWillLogs, &expectedDidLogs] { + REQUIRE(WillLogsCollector::logs == expectedWillLogs); + REQUIRE(DidLogsCollector::logs == expectedDidLogs); + }; + + auto willRunQuery = GENERATE(Callback(WillLogsCollector()), Callback()); + auto didRunQuery = GENERATE(Callback(DidLogsCollector()), Callback()); + + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + int userId = 0; + std::string date; + }; + struct VisitLog { + int id = 0; + std::string message; + }; + + auto pushExpected = + [&willRunQuery, &didRunQuery, &expectedWillLogs, &expectedDidLogs](const std::string& expected) { + if (willRunQuery) { + expectedWillLogs.push_back(expected); + } + if (didRunQuery) { + expectedDidLogs.push_back(expected); + } + }; + + auto requireLogsAreEmpty = [] { + REQUIRE(WillLogsCollector::logs.empty()); + REQUIRE(DidLogsCollector::logs.empty()); + }; + + auto storage = + make_storage("", + make_trigger("visits_log_insert", + after() + .insert() + .on() + .for_each_row() + .when(gt(new_(&Visit::userId), 0)) + .begin(insert(into(), + columns(&VisitLog::message), + values(std::make_tuple("User " || c(new_(&Visit::userId)) || + " visited on " || new_(&Visit::date))))) + .end()), + make_index("user_id_index", &User::id), + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("user_id", &Visit::userId), + make_column("date", &Visit::date)), + make_table("visits_log", + make_column("id", &VisitLog::id, primary_key()), + make_column("message", &VisitLog::message)), + will_run_query(willRunQuery), + did_run_query(didRunQuery)); + storage.sync_schema(); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("misc") { + SECTION("transaction_guard") { + enum class ActionAfter { + rollback, + commit, + nothing, + }; + + auto guard = storage.transaction_guard(); + pushExpected("BEGIN TRANSACTION"); + runRequire(); + + SECTION("nothing") { + SECTION("nothing") { + pushExpected("ROLLBACK"); + } + SECTION("commit_on_destroy = true") { + guard.commit_on_destroy = true; + pushExpected("COMMIT"); + } + SECTION("commit_on_destroy = false") { + guard.commit_on_destroy = false; + pushExpected("ROLLBACK"); + } + } + SECTION("commit") { + guard.commit(); + pushExpected("COMMIT"); + } + SECTION("rollback") { + guard.rollback(); + pushExpected("ROLLBACK"); + } + } + SECTION("drop_index") { + storage.drop_index("user_id_index"); + pushExpected(R"(DROP INDEX "user_id_index")"); + } + SECTION("drop_index_if_exists") { + storage.drop_index_if_exists("user_id_index"); + pushExpected(R"(DROP INDEX IF EXISTS "user_id_index")"); + } + SECTION("drop_trigger") { + storage.drop_trigger("visits_log_insert"); + pushExpected(R"(DROP TRIGGER "visits_log_insert")"); + } + SECTION("drop_trigger_if_exists") { + const auto [value, expected] = GENERATE(table({ + {"one", R"(DROP TRIGGER IF EXISTS "one")"}, + {"two", R"(DROP TRIGGER IF EXISTS "two")"}, + })); + storage.drop_trigger_if_exists(value); + pushExpected(expected); + } + SECTION("vacuum") { + storage.vacuum(); + pushExpected("VACUUM"); + } + SECTION("drop_table") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(DROP TABLE "users")"}, + {"visits", R"(DROP TABLE "visits")"}, + })); + storage.drop_table(value); + pushExpected(expected); + } + SECTION("drop_table_if_exists") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(DROP TABLE IF EXISTS "users")"}, + {"visits", R"(DROP TABLE IF EXISTS "visits")"}, + })); + storage.drop_table_if_exists(value); + pushExpected(expected); + } + SECTION("changes") { + std::ignore = storage.changes(); + } + SECTION("total_changes") { + std::ignore = storage.total_changes(); + } + SECTION("last_insert_rowid") { + std::ignore = storage.last_insert_rowid(); + } + SECTION("busy_timeout") { + std::ignore = storage.busy_timeout(100); + } + SECTION("libversion") { + std::ignore = storage.libversion(); + } + SECTION("transaction") { + using Transaction = std::function; + + const auto [transactionLambda, expectedVector] = GENERATE(table>({ + {Transaction(), {}}, + {Transaction([] { + return false; + }), + {"BEGIN TRANSACTION", "ROLLBACK"}}, + {Transaction([] { + return true; + }), + {"BEGIN TRANSACTION", "COMMIT"}}, + })); + storage.transaction(transactionLambda); + for (auto& expected: expectedVector) { + pushExpected(expected); + } + } + SECTION("current_time") { + std::ignore = storage.current_time(); + pushExpected("SELECT CURRENT_TIME"); + } + SECTION("current_date") { + std::ignore = storage.current_date(); + pushExpected("SELECT CURRENT_DATE"); + } + SECTION("current_timestamp") { + std::ignore = storage.current_timestamp(); + pushExpected("SELECT CURRENT_TIMESTAMP"); + } + SECTION("db_release_memory") { + std::ignore = storage.db_release_memory(); + } + SECTION("table_names") { + std::ignore = storage.table_names(); + pushExpected("SELECT name FROM sqlite_master WHERE type='table'"); + } + SECTION("open_forever") { + storage.open_forever(); + } + SECTION("begin_transaction") { + using storage_base = internal::storage_base; + + enum class Action { + commit, + rollback, + nothing, + }; + + const auto [transactionFunction, expected] = GENERATE(table({ + {&storage_base::begin_transaction, "BEGIN TRANSACTION"}, + {&storage_base::begin_deferred_transaction, "BEGIN DEFERRED TRANSACTION"}, + {&storage_base::begin_immediate_transaction, "BEGIN IMMEDIATE TRANSACTION"}, + {&storage_base::begin_exclusive_transaction, "BEGIN EXCLUSIVE TRANSACTION"}, + })); + (storage.*transactionFunction)(); + pushExpected(expected); + const auto postAction = GENERATE(Action::commit, Action::rollback, Action::nothing); + switch (postAction) { + case Action::commit: + storage.commit(); + pushExpected("COMMIT"); + break; + case Action::rollback: + storage.rollback(); + pushExpected("ROLLBACK"); + break; + case Action::nothing: + break; + } + } + SECTION("filename") { + std::ignore = storage.filename(); + } + SECTION("is_opened") { + std::ignore = storage.is_opened(); + } + SECTION("get_autocommit") { + std::ignore = storage.get_autocommit(); + } + SECTION("busy_handler") { + storage.busy_handler([](int argument) -> int { + std::ignore = argument; + return 0; + }); + } + SECTION("rename_table") { + SECTION("in database") { + storage.rename_table("users", "winners"); + pushExpected(R"(ALTER TABLE "users" RENAME TO "winners")"); + } + SECTION("in storage schema") { + storage.rename_table("winners"); + } + } + SECTION("dump") { + SECTION("users") { + std::ignore = storage.dump(User{1, "Tate McRae"}); + } + SECTION("visits") { + std::ignore = storage.dump(Visit{1, 1, "today"}); + } + } + SECTION("tablename") { + SECTION("users") { + std::ignore = storage.tablename(); + } + SECTION("visits") { + std::ignore = storage.tablename(); + } + } + SECTION("find_column_name") { + SECTION("users.id") { + std::ignore = storage.find_column_name(&User::id); + } + SECTION("users.name") { + std::ignore = storage.find_column_name(&User::name); + } + SECTION("visits.id") { + std::ignore = storage.find_column_name(&Visit::id); + } + SECTION("visits.user_id") { + std::ignore = storage.find_column_name(&Visit::userId); + } + SECTION("visits.date") { + std::ignore = storage.find_column_name(&Visit::date); + } + } + SECTION("table_exists") { + SECTION("users") { + std::ignore = storage.table_exists("users"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'users')"); + } + SECTION("visits") { + std::ignore = storage.table_exists("visits"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'visits')"); + } + SECTION("non_existing") { + std::ignore = storage.table_exists("non_existing"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'non_existing')"); + } + } + } + SECTION("functions") { + SECTION("total") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.total(&User::id); + pushExpected(R"(SELECT TOTAL("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.total(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT TOTAL("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.total(&Visit::id); + pushExpected(R"(SELECT TOTAL("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.total(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT TOTAL("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("sum") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.sum(&User::id); + pushExpected(R"(SELECT SUM("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.sum(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT SUM("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.sum(&Visit::id); + pushExpected(R"(SELECT SUM("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.sum(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT SUM("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("min") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.min(&User::id); + pushExpected(R"(SELECT MIN("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.min(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT MIN("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.min(&Visit::id); + pushExpected(R"(SELECT MIN("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.min(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT MIN("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("max") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.max(&User::id); + pushExpected(R"(SELECT MAX("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.max(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT MAX("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.max(&Visit::id); + pushExpected(R"(SELECT MAX("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.max(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT MAX("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("group_concat") { + SECTION("users") { + SECTION("2 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&User::name); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name") FROM "users")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&User::name, where(c(&User::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name") FROM "users" WHERE ("users"."id" > ?))"); + } + } + SECTION("3 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&User::name, "-"); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name", ?) FROM "users")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&User::name, "-", where(c(&User::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name", ?) FROM "users" WHERE ("users"."id" > ?))"); + } + } + } + SECTION("visits") { + SECTION("2 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&Visit::date); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date") FROM "visits")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&Visit::date, where(c(&Visit::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date") FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + SECTION("3 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&Visit::date, "-"); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date", ?) FROM "visits")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&Visit::date, "-", where(c(&Visit::id) > 20)); + pushExpected( + R"(SELECT GROUP_CONCAT("visits"."date", ?) FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + } + } + SECTION("avg") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.avg(&User::id); + pushExpected(R"(SELECT AVG("users"."id") FROM "users")"); + } + SECTION("where id > 10") { + std::ignore = storage.avg(&User::id, where(c(&User::id) > 10)); + pushExpected(R"(SELECT AVG("users"."id") FROM "users" WHERE ("users"."id" > ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.avg(&Visit::id); + pushExpected(R"(SELECT AVG("visits"."id") FROM "visits")"); + } + SECTION("where id > 10") { + std::ignore = storage.avg(&Visit::id, where(c(&Visit::id) > 10)); + pushExpected(R"(SELECT AVG("visits"."id") FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + } + SECTION("count") { + SECTION("count(*)") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.count(); + pushExpected(R"(SELECT COUNT(*) FROM "users")"); + } + SECTION("where id < 10") { + std::ignore = storage.count(where(c(&User::id) < 10)); + pushExpected(R"(SELECT COUNT(*) FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.count(); + pushExpected(R"(SELECT COUNT(*) FROM "visits")"); + } + SECTION("where id < 10") { + std::ignore = storage.count(where(c(&Visit::id) < 10)); + pushExpected(R"(SELECT COUNT(*) FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } + SECTION("count(X)") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.count(&User::id); + pushExpected(R"(SELECT COUNT("users"."id") FROM "users")"); + } + SECTION("where id < 20") { + std::ignore = storage.count(&User::id, where(c(&User::id) < 20)); + pushExpected(R"(SELECT COUNT("users"."id") FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.count(&Visit::id); + pushExpected(R"(SELECT COUNT("visits"."id") FROM "visits")"); + } + SECTION("where id < 20") { + std::ignore = storage.count(&Visit::id, where(c(&Visit::id) < 20)); + pushExpected(R"(SELECT COUNT("visits"."id") FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } + } + } + SECTION("non crud") { + SECTION("insert_range") { + SECTION("users") { + const std::vector usersToInsert = { + {1, "Tate McRae"}, + {2, "Machine Gun Kelly"}, + }; + SECTION("simple") { + storage.insert_range(usersToInsert.begin(), usersToInsert.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert_range(usersToInsert.begin(), usersToInsert.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?), (?))"); + } + SECTION("visits") { + const std::vector visitsToInsert = { + {1, 1, "today"}, + {2, 10, "yesterday"}, + }; + SECTION("simple") { + storage.insert_range(visitsToInsert.begin(), visitsToInsert.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert_range(visitsToInsert.begin(), visitsToInsert.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?), (?, ?))"); + } + } + SECTION("raw replace") { + SECTION("users") { + SECTION("name") { + SECTION("simple") { + storage.replace(into(), columns(&User::name), values(std::make_tuple("TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + replace(into(), columns(&User::name), values(std::make_tuple("TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("name") VALUES (?))"); + } + SECTION("id, name") { + SECTION("simple") { + storage.replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"); + } + } + SECTION("visits") { + SECTION("user_id, date") { + SECTION("simple") { + storage.replace(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + SECTION("id, user_id, date") { + SECTION("simple") { + storage.replace(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + } + SECTION("insert") { + SECTION("implicit") { + SECTION("users") { + SECTION("simple") { + storage.insert(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?))"); + } + SECTION("visits") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(Visit{1, 1, "today"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + } + SECTION("explicit + raw") { + SECTION("users") { + SECTION("name") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(User{1, "TateMcRae"}, columns(&User::name)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(User{1, "TateMcRae"}, columns(&User::name))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&User::name), + values(std::make_tuple("TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(into(), columns(&User::name), values(std::make_tuple("TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?))"); + } + SECTION("id, name") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(User{1, "TateMcRae"}, columns(&User::id, &User::name)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(insert(User{1, "TateMcRae"}, columns(&User::id, &User::name))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"); + } + } + SECTION("visits") { + SECTION("user_id, date") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}, columns(&Visit::userId, &Visit::date)); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(Visit{1, 1, "today"}, columns(&Visit::userId, &Visit::date))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + SECTION("id, user_id, date") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}, columns(&Visit::id, &Visit::userId, &Visit::date)); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(Visit{1, 1, "today"}, columns(&Visit::id, &Visit::userId, &Visit::date))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today"))); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(insert(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + } + } + SECTION("select") { + SECTION("users") { + SECTION("simple") { + std::ignore = storage.select(&User::id); + } + SECTION("prepared statement") { + auto statement = storage.prepare(select(&User::id)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(SELECT "users"."id" FROM "users")"); + } + SECTION("visits") { + SECTION("simple") { + std::ignore = storage.select(&Visit::id); + } + SECTION("prepared statement") { + auto statement = storage.prepare(select(&Visit::id)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(SELECT "visits"."id" FROM "visits")"); + } + } + SECTION("get_all + get_all_pointer + get_all_optional") { + SECTION("users") { + SECTION("no conditions") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "users"."id", "users"."name" FROM "users")"); + } + SECTION("where id < 10") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "users"."id", "users"."name" FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "visits"."id", "visits"."user_id", "visits"."date" FROM "visits")"); + } + SECTION("where id < 10") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected( + R"(SELECT "visits"."id", "visits"."user_id", "visits"."date" FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } + SECTION("remove_all") { + SECTION("users") { + SECTION("no conditions") { + SECTION("simple") { + storage.remove_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users")"); + } + SECTION("where id = 1") { + SECTION("simple") { + storage.remove_all(where(c(&User::id) == 1)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all(where(c(&User::id) == 1))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users" WHERE ("users"."id" = ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("simple") { + storage.remove_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits")"); + } + SECTION("where id = 1") { + SECTION("simple") { + storage.remove_all(where(c(&Visit::id) == 1)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all(where(c(&Visit::id) == 1))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits" WHERE ("visits"."id" = ?))"); + } + } + } + SECTION("update_all") { + SECTION("users") { + SECTION("no conditions") { + SECTION("simple") { + storage.update_all(set(c(&User::name) = "Jade")); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update_all(set(c(&User::name) = "Jade"))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ?)"); + } + SECTION("where id = 10") { + SECTION("simple") { + storage.update_all(set(c(&User::name) = "Jade"), where(c(&User::id) == 10)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(update_all(set(c(&User::name) = "Jade"), where(c(&User::id) == 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ? WHERE ("users"."id" = ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("simple") { + storage.update_all(set(c(&Visit::date) = "Jade")); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update_all(set(c(&Visit::date) = "Jade"))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "date" = ?)"); + } + SECTION("where id = 10") { + SECTION("simple") { + storage.update_all(set(c(&Visit::date) = "Jade"), where(c(&Visit::id) == 10)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(update_all(set(c(&Visit::date) = "Jade"), where(c(&Visit::id) == 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "date" = ? WHERE ("visits"."id" = ?))"); + } + } + } + } + SECTION("crud") { + SECTION("replace_range") { + SECTION("users") { + const std::vector usersToReplace = { + {1, "Tate McRae"}, + {2, "Machine Gun Kelly"}, + }; + SECTION("simple") { + storage.replace_range(usersToReplace.begin(), usersToReplace.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace_range(usersToReplace.begin(), usersToReplace.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?), (?, ?))"); + } + SECTION("visits") { + const std::vector visitsToReplace = { + {1, 1, "today"}, + {2, 10, "yesterday"}, + }; + SECTION("simple") { + storage.replace_range(visitsToReplace.begin(), visitsToReplace.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace_range(visitsToReplace.begin(), visitsToReplace.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?), (?, ?, ?))"); + } + } + SECTION("replace") { + SECTION("users") { + SECTION("simple") { + storage.replace(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"); + } + SECTION("visits") { + SECTION("simple") { + storage.replace(Visit{1, 5, "tomorrow"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(Visit{1, 5, "tomorrow"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + SECTION("get + get_pointer + get_no_throw + get_optional") { + SECTION("users") { + storage.insert(User{1, "Tate McRae"}); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("get") { + SECTION("simple") { + std::ignore = storage.get(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_pointer") { + SECTION("simple") { + std::ignore = storage.get_pointer(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_pointer(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_no_throw") { + std::ignore = storage.get_no_throw(1); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_optional") { + SECTION("simple") { + std::ignore = storage.get_optional(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_optional(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "id", "name" FROM "users" WHERE "id" = ?)"); + } + SECTION("visits") { + storage.insert(Visit{1, 1, "today"}); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("get") { + SECTION("simple") { + std::ignore = storage.get(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_pointer") { + SECTION("simple") { + std::ignore = storage.get_pointer(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_pointer(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_no_throw") { + std::ignore = storage.get_no_throw(1); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_optional") { + SECTION("simple") { + std::ignore = storage.get_optional(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_optional(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "id", "user_id", "date" FROM "visits" WHERE "id" = ?)"); + } + } + SECTION("remove") { + SECTION("users") { + SECTION("simple") { + storage.remove(2); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove(2)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users" WHERE "id" = ?)"); + } + SECTION("visits") { + SECTION("simple") { + storage.remove(2); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove(2)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits" WHERE "id" = ?)"); + } + } + SECTION("update") { + SECTION("users") { + SECTION("simple") { + storage.update(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ? WHERE "id" = ?)"); + } + SECTION("visits") { + SECTION("simple") { + storage.update(Visit{1, 2, "yesterday"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update(Visit{1, 2, "yesterday"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "user_id" = ?, "date" = ? WHERE "id" = ?)"); + } + } + } + SECTION("pragma") { + SECTION("application_id") { + SECTION("get") { + std::ignore = storage.pragma.application_id(); + pushExpected("PRAGMA application_id"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({{2, "PRAGMA application_id = 2"}, + {3, "PRAGMA application_id = 3"}, + {4, "PRAGMA application_id = 4"}})); + storage.pragma.application_id(value); + pushExpected(expected); + } + } + SECTION("module_list") { + std::ignore = storage.pragma.module_list(); + pushExpected("PRAGMA module_list"); + } + SECTION("recursive_triggers") { + SECTION("get") { + std::ignore = storage.pragma.recursive_triggers(); + pushExpected("PRAGMA recursive_triggers"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {true, "PRAGMA recursive_triggers = 1"}, + {false, "PRAGMA recursive_triggers = 0"}, + })); + storage.pragma.recursive_triggers(value); + pushExpected(expected); + } + } + SECTION("busy_timeout") { + SECTION("get") { + std::ignore = storage.pragma.busy_timeout(); + pushExpected("PRAGMA busy_timeout"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA busy_timeout = 1"}, + {2, "PRAGMA busy_timeout = 2"}, + {3, "PRAGMA busy_timeout = 3"}, + })); + storage.pragma.busy_timeout(value); + pushExpected(expected); + } + } + SECTION("locking_mode") { + SECTION("get") { + std::ignore = storage.pragma.locking_mode(); + pushExpected("PRAGMA locking_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {locking_mode::NORMAL, "PRAGMA locking_mode = NORMAL"}, + {locking_mode::EXCLUSIVE, "PRAGMA locking_mode = EXCLUSIVE"}, + })); + storage.pragma.locking_mode(value); + pushExpected(expected); + } + } + SECTION("journal_mode") { + SECTION("get") { + std::ignore = storage.pragma.journal_mode(); + pushExpected("PRAGMA journal_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {journal_mode::DELETE_, "PRAGMA journal_mode = DELETE"}, + {journal_mode::TRUNCATE, "PRAGMA journal_mode = TRUNCATE"}, + {journal_mode::PERSIST, "PRAGMA journal_mode = PERSIST"}, + {journal_mode::MEMORY, "PRAGMA journal_mode = MEMORY"}, + {journal_mode::WAL, "PRAGMA journal_mode = WAL"}, + {journal_mode::OFF, "PRAGMA journal_mode = OFF"}, + })); + storage.pragma.journal_mode(value); + pushExpected(expected); + } + } + SECTION("synchronous") { + SECTION("get") { + std::ignore = storage.pragma.synchronous(); + pushExpected("PRAGMA synchronous"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA synchronous = 0"}, + {1, "PRAGMA synchronous = 1"}, + {2, "PRAGMA synchronous = 2"}, + {3, "PRAGMA synchronous = 3"}, + })); + storage.pragma.synchronous(value); + pushExpected(expected); + } + } + SECTION("user_version") { + SECTION("get") { + std::ignore = storage.pragma.user_version(); + pushExpected("PRAGMA user_version"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA user_version = 0"}, + {1, "PRAGMA user_version = 1"}, + {2, "PRAGMA user_version = 2"}, + {3, "PRAGMA user_version = 3"}, + {4, "PRAGMA user_version = 4"}, + })); + storage.pragma.user_version(value); + pushExpected(expected); + } + } + SECTION("auto_vacuum") { + SECTION("get") { + std::ignore = storage.pragma.auto_vacuum(); + pushExpected("PRAGMA auto_vacuum"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA auto_vacuum = 0"}, + {1, "PRAGMA auto_vacuum = 1"}, + {2, "PRAGMA auto_vacuum = 2"}, + })); + storage.pragma.auto_vacuum(value); + pushExpected(expected); + } + } + SECTION("max_page_count") { + SECTION("get") { + std::ignore = storage.pragma.max_page_count(); + pushExpected("PRAGMA max_page_count"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {10, "PRAGMA max_page_count = 10"}, + {20, "PRAGMA max_page_count = 20"}, + {30, "PRAGMA max_page_count = 30"}, + })); + storage.pragma.max_page_count(value); + pushExpected(expected); + } + } + SECTION("integrity_check") { + SECTION("get") { + std::ignore = storage.pragma.integrity_check(); + pushExpected("PRAGMA integrity_check"); + } + SECTION("set table-name") { + const auto [value, expected] = GENERATE(table({ + {"users", "PRAGMA integrity_check(users)"}, + {"visits", "PRAGMA integrity_check(visits)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + SECTION("set N") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA integrity_check(1)"}, + {2, "PRAGMA integrity_check(2)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + } + SECTION("quick_check") { + std::ignore = storage.pragma.quick_check(); + pushExpected("PRAGMA quick_check"); + } + SECTION("table_xinfo") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(PRAGMA table_xinfo("users"))"}, + {"visits", R"(PRAGMA table_xinfo("visits"))"}, + })); + std::ignore = storage.pragma.table_xinfo(value); + pushExpected(expected); + } + SECTION("table_info") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(PRAGMA table_info("users"))"}, + {"visits", R"(PRAGMA table_info("visits"))"}, + })); + std::ignore = storage.pragma.table_info(value); + pushExpected(expected); + } + } + runRequire(); +} diff --git a/tests/named_module/named_module.cpp b/tests/named_module/named_module.cpp new file mode 100644 index 000000000..a361917a0 --- /dev/null +++ b/tests/named_module/named_module.cpp @@ -0,0 +1,3 @@ +import sqlite_orm; + +int main() {} diff --git a/tests/operators/binary_operators.cpp b/tests/operators/binary_operators.cpp index 0f984fe67..b31457b10 100644 --- a/tests/operators/binary_operators.cpp +++ b/tests/operators/binary_operators.cpp @@ -8,11 +8,6 @@ TEST_CASE("binary operators") { struct User { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif }; auto storage = make_storage({}, make_table("users", make_column("id", &User::id))); storage.sync_schema(); diff --git a/tests/operators/bitwise_operators.cpp b/tests/operators/bitwise_operators.cpp index c65241555..d5b31900a 100644 --- a/tests/operators/bitwise_operators.cpp +++ b/tests/operators/bitwise_operators.cpp @@ -7,11 +7,6 @@ TEST_CASE("bitwise operators") { struct Entry { int lhs = 0; int rhs = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Entry() = default; - Entry(int lhs, int rhs) : lhs{lhs}, rhs{rhs} {} -#endif }; auto storage = make_storage({}, make_table("entries", make_column("lhs", &Entry::lhs), make_column("rhs", &Entry::rhs))); diff --git a/tests/operators/glob.cpp b/tests/operators/glob.cpp index 45fc3d001..eac12beec 100644 --- a/tests/operators/glob.cpp +++ b/tests/operators/glob.cpp @@ -15,12 +15,6 @@ namespace { std::string lastName; float salary = 0; int deptId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() = default; - Employee(int id, std::string firstName, std::string lastName, float salary, int deptId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} -#endif }; } TEST_CASE("Glob") { diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index 2997403fa..bf5931119 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -9,14 +9,13 @@ TEST_CASE("In") { struct User { int id = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif - +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id; } +#endif std::ostream& operator<<(std::ostream& os) const { return os << this->id; @@ -87,7 +86,7 @@ TEST_CASE("In") { } { struct Letter { - int id; + int id = 0; std::string name; }; auto storage = make_storage( diff --git a/tests/operators/is_null.cpp b/tests/operators/is_null.cpp index 473559e99..e75d4cc0e 100644 --- a/tests/operators/is_null.cpp +++ b/tests/operators/is_null.cpp @@ -7,11 +7,6 @@ TEST_CASE("Is null") { struct User { int id = 0; std::unique_ptr name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, decltype(name) name = nullptr) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( "", diff --git a/tests/operators/like.cpp b/tests/operators/like.cpp index 2b01a9a7b..e921245ec 100644 --- a/tests/operators/like.cpp +++ b/tests/operators/like.cpp @@ -7,11 +7,6 @@ TEST_CASE("Like operator") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Pattern { std::string value; diff --git a/tests/operators/not_operator.cpp b/tests/operators/not_operator.cpp index 638a5784e..52ea46fe7 100644 --- a/tests/operators/not_operator.cpp +++ b/tests/operators/not_operator.cpp @@ -6,11 +6,6 @@ using namespace sqlite_orm; TEST_CASE("Not operator") { struct Object { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id) : id{id} {} -#endif }; auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 062f1a10d..39819f2c2 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -1,5 +1,5 @@ #include -#include // ::remove +#include // std::remove #include using namespace sqlite_orm; @@ -12,7 +12,7 @@ TEST_CASE("module_list") { TEST_CASE("recursive_triggers") { auto filename = "recursive_triggers.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.open_forever(); storage.pragma.recursive_triggers(true); @@ -37,7 +37,7 @@ TEST_CASE("locking_mode") { TEST_CASE("journal_mode") { auto filename = "journal_mode.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); auto storageCopy = storage; decltype(storage)* stor = nullptr; @@ -118,7 +118,7 @@ TEST_CASE("User version") { TEST_CASE("Auto vacuum") { auto filename = "autovacuum.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); @@ -160,7 +160,7 @@ TEST_CASE("Integrity Check") { }; auto filename = "integrity.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -185,7 +185,7 @@ TEST_CASE("Quick Check") { }; auto filename = "quick_check.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -201,7 +201,7 @@ TEST_CASE("Quick Check") { TEST_CASE("application_id") { auto filename = "application_id.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.pragma.application_id(3); diff --git a/tests/prepared_statement_tests/get.cpp b/tests/prepared_statement_tests/get.cpp index bad7422fd..fb863764c 100644 --- a/tests/prepared_statement_tests/get.cpp +++ b/tests/prepared_statement_tests/get.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -8,8 +9,8 @@ using namespace sqlite_orm; TEST_CASE("Prepared get") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); const int defaultVisitTime = 50; @@ -73,7 +74,7 @@ TEST_CASE("Prepared get") { } { get<0>(statement) = 4; - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, notFoundExceptionMatcher); } } } @@ -95,7 +96,7 @@ TEST_CASE("Prepared get") { //.. } SECTION("execute") { - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, notFoundExceptionMatcher); } } { diff --git a/tests/prepared_statement_tests/insert.cpp b/tests/prepared_statement_tests/insert.cpp index 6dbbc1f8c..c1b3808d6 100644 --- a/tests/prepared_statement_tests/insert.cpp +++ b/tests/prepared_statement_tests/insert.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::strcmp #include "prepared_common.h" @@ -76,7 +77,7 @@ TEST_CASE("Prepared insert") { insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); } SECTION("no statement") { storage.insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); @@ -93,9 +94,9 @@ TEST_CASE("Prepared insert") { values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); REQUIRE(get<2>(statement) == 5); - REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + REQUIRE(std::strcmp(get<3>(statement), "Calvin") == 0); } SECTION("no statement") { storage.insert(into(), diff --git a/tests/prepared_statement_tests/insert_explicit.cpp b/tests/prepared_statement_tests/insert_explicit.cpp index 847e1594a..17af43db9 100644 --- a/tests/prepared_statement_tests/insert_explicit.cpp +++ b/tests/prepared_statement_tests/insert_explicit.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -8,8 +9,12 @@ using namespace sqlite_orm; TEST_CASE("Prepared insert explicit") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; +#if SQLITE_VERSION_NUMBER >= 3037002 + const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_PRIMARYKEY)); +#else + const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif const int defaultVisitTime = 50; @@ -68,7 +73,7 @@ TEST_CASE("Prepared insert explicit") { { user.id = 6; user.name = "Nate Dogg"; - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, primaryKeyExceptionMatcher); get<0>(statement) = user; auto insertedId = storage.execute(statement); @@ -102,7 +107,7 @@ TEST_CASE("Prepared insert explicit") { REQUIRE(insertedVisit.time == defaultVisitTime); } { - Visit visit{-1, 2, defaultVisitTime + 2}; + Visit visit{0, 2, defaultVisitTime + 2}; auto statement = storage.prepare(insert(visit, columns(&Visit::userId))); auto insertedId = storage.execute(statement); REQUIRE(insertedId == 3); diff --git a/tests/prepared_statement_tests/insert_range.cpp b/tests/prepared_statement_tests/insert_range.cpp index 9a94ecd4c..42a1b9ea8 100644 --- a/tests/prepared_statement_tests/insert_range.cpp +++ b/tests/prepared_statement_tests/insert_range.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -9,8 +10,8 @@ using namespace sqlite_orm; TEST_CASE("Prepared insert range") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher emptyRangeExceptionMatcher(orm_error_code::empty_range); const int defaultVisitTime = 50; @@ -41,22 +42,21 @@ TEST_CASE("Prepared insert range") { storage.replace(UserAndVisit{3, 1, "Shine on"}); std::vector users; - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); + std::vector expected{{1, "Team BS"}, {2, "Shy'm"}, {3, "Maître Gims"}}; SECTION("empty") { SECTION("strict") { - REQUIRE_THROWS_WITH(storage.prepare(insert_range(users.begin(), users.end())), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_MATCHES(storage.prepare(insert_range(users.begin(), users.end())), + std::system_error, + emptyRangeExceptionMatcher); } SECTION("container with pointers") { std::vector> usersPointers; - REQUIRE_THROWS_WITH( + REQUIRE_THROWS_MATCHES( storage.prepare( insert_range(usersPointers.begin(), usersPointers.end(), &std::unique_ptr::operator*)), - ContainsSubstring("incomplete input")); + std::system_error, + emptyRangeExceptionMatcher); } } SECTION("one") { diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index 1d4f81927..8803d5ec8 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -10,44 +10,31 @@ namespace PreparedStatementTests { int id = 0; std::string name; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } + + friend bool operator!=(const User& lhs, const User& rhs) { + return !(lhs == rhs); + } #endif }; struct Visit { int id = 0; - decltype(User::id) userId; + decltype(User::id) userId = 0; long time = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int id, decltype(Visit::userId) userId, long time) : id{id}, userId{userId}, time{time} {} -#endif }; struct UserAndVisit { - decltype(User::id) userId; - decltype(Visit::id) visitId; + decltype(User::id) userId = 0; + decltype(Visit::id) visitId = 0; std::string description; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserAndVisit() = default; - UserAndVisit(decltype(UserAndVisit::userId) userId, - decltype(UserAndVisit::visitId) visitId, - std::string description) : userId{userId}, visitId{visitId}, description{std::move(description)} {} -#endif }; - inline bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } - - inline bool operator!=(const User& lhs, const User& rhs) { - return !(lhs == rhs); - } - inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { auto sql = statement.sql(); std::ignore = sql; diff --git a/tests/prepared_statement_tests/replace.cpp b/tests/prepared_statement_tests/replace.cpp index e91bd5542..de9658fda 100644 --- a/tests/prepared_statement_tests/replace.cpp +++ b/tests/prepared_statement_tests/replace.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::strcmp #include "prepared_common.h" @@ -41,7 +42,7 @@ TEST_CASE("Prepared replace") { replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); } SECTION("no statement") { storage.replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); @@ -56,9 +57,9 @@ TEST_CASE("Prepared replace") { values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); REQUIRE(get<2>(statement) == 5); - REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + REQUIRE(std::strcmp(get<3>(statement), "Calvin") == 0); } SECTION("no statement") { storage.replace(into(), diff --git a/tests/prepared_statement_tests/replace_range.cpp b/tests/prepared_statement_tests/replace_range.cpp index 022a2bf44..0505bb813 100644 --- a/tests/prepared_statement_tests/replace_range.cpp +++ b/tests/prepared_statement_tests/replace_range.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -9,6 +10,7 @@ using namespace sqlite_orm; TEST_CASE("Prepared replace range") { using namespace PreparedStatementTests; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher emptyRangeExceptionMatcher(orm_error_code::empty_range); const int defaultVisitTime = 50; @@ -41,22 +43,21 @@ TEST_CASE("Prepared replace range") { std::vector users; std::vector> userPointers; std::vector expected; - auto lambda = [](const std::unique_ptr& pointer) -> const User& { - return *pointer; - }; SECTION("empty") { - using Catch::Matchers::ContainsSubstring; - expected.push_back(User{1, "Team BS"}); expected.push_back(User{2, "Shy'm"}); expected.push_back(User{3, "Maître Gims"}); SECTION("straight") { - REQUIRE_THROWS_WITH(storage.prepare(replace_range(users.begin(), users.end())), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_MATCHES(storage.prepare(replace_range(users.begin(), users.end())), + std::system_error, + emptyRangeExceptionMatcher); } SECTION("pointers") { - REQUIRE_THROWS_WITH(storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_MATCHES( + storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)), + std::system_error, + emptyRangeExceptionMatcher); } } SECTION("one existing") { @@ -73,7 +74,8 @@ TEST_CASE("Prepared replace range") { } SECTION("pointers") { userPointers.push_back(std::make_unique(user)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); @@ -97,7 +99,8 @@ TEST_CASE("Prepared replace range") { SECTION("pointers") { userPointers.push_back(std::make_unique(user)); userPointers.push_back(std::make_unique(user2)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); @@ -121,7 +124,8 @@ TEST_CASE("Prepared replace range") { userPointers.push_back(std::make_unique(user)); userPointers.push_back(std::make_unique(user2)); userPointers.push_back(std::make_unique(user3)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); diff --git a/tests/prepared_statement_tests/select.cpp b/tests/prepared_statement_tests/select.cpp index 47b090bb5..2c4950e84 100644 --- a/tests/prepared_statement_tests/select.cpp +++ b/tests/prepared_statement_tests/select.cpp @@ -373,11 +373,17 @@ TEST_CASE("Prepared select") { decltype(User::id) id = 0; decltype(User::name) name; +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED Z(decltype(id) id, decltype(name) name) : id{id}, name{std::move(name)} {} +#endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Z&) const = default; +#else bool operator==(const Z& z) const { return this->id == z.id && this->name == z.name; } +#endif }; constexpr auto z_struct = struct_(&User::id, &User::name); auto statement = storage.prepare(select(z_struct)); diff --git a/tests/row_extractor.cpp b/tests/row_extractor.cpp index 95d3bda7b..6e53001fe 100644 --- a/tests/row_extractor.cpp +++ b/tests/row_extractor.cpp @@ -12,7 +12,7 @@ enum class Gender { }; struct SuperHero { - int id; + int id = 0; std::string name; Gender gender = Gender::Invalid; }; @@ -132,9 +132,9 @@ TEST_CASE("Custom row extractors") { storage.sync_schema(); storage.transaction([&storage]() { - storage.insert(SuperHero{-1, "Batman", Gender::Male}); - storage.insert(SuperHero{-1, "Wonder woman", Gender::Female}); - storage.insert(SuperHero{-1, "Superman", Gender::Male}); + storage.insert(SuperHero{0, "Batman", Gender::Male}); + storage.insert(SuperHero{0, "Wonder woman", Gender::Female}); + storage.insert(SuperHero{0, "Superman", Gender::Male}); return true; }); @@ -149,7 +149,8 @@ TEST_CASE("Custom row extractors") { SECTION("column text") { auto firstGender = select(distinct(&SuperHero::gender), order_by(&SuperHero::gender), limit(1)); Gender gender = Gender::Invalid; - internal::perform_exec(db, storage.dump(firstGender), &extract_single_value, &gender); + internal::sqlite_executor executor; + executor.perform_exec(db, storage.dump(firstGender), &extract_single_value, &gender); REQUIRE(gender == Gender::Female); } SECTION("udf") { diff --git a/tests/schema/index_tests.cpp b/tests/schema/index_tests.cpp index 080967656..7f1158488 100644 --- a/tests/schema/index_tests.cpp +++ b/tests/schema/index_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" using namespace sqlite_orm; @@ -14,6 +15,10 @@ TEST_CASE("index") { auto storage = make_storage({}, make_index("id_index", &User::id), table); REQUIRE_NOTHROW(storage.sync_schema()); } + SECTION("unspecified") { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id)), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } SECTION("asc") { auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).asc()), table); REQUIRE_NOTHROW(storage.sync_schema()); @@ -62,7 +67,11 @@ TEST_CASE("index") { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED TEST_CASE("filtered index") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3037002 + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); +#else + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct Test { std::optional field1 = 0; @@ -76,7 +85,7 @@ TEST_CASE("filtered index") { REQUIRE_NOTHROW(storage.sync_schema()); storage.insert(Test{1, std::nullopt}); - REQUIRE_THROWS_WITH(storage.insert(Test{1, std::nullopt}), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Test{1, std::nullopt}), std::system_error, uniqueExceptionMatcher); } SECTION("2") { auto storage = make_storage( @@ -102,11 +111,6 @@ TEST_CASE("Compound index") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 03bb73299..3ff7c2759 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -11,16 +11,31 @@ TEST_CASE("virtual table") { std::string title; std::string body; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Post&) const = default; +#else bool operator==(const Post& other) const { return this->title == other.title && this->body == other.body; } +#endif }; + auto virtualTable = + make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); + { + const auto compareColumnName = [](const std::string* foundValue, std::string expectedValue) { + if (!foundValue) { + return false; + } + return *foundValue == expectedValue; + }; + REQUIRE(compareColumnName(virtualTable.find_column_name(&Post::title), std::string("title"))); + REQUIRE(compareColumnName(virtualTable.find_column_name(&Post::body), std::string("body"))); + } + /// CREATE VIRTUAL TABLE posts /// USING FTS5(title, body); - auto storage = make_storage( - "", - make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body)))); + auto storage = make_storage("", std::move(virtualTable)); storage.sync_schema(); storage.sync_schema_simulate(); @@ -76,4 +91,36 @@ TEST_CASE("virtual table") { where(match("SQLite")), order_by(rank())); } + +TEST_CASE("issue1410") { + struct NormalTable { + int id; + std::string text; + int otherValue; + }; + + struct SearchTable { + int normal_table_id; + std::string text; + }; + + auto storage = + make_storage("", + make_table("normal_table", + make_column("id", &NormalTable::id, primary_key().autoincrement()), + make_column("path", &NormalTable::text), + make_column("other_value", &NormalTable::otherValue)), + + make_virtual_table("search_table", + using_fts5(make_column("text", &SearchTable::text), + make_column("normal_table_id", &SearchTable::normal_table_id), + tokenize("trigram")))); + storage.sync_schema(); + auto rows = storage.iterate( + where(eq(&NormalTable::id, select(&SearchTable::normal_table_id, where(match("Some Text")))))); + + for (const auto& row: rows) { + std::ignore = row; + } // has to be compiled +} #endif diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index 034b8869d..8871e8e10 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -1,20 +1,26 @@ #include #include +#include // std::time_t + using namespace sqlite_orm; namespace { struct Employee { - int id; + int id = 0; std::string name; - int age; + int age = 0; std::string address; // optional double salary; // optional +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Employee&) const = default; +#else 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; } +#endif }; } TEST_CASE("select constraints") { @@ -30,13 +36,13 @@ TEST_CASE("select constraints") { 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}; + Employee paul{0, "Paul", 32, "California", 20000.0}; + Employee allen{0, "Allen", 25, "Texas", 15000.0}; + Employee teddy{0, "Teddy", 23, "Norway", 20000.0}; + Employee mark{0, "Mark", 25, "Rich-Mond", 65000.0}; + Employee david{0, "David", 27, "Texas", 85000.0}; + Employee kim{0, "Kim", 22, "South-Hall", 45000.0}; + Employee james{0, "James", 24, "Houston", 10000.0}; // insert employees. `insert` function returns id of inserted object.. paul.id = storage.insert(paul); @@ -69,9 +75,9 @@ TEST_CASE("select constraints") { } } SECTION("distinct") { - storage.insert(Employee{-1, "Paul", 24, "Houston", 20000.0}); - storage.insert(Employee{-1, "James", 44, "Norway", 5000.0}); - storage.insert(Employee{-1, "James", 45, "Texas", 5000.0}); + storage.insert(Employee{0, "Paul", 24, "Houston", 20000.0}); + storage.insert(Employee{0, "James", 44, "Norway", 5000.0}); + storage.insert(Employee{0, "James", 45, "Texas", 5000.0}); std::vector names; decltype(names) expected; @@ -218,22 +224,12 @@ namespace { struct User1 { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User1() = default; - User1(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Visit1 { int id = 0; int userId = 0; - time_t time = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit1() = default; - Visit1(int id, int userId, time_t time) : id{id}, userId{userId}, time{time} {} -#endif + std::time_t time = 0; }; } #if SQLITE_VERSION_NUMBER >= 3006019 @@ -270,24 +266,12 @@ namespace { std::string firstName; std::string lastName; std::string country; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User2() = default; - User2(int id, std::string firstName, std::string lastName, std::string country) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, country{country} {} -#endif }; struct Track2 { int id = 0; std::string name; long milliseconds = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Track2() = default; - Track2(int id, std::string name, long milliseconds) : - id{id}, name{std::move(name)}, milliseconds{milliseconds} {} -#endif }; } @@ -379,11 +363,6 @@ namespace { int id = 0; int age = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User3() = default; - User3(int id, int age, std::string name) : id{id}, age{age}, name{std::move(name)} {} -#endif }; } @@ -423,14 +402,13 @@ namespace { int id = 0; std::string firstName; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User4() = default; - User4(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} -#endif - +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User4&) const = default; +#else bool operator==(const User4& user) const { return this->id == user.id && this->firstName == user.firstName; } +#endif }; } TEST_CASE("collate") { @@ -458,12 +436,6 @@ namespace { std::string firstName; std::string lastName; long registerTime = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User5() = default; - User5(int id, std::string firstName, std::string lastName, long registerTime) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, registerTime{registerTime} {} -#endif }; } diff --git a/tests/statement_serializer_tests/column_constraints/primary_key.cpp b/tests/statement_serializer_tests/column_constraints/primary_key.cpp index 53e5fadf5..0348456a7 100644 --- a/tests/statement_serializer_tests/column_constraints/primary_key.cpp +++ b/tests/statement_serializer_tests/column_constraints/primary_key.cpp @@ -3,207 +3,189 @@ using namespace sqlite_orm; -TEST_CASE("statement_serializer primary key") { +TEST_CASE("statement_serializer primary key column constraint") { std::string value; decltype(value) expected; - SECTION("empty") { - internal::db_objects_tuple<> storage; - internal::serializer_context> context{storage}; - auto pk = primary_key(); + + using db_objects_t = internal::db_objects_tuple<>; + auto dbObjects = db_objects_t{}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + SECTION("default pk") { + constexpr auto pk = primary_key(); value = serialize(pk, context); expected = "PRIMARY KEY"; } - SECTION("not empty") { - struct User { - int id = 0; - std::string name; - }; - auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); - using db_objects_t = internal::db_objects_tuple; - auto dbObjects = db_objects_t{table}; - using context_t = internal::serializer_context; - context_t context{dbObjects}; - SECTION("single column pk") { - auto pk = primary_key(&User::id); - value = serialize(pk, context); - expected = R"(PRIMARY KEY("id"))"; - } - SECTION("double column pk") { - auto pk = primary_key(&User::id, &User::name); - value = serialize(pk, context); - expected = R"(PRIMARY KEY("id", "name"))"; - } - SECTION("empty pk asc") { - auto pk = primary_key().asc(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC"; - } - SECTION("empty pk asc autoincrement") { - auto pk = primary_key().asc().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC AUTOINCREMENT"; - } - SECTION("empty pk desc") { - auto pk = primary_key().desc(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC"; - } - SECTION("empty pk desc autoincrement") { - auto pk = primary_key().desc().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC AUTOINCREMENT"; - } - SECTION("empty pk on conflict rollback") { - auto pk = primary_key().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ROLLBACK"; - } - SECTION("empty pk on conflict abort") { - auto pk = primary_key().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ABORT"; - } - SECTION("empty pk on conflict fail") { - auto pk = primary_key().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT FAIL"; - } - SECTION("empty pk on conflict ignore") { - auto pk = primary_key().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT IGNORE"; - } - SECTION("empty pk on conflict replace") { - auto pk = primary_key().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT REPLACE"; - } - SECTION("empty pk asc on conflict rollback") { - auto pk = primary_key().asc().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK"; - } - SECTION("empty pk asc on conflict abort") { - auto pk = primary_key().asc().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ABORT"; - } - SECTION("empty pk asc on conflict fail") { - auto pk = primary_key().asc().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT FAIL"; - } - SECTION("empty pk asc on conflict ignore") { - auto pk = primary_key().asc().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT IGNORE"; - } - SECTION("empty pk asc on conflict replace") { - auto pk = primary_key().asc().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT REPLACE"; - } - SECTION("empty pk desc on conflict rollback") { - auto pk = primary_key().desc().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK"; - } - SECTION("empty pk desc on conflict abort") { - auto pk = primary_key().desc().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ABORT"; - } - SECTION("empty pk desc on conflict fail") { - auto pk = primary_key().desc().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT FAIL"; - } - SECTION("empty pk desc on conflict ignore") { - auto pk = primary_key().desc().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT IGNORE"; - } - SECTION("empty pk desc on conflict replace") { - auto pk = primary_key().desc().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT REPLACE"; - } + SECTION("pk asc") { + constexpr auto pk = primary_key().asc(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC"; + } + SECTION("pk asc autoincrement") { + constexpr auto pk = primary_key().asc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC AUTOINCREMENT"; + } + SECTION("pk desc") { + constexpr auto pk = primary_key().desc(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC"; + } + SECTION("pk desc autoincrement") { + constexpr auto pk = primary_key().desc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC AUTOINCREMENT"; + } + SECTION("pk on conflict rollback") { + constexpr auto pk = primary_key().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK"; + } + SECTION("pk on conflict abort") { + constexpr auto pk = primary_key().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT"; + } + SECTION("pk on conflict fail") { + constexpr auto pk = primary_key().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL"; + } + SECTION("pk on conflict ignore") { + constexpr auto pk = primary_key().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE"; + } + SECTION("pk on conflict replace") { + constexpr auto pk = primary_key().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE"; + } + SECTION("pk asc on conflict rollback") { + constexpr auto pk = primary_key().asc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK"; + } + SECTION("pk asc on conflict abort") { + constexpr auto pk = primary_key().asc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT"; + } + SECTION("pk asc on conflict fail") { + constexpr auto pk = primary_key().asc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL"; + } + SECTION("pk asc on conflict ignore") { + constexpr auto pk = primary_key().asc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE"; + } + SECTION("pk asc on conflict replace") { + constexpr auto pk = primary_key().asc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE"; + } + SECTION("pk desc on conflict rollback") { + constexpr auto pk = primary_key().desc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK"; + } + SECTION("pk desc on conflict abort") { + constexpr auto pk = primary_key().desc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT"; + } + SECTION("pk desc on conflict fail") { + constexpr auto pk = primary_key().desc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL"; + } + SECTION("pk desc on conflict ignore") { + constexpr auto pk = primary_key().desc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE"; + } + SECTION("pk desc on conflict replace") { + constexpr auto pk = primary_key().desc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE"; + } - SECTION("empty pk on conflict rollback autoincrement") { - auto pk = primary_key().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk on conflict abort autoincrement") { - auto pk = primary_key().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk on conflict fail autoincrement") { - auto pk = primary_key().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk on conflict ignore autoincrement") { - auto pk = primary_key().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk on conflict replace autoincrement") { - auto pk = primary_key().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict rollback autoincrement") { - auto pk = primary_key().asc().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict abort autoincrement") { - auto pk = primary_key().asc().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict fail autoincrement") { - auto pk = primary_key().asc().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict ignore autoincrement") { - auto pk = primary_key().asc().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict replace autoincrement") { - auto pk = primary_key().asc().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT REPLACE AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict rollback autoincrement") { - auto pk = primary_key().desc().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict abort autoincrement") { - auto pk = primary_key().desc().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict fail autoincrement") { - auto pk = primary_key().desc().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict ignore autoincrement") { - auto pk = primary_key().desc().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict replace autoincrement") { - auto pk = primary_key().desc().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT REPLACE AUTOINCREMENT"; - } - } - REQUIRE(value == expected); + SECTION("pk on conflict rollback autoincrement") { + constexpr auto pk = primary_key().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk on conflict abort autoincrement") { + constexpr auto pk = primary_key().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk on conflict fail autoincrement") { + constexpr auto pk = primary_key().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk on conflict ignore autoincrement") { + constexpr auto pk = primary_key().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk on conflict replace autoincrement") { + constexpr auto pk = primary_key().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("pk asc on conflict rollback autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk asc on conflict abort autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk asc on conflict fail autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk asc on conflict ignore autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk asc on conflict replace autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("pk desc on conflict rollback autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk desc on conflict abort autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk desc on conflict fail autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk desc on conflict ignore autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk desc on conflict replace autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE AUTOINCREMENT"; + } } diff --git a/tests/statement_serializer_tests/conditions.cpp b/tests/statement_serializer_tests/conditions.cpp index 31a6db640..48d0b1b46 100644 --- a/tests/statement_serializer_tests/conditions.cpp +++ b/tests/statement_serializer_tests/conditions.cpp @@ -6,17 +6,18 @@ using namespace sqlite_orm; TEST_CASE("statement_serializer conditions") { std::string value, expected; - SECTION("using") { - struct User { - int64 id; - }; + struct User { + int64 id; + std::string name; + }; - auto t1 = make_table("user", make_column("id", &User::id)); - auto storage = internal::db_objects_tuple{t1}; - using db_objects_tuple = decltype(storage); + auto t1 = make_table("user", make_column("id", &User::id), make_column("name", &User::name)); + auto storage = internal::db_objects_tuple{t1}; + using db_objects_tuple = decltype(storage); - internal::serializer_context ctx{storage}; + internal::serializer_context ctx{storage}; + SECTION("using") { SECTION("using column") { auto expression = using_(&User::id); value = serialize(expression, ctx); @@ -29,16 +30,56 @@ TEST_CASE("statement_serializer conditions") { } } SECTION("order by") { - auto storage = internal::db_objects_tuple<>{}; - using db_objects_tuple = decltype(storage); - - internal::serializer_context ctx{storage}; - SECTION("positional ordinal") { auto expression = order_by(1); value = serialize(expression, ctx); expected = "ORDER BY 1"; } + SECTION("normal") { + auto expression = order_by(&User::id); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id")"; + } + SECTION("asc") { + auto expression = order_by(&User::id).asc(); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC)"; + } + SECTION("desc") { + auto expression = order_by(&User::id).desc(); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC)"; + } + SECTION("multi, single") { + auto expression = multi_order_by(order_by(&User::id)); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id")"; + } + SECTION("multi, single, asc") { + auto expression = multi_order_by(order_by(&User::id).asc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC)"; + } + SECTION("multi, single, desc") { + auto expression = multi_order_by(order_by(&User::id).desc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC)"; + } + SECTION("multi, several") { + auto expression = multi_order_by(order_by(&User::id), order_by(&User::name)); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id", "user"."name")"; + } + SECTION("multi, several, asc") { + auto expression = multi_order_by(order_by(&User::id).asc(), order_by(&User::name).asc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC, "user"."name" ASC)"; + } + SECTION("multi, several, desc") { + auto expression = multi_order_by(order_by(&User::id).desc(), order_by(&User::name).desc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC, "user"."name" DESC)"; + } } REQUIRE(value == expected); diff --git a/tests/statement_serializer_tests/statements/insert_replace.cpp b/tests/statement_serializer_tests/statements/insert_replace.cpp index 1a470b2ee..3a5b16f14 100644 --- a/tests/statement_serializer_tests/statements/insert_replace.cpp +++ b/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -13,20 +13,10 @@ TEST_CASE("statement_serializer insert/replace") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct UserBackup { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserBackup() = default; - UserBackup(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); auto table2 = diff --git a/tests/statement_serializer_tests/statements/update.cpp b/tests/statement_serializer_tests/statements/update.cpp index d76c3622d..b0e6e6a12 100644 --- a/tests/statement_serializer_tests/statements/update.cpp +++ b/tests/statement_serializer_tests/statements/update.cpp @@ -10,11 +10,6 @@ TEST_CASE("statement_serializer update") { int type = 0; int index = 0; double value = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - A() = default; - A(int address, int type, int index, double value) : address{address}, type{type}, index{index}, value{value} {} -#endif }; std::string value; decltype(value) expected; diff --git a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp index c3bafb405..7db1273c3 100644 --- a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp +++ b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp @@ -2,6 +2,7 @@ #include #include // std::is_same +#include // std::time_t #include "../../static_tests/static_tests_storage_traits.h" @@ -287,11 +288,6 @@ TEST_CASE("statement_serializer foreign key") { SECTION("one to explicit one") { struct Object { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id) : id{id} {} -#endif }; struct User : Object { @@ -343,7 +339,7 @@ TEST_CASE("statement_serializer foreign key") { struct UserVisit { int userId = 0; std::string userFirstName; - time_t time = 0; + std::time_t time = 0; }; auto fk = foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName); @@ -358,16 +354,16 @@ TEST_CASE("statement_serializer foreign key") { make_column("last_name", &User::lastName), primary_key(&User::id, &User::firstName)); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); auto visitsTable = make_table("visits", make_column("user_id", &UserVisit::userId), make_column("user_first_name", &UserVisit::userFirstName), make_column("time", &UserVisit::time), fk); - STATIC_REQUIRE(table_foreign_keys_count::value == 1); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 1); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); using db_objects_t = internal::db_objects_tuple; diff --git a/tests/statement_serializer_tests/table_constraints/primary_key.cpp b/tests/statement_serializer_tests/table_constraints/primary_key.cpp new file mode 100644 index 000000000..d843e3320 --- /dev/null +++ b/tests/statement_serializer_tests/table_constraints/primary_key.cpp @@ -0,0 +1,64 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer primary key table constraint") { + struct User { + int id = 0; + std::string name; + }; + struct DerivedUser : User {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived_user = c(); +#endif + + std::string value; + decltype(value) expected; + SECTION("with inheritance") { + auto table1 = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + auto table2 = make_table("derived_users", + make_column("derived_id", &DerivedUser::id), + make_column("derived_name", &DerivedUser::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table1, table2}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + context.skip_table_name = false; + + SECTION("single column pk") { + constexpr auto pk = primary_key(&User::id); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id"))"; + } + SECTION("composite pk") { + constexpr auto pk = primary_key(&User::id, &User::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id", "name"))"; + } + SECTION("base, composite pk") { + constexpr auto pk = primary_key(&User::id, &User::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id", "name"))"; + } + SECTION("inheritance, composite fk, column pointer") { + constexpr auto pk = + primary_key(column(&DerivedUser::id), column(&DerivedUser::name)); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } + SECTION("inheritance, composite fk, as field") { + constexpr auto pk = primary_key(&DerivedUser::id, &DerivedUser::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("inheritance, composite fk, as field") { + constexpr auto pk = primary_key(&DerivedUser::id, &DerivedUser::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } +#endif + REQUIRE(value == expected); + } +} diff --git a/tests/static_tests/alias.cpp b/tests/static_tests/alias.cpp index cfa83bfda..11b611292 100644 --- a/tests/static_tests/alias.cpp +++ b/tests/static_tests/alias.cpp @@ -31,17 +31,20 @@ void runTest(ColAlias /*colRef*/) { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES -template> -concept table_alias_callable = requires { +template, + typename O = internal::auto_type_t> +concept table_alias_callable = orm_table_alias && requires { { get_all() } -> same_as>>; { count() } -> same_as>; }; -template> -concept storage_table_alias_callable = requires(S& storage) { - { storage.get_all() } -> same_as>; - { storage.count() } -> same_as; - { storage.iterate() } -> same_as>; +template> +concept storage_table_alias_callable = orm_table_alias && requires(S& storage) { + { storage.template get_all() } -> same_as>; + { storage.template count() } -> same_as; + { storage.template iterate() } -> same_as>; }; #endif diff --git a/tests/static_tests/column.cpp b/tests/static_tests/column.cpp index 2c794a069..afb43778b 100644 --- a/tests/static_tests/column.cpp +++ b/tests/static_tests/column.cpp @@ -4,9 +4,12 @@ #include "static_tests_common.h" using namespace sqlite_orm; +using internal::as_field_of; +using internal::compare_fields; +using internal::is_field_of_v; TEST_CASE("Column") { - { + SECTION("string type") { using column_type = decltype(make_column("name", &User::name)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -17,7 +20,7 @@ TEST_CASE("Column") { using field_type = column_type::field_type; STATIC_REQUIRE(type_is_nullable::value); } - { + SECTION("int type, object field") { using column_type = decltype(make_column("id", &User::id)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -25,17 +28,17 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by const ref") { using column_type = decltype(make_column("id", &User::getIdByRefConst, &User::setIdByVal)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); } - { + SECTION("setter by val") { using column_type = decltype(make_column("id", &User::setIdByVal, &User::getIdByRefConst)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -43,7 +46,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by ref") { using column_type = decltype(make_column("id", &User::getIdByRef, &User::setIdByConstRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -51,7 +54,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("setter by const ref") { using column_type = decltype(make_column("id", &User::setIdByConstRef, &User::getIdByRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -59,7 +62,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by const val") { using column_type = decltype(make_column("id", &User::getIdByValConst, &User::setIdByRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -67,7 +70,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("setter by ref") { using column_type = decltype(make_column("id", &User::setIdByRef, &User::getIdByValConst)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -75,22 +78,31 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("inheritance") { using column_type = decltype(column(&Token::id)); STATIC_REQUIRE(std::is_same::value); using field_type = column_type::field_type; + STATIC_REQUIRE(std::is_member_pointer::value); + STATIC_REQUIRE_FALSE(std::is_member_function_pointer::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::type, Token>::value); STATIC_REQUIRE(std::is_same::type, Object>::value); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE(compare_fields(&Object::id, as_field_of(&Object::id))); + STATIC_REQUIRE(compare_fields(as_field_of(&Object::id), &Object::id)); + STATIC_REQUIRE(compare_fields(&User::id, &User::id)); + STATIC_REQUIRE(compare_fields(&User::setIdByVal, &User::setIdByVal)); + STATIC_REQUIRE_FALSE(compare_fields(&User::id, &Object::id)); + STATIC_REQUIRE_FALSE(compare_fields(&Object::id, &User::id)); + STATIC_REQUIRE_FALSE(compare_fields(&User::id, &User::name)); + STATIC_REQUIRE_FALSE(compare_fields(internal::empty_setter{}, &User::setIdByVal)); + STATIC_REQUIRE_FALSE(compare_fields(&User::setIdByVal, internal::empty_setter{})); STATIC_REQUIRE(std::is_same::type, Object>::value); STATIC_REQUIRE(std::is_same, field_type>::type, int>::value); - STATIC_REQUIRE(std::is_member_pointer::value); - STATIC_REQUIRE_FALSE(std::is_member_function_pointer::value); } } diff --git a/tests/static_tests/column_pointer.cpp b/tests/static_tests/column_pointer.cpp index 74cb0ce73..4d4df3ef4 100644 --- a/tests/static_tests/column_pointer.cpp +++ b/tests/static_tests/column_pointer.cpp @@ -62,19 +62,19 @@ concept storage_field_callable = requires(S& storage, C field) { { storage.group_concat(field, 42) } -> same_as; }; -template -concept refers_to_recordset_callable = requires { +template +concept refers_to_recordset_callable = orm_refers_to_recordset && requires { { count() } -> same_as>>; }; -template> -concept refers_to_table_callable = requires { +template> +concept refers_to_table_callable = orm_refers_to_table && requires { { get_all() } -> same_as, std::vector>>; { count() } -> same_as>>; }; -template> -concept table_reference_callable = requires { +template> +concept table_reference_callable = orm_table_reference && requires { { get
(42) } -> same_as>; { get_pointer
(42) } -> same_as>; { get_optional
(42) } -> same_as>; @@ -86,25 +86,25 @@ concept table_reference_callable = requires { { count
() } -> same_as>; }; -template> -concept storage_refers_to_table_callable = requires(S& storage) { - { storage.get_all() } -> same_as>; - { storage.count() } -> same_as; - { storage.iterate() } -> same_as>; +template> +concept storage_refers_to_table_callable = orm_refers_to_table && requires(S& storage) { + { storage.template get_all() } -> same_as>; + { storage.template count() } -> same_as; + { storage.template iterate() } -> same_as>; }; -template> -concept storage_table_reference_callable = requires(S& storage) { - { storage.get
(42) } -> same_as; - { storage.get_pointer
(42) } -> same_as>; - { storage.get_optional
(42) } -> same_as>; - { storage.get_all
() } -> same_as>; - { storage.get_all_pointer
() } -> same_as>>; - { storage.get_all_optional
() } -> same_as>>; - { storage.remove
(42) } -> same_as; - { storage.remove_all
() } -> same_as; - { storage.count
() } -> same_as; - { storage.iterate
() } -> same_as>; +template> +concept storage_table_reference_callable = orm_table_reference && requires(S& storage) { + { storage.template get
(42) } -> same_as; + { storage.template get_pointer
(42) } -> same_as>; + { storage.template get_optional
(42) } -> same_as>; + { storage.template get_all
() } -> same_as>; + { storage.template get_all_pointer
() } -> same_as>>; + { storage.template get_all_optional
() } -> same_as>>; + { storage.template remove
(42) } -> same_as; + { storage.template remove_all
() } -> same_as; + { storage.template count
() } -> same_as; + { storage.template iterate
() } -> same_as>; }; #endif diff --git a/tests/static_tests/core_function_return_types.cpp b/tests/static_tests/core_function_return_types.cpp index 333bc30b4..df9daf928 100644 --- a/tests/static_tests/core_function_return_types.cpp +++ b/tests/static_tests/core_function_return_types.cpp @@ -36,7 +36,7 @@ TEST_CASE("Builtin function return types") { // note: return type nullptr_t doesn't make sense but works for unit tests to assert intention STATIC_REQUIRE(is_same(&User::flag, nullptr))::return_type, nullptr_t>::value); -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) STATIC_REQUIRE(is_same_v>); STATIC_REQUIRE(is_same_v>); STATIC_REQUIRE(is_same_v>); diff --git a/tests/static_tests/foreign_key.cpp b/tests/static_tests/foreign_key.cpp index c869e304e..50ca5f8fa 100644 --- a/tests/static_tests/foreign_key.cpp +++ b/tests/static_tests/foreign_key.cpp @@ -9,6 +9,8 @@ #if SQLITE_VERSION_NUMBER >= 3006019 using namespace sqlite_orm; +using internal::as_field_of_t; +using internal::column_pointer; TEST_CASE("foreign key static") { struct FunctionDecl { @@ -40,6 +42,19 @@ TEST_CASE("foreign key static") { bool is_global; std::string file_path; }; + struct Base { + // Note: `long` was chosen as a different type than `int` for the primary key columns to ensure that the following static asserts work as expected. + long id; + long id2; + + int parentId; + int parentId2; + }; + struct Derived : Base {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived = c(); +#endif + auto cppInclusionIncluderPathFk = foreign_key(&CppInclusion::includer_path).references(&File::path); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); @@ -65,6 +80,72 @@ TEST_CASE("foreign key static") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); + SECTION("inheritance, single fk") { + using selfRefFk1 = + decltype(foreign_key(column(&Derived::parentId)).references(column(&Derived::id))); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + using selfRefFk2 = decltype(foreign_key(&Derived::parentId).references(&Derived::id)); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + using selfRefFk3 = decltype(foreign_key(&Derived::parentId).references(&Derived::id)); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); +#endif + } + SECTION("inheritance, composite fk") { + using selfRefMultiFk1 = + decltype(foreign_key(column(&Derived::parentId), column(&Derived::parentId2)) + .references(column(&Derived::id), column(&Derived::id2))); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + using selfRefMultiFk2 = decltype(foreign_key(&Derived::parentId, &Derived::parentId2) + .references(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + using selfRefMultiFk3 = decltype(foreign_key(&Derived::parentId, &Derived::parentId2) + .references(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); +#endif + } + auto storage = make_storage({}, make_table("files", make_column("path", &File::path, primary_key())), make_table("c_inclusions", diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index cbb827d43..ea27d0ad6 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -15,14 +15,14 @@ using internal::quoted_scalar_function; #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept storage_scalar_callable = requires(S& storage) { - { storage.create_scalar_function() }; - { storage.delete_scalar_function() }; + { storage.template create_scalar_function() }; + { storage.template delete_scalar_function() }; }; template concept storage_aggregate_callable = requires(S& storage) { - { storage.create_aggregate_function() }; - { storage.delete_aggregate_function() }; + { storage.template create_aggregate_function() }; + { storage.template delete_aggregate_function() }; }; #endif diff --git a/tests/static_tests/functional/functional.cpp b/tests/static_tests/functional/functional.cpp new file mode 100644 index 000000000..372d8cc55 --- /dev/null +++ b/tests/static_tests/functional/functional.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include // std::true_type + +using namespace sqlite_orm; + +TEST_CASE("always_default") { + STATIC_REQUIRE(internal::always_default()()); +} + +// https://rextester.com/WTEWN51158 +// THE BROKEN CASE, Fixed In: Visual Studio 2019 version 16.0 Preview 5 +// It appears that when the base base class in question has more than 1 template parameter +// and the second template parameter is not varadic, the dudction in above in [A] is +// unable to deduce Ts... This works in both gcc and clang +template +struct basea {}; + +// WORKING with 1 template parameter +template +struct baseb {}; + +// WORKING if the second parameter is varadic +template +struct basec {}; + +TEST_CASE("is_base_template_of") { + struct testa : basea {}; + struct testb : baseb {}; + struct testc : basec {}; + + STATIC_REQUIRE(internal::is_base_template_of::value); + STATIC_REQUIRE(internal::is_base_template_of::value); + STATIC_REQUIRE(internal::is_base_template_of::value); +} diff --git a/tests/static_tests/functional/index_sequence_util.cpp b/tests/static_tests/functional/index_sequence_util.cpp index 0b3d37695..712cb9c5f 100644 --- a/tests/static_tests/functional/index_sequence_util.cpp +++ b/tests/static_tests/functional/index_sequence_util.cpp @@ -8,9 +8,7 @@ using internal::index_sequence_value_at; TEST_CASE("index sequence value at") { STATIC_REQUIRE(index_sequence_value_at<0>(index_sequence<1, 3>{}) == 1); -#if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) || defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(index_sequence_value_at<1>(index_sequence<1, 3>{}) == 3); -#endif } TEST_CASE("flatten index sequence") { diff --git a/tests/static_tests/functional/same_or_void.cpp b/tests/static_tests/functional/same_or_void.cpp index a83c2dd6a..22bae8edf 100644 --- a/tests/static_tests/functional/same_or_void.cpp +++ b/tests/static_tests/functional/same_or_void.cpp @@ -9,30 +9,30 @@ using namespace sqlite_orm; TEST_CASE("same_or_void") { using internal::common_type_of_t; - using internal::same_or_void; + using internal::same_or_void_t; // one argument - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, std::string>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, std::string>::value); + STATIC_REQUIRE(std::is_same, long>::value); // two arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, std::string>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, std::string>::value); + STATIC_REQUIRE(std::is_same, void>::value); // three arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, long>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, void>::value); // four arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, long>::value); + STATIC_REQUIRE(std::is_same, void>::value); // type pack, e.g. tuple STATIC_REQUIRE(std::is_same>, int>::value); diff --git a/tests/static_tests/functional/static_if_tests.cpp b/tests/static_tests/functional/static_if_tests.cpp deleted file mode 100644 index feacbc500..000000000 --- a/tests/static_tests/functional/static_if_tests.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include - -using namespace sqlite_orm; -using internal::call_if_constexpr; -using internal::static_if; - -TEST_CASE("static_if") { - using called_pair = std::pair; - called_pair called; - { // simple true - called = {}; - static_if( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{1, 0}); - } - { // simple false - called = {}; - static_if( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { // tuple is empty - called = {}; - static_if>::value>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{1, 0}); - } - { // tuple is not empty - called = {}; - static_if>>>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { - struct User { - std::string name; - }; - auto ch = check(length(&User::name) > 5); - STATIC_REQUIRE_FALSE(internal::is_column_v); - called = {}; - static_if>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { // simple true - called = {}; - call_if_constexpr([&called] { - ++called.first; - }); - REQUIRE(called == called_pair{1, 0}); - } - { // simple false - called = {}; - call_if_constexpr([&called] { - ++called.first; - }); - REQUIRE(called == called_pair{0, 0}); - } -} diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index d67539098..5fab693e5 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -7,16 +7,14 @@ using namespace sqlite_orm; using internal::create_from_tuple; using internal::field_type_t; using internal::literal_holder; -using internal::transform_tuple_t; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) using internal::nested_tuple_size_for_t; using internal::recombine_tuple; -#endif +using internal::transform_tuple_t; template using make_literal_holder = literal_holder; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) +#if __cpp_lib_constexpr_functional >= 201907L struct tuple_maker { template constexpr auto operator()(Types&&... types) const { @@ -49,8 +47,10 @@ TEST_CASE("tuple_helper static") { #if __cpp_lib_constexpr_algorithms >= 201806L STATIC_REQUIRE(create_from_tuple>(std::make_tuple(1, 2), polyfill::identity{}) == std::array{1, 2}); + STATIC_REQUIRE(create_from_tuple(std::make_tuple(1, 2), polyfill::identity{}) == + std::tuple{1, 2}); #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) +#if __cpp_lib_constexpr_functional >= 201907L STATIC_REQUIRE(recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == std::make_tuple(3, 1, 2)); diff --git a/tests/static_tests/is_bindable.cpp b/tests/static_tests/is_bindable.cpp index 4dc6ab529..71fde2e89 100644 --- a/tests/static_tests/is_bindable.cpp +++ b/tests/static_tests/is_bindable.cpp @@ -111,17 +111,5 @@ TEST_CASE("is_bindable") { { auto func = datetime("now"); STATIC_REQUIRE_FALSE(is_bindable_v); - bool trueCalled = false; - bool falseCalled = false; - auto dummy = 5; // for gcc compilation - internal::static_if>( - [&trueCalled](int&) { - trueCalled = true; - }, - [&falseCalled](int&) { - falseCalled = true; - })(dummy); - REQUIRE_FALSE(trueCalled); - REQUIRE(falseCalled); } } diff --git a/tests/static_tests/iterator_t.cpp b/tests/static_tests/iterator_t.cpp index 554692cf5..efd7dab3b 100644 --- a/tests/static_tests/iterator_t.cpp +++ b/tests/static_tests/iterator_t.cpp @@ -72,8 +72,8 @@ concept can_view_mapped = requires(V view) { template concept storage_iterate_mapped = requires(S& storage_type) { - { storage_type.iterate() } -> std::same_as>; - { storage_type.iterate() } -> can_view_mapped; + { storage_type.template iterate() } -> std::same_as>; + { storage_type.template iterate() } -> can_view_mapped; }; #endif diff --git a/tests/static_tests/member_traits_tests.cpp b/tests/static_tests/member_traits_tests.cpp index 597fa9fa2..3062d9850 100644 --- a/tests/static_tests/member_traits_tests.cpp +++ b/tests/static_tests/member_traits_tests.cpp @@ -4,6 +4,8 @@ using namespace sqlite_orm; TEST_CASE("member_traits_tests") { + using internal::as_field_of; + using internal::as_field_of_t; using internal::getter_field_type_t; using internal::is_getter; using internal::is_setter; @@ -105,20 +107,22 @@ TEST_CASE("member_traits_tests") { } }; + struct DerivedUser : User {}; + STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); -#endif + STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE_FALSE(is_getter::value); @@ -141,11 +145,10 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); -#endif STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); @@ -166,7 +169,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -190,7 +193,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -202,7 +205,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -214,7 +217,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -242,7 +245,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -266,7 +269,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -278,7 +281,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -290,5 +293,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif + + STATIC_REQUIRE(is_same(&User::id))>, DerivedUser>::value); + STATIC_REQUIRE(is_same>, DerivedUser>::value); } diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 7109cbf72..89cf2894a 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -595,14 +595,14 @@ TEST_CASE("Node tuple") { using Like = decltype(lk); using NodeTuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "like(&User::name, \"S%\") type 0"); + static_assert(is_same::value, R"(like(&User::name, "S%") type 0)"); } SECTION("like(&User::name, std::string('pattern'), '%')") { auto lk = like(&User::name, std::string("pattern"), "%"); using Like = decltype(lk); using NodeTuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "like(&User::name, std::string(\"pattern\"), \"%\")"); + static_assert(is_same::value, R"(like(&User::name, std::string("pattern"), "%"))"); } SECTION("like(&User::name, std::string('pattern')).escape('%')") { auto lk = like(&User::name, std::string("pattern")).escape("%"); @@ -610,7 +610,7 @@ TEST_CASE("Node tuple") { using NodeTuple = node_tuple_t; using Expected = tuple; static_assert(is_same::value, - "like(&User::name, std::string(\"pattern\")).escape(\"%\")"); + R"(like(&User::name, std::string("pattern")).escape("%"))"); } } SECTION("order_by_t") { @@ -630,12 +630,16 @@ TEST_CASE("Node tuple") { SECTION("column alias in expression") { STATIC_REQUIRE(is_same() > 1))>, tuple>::value); } + SECTION("multi") { + STATIC_REQUIRE(is_same, + tuple>::value); + } } SECTION("glob_t") { auto gl = glob(&User::name, "H*"); using Glob = decltype(gl); using Tuple = node_tuple_t; - static_assert(is_same>::value, "glob(&User::name, \"H*\")"); + static_assert(is_same>::value, R"(glob(&User::name, "H*"))"); } SECTION("between_t") { auto bet = between(&User::id, 10, 20); @@ -656,7 +660,7 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not is_equal(20, \"20\")"); + static_assert(is_same::value, R"(not is_equal(20, "20"))"); } SECTION("not is_not_equal(&User::id, 15.0)") { auto c = not is_not_equal(&User::id, 15.0); @@ -684,7 +688,7 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not less_than(&User::id, std::string(\"6\"))"); + static_assert(is_same::value, R"(not less_than(&User::id, std::string("6")))"); } SECTION("not less_or_equal(&User::id, 10)") { auto c = not less_or_equal(&User::id, 10); @@ -719,14 +723,14 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not like(&User::name, \"*D*\")"); + static_assert(is_same::value, R"(not like(&User::name, "*D*"))"); } SECTION("not glob(&User::name, std::string('_A_'))") { auto c = not glob(&User::name, std::string("_A_")); using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not glob(&User::name, std::string(\"_A_\"))"); + static_assert(is_same::value, R"(not glob(&User::name, std::string("_A_")))"); } SECTION("not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { auto c = not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); diff --git a/tests/static_tests/primary_key.cpp b/tests/static_tests/primary_key.cpp new file mode 100644 index 000000000..1c2c9429a --- /dev/null +++ b/tests/static_tests/primary_key.cpp @@ -0,0 +1,36 @@ +#include +#include + +using namespace sqlite_orm; +using internal::as_field_of_t; +using internal::column_pointer; + +TEST_CASE("primary key static") { + struct Base { + int id; + int id2; + }; + struct Derived : Base {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived = c(); +#endif + + SECTION("inheritance, composite pk") { + using compositePk1 = decltype(primary_key(column(&Derived::id), column(&Derived::id2))); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + + using compositePk2 = decltype(primary_key(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + using compositePk3 = decltype(primary_key(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); +#endif + } +} diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index 453cf2955..16a73ced4 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -1,8 +1,8 @@ #pragma once -/* - * All symbols used to be in dev/storage_traits.h up to sqlite_orm 1.7. - * Because they were not used, they were moved to unit tests, - * and for simplicitly the namespace sqlite_orm::internal::storage_traits was kept. +/* + * All symbols used to be in dev/storage_traits.h up to sqlite_orm 1.7. + * Because they were not used, they were moved to unit tests, + * and for simplicitly the namespace sqlite_orm::internal::storage_traits was kept. */ #include // std::integral_constant @@ -25,53 +25,14 @@ namespace sqlite_orm { template<> struct storage_columns_count_impl : std::integral_constant {}; - /** - * S - storage_t - * Lookup - mapped or not mapped data type + /** + * S - storage_t + * Lookup - mapped or not mapped data type */ template struct storage_columns_count : storage_columns_count_impl> {}; - /** - * Table A `table_t<>` - * Lookup is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has Lookup = User) - */ - template - struct table_foreign_keys_count - : count_filtered_tuple, - check_if_is_type::template fn, - filter_tuple_sequence_t, is_foreign_key>, - target_type_t> {}; - - /** - * DBOs - db_objects_tuple - * Lookup - type mapped to storage - */ - template - struct storage_foreign_keys_count_impl : std::integral_constant {}; - -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = (table_foreign_keys_count::value + ...); - }; -#else - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = table_foreign_keys_count::value + - storage_foreign_keys_count_impl, Lookup>::value; - }; -#endif - - /** - * S - storage class - * Lookup - type mapped to storage - * This class tells how many types mapped to DBOs have foreign keys to Lookup - */ - template - struct storage_foreign_keys_count : storage_foreign_keys_count_impl {}; - template using table_foreign_keys_t = filter_tuple_t, @@ -79,10 +40,10 @@ namespace sqlite_orm { target_type_t, filter_tuple_sequence_t, is_foreign_key>>; - /* - * Implementation note: must be a struct instead of an alias template because the foreign keys tuple - * must be hoisted into a named alias, otherwise type replacement may fail for legacy compilers - * if an alias template has a dependent expression in it [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + /* + * Implementation note: must be a struct instead of an alias template because the foreign keys tuple + * must be hoisted into a named alias, otherwise type replacement may fail for legacy compilers + * if an alias template has a dependent expression in it [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. */ template struct table_fk_references { @@ -91,9 +52,9 @@ namespace sqlite_orm { using type = transform_tuple_t; }; - /** - * DBOs - db_objects_tuple - * Lookup - type mapped to storage + /** + * DBOs - db_objects_tuple + * Lookup - type mapped to storage */ template struct storage_fk_references_impl; @@ -109,16 +70,42 @@ namespace sqlite_orm { struct storage_foreign_keys_impl, Lookup> : conc_tuple...> {}; - /** - * S - storage class - * Lookup - type mapped to storage - * type holds `std::tuple` with types that has references to Lookup as foreign keys + /** + * S - storage class + * Lookup - type mapped to storage + * type holds `std::tuple` with types that has references to Lookup as foreign keys */ template struct storage_fk_references : storage_fk_references_impl {}; template struct storage_foreign_keys : storage_foreign_keys_impl {}; + + template + constexpr int table_foreign_keys_target_count() { + using namespace sqlite_orm::internal; + + using elements_type = elements_type_t
; + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + + return int(filtered_index_sequence::size()); + } + + template + constexpr int storage_foreign_keys_target_count() { + using namespace sqlite_orm::internal; + + int res = 0; + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_foreign_keys_target_count(); + }); + return res; + } } } } diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index ff782f178..7020a7969 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -5,13 +5,11 @@ using namespace sqlite_orm; using internal::is_column; using internal::is_primary_key; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) template using dedicated_pk_columns_count_t = internal::nested_tuple_size_for_t>; -#endif TEST_CASE("table static count_of()") { struct User { @@ -23,72 +21,56 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 inline pk auto table = make_table("users", make_column("id", &User::id, primary_key())); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 inline pk autoincrement auto table = make_table("users", make_column("id", &User::id, primary_key().autoincrement())); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 dedicated pk auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id)); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 1 column with 1 dedicated pk autoincrement auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id).autoincrement()); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns no pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name)); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 inline id pk auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("id", &User::name)); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 inline name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name, primary_key())); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 dedicated id pk auto table = @@ -96,9 +78,7 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns with 1 dedicated name pk auto table = @@ -106,9 +86,7 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns with 2 dedicated pks auto table = make_table("users", @@ -118,8 +96,6 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 2); -#endif } } diff --git a/tests/static_tests/vfs_open_mode_static_tests.cpp b/tests/static_tests/vfs_open_mode_static_tests.cpp new file mode 100644 index 000000000..6c20b4a89 --- /dev/null +++ b/tests/static_tests/vfs_open_mode_static_tests.cpp @@ -0,0 +1,12 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("db_open_mode flag conversion returns expected flags") { + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::default_) == + (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::create_readwrite) == + (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::readonly) == (SQLITE_OPEN_READONLY)); +} diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index 15ad250f9..215be01ed 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -1,6 +1,8 @@ #include #include #include +#include // std::strcmp +#include "catch_matchers.h" using namespace sqlite_orm; @@ -8,11 +10,6 @@ TEST_CASE("explicit from") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( {}, @@ -78,11 +75,6 @@ TEST_CASE("update set null") { struct User { int id = 0; std::unique_ptr name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, decltype(name) name) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( @@ -123,7 +115,7 @@ TEST_CASE("update set null") { TEST_CASE("InsertRange") { struct Object { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -133,7 +125,7 @@ TEST_CASE("InsertRange") { }; struct ObjectWithoutRowid { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -278,9 +270,9 @@ TEST_CASE("Select") { throw std::runtime_error(sqlite3_errmsg(db)); } REQUIRE(sqlite3_column_int(stmt, 0) == firstId); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 1), "best") == 0); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 2), "behaviour") == 0); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 3), "hey") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 1), "best") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 2), "behaviour") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 3), "hey") == 0); REQUIRE(sqlite3_column_int(stmt, 4) == 5); sqlite3_finalize(stmt); } @@ -387,7 +379,7 @@ TEST_CASE("Select") { TEST_CASE("Replace query") { struct Object { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -493,7 +485,7 @@ TEST_CASE("Replace query") { TEST_CASE("Remove all") { struct Object { - int id; + int id = 0; std::string name; }; @@ -513,7 +505,11 @@ TEST_CASE("Remove all") { } TEST_CASE("Explicit insert") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3037002 + const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_NOTNULL)); +#else + const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct User { int id; @@ -602,8 +598,9 @@ TEST_CASE("Explicit insert") { SECTION("one column") { User user4; user4.name = "Egor"; - REQUIRE_THROWS_WITH(storage.insert(user4, columns(&User::name)), - ContainsSubstring("NOT NULL constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(user4, columns(&User::name)), + std::system_error, + notNullExceptionMatcher); } } SECTION("visit") { @@ -635,8 +632,9 @@ TEST_CASE("Explicit insert") { Visit visit3; visit3.setId(10); SECTION("getter") { - REQUIRE_THROWS_WITH(storage.insert(visit3, columns(&Visit::id)), - ContainsSubstring("NOT NULL constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(visit3, columns(&Visit::id)), + std::system_error, + notNullExceptionMatcher); } } } diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 437eced79..c3844ab53 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -4,6 +4,61 @@ using namespace sqlite_orm; +#ifdef SQLITE_ORM_CTAD_SUPPORTED +TEST_CASE("connection control") { + const auto openForever = GENERATE(false, true); + SECTION("") { + bool onOpenCalled = false; + int nOnOpenCalled = 0; + SECTION("empty") { + auto storage = + make_storage("", connection_control{openForever}, on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); + } +#if __cpp_designated_initializers >= 201707L + SECTION("empty C++20") { + auto storage = make_storage("", + connection_control{.open_forever = openForever}, + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); + } +#endif + SECTION("memory") { + auto storage = make_storage(":memory:", + connection_control{openForever}, + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); + } + SECTION("file name") { + auto storage = make_storage("myDatabase.sqlite", + connection_control{openForever}, + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); + REQUIRE(storage.is_opened() == openForever); + REQUIRE(onOpenCalled == openForever); + REQUIRE(nOnOpenCalled == int(openForever)); + } + } +} +#endif + TEST_CASE("Current time/date/timestamp") { auto storage = make_storage(""); SECTION("time") { @@ -454,16 +509,14 @@ TEST_CASE("insert with generated column") { double tax = 0; double netPrice = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() = default; - Product(std::string name, double price, double discount, double tax, double netPrice) : - name{std::move(name)}, price{price}, discount{discount}, tax{tax}, netPrice{netPrice} {} -#endif - +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Product&) const = default; +#else bool operator==(const Product& other) const { return this->name == other.name && this->price == other.price && this->discount == other.discount && this->tax == other.tax && this->netPrice == other.netPrice; } +#endif }; auto storage = make_storage({}, diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 6c0aab30f..1df6109f0 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -1,4 +1,5 @@ #include +#include // std::remove #include #include @@ -16,7 +17,7 @@ TEST_CASE("Sync schema") { // this is an old version of user.. struct UserBefore { - int id; + int id = 0; std::string name; std::unique_ptr categoryId; std::unique_ptr surname; @@ -24,17 +25,17 @@ TEST_CASE("Sync schema") { // this is a new version of user struct UserAfter { - int id; + int id = 0; std::string name; - bool operator==(const UserBefore& userBefore) const { - return this->id == userBefore.id && this->name == userBefore.name; + bool operator==(const UserBefore& before) const { + return this->id == before.id && this->name == before.name; } }; // create an old storage auto filename = "sync_schema_text.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename, make_table("users", make_column("id", &UserBefore::id, primary_key()), @@ -117,12 +118,12 @@ TEST_CASE("Sync schema") { TEST_CASE("issue854") { struct Base { std::string name; - int64_t timestamp; - int64_t value; + int64_t timestamp = 0; + int64_t value = 0; }; struct A : public Base { - int64_t id; + int64_t id = 0; }; auto storage = make_storage({}, make_table("entries", @@ -137,20 +138,14 @@ TEST_CASE("issue521") { auto storagePath = "issue521.sqlite"; struct MockDatabasePoco { - int id{-1}; + int id = 0; std::string name; std::uint32_t alpha{0}; float beta{0.0}; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MockDatabasePoco() = default; - MockDatabasePoco(int id, std::string name, uint32_t alpha, float beta) : - id{id}, name{std::move(name)}, alpha{alpha}, beta{beta} {} -#endif }; std::vector pocosToInsert; - ::remove(storagePath); + std::remove(storagePath); { // --- Create the initial database auto storage = sqlite_orm::make_storage( @@ -167,8 +162,8 @@ TEST_CASE("issue521") { // --- Insert two rows pocosToInsert.clear(); - pocosToInsert.push_back({-1, "Michael", 10, 10.10f}); - pocosToInsert.push_back({-1, "Joyce", 20, 20.20f}); + pocosToInsert.push_back({0, "Michael", 10, 10.10f}); + pocosToInsert.push_back({0, "Joyce", 20, 20.20f}); for (auto& poco: pocosToInsert) { auto insertedId = storage.insert(poco); @@ -310,7 +305,7 @@ TEST_CASE("sync_schema") { const std::string name = "name"; const std::string age = "age"; } columnNames; - ::remove(storagePath); + std::remove(storagePath); { auto storage = make_storage(storagePath, make_table(tableName, @@ -476,7 +471,7 @@ TEST_CASE("sync_schema") { TEST_CASE("sync_schema_simulate") { struct Cols { - int Col1; + int Col1 = 0; }; auto storage = @@ -485,23 +480,23 @@ TEST_CASE("sync_schema_simulate") { storage.sync_schema(); storage.sync_schema_simulate(); } + #if SQLITE_VERSION_NUMBER >= 3031000 TEST_CASE("sync_schema with generated columns") { struct User { int id = 0; int hash = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, int hash = 0) : id{id}, hash{hash} {} -#endif - +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id && this->hash == other.hash; } +#endif }; auto storagePath = "sync_schema_with_generated.sqlite"; - ::remove(storagePath); + std::remove(storagePath); auto storage1 = make_storage(storagePath, make_table("users", make_column("id", &User::id))); storage1.sync_schema(); storage1.insert(User{5}); diff --git a/tests/table_name_collector.cpp b/tests/table_name_collector.cpp index d4a838c17..36f5a79d6 100644 --- a/tests/table_name_collector.cpp +++ b/tests/table_name_collector.cpp @@ -18,6 +18,11 @@ TEST_CASE("table name collector") { internal::serializer_context context{dbObjects}; auto collector = internal::make_table_name_collector(context.db_objects); + SECTION("static tests") { + STATIC_REQUIRE(polyfill::is_invocable>, + polyfill::bool_constant, + const internal::highlight_t&>::value); + } SECTION("from table") { SECTION("regular column") { auto expression = &User::id; diff --git a/tests/tests.cpp b/tests/tests.cpp index 8fd149691..ef5f507d3 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,9 +1,11 @@ #include #include +#include "catch_matchers.h" #include // std::vector #include // std::string -#include // remove +#include // std::remove +#include // std::strncmp using namespace sqlite_orm; @@ -101,7 +103,11 @@ TEST_CASE("Limits") { } TEST_CASE("Custom collate") { - using Catch::Matchers::ContainsSubstring; +#if defined(SQLITE_ORM_STRING_VIEW_SUPPORTED) || SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)); +#else + const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR)); +#endif struct Item { int id; @@ -111,7 +117,7 @@ TEST_CASE("Custom collate") { struct OtotoCollation { int operator()(int leftLength, const void* lhs, int rightLength, const void* rhs) const { if (leftLength == rightLength) { - return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + return std::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; } @@ -141,7 +147,7 @@ TEST_CASE("Custom collate") { } auto filename = "custom_collate.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); @@ -153,7 +159,7 @@ TEST_CASE("Custom collate") { if (useLegacyScript) { storage.create_collation("ototo", [](int leftLength, const void* lhs, int rightLength, const void* rhs) { if (leftLength == rightLength) { - return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + return std::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; } @@ -186,12 +192,16 @@ TEST_CASE("Custom collate") { } else { storage.delete_collation(); } - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))), - ContainsSubstring("no such collation sequence")); - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())), - ContainsSubstring("no such collation sequence")); - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))), - ContainsSubstring("no such collation sequence")); + REQUIRE_THROWS_MATCHES(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))), + std::system_error, + collSequExceptionMatcher); + REQUIRE_THROWS_MATCHES( + storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())), + std::system_error, + collSequExceptionMatcher); + REQUIRE_THROWS_MATCHES(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))), + std::system_error, + collSequExceptionMatcher); rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("alwaysequal")), diff --git a/tests/tests4.cpp b/tests/tests4.cpp index f2626497e..50e855b96 100644 --- a/tests/tests4.cpp +++ b/tests/tests4.cpp @@ -74,22 +74,12 @@ TEST_CASE("join") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Visit { int id = 0; int userId = 0; time_t date = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int id, int userId, time_t date) : id{id}, userId{userId}, date{date} {} -#endif }; auto storage = diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index a23fdba03..5654a5c8e 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -9,20 +10,19 @@ namespace { int id = 0; std::string name; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif - +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Object&) const = default; +#else bool operator==(const Object& other) const { return this->id == other.id && this->name == other.name; } +#endif }; } TEST_CASE("transaction") { auto filename = "transaction_test.sqlite"; - ::remove(filename); + std::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))); @@ -64,7 +64,10 @@ TEST_CASE("begin_transaction") { } TEST_CASE("Transaction guard") { - ::remove("guard.sqlite"); + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); + const ErrorCodeExceptionMatcher busyExceptionMatcher(sqlite_errc(SQLITE_BUSY)); + + std::remove("guard.sqlite"); auto table = make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)); auto storage = make_storage("guard.sqlite", table); @@ -74,7 +77,6 @@ TEST_CASE("Transaction guard") { storage.insert(Object{0, "Jack"}); - const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); SECTION("insert, call make a storage to call an exception and check that rollback was fired") { auto countBefore = storage.count(); SECTION("transaction_guard") { @@ -329,8 +331,6 @@ TEST_CASE("Transaction guard") { REQUIRE(storage.count() == countBefore); } SECTION("exception propagated from dtor") { - using Catch::Matchers::ContainsSubstring; - // create a second database connection auto storage2 = make_storage("guard.sqlite", table); auto guard2 = storage2.transaction_guard(); @@ -340,7 +340,7 @@ TEST_CASE("Transaction guard") { auto guard = new (&buffer) internal::transaction_guard_t{storage.transaction_guard()}; storage.insert({}); guard->commit_on_destroy = true; - REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); + REQUIRE_THROWS_MATCHES(guard->~transaction_guard_t(), std::system_error, busyExceptionMatcher); } - ::remove("guard.sqlite"); + std::remove("guard.sqlite"); } diff --git a/tests/trigger_tests.cpp b/tests/trigger_tests.cpp index aad420adc..f930f4307 100644 --- a/tests/trigger_tests.cpp +++ b/tests/trigger_tests.cpp @@ -5,37 +5,22 @@ using namespace sqlite_orm; TEST_CASE("triggers_basics") { struct TestInsert { - int id; + int id = 0; std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestInsert() = default; - TestInsert(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; struct TestUpdate { - int id; + int id = 0; std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestUpdate() = default; - TestUpdate(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; struct TestDelete { - int id; + int id = 0; std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestDelete() = default; - TestDelete(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; TestInsert test_insert{4, "test", 1, 2}; diff --git a/tests/tuple_iteration.cpp b/tests/tuple_iteration.cpp index a08c92881..1db32fffc 100644 --- a/tests/tuple_iteration.cpp +++ b/tests/tuple_iteration.cpp @@ -12,7 +12,7 @@ TEST_CASE("tuple iteration") { std::vector expected; std::vector types; SECTION("iterate_tuple with tuple instance") { - auto lambda = [&types](const auto& item) { + const auto lambda = [&types](const auto& item) { types.emplace_back(typeid(item)); }; SECTION("empty") { @@ -29,15 +29,25 @@ TEST_CASE("tuple iteration") { iterate_tuple(tuple, lambda); expected = {typeid(std::string), typeid(long)}; } + SECTION("std::string, long, reversed") { + std::tuple tuple; + iterate_tuple(tuple, lambda); + expected = {typeid(long), typeid(std::string)}; + } SECTION("index selection") { constexpr size_t selectedIdx = 1; - std::tuple tuple; + std::tuple tuple; iterate_tuple(tuple, std::index_sequence{}, lambda); expected = {typeid(long)}; } + SECTION("index selection, reversed") { + std::tuple tuple; + iterate_tuple(tuple, std::index_sequence<1, 2>{}, lambda); + expected = {typeid(int), typeid(long)}; + } } SECTION("iterate_tuple with no tuple instance") { - auto lambda = [&types](auto* itemPointer) { + const auto lambda = [&types](auto* itemPointer) { using Item = std::remove_pointer_t; types.emplace_back(typeid(Item)); }; diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index ddf7d6ad2..4172cbc17 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -16,15 +16,14 @@ namespace { int id = 0; std::string attributes; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Item() = default; - Item(int id, std::string attributes) : id{id}, attributes{std::move(attributes)} {} +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const Item&, const Item&) = default; +#else + friend bool operator==(const Item& lhs, const Item& rhs) { + return lhs.id == rhs.id && lhs.attributes == rhs.attributes; + } #endif }; - - inline bool operator==(const Item& lhs, const Item& rhs) { - return lhs.id == rhs.id && lhs.attributes == rhs.attributes; - } } TEST_CASE("get_all with two tables") { diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp index 1a2343cdd..19160a17d 100644 --- a/tests/unique_cases/issue663.cpp +++ b/tests/unique_cases/issue663.cpp @@ -65,16 +65,18 @@ namespace { namespace { struct User1 { + int id = 0; + std::string name; + int age = 0; + std::string email; + +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User1&) const = default; +#else bool operator==(const User1& rhs) const { return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); } - bool operator!=(const User1& rhs) const { - return !(rhs == *this); - } - int id; - std::string name; - int age; - std::string email; +#endif }; } diff --git a/tests/unique_cases/issue937.cpp b/tests/unique_cases/issue937.cpp index b0baf876a..19f7370a0 100644 --- a/tests/unique_cases/issue937.cpp +++ b/tests/unique_cases/issue937.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #if SQLITE_VERSION_NUMBER >= 3006019 using namespace sqlite_orm; @@ -32,7 +33,7 @@ TEST_CASE("issue937") { using namespace sqlite_orm; - ::remove("SQLCookbook.sqlite"); + std::remove("SQLCookbook.sqlite"); auto storage = make_storage("SQLCookbook.sqlite", make_table("Emp", make_column("empno", &Employee::m_empno, primary_key().autoincrement()), diff --git a/tests/unique_cases/prepare_get_all_with_case.cpp b/tests/unique_cases/prepare_get_all_with_case.cpp index 60d21dcb4..e0657fcfb 100644 --- a/tests/unique_cases/prepare_get_all_with_case.cpp +++ b/tests/unique_cases/prepare_get_all_with_case.cpp @@ -7,11 +7,6 @@ TEST_CASE("Prepare with case") { struct UserProfile { int id = 0; std::string firstName; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserProfile() = default; - UserProfile(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} -#endif }; auto storage = make_storage({}, diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 45548f607..8f7361ffa 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -278,7 +279,12 @@ struct NonDefaultCtorAggregateFunction { }; TEST_CASE("custom functions") { - using Catch::Matchers::ContainsSubstring; + const ErrorCodeExceptionMatcher noMemExceptionMatcher(sqlite_errc(SQLITE_NOMEM)); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::function_not_found); +#else + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(sqlite_errc(SQLITE_ERROR)); +#endif SqrtFunction::callsCount = 0; StatelessHasPrefixFunction::callsCount = 0; @@ -291,15 +297,10 @@ TEST_CASE("custom functions") { } SECTION("file") { path = "custom_function.sqlite"; - ::remove(path.c_str()); + std::remove(path.c_str()); } struct User { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif }; auto storage = make_storage(path, make_table("users", make_column("id", &User::id))); storage.sync_schema(); @@ -313,11 +314,11 @@ TEST_CASE("custom functions") { // test w/o a result set, i.e when the final aggregate call is the first to require the aggregate function REQUIRE_THROWS_MATCHES(storage.select(func(&User::id)), std::system_error, - ErrorCodeExceptionMatcher(sqlite_errc(SQLITE_NOMEM))); + noMemExceptionMatcher); storage.delete_aggregate_function(); // call before creation - REQUIRE_THROWS_WITH(storage.select(func(4)), ContainsSubstring("no such function")); + REQUIRE_THROWS_MATCHES(storage.select(func(4)), std::system_error, notFoundExceptionMatcher); // create function REQUIRE(SqrtFunction::callsCount == 0); @@ -429,7 +430,7 @@ TEST_CASE("custom functions") { storage.create_aggregate_function(); REQUIRE_THROWS_MATCHES(storage.select(func(&User::id)), std::system_error, - ErrorCodeExceptionMatcher(sqlite_errc(SQLITE_NOMEM))); + noMemExceptionMatcher); storage.delete_aggregate_function(); storage.create_scalar_function(); @@ -614,14 +615,17 @@ TEST_CASE("generalized scalar udf") { storage.delete_scalar_function(); } SECTION("non-copyable function object") { - constexpr auto idfunc_f = "idfunc"_scalar.quote(); - storage.create_scalar_function(); + // note: unlike msvc, gcc+clang require a constant template parameter to be copyable (and probably rightly so); + // so we must explicitly use an ordinary l-value expression from a global quoted function. + + static constexpr auto idfunc_f = "idfunc"_scalar.quote(); + storage.create_scalar_function<(idfunc_f)>(); { auto rows = storage.select(idfunc_f(1)); decltype(rows) expected{1}; REQUIRE(rows == expected); } - storage.delete_scalar_function(); + storage.delete_scalar_function<(idfunc_f)>(); } SECTION("stateful function object") { constexpr auto offset0_f = "offset0"_scalar.quote(offset0); diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp new file mode 100644 index 000000000..53a189659 --- /dev/null +++ b/tests/vfs_open_mode_tests.cpp @@ -0,0 +1,105 @@ +#include +#include +#include // std::remove +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif +#include "catch_matchers.h" + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + +struct User { + std::string id; +}; + +using namespace sqlite_orm; + +static const auto default_table = make_table("users", make_column("id", &User::id, primary_key())); + +TEST_CASE("vfs modes open successfully") { + +#if defined(SQLITE_ORM_APPLE) + internal::string_constant_type vfs = + GENERATE(unix_vfs_name, unix_posix_vfs_name, unix_dotfile_vfs_name, unix_afp_vfs_name); +#elif defined(SQLITE_ORM_UNIX) + internal::string_constant_type vfs = GENERATE(unix_vfs_name, unix_posix_vfs_name, unix_dotfile_vfs_name); +#elif defined(SQLITE_ORM_WIN) + internal::string_constant_type vfs = GENERATE(win32_vfs_name, win32_longpath_vfs_name); +#endif + + connection_control options{true, std::string(vfs)}; + auto storage = make_storage(":memory:", options, default_table); + UNSCOPED_INFO("FAILED VFS: " << vfs); + REQUIRE_NOTHROW(storage.open_forever()); + + REQUIRE(storage.vfs_name() == vfs); + REQUIRE(storage.open_mode() == db_open_mode::default_); + + SECTION("Storage copy operator carries over vfs option") { + auto storage_copy = storage; + REQUIRE(storage_copy.is_opened()); + REQUIRE(storage_copy.vfs_name() == vfs); + REQUIRE(storage_copy.open_mode() == db_open_mode::default_); + } +} + +TEST_CASE("readwrite/readonly open modes behaves as expected") { + const bool in_memory = GENERATE(true, false); + + const char* tmp_filename; + if (in_memory) { + tmp_filename = ":memory:"; + } else { + tmp_filename = "open_mode.sqlite"; + } + + connection_control options{true}, readonly_options{true}; + options.open_mode = db_open_mode::create_readwrite; + readonly_options.open_mode = db_open_mode::readonly; + + if (!in_memory) { + std::remove(tmp_filename); + } + + SECTION("rw+ro") { + auto storage = make_storage(tmp_filename, default_table, options); + CHECK(storage.is_opened()); + REQUIRE(storage.open_mode() == db_open_mode::create_readwrite); + REQUIRE_FALSE(storage.db_readonly()); + + storage.sync_schema(); + const User dummy{"dummy"}; + storage.replace(dummy); + + SECTION("readonly open mode behaves as expected") { + auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); + CHECK(readonly_storage.is_opened()); + REQUIRE(readonly_storage.open_mode() == db_open_mode::readonly); + REQUIRE(readonly_storage.db_readonly()); + + if (in_memory) { + SKIP("skipped for in-memory"); + } + REQUIRE_NOTHROW(readonly_storage.sync_schema()); + CHECK_NOTHROW(readonly_storage.get(dummy.id)); + const ErrorCodeExceptionMatcher readOnlyExceptionMatcher(sqlite_errc{SQLITE_READONLY}); + REQUIRE_THROWS_MATCHES(readonly_storage.remove(dummy.id), + std::system_error, + readOnlyExceptionMatcher); + } + } + SECTION("readonly fails with non-existing files") { + if (in_memory) { + SKIP("skipped for in-memory"); + } + +#if __cpp_lib_filesystem >= 201703L + CHECK_FALSE(std::filesystem::exists(tmp_filename)); +#endif + const ErrorCodeExceptionMatcher cantOpenExceptionMatcher(sqlite_errc{SQLITE_CANTOPEN}); + REQUIRE_THROWS_MATCHES(make_storage(tmp_filename, readonly_options, default_table), + std::system_error, + cantOpenExceptionMatcher); + } +} +#endif