From dd56a8d57e1bf071194bc245417a267d837f8a71 Mon Sep 17 00:00:00 2001 From: Lukas Forsberg Date: Thu, 23 Oct 2025 21:28:52 +0200 Subject: [PATCH] new db interface --- CMakeLists.txt | 1 + src/database/database.cpp | 4 +- src/database/database.hpp | 24 + src/database/databasepool.h | 40 + src/database/sqlite_orm.h | 24841 +++++++++++++++++++++++++++++++ src/shadowrun/ShadowrunApi.cpp | 3 - src/shadowrun/ShadowrunDb.cpp | 64 +- src/shadowrun/ShadowrunDb.hpp | 17 +- 8 files changed, 24936 insertions(+), 58 deletions(-) create mode 100644 src/database/databasepool.h create mode 100644 src/database/sqlite_orm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f4f4e7..c712884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ add_executable(${TARGET_NAME} src/database/database.cpp src/database/database.hpp + src/database/sqlite_orm.h # Shadowrun src/shadowrun/HtmxShItemList.cpp diff --git a/src/database/database.cpp b/src/database/database.cpp index 4b55933..6feba3b 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -1,5 +1,5 @@ #include "utils.hpp" -#include "database.hpp" +#include "databasepool.h" #include using namespace std; @@ -110,3 +110,5 @@ bool Database::open(){ } return true; } + +DatabasePool dbpool(std::thread::hardware_concurrency()); \ No newline at end of file diff --git a/src/database/database.hpp b/src/database/database.hpp index adf2964..6d24a4f 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -9,12 +9,16 @@ #include #include #include "crow.h" +#include "sqlite_orm.h" +#include "ShadowrunDb.hpp" class Database { typedef std::vector> QueryData; public: + static constexpr std::string dbFile = "app.db"; + Database(); ~Database(); @@ -74,6 +78,26 @@ private: sqlite3_stmt* prepareStmt(const std::string& sql); sqlite3* m_db; + }; + +inline auto make_database() { + return sqlite_orm::make_storage(Database::dbFile, + sqlite_orm::make_table("shadowrun_characters", + sqlite_orm::make_column("id", &shadowrun::ShadowrunCharacter::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("name", &shadowrun::ShadowrunCharacter::name, sqlite_orm::not_null()), + sqlite_orm::make_column("created_at", &shadowrun::ShadowrunCharacter::created_at, sqlite_orm::default_value("CURRENT_TIMESTAMP")) + ), + sqlite_orm::make_table("shadowrun_data", + sqlite_orm::make_column("id", &shadowrun::ShadowrunData::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("character_id", &shadowrun::ShadowrunData::character_id, sqlite_orm::not_null()), + sqlite_orm::make_column("name", &shadowrun::ShadowrunData::name, sqlite_orm::not_null()), + sqlite_orm::make_column("value", &shadowrun::ShadowrunData::value), + sqlite_orm::make_column("created_at", &shadowrun::ShadowrunData::created_at, sqlite_orm::default_value("CURRENT_TIMESTAMP")), + sqlite_orm::make_column("updated_at", &shadowrun::ShadowrunData::updated_at, sqlite_orm::default_value("CURRENT_TIMESTAMP")), + sqlite_orm::foreign_key(&shadowrun::ShadowrunData::character_id).references(&shadowrun::ShadowrunCharacter::id).on_delete.cascade() + )); + } + #endif // __DATABASE_H__ \ No newline at end of file diff --git a/src/database/databasepool.h b/src/database/databasepool.h new file mode 100644 index 0000000..c783763 --- /dev/null +++ b/src/database/databasepool.h @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include "database.hpp" + +class DatabasePool { +public: + DatabasePool(size_t size) { + for (size_t i = 0; i < size; ++i){ + auto db = std::make_shared(make_database()); + db->sync_schema(); + pool.push(db); + } + } + + std::shared_ptr acquire() { + std::unique_lock lock(mutex); + cv.wait(lock, [&]{ return !pool.empty(); }); + auto db = pool.front(); + pool.pop(); + return db; + } + + void release(std::shared_ptr db) { + std::unique_lock lock(mutex); + pool.push(db); + lock.unlock(); + cv.notify_one(); + } + +private: + std::queue> pool; + std::mutex mutex; + std::condition_variable cv; +}; + + +extern DatabasePool dbpool; \ No newline at end of file diff --git a/src/database/sqlite_orm.h b/src/database/sqlite_orm.h new file mode 100644 index 0000000..a4dac20 --- /dev/null +++ b/src/database/sqlite_orm.h @@ -0,0 +1,24841 @@ +#pragma once + +#if defined(_MSC_VER) +__pragma(push_macro("min")) +#undef min +__pragma(push_macro("max")) +#undef max +#endif // defined(_MSC_VER) +#pragma once + +#include +#pragma once + +// #include "cxx_universal.h" + +/* + * This header makes central C++ functionality on which sqlite_orm depends universally available: + * - alternative operator representations + * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - C++ core language feature macros + * - macros for dealing with compiler quirks + */ + +#include // alternative operator representations +#include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early + +// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). +// actually it should be available when including stddef.h +using std::nullptr_t; + +// #include "cxx_core_features.h" + +/* + * This header detects core C++ language features on which sqlite_orm depends. + * May be updated/overwritten by cxx_compiler_quirks.h + */ + +#ifdef __has_cpp_attribute +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L +#endif + +#ifdef __has_include +#define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) +#else +#define SQLITE_ORM_HAS_INCLUDE(file) 0L +#endif + +#if __cpp_aggregate_nsdmi >= 201304L +#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#endif + +#if __cpp_constexpr >= 201304L +#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_noexcept_function_type >= 201510L +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED +#endif + +#if __cpp_aggregate_bases >= 201603L +#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED +#endif + +#if __cpp_fold_expressions >= 201603L +#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED +#endif + +#if __cpp_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 + +#if __cpp_structured_bindings >= 201606L +#define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED +#endif + +#if __cpp_generic_lambdas >= 201707L +#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +#else +#endif + +#if __cpp_init_captures >= 201803L +#define SQLITE_ORM_PACK_EXPANSION_IN_INIT_CAPTURE_SUPPORTED +#endif + +#if __cpp_consteval >= 201811L +#define SQLITE_ORM_CONSTEVAL_SUPPORTED +#endif + +#if __cpp_char8_t >= 201811L +#define SQLITE_ORM_CHAR8T_SUPPORTED +#endif + +#if __cpp_aggregate_paren_init >= 201902L +#define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED +#endif + +#if __cpp_concepts >= 201907L +#define SQLITE_ORM_CONCEPTS_SUPPORTED +#endif + +#if __cpp_nontype_template_args >= 201911L +#define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED +#endif + +#if __cpp_nontype_template_args >= 201911L +#define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED +#endif + +#if __cpp_pack_indexing >= 202311L +#define SQLITE_ORM_PACK_INDEXING_SUPPORTED +#endif + +#if __cplusplus >= 202002L +#define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#endif + +// #include "cxx_compiler_quirks.h" + +/* + * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. + * May amend cxx_core_features.h + */ + +#ifdef __clang__ +#define SQLITE_ORM_DO_PRAGMA(...) _Pragma(#__VA_ARGS__) +#endif + +#ifdef __clang__ +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic push) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic ignored warnoption) \ + __VA_ARGS__ \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic pop) + +#else +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) __VA_ARGS__ +#endif + +// clang has the bad habit of diagnosing missing brace-init-lists when constructing aggregates with base classes. +// This is a false positive, since the C++ standard is quite clear that braces for nested or base objects may be omitted, +// see https://en.cppreference.com/w/cpp/language/aggregate_initialization: +// "The braces around the nested initializer lists may be elided (omitted), +// in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, +// and the subsequent initializer clauses are used to initialize the following members of the object." +// In this sense clang should only warn about missing field initializers. +// Because we know what we are doing, we suppress the diagnostic message +#define SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(...) SQLITE_ORM_CLANG_SUPPRESS("-Wmissing-braces", __VA_ARGS__) + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +// Type replacement may fail if an alias template has a non-type template parameter from a dependent expression in it, +// `e.g. template using is_something = std::bool_constant>;` +// Remedy, e.g.: use a derived struct: `template struct is_somthing : std::bool_constant>;` +#define SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR +#endif + +// These compilers are known to have problems with alias templates in SFINAE contexts: +// clang 3.5 +// gcc 8.3 +// msvc 15.9 +// Type replacement may fail if an alias template has dependent expression or decltype in it. +// In these cases we have to use helper structures to break down the type alias. +// Note that the detection of specific compilers is so complicated because some compilers emulate other compilers, +// so we simply exclude all compilers that do not support C++20, even though this test is actually inaccurate. +#if (defined(_MSC_VER) && (_MSC_VER < 1920)) || (!defined(_MSC_VER) && (__cplusplus < 202002L)) +#define SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE +#endif + +// overwrite SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED +#if (__cpp_nontype_template_args < 201911L) && \ + (defined(__clang__) && (__clang_major__ >= 12) && (__cplusplus >= 202002L)) +#define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED +#endif + +// clang 10 chokes on concepts that don't depend on template parameters; +// when it tries to instantiate an expression in a requires expression, which results in an error, +// the compiler reports an error instead of dismissing the templated function. +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && (defined(__clang__) && (__clang_major__ == 10)) +#define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS +#endif + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#ifdef SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED +#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 constexpr +#else +#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 +#endif + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#define SQLITE_ORM_INLINE_VAR inline +#else +#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 +#define SQLITE_ORM_CONSTEXPR_CPP20 +#endif + +#if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L +#define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] +#else +#define SQLITE_ORM_NOUNIQUEADDRESS +#endif + +#if SQLITE_ORM_HAS_CPP_ATTRIBUTE(likely) >= 201803L +#define SQLITE_ORM_CPP_LIKELY [[likely]] +#define SQLITE_ORM_CPP_UNLIKELY [[unlikely]] +#else +#define SQLITE_ORM_CPP_LIKELY +#define SQLITE_ORM_CPP_UNLIKELY +#endif + +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED +#define SQLITE_ORM_CONSTEVAL consteval +#else +#define SQLITE_ORM_CONSTEVAL constexpr +#endif + +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && __cpp_lib_concepts >= 202002L +#define SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#endif + +#if __cpp_lib_ranges >= 201911L +#define SQLITE_ORM_CPP20_RANGES_SUPPORTED +#endif + +// C++20 or later (unfortunately there's no feature test macro). +// Stupidly, clang says C++20, but `std::default_sentinel_t` was only implemented in libc++ 13 and libstd++-v3 10 +// (the latter is used on Linux). +// gcc got it right and reports C++20 only starting with v10. +// The check here doesn't care and checks the library versions in use. +// +// Another way of detection might be the feature-test macro __cpp_lib_concepts +#if (__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13000) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) +#define SQLITE_ORM_STL_HAS_DEFAULT_SENTINEL +#endif + +#if (defined(SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED) && defined(SQLITE_ORM_INLINE_VARIABLES_SUPPORTED) && \ + defined(SQLITE_ORM_CONSTEVAL_SUPPORTED)) && \ + (defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED)) +#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 +#endif + +// define the inline namespace "literals" so that it is available even if it was not introduced by a feature +namespace sqlite_orm { + inline namespace literals {} +} +#pragma once + +#include +#include // std::unique_ptr/shared_ptr, std::make_unique +#include // std::system_error +#include // std::string +#include // std::remove_reference, std::remove_cvref, std::decay +#include // std::identity +#include // std::stringstream +#include // std::flush +#include // std::map +#include // std::vector +#include // std::tuple_size, std::tuple, std::make_tuple, std::tie +#include // std::forward, std::pair +#include // std::for_each, std::ranges::for_each +// #include "functional/cxx_optional.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +#include + +// #include "mpl/conditional.h" + +namespace sqlite_orm { + namespace internal { + namespace mpl { + + /* + * Binary quoted metafunction equivalent to `std::conditional`, + * using an improved implementation in respect to memoization. + * + * Because `conditional` is only typed on a single bool non-type template parameter, + * the compiler only ever needs to memoize 2 instances of this class template. + * The type selection is a nested cheap alias template. + */ + template + struct conditional { + template + using fn = A; + }; + + template<> + struct conditional { + template + using fn = B; + }; + + // directly invoke `conditional` + template + using conditional_t = typename conditional::template fn; + } + } + + namespace mpl = internal::mpl; +} + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_void_t >= 201411L + using std::void_t; +#else + /* + * Implementation note: Conservative implementation due to CWG issue 1558 (Unused arguments in alias template specializations). + */ + template + struct always_void { + using type = void; + }; + template + using void_t = typename always_void::type; +#endif + +#if __cpp_lib_bool_constant >= 201505L + using std::bool_constant; +#else + template + using bool_constant = std::integral_constant; +#endif + +#if __cpp_lib_logical_traits >= 201510L && __cpp_lib_type_trait_variable_templates >= 201510L + using std::conjunction; + using std::conjunction_v; + using std::disjunction; + using std::disjunction_v; + using std::negation; + using std::negation_v; +#else + template + struct conjunction : std::true_type {}; + template + struct conjunction : B1 {}; + template + struct conjunction : mpl::conditional_t, B1> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool conjunction_v = conjunction::value; + + template + struct disjunction : std::false_type {}; + template + struct disjunction : B1 {}; + template + struct disjunction : mpl::conditional_t> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool disjunction_v = disjunction::value; + + template + struct negation : bool_constant {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool negation_v = negation::value; +#endif + +#if __cpp_lib_remove_cvref >= 201711L + using std::remove_cvref, std::remove_cvref_t; +#else + template + struct remove_cvref : std::remove_cv> {}; + + template + using remove_cvref_t = typename remove_cvref::type; +#endif + +#if __cpp_lib_type_identity >= 201806L + using std::type_identity, std::type_identity_t; +#else + template + struct type_identity { + using type = T; + }; + + template + using type_identity_t = typename type_identity::type; +#endif + +#if 0 // __cpp_lib_detect >= 0L // library fundamentals TS v2, [meta.detect] + using std::nonesuch; + using std::detector; + using std::is_detected, std::is_detected_v; + using std::detected, std::detected_t; + using std::detected_or, std::detected_or_t; +#else + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + template class Op, class... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template class Op, class... Args> + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + + template class Op, class... Args> + using is_detected = typename detector::value_t; + + template class Op, class... Args> + using detected = detector; + + template class Op, class... Args> + using detected_t = typename detector::type; + + template class Op, class... Args> + using detected_or = detector; + + template class Op, class... Args> + using detected_or_t = typename detected_or::type; + + template class Op, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected::value; +#endif + +#if 0 // proposed but not pursued + using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v; +#else + // is_specialization_of: https://github.com/cplusplus/papers/issues/812 + + template class Primary> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false; + + template class Primary, class... Types> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v, Primary> = true; + + template class Primary> + struct is_specialization_of : bool_constant> {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool always_false_v = false; + + template + using index_constant = std::integral_constant; + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "functional/cxx_functional_polyfill.h" + +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +// #include "cxx_type_traits_polyfill.h" + +// #include "../member_traits/member_traits.h" + +#include // std::enable_if, std::is_function, std::true_type, std::false_type + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + // SFINAE friendly trait to get a member object pointer's field type + template + struct object_field_type {}; + + template + using object_field_type_t = typename object_field_type::type; + + template + struct object_field_type : std::enable_if::value, F> {}; + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) + template + struct getter_field_type {}; + + template + using getter_field_type_t = typename getter_field_type::type; + + template + struct getter_field_type : getter_field_type {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; +#endif + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; + + template + using setter_field_type_t = typename setter_field_type::type; + + template + struct setter_field_type : setter_field_type {}; + + template + struct setter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif + + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; + + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; + + template + using member_field_type_t = typename member_field_type::type; + + template + struct member_object_type {}; + + template + struct member_object_type : polyfill::type_identity {}; + + template + using member_object_type_t = typename member_object_type::type; + } +} + +namespace sqlite_orm { + namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 + // (the latter is used on Linux). + // gcc got it right and reports C++20 only starting with v10. + // The check here doesn't care and checks the library versions in use. + // + // Another way of detection would be the constrained algorithms feature-test macro __cpp_lib_ranges +#if (__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13000) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } + + using is_transparent = int; + }; +#endif + +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward(object).*callable; + } + + // pointer-to-member-function+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward(object).*callable)(std::forward(args)...); + } + + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template>, + std::reference_wrapper>>::value, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { + return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); + } + + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "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)...); + } + } +#else + template + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward(trueFn); + } + + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } + + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } + + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); + } + + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } + +} + +// #include "functional/mpl.h" + +/* + * Symbols for 'template metaprogramming' (compile-time template programming), + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. + * + * Currently, the focus is on facilitating advanced type filtering, + * such as filtering columns by constraints having various traits. + * Hence it contains only a very small subset of a full MPL. + * + * Three key concepts are critical to understanding: + * 1. A 'trait' is a class template with a nested `type` typename. + * The term 'trait' might be too narrow or not entirely accurate, however in the STL those class templates are summarized as "Type transformations". + * hence being "transformation type traits". + * It was the traditional way of transforming types before the arrival of alias templates. + * E.g. `template struct x { using type = T; };` + * They are of course still available today, but are rather used as building blocks. + * 2. A 'metafunction' is an alias template for a class template or a nested template expression, whose instantiation yields a type. + * E.g. `template using alias_op_t = typename x::type` + * 3. A 'quoted metafunction' (aka 'metafunction class') is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn". + * Correspondingly, a quoted metafunction invocation is defined as invocation of its nested "fn" metafunction. + * + * Conventions: + * - "Fn" is the name of a template template parameter for a metafunction. + * - "Q" is the name of class template parameter for a quoted metafunction. + * - "_fn" is a suffix for a class or alias template that accepts metafunctions and turns them into quoted metafunctions. + * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). + */ + +#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" + +// #include "mpl/conditional.h" + +namespace sqlite_orm { + namespace internal { + namespace mpl { + template class Fn> + struct indirectly_test_metafunction; + + /* + * Determines whether a class template has a nested metafunction `fn`. + * + * Implementation note: the technique of specialiazing on the inline variable must come first because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_quoted_metafuntion_v>> = true; + + template + struct is_quoted_metafuntion : polyfill::bool_constant> {}; + + /* + * Type pack. + */ + template + struct pack {}; + + /* + * The indirection through `defer_fn` works around the language inability + * to expand `Args...` into a fixed parameter list of an alias template. + * + * Also, legacy compilers need an extra layer of indirection, otherwise type replacement may fail + * if alias template `Fn` has a dependent expression in it. + */ + template class Fn, class... Args> + struct defer_fn { + using type = Fn; + }; + + /* + * The indirection through `defer` works around the language inability + * to expand `Args...` into a fixed parameter list of an alias template. + */ + template + struct defer { + using type = typename Q::template fn; + }; + + /* + * Invoke metafunction. + */ + template class Fn, class... Args> + using invoke_fn_t = typename defer_fn::type; + + /* + * Invoke quoted metafunction by invoking its nested metafunction. + */ + template + using invoke_t = typename defer::type; + + /* + * Turn metafunction into a quoted metafunction. + * + * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). + * This is necessary because `fn` is a proxy to the originally quoted metafunction, + * and the instantiation of the metafunction might be an invalid expression. + */ + template class Fn> + struct quote_fn { + template class, class...> + struct invoke_this_fn { + // error N: 'type': is not a member of any direct or indirect base class of 'quote_fn::invoke_this_fn' + // means that the metafunction cannot be called with the passed arguments. + }; + + template class F, class... Args> + struct invoke_this_fn>, F, Args...> { + using type = F; + }; + + template + using fn = typename invoke_this_fn::type; + }; + + /* + * Indirection wrapper for higher-order metafunctions, + * specialized on the argument indexes where metafunctions appear. + */ + template + struct higherorder; + + template<> + struct higherorder<0u> { + template class Fn, class... Args2> class HigherFn, class Q, class... Args> + struct defer_higher_fn { + using type = HigherFn; + }; + + /* + * Turn higher-order metafunction into a quoted metafunction. + */ + template class Fn, class... Args2> class HigherFn> + struct quote_fn { + template + using fn = typename defer_higher_fn::type; + }; + }; + + /* + * Quoted metafunction that extracts the nested metafunction of its quoted metafunction argument, + * quotes the extracted metafunction and passes it on to the next quoted metafunction + * (kind of the inverse of quoting). + */ + template + struct pass_extracted_fn_to { + template + struct invoke_this_fn { + using type = typename Q::template fn; + }; + + // extract class template, quote, pass on + template class Fn, class... T> + struct invoke_this_fn> { + using type = typename Q::template fn>; + }; + + template + using fn = typename invoke_this_fn::type; + }; + + /* + * Quoted metafunction that invokes the specified quoted metafunctions, + * and passes their results on to the next quoted metafunction. + */ + template + struct pass_result_of { + // invoke `Fn`, pass on their result + template + using fn = typename Q::template fn::type...>; + }; + + /* + * Quoted metafunction that invokes the specified metafunctions, + * and passes their results on to the next quoted metafunction. + */ + template class... Fn> + using pass_result_of_fn = pass_result_of...>; + + /* + * Bind arguments at the front of a quoted metafunction. + */ + template + struct bind_front { + template + using fn = typename Q::template fn; + }; + + /* + * Bind arguments at the back of a quoted metafunction. + */ + template + struct bind_back { + template + using fn = typename Q::template fn; + }; + + /* + * Quoted metafunction equivalent to `polyfill::always_false`. + * It ignores arguments passed to the metafunction, and always returns the specified type. + */ + template + struct always { + template + using fn = T; + }; + + /* + * Unary quoted metafunction equivalent to `std::type_identity_t`. + */ + using identity = quote_fn; + + /* + * Quoted metafunction equivalent to `std::negation`. + */ + template + using not_ = pass_result_of, TraitQ>; + + /* + * Quoted metafunction equivalent to `std::conjunction`. + */ + template + struct conjunction { + template + using fn = std::true_type; + }; + + template + struct conjunction { + // match last or `std::false_type` + template + struct invoke_this_fn { + static_assert(std::is_same::value || + std::is_same::value, + "Resulting trait must be a std::bool_constant"); + using type = ResultTrait; + }; + + // match `std::true_type` and one or more remaining + template + struct invoke_this_fn, std::true_type, NextQ, RestQ...> + : invoke_this_fn, + // access resulting trait::type + typename defer::type::type, + RestQ...> {}; + + template + using fn = typename invoke_this_fn, + // access resulting trait::type + typename defer::type::type, + TraitQ...>::type; + }; + + /* + * Quoted metafunction equivalent to `std::disjunction`. + */ + template + struct disjunction { + template + using fn = std::false_type; + }; + + template + struct disjunction { + // match last or `std::true_type` + template + struct invoke_this_fn { + static_assert(std::is_same::value || + std::is_same::value, + "Resulting trait must be a std::bool_constant"); + using type = ResultTrait; + }; + + // match `std::false_type` and one or more remaining + template + struct invoke_this_fn, std::false_type, NextQ, RestQ...> + : invoke_this_fn, + // access resulting trait::type + typename defer::type::type, + RestQ...> {}; + + template + using fn = typename invoke_this_fn, + // access resulting trait::type + typename defer::type::type, + TraitQ...>::type; + }; + + /* + * Metafunction equivalent to `std::conjunction`. + */ + template class... TraitFn> + using conjunction_fn = pass_result_of_fn, TraitFn...>; + + /* + * Metafunction equivalent to `std::disjunction`. + */ + template class... TraitFn> + using disjunction_fn = pass_result_of_fn, TraitFn...>; + + /* + * Metafunction equivalent to `std::negation`. + */ + template class Fn> + using not_fn = pass_result_of_fn, Fn>; + + /* + * Bind arguments at the front of a metafunction. + */ + template class Fn, class... Bound> + using bind_front_fn = bind_front, Bound...>; + + /* + * Bind arguments at the back of a metafunction. + */ + template class Fn, class... Bound> + using bind_back_fn = bind_back, Bound...>; + + /* + * Bind a metafunction and arguments at the front of a higher-order metafunction. + */ + template class Fn, class... Args2> class HigherFn, + template class BoundFn, + class... Bound> + using bind_front_higherorder_fn = + bind_front::quote_fn, quote_fn, Bound...>; + +#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) { + ++i; + } + return i; + } + + constexpr size_t count_true_helper(std::initializer_list values) { + size_t n = 0; + for (auto first = values.begin(); first != values.end(); ++first) { + n += *first; + } + 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, + * and returns the index constant of the first element for which the predicate returns true. + */ + template + struct finds { + template + struct invoke_this_fn { + static_assert(polyfill::always_false_v, + "`finds` must be invoked with a type list as first argument."); + }; + + 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...}); + using type = polyfill::index_constant; + }; + + template + using fn = typename invoke_this_fn::type; + }; + + template class PredicateFn> + using finds_fn = finds>; + + /* + * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, + * and returns the index constant of the first element for which the predicate returns true. + */ + template + struct counts { + template + struct invoke_this_fn { + static_assert(polyfill::always_false_v, + "`counts` must be invoked with a type list as first argument."); + }; + + 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...}); + using type = polyfill::index_constant; + }; + + template + using fn = typename invoke_this_fn::type; + }; + + template class PredicateFn> + using counts_fn = counts>; + + /* + * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, + * and returns the index constant of the first element for which the predicate returns true. + */ + template + struct contains { + template + struct invoke_this_fn { + static_assert(polyfill::always_false_v, + "`contains` must be invoked with a type list as first argument."); + }; + + 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...})); + using type = polyfill::bool_constant; + }; + + template + using fn = typename invoke_this_fn::type; + }; + + template class TraitFn> + using contains_fn = contains>; + } + } + + namespace mpl = internal::mpl; + + // convenience quoted metafunctions + namespace internal { + /* + * Quoted trait metafunction that checks if a type has the specified trait. + */ + template class TraitFn, class... Bound> + using check_if = + mpl::conditional_t, mpl::bind_front_fn>; + + /* + * Quoted trait metafunction that checks if a type doesn't have the specified trait. + */ + template class TraitFn> + using check_if_not = mpl::not_fn; + + /* + * Quoted trait metafunction that checks if a type is the same as the specified type. + * Commonly used named abbreviation for `check_if`. + */ + template + using check_if_is_type = mpl::bind_front_fn; + + /* + * Quoted trait metafunction that checks if a type's template matches the specified template + * (similar to `is_specialization_of`). + */ + template class Template> + using check_if_is_template = + mpl::pass_extracted_fn_to>>; + + /* + * Quoted metafunction that finds the index of the given type in a tuple. + */ + template + using finds_if_has_type = mpl::finds>; + + /* + * Quoted metafunction that finds the index of the given class template in a tuple. + */ + template class Template> + using finds_if_has_template = mpl::finds>; + + /* + * Quoted trait metafunction that counts tuple elements having a given trait. + */ + template class TraitFn> + using counts_if_has = mpl::counts_fn; + + /* + * Quoted trait metafunction that checks whether a tuple contains a type with given trait. + */ + template class TraitFn> + using check_if_has = mpl::contains_fn; + + /* + * Quoted trait metafunction that checks whether a tuple doesn't contain a type with given trait. + */ + template class TraitFn> + using check_if_has_not = mpl::not_>; + + /* + * Quoted metafunction that checks whether a tuple contains given type. + */ + template + using check_if_has_type = mpl::contains>; + + /* + * Quoted metafunction that checks whether a tuple contains a given template. + * + * Note: we are using 2 small tricks: + * 1. A template template parameter can be treated like a metafunction, so we can just "quote" a 'primary' + * template into the MPL system (e.g. `std::vector`). + * 2. This quoted metafunction does the opposite of the trait metafunction `is_specialization`: + * `is_specialization` tries to instantiate the primary template template parameter using the + * template parameters of a template type, then compares both instantiated types. + * Here instead, `pass_extracted_fn_to` extracts the template template parameter from a template type, + * then compares the resulting template template parameters. + */ + template class Template> + using check_if_has_template = mpl::contains>; + } +} + +// #include "tuple_helper/tuple_traits.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/mpl.h" + +namespace sqlite_orm { + // convenience metafunction algorithms + namespace internal { + /* + * Higher-order trait metafunction that checks whether a tuple contains a type with given trait (possibly projected). + * + * `ProjOp` is a metafunction + */ + template class TraitFn, + template class ProjOp = polyfill::type_identity_t> + using tuple_has = mpl::invoke_t, Pack, mpl::quote_fn>; + + /* + * Higher-order trait metafunction that checks whether a tuple contains the specified type (possibly projected). + * + * `ProjOp` is a metafunction + */ + template class ProjOp = polyfill::type_identity_t> + using tuple_has_type = mpl::invoke_t, Pack, mpl::quote_fn>; + + /* + * Higher-order trait metafunction that checks whether a tuple contains the specified class template (possibly projected). + * + * `ProjOp` is a metafunction + */ + template class Template, + template class ProjOp = polyfill::type_identity_t> + using tuple_has_template = mpl::invoke_t, Pack, mpl::quote_fn>; + + /* + * Higher-order metafunction returning the first index constant of the desired type in a tuple (possibly projected). + */ + template class ProjOp = polyfill::type_identity_t> + using find_tuple_type = mpl::invoke_t, Pack, mpl::quote_fn>; + + /* + * Higher-order metafunction returning the first index constant of the desired class template in a tuple (possibly projected). + * + * `ProjOp` is a metafunction + */ + template class Template, + template class ProjOp = polyfill::type_identity_t> + using find_tuple_template = mpl::invoke_t, Pack, mpl::quote_fn>; + + /* + * Higher-order trait metafunction that counts the types having the specified trait in a tuple (possibly projected). + * + * `Pred` is a predicate metafunction with a nested bool member named `value` + * `ProjOp` is a metafunction + */ + template class Pred, template class ProjOp = polyfill::type_identity_t> + using count_tuple = mpl::invoke_t, Pack, mpl::quote_fn>; + } +} + +// #include "tuple_helper/tuple_filter.h" + +#include // std::integral_constant, std::index_sequence, std::conditional, std::declval +#include // std::tuple, std::tuple_cat, std::tuple_element + +// #include "../functional/mpl/conditional.h" + +// #include "../functional/index_sequence_util.h" + +#include // std::index_sequence + +namespace sqlite_orm { + namespace internal { +#if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) + /** + * 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. + */ + template + SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { + static_assert(Pos < sizeof...(Idx)); +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED + size_t result; +#else + size_t result = 0; +#endif + size_t i = 0; + // note: `(void)` cast silences warning 'expression result unused' + (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 + struct flatten_idxseq { + using type = std::index_sequence<>; + }; + + template + struct flatten_idxseq> { + using type = std::index_sequence; + }; + + template + struct flatten_idxseq, std::index_sequence, Seq...> + : flatten_idxseq, Seq...> {}; + + template + using flatten_idxseq_t = typename flatten_idxseq::type; + } +} + +namespace sqlite_orm { + namespace internal { + + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + + template + struct conc_tuple { + using type = tuple_cat_t; + }; + + template + struct tuple_from_index_sequence; + + template + struct tuple_from_index_sequence> { + using type = std::tuple...>; + }; + + template + using tuple_from_index_sequence_t = typename tuple_from_index_sequence::type; + + template class Pred, template class Proj, class Seq> + struct filter_tuple_sequence; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>>::value, + std::index_sequence, + std::index_sequence<>>...> {}; +#else + template class Pred, class SFINAE = void> + struct tuple_seq_single { + using type = std::index_sequence<>; + }; + + template class Pred> + struct tuple_seq_single::value>> { + using type = std::index_sequence; + }; + + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>, + Pred>::type...> {}; +#endif + + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction + */ + template class Pred, + template class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction + */ + template class Pred, + template class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_t = tuple_from_index_sequence_t>; + + /* + * Count a tuple, picking only those elements specified in the index sequence. + * + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction + * + * Implementation note: must be distinct from a `count_tuple` w/o index sequence parameter because legacy compilers have problems + * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template class Pred, + class Seq, + template class FilterProj = polyfill::type_identity_t> + struct count_filtered_tuple + : std::integral_constant::size()> {}; + } +} + +// #include "tuple_helper/tuple_transformer.h" + +#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 + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + + template class Op> + struct tuple_transformer; + + template class Pack, class... Types, template class Op> + struct tuple_transformer, Op> { + using type = Pack...>; + }; + + /* + * Transform specified tuple. + * + * `Op` is a metafunction. + */ + 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. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. + */ + template + SQLITE_ORM_CONSTEXPR_CPP20 auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { + return combine(initial, polyfill::invoke(project, std::get(tpl))...); + } + + /* + * Apply a projection to a tuple's elements, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. + */ + template + SQLITE_ORM_CONSTEXPR_CPP20 auto + recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + return recombine_tuple(std::move(combine), + std::forward(tpl), + std::make_index_sequence::value>{}, + std::move(project), + std::move(initial)); + } + + /* + * Function object that takes integral constants and returns the sum of their values as an integral constant. + * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. + */ + struct plus_fold_integrals { + template + constexpr auto operator()(const Integrals&...) const { + using integral_type = std::common_type_t; + return std::integral_constant{}; + } + }; + + /* + * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). + * The projection is applied on the argument type, not the argument value/object. + */ + template class NestedProject> + struct project_nested_tuple_size { + template + constexpr auto operator()(const T&) const { + return typename std::tuple_size>::type{}; + } + }; + + template class NestedProject, class Tpl, class IdxSeq> + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, + std::declval(), + IdxSeq{}, + project_nested_tuple_size{}, + std::integral_constant{})); +#endif + + template + constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + /* + * Like `std::make_from_tuple`, but using a projection on the tuple elements. + */ + template + constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } + } +} + +// #include "tuple_helper/tuple_iteration.h" + +#include // std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move + +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; + // 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, + std::make_index_sequence>::value>{}, + std::forward(lambda)); + } + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence, L&& lambda) { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; + } +#endif + template + 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); + } + }; + + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } + } +} + +// #include "type_traits.h" + +#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 + +// #include "functional/cxx_core_features.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template + using is_any_of = polyfill::disjunction...>; + + template + struct value_unref_type : polyfill::remove_cvref {}; + + template + struct value_unref_type> : std::remove_const {}; + + template + using value_unref_type_t = typename value_unref_type::type; + + template + using is_eval_order_garanteed = +#if __cpp_lib_is_aggregate >= 201703L + std::is_aggregate; +#else + std::is_pod; +#endif + + // enable_if for types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; + + // enable_if for types + template class Op, class... Args> + using match_if_not = std::enable_if_t>::value>; + + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t::value>; + + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; + + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; + + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = + std::enable_if_t::value, bool>; + } + + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_type_t = typename decltype(a)::type; +#endif + + template + using value_type_t = typename T::value_type; + + template + using field_type_t = typename T::field_type; + + template + using constraints_type_t = typename T::constraints_type; + + template + using columns_tuple_t = typename T::columns_tuple; + + template + using object_type_t = typename T::object_type; + + template + using elements_type_t = typename T::elements_type; + + template + using table_type_t = typename T::table_type; + + template + using target_type_t = typename T::target_type; + + template + using left_type_t = typename T::left_type; + + template + using right_type_t = typename T::right_type; + + template + using on_type_t = typename T::on_type; + + template + using expression_type_t = typename T::expression_type; + + template + using alias_type_t = typename As::alias_type; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using udf_type_t = typename T::udf_type; + + template + using auto_udf_type_t = typename decltype(a)::udf_type; +#endif + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + using cte_moniker_type_t = typename T::cte_moniker_type; + + template + using cte_mapper_type_t = typename T::cte_mapper_type; + + // T::alias_type or nonesuch + template + using alias_holder_type_or_none = polyfill::detected; + + template + using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; +#endif + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept stateless = std::is_empty_v; +#endif + } + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept orm_names_type = requires { typename T::type; }; +#endif +} + +// #include "alias.h" + +#include // std::enable_if, std::is_same +#include // std::make_index_sequence, std::move +#include // std::string +#include // std::stringstream +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl/conditional.h" + +// #include "functional/cstring_literal.h" + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include // std::index_sequence +#include // std::copy_n +#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. + */ + template + struct cstring_literal { + static constexpr size_t size() { + return N - 1; + } + + constexpr cstring_literal(const char (&cstr)[N]) { + std::copy_n(cstr, N, this->cstr); + } + + char cstr[N]; + }; + + template class Template, cstring_literal literal, size_t... Idx> + consteval auto explode_into(std::index_sequence) { + return Template{}; + } +} +#endif + +// #include "type_traits.h" + +// #include "alias_traits.h" + +#include // std::is_base_of, std::is_same +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "table_reference.h" + +#include // std::remove_const, std::type_identity + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + /* + * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. + */ + template + struct table_reference : polyfill::type_identity {}; + + template + struct decay_table_ref : std::remove_const {}; + template + struct decay_table_ref> : polyfill::type_identity {}; + template + struct decay_table_ref> : polyfill::type_identity {}; + + template + using decay_table_ref_t = typename decay_table_ref::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_decay_table_ref_t = typename decay_table_ref::type; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = + polyfill::is_specialization_of_v, table_reference>; + + template + struct is_table_reference : polyfill::bool_constant> {}; + } + +#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. + */ + template + concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; +#endif +} + +namespace sqlite_orm { + + /** @short Base class for a custom table alias, column alias or expression alias. + */ + struct alias_tag {}; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + + template + struct is_alias : polyfill::bool_constant> {}; + + /** @short Alias of a column in a record set, see `orm_column_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = + polyfill::conjunction, polyfill::negation>>::value; + + template + struct is_column_alias : is_alias {}; + + /** @short Alias of any type of record set, see `orm_recordset_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = + polyfill::conjunction, polyfill::is_detected>::value; + + template + struct is_recordset_alias : polyfill::bool_constant> {}; + + /** @short Alias of a concrete table, see `orm_table_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction< + is_recordset_alias, + polyfill::negation, std::remove_const_t>>>::value; + + template + struct is_table_alias : polyfill::bool_constant> {}; + + /** @short Moniker of a CTE, see `orm_cte_moniker`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v = +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + polyfill::conjunction_v, + std::is_same, std::remove_const_t>>; +#else + false; +#endif + + template + using is_cte_moniker = polyfill::bool_constant>; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + concept orm_alias = std::derived_from; + + /** @short Specifies that a type is an alias of a column in a record set. + * + * A column alias has the following traits: + * - is derived from `alias_tag` + * - must not have a nested `type` typename + */ + template + concept orm_column_alias = (orm_alias && !orm_names_type); + + /** @short Specifies that a type is an alias of any type of record set. + * + * A record set alias has the following traits: + * - is derived from `alias_tag`. + * - has a nested `type` typename, which refers to a mapped object. + */ + template + concept orm_recordset_alias = (orm_alias && orm_names_type); + + /** @short Specifies that a type is an alias of a concrete table. + * + * A concrete table alias has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself). + */ + template + concept orm_table_alias = (orm_recordset_alias && !std::same_as>); + + /** @short Moniker of a CTE. + * + * A CTE moniker has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to itself. + */ + template + concept orm_cte_moniker = (orm_recordset_alias && std::same_as>); + + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ + template + concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + + /** @short Specifies that a type refers to a recordset. + */ + template + concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); + + /** @short Specifies that a type is a mapped recordset (table reference). + */ + template + concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#endif +} + +// #include "table_type_of.h" + +#include // std::enable_if, std::is_convertible + +namespace sqlite_orm { + + namespace internal { + + template + struct column_pointer; + + template + struct indexed_column_t; + + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of(&User::id))>::type` is `User` + * - `table_type_of*&User::id)>::type` is `User` + */ + template + struct table_type_of; + + template + struct table_type_of { + using type = O; + }; + + template + struct table_type_of> { + using type = T; + }; + + template + struct table_type_of> : table_type_of {}; + + 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; + } +} + +// #include "tags.h" + +// #include "functional/cxx_functional_polyfill.h" + +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; + + /** + * Inherit from this class to support arithmetic types overloading + */ + struct arithmetic_t {}; + + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; + + /** + * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; + + template + using is_operator_argument = polyfill::bool_constant>; + } +} + +// #include "column_pointer.h" + +#include // std::enable_if, std::is_convertible +#include // std::move + +// #include "functional/cxx_core_features.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "table_reference.h" + +// #include "alias_traits.h" + +// #include "tags.h" + +namespace sqlite_orm { + namespace internal { + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using type = T; + using field_type = F; + + field_type field; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = + polyfill::is_specialization_of::value; + + template + struct is_column_pointer : polyfill::bool_constant> {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = + true; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + struct alias_holder; +#endif + } + + /** + * Explicitly refer to a column, used in contexts + * where the automatic object mapping deduction needs to be overridden. + * + * Example: + * struct BaseType : { int64 id; }; + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); + */ + template = true> + constexpr internal::column_pointer column(F Base::* field) { + static_assert(std::is_convertible::value, "Field must be from derived class"); + return {field}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Explicitly refer to a column. + */ + template + constexpr auto column(F O::* field) { + return column>(field); + } + + // Intentionally place pointer-to-member operator for table references in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + /** + * Explicitly refer to a column. + */ + template + constexpr auto operator->*(const R& /*table*/, F O::* field) { + return column(field); + } + } + + /** + * Make a table reference. + */ + template + requires (!orm_recordset_alias) + consteval internal::table_reference column() { + return {}; + } + + /** + * Make a table reference. + */ + template + requires (!orm_recordset_alias) + consteval internal::table_reference c() { + return {}; + } +#endif + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Explicitly refer to a column alias mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * using cte_1 = decltype(1_ctealias); + * storage.with(cte()(select(&Object::id)), select(column(&Object::id))); + * storage.with(cte()(select(&Object::id)), select(column(1_colalias))); + * storage.with(cte()(select(as(&Object::id))), select(column(colalias_a{}))); + * storage.with(cte(colalias_a{})(select(&Object::id)), select(column(colalias_a{}))); + * storage.with(cte()(select(as(&Object::id))), select(column(get()))); + */ + template = true> + constexpr auto column(F field) { + using namespace ::sqlite_orm::internal; + + static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); + + if constexpr (polyfill::is_specialization_of_v) { + static_assert(is_column_alias_v>); + return column_pointer{{}}; + } else if constexpr (is_column_alias_v) { + return column_pointer>{{}}; + } else { + return column_pointer{std::move(field)}; + } + (void)field; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Explicitly refer to a column mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(&Object::id))); + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(1_colalias))); + */ + template + constexpr auto column(F field) { + using Moniker = std::remove_const_t; + return column(std::forward(field)); + } + + /** + * Explicitly refer to a column mapped into a CTE or subquery. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + * + * Example: + * struct Object { ... }; + * using cte_1 = decltype(1_ctealias); + * storage.with(cte()(select(&Object::id)), select(1_ctealias->*&Object::id)); + * storage.with(cte()(select(&Object::id)), select(1_ctealias->*1_colalias)); + */ + template + constexpr auto operator->*(const Moniker& /*moniker*/, F field) { + return column(std::forward(field)); + } +#endif +#endif +} + +namespace sqlite_orm { + + namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + inline constexpr bool is_operator_argument_v>> = true; +#endif + + /** + * This is a common built-in class used for character based table aliases. + * For convenience there exist public type aliases `alias_a`, `alias_b`, ... + * The easiest way to create a table alias is using `"z"_alias.for_()`. + */ + template + struct recordset_alias : alias_tag { + using type = T; + + static std::string get() { + return {A, X...}; + } + }; + + /** + * Column expression with table alias attached like 'C.ID'. This is not a column alias + */ + template + struct alias_column_t { + using alias_type = T; + using column_type = C; + + column_type column; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = + true; + + struct basic_table; + + /* + * Encapsulates extracting the alias identifier of a non-alias. + * + * `extract()` always returns the empty string. + * `as_alias()` is used in contexts where a table might be aliased, and the empty string is returned. + * `as_qualifier()` is used in contexts where a table might be aliased, and the given table's name is returned. + */ + template + struct alias_extractor { + static std::string extract() { + return {}; + } + + static std::string as_alias() { + return {}; + } + + template + static const std::string& as_qualifier(const X& table) { + return table.name; + } + }; + + /* + * Encapsulates extracting the alias identifier of an alias. + * + * `extract()` always returns the alias identifier or CTE moniker. + * `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned. + * `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned. + */ + template + struct alias_extractor> { + static std::string extract() { + std::stringstream ss; + ss << A::get(); + return ss.str(); + } + + // for column and regular table aliases -> alias identifier + template, A> = true> + static std::string as_alias() { + return alias_extractor::extract(); + } + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + // for CTE monikers -> empty + template, A> = true> + static std::string as_alias() { + return {}; + } +#endif + + // for regular table aliases -> alias identifier + template = true> + static std::string as_qualifier(const basic_table&) { + return alias_extractor::extract(); + } + }; + + /** + * Used to store alias for expression + */ + template + struct as_t { + using alias_type = T; + using expression_type = E; + + expression_type expression; + }; + + /** + * Built-in column alias. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + * The easiest way to create a column alias is using `"xyz"_col`. + */ + template + struct column_alias : alias_tag { + static std::string get() { + return {A, X...}; + } + }; + + template + struct alias_holder { + using type = T; + + alias_holder() = default; + // CTE feature needs it to implicitly convert a column alias to an alias_holder; see `cte()` factory function + alias_holder(const T&) noexcept {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + struct recordset_alias_builder { + template + [[nodiscard]] consteval recordset_alias for_() const { + return {}; + } + + template + [[nodiscard]] consteval auto for_() const { + using T = std::remove_const_t; + return recordset_alias{}; + } + }; +#endif + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + SQLITE_ORM_CONSTEVAL auto n_to_colalias() { + constexpr column_alias<'1' + n % 10, C...> colalias{}; + if constexpr (n > 10) { + return n_to_colalias(); + } else { + return colalias; + } + } + + template + inline constexpr bool is_builtin_numeric_column_alias_v = false; + template + inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); +#endif + } + + /** + * Using a column pointer, create a column reference to an aliased table column. + * + * Example: + * using als = alias_u; + * select(alias_column(column(&User::id))) + */ + template, + polyfill::negation>>>::value, + bool> = true> + constexpr auto alias_column(C field) { + using namespace ::sqlite_orm::internal; + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); + + return alias_column_t{std::move(field)}; + } + + /** + * Using an object member field, create a column reference to an aliased table column. + * + * @note The object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * using als = alias_u; + * select(alias_column(&User::id)) + */ + template, + polyfill::negation>>>::value, + bool> = true> + constexpr auto alias_column(F O::* field) { + using namespace ::sqlite_orm::internal; + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); + + using C1 = + mpl::conditional_t::value, F O::*, column_pointer>; + return alias_column_t{C1{field}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * constexpr orm_table_alias auto als = "u"_alias.for_(); + * select(alias_column(&User::id)) + */ + template + requires (!orm_cte_moniker>) + constexpr auto alias_column(C field) { + using namespace ::sqlite_orm::internal; + using A = decltype(als); + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); + + if constexpr (is_column_pointer_v) { + return alias_column_t{std::move(field)}; + } else if constexpr (std::is_same_v, aliased_type>) { + return alias_column_t{field}; + } else { + // wrap in column_pointer + using C1 = column_pointer; + return alias_column_t{{field}}; + } + } + + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + * + * Example: + * constexpr auto als = "u"_alias.for_(); + * select(als->*&User::id) + */ + template + requires (!orm_cte_moniker>) + constexpr auto operator->*(const A& /*tableAlias*/, F field) { + return alias_column(std::move(field)); + } +#endif + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Create a column reference to an aliased CTE column. + */ + template, internal::is_cte_moniker>>, + bool> = true> + constexpr auto alias_column(C c) { + using namespace ::sqlite_orm::internal; + using cte_moniker_t = type_t; + + if constexpr (is_column_pointer_v) { + static_assert(std::is_same, cte_moniker_t>::value, + "Column pointer must match aliased CTE"); + return alias_column_t{c}; + } else { + auto cp = column(c); + return alias_column_t{std::move(cp)}; + } + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a column reference to an aliased CTE column. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + */ + template + requires (orm_cte_moniker>) + constexpr auto operator->*(const A& /*tableAlias*/, C c) { + return alias_column(std::move(c)); + } + + /** + * Create a column reference to an aliased CTE column. + */ + template + requires (orm_cte_moniker>) + constexpr auto alias_column(C c) { + using A = std::remove_const_t; + return alias_column(std::move(c)); + } +#endif +#endif + + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Alias a column expression. + */ + template + auto as(E expression) { + return internal::as_t{std::move(expression)}; + } + + /** + * Alias a column expression. + */ + template + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } +#else + /** + * Alias a column expression. + */ + template = true> + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } +#endif + + /** + * Wrap a column alias in an alias holder. + */ + template + internal::alias_holder get() { + static_assert(internal::is_column_alias_v, ""); + return {}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get() { + return internal::alias_holder{}; + } +#endif + + template + using alias_a = internal::recordset_alias; + template + using alias_b = internal::recordset_alias; + template + using alias_c = internal::recordset_alias; + template + using alias_d = internal::recordset_alias; + template + using alias_e = internal::recordset_alias; + template + using alias_f = internal::recordset_alias; + template + using alias_g = internal::recordset_alias; + template + using alias_h = internal::recordset_alias; + template + using alias_i = internal::recordset_alias; + template + using alias_j = internal::recordset_alias; + template + using alias_k = internal::recordset_alias; + template + using alias_l = internal::recordset_alias; + template + using alias_m = internal::recordset_alias; + template + using alias_n = internal::recordset_alias; + template + using alias_o = internal::recordset_alias; + template + using alias_p = internal::recordset_alias; + template + using alias_q = internal::recordset_alias; + template + using alias_r = internal::recordset_alias; + template + using alias_s = internal::recordset_alias; + template + using alias_t = internal::recordset_alias; + template + using alias_u = internal::recordset_alias; + template + using alias_v = internal::recordset_alias; + template + using alias_w = internal::recordset_alias; + template + using alias_x = internal::recordset_alias; + template + using alias_y = internal::recordset_alias; + template + using alias_z = internal::recordset_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Create a table alias. + * + * Examples: + * constexpr orm_table_alias auto z_alias = alias<'z'>.for_(); + */ + template + inline constexpr internal::recordset_alias_builder alias{}; + + inline namespace literals { + /** @short Create a table alias. + * + * Examples: + * constexpr orm_table_alias auto z_alias = "z"_alias.for_(); + */ + template + [[nodiscard]] consteval auto operator"" _alias() { + return internal::explode_into( + std::make_index_sequence{}); + } + + /** @short Create a column alias. + * column_alias<'a'[, ...]> from a string literal. + * E.g. "a"_col, "b"_col + */ + template + [[nodiscard]] consteval auto operator"" _col() { + return internal::explode_into(std::make_index_sequence{}); + } + } +#endif + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + inline namespace literals { + /** + * column_alias<'1'[, ...]> from a numeric literal. + * E.g. 1_colalias, 2_colalias + */ + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() { + // numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions, + // which start at "1". + static_assert(std::array{Chars...}[0] > '0'); + return internal::column_alias{}; + } + } +#endif +} + +// #include "error_code.h" + +#include +#include // std::error_code, std::system_error +#include // std::string +#include +#include // std::ostringstream +#include + +namespace sqlite_orm { + + /** @short Enables classifying sqlite error codes. + + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; + + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + incorrect_locking_mode_string, + invalid_collate_argument_enum, + failed_to_init_a_backup, + unknown_member_value, + incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, + value_is_null, + no_tables_specified, + }; +} + +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; +} + +namespace sqlite_orm { + + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } + + std::string message(int c) const override final { + switch (static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + return "Unknown member value"; + case orm_error_code::incorrect_order: + return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; + default: + return "unknown error"; + } + } + }; + + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } + + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; + + inline const orm_error_category& get_orm_error_category() { + static orm_error_category res; + return res; + } + + inline const sqlite_error_category& get_sqlite_error_category() { + static sqlite_error_category res; + return res; + } + + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } + + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast(ev), get_orm_error_category()}; + } + + template + std::string get_error_message(sqlite3* db, T&&... args) { + std::ostringstream stream; + using unpack = int[]; + (void)unpack{0, (stream << args, 0)...}; + stream << sqlite3_errmsg(db); + return stream.str(); + } + + template + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; + } + + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } + + inline std::system_error sqlite_to_system_error(sqlite3* db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; + } + + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} + +// #include "type_printer.h" + +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "is_std_ptr.h" + +#include +#include + +namespace sqlite_orm { + + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; + + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; + + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} + +namespace sqlite_orm { + + /** + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) + */ + template + struct type_printer {}; + + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; + + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; + + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; + + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; + + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>::value>> : integer_printer { + }; + + template + struct type_printer::value>> : real_printer {}; + + template + struct type_printer, + std::is_base_of, + std::is_base_of>::value>> + : text_printer {}; + + template + struct type_printer::value>> : type_printer {}; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif + + template<> + struct type_printer, void> : blob_printer {}; +} + +// #include "constraints.h" + +#include // std::is_base_of, std::false_type, std::true_type +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/same_or_void.h" + +#include // std::common_type + +namespace sqlite_orm { + namespace internal { + + /** + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. + */ + template + struct same_or_void { + using type = void; + }; + + template + struct same_or_void { + using type = A; + }; + + template + struct same_or_void { + using type = A; + }; + + template + using same_or_void_t = typename same_or_void::type; + + template + struct same_or_void : same_or_void {}; + + template + struct common_type_of; + + template class Pack, class... Types> + struct common_type_of> : std::common_type {}; + + /** + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. + * + * @note: SFINAE friendly like `std::common_type`. + */ + template + using common_type_of_t = typename common_type_of::type; + } +} + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" + +// #include "collate_argument.h" + +namespace sqlite_orm { + + namespace internal { + + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } +} + +// #include "error_code.h" + +// #include "table_type_of.h" + +// #include "type_printer.h" + +// #include "column_pointer.h" + +namespace sqlite_orm { + + namespace internal { + + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; + + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; + + template + struct primary_key_with_autoincrement : T { + using primary_key_type = T; + + 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 + }; + + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used within `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using self = primary_key_t; + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; + + columns_tuple columns; + + primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } + + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } + + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } + + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } + + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } + + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } + + self on_conflict_ignore() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; + return res; + } + + self on_conflict_replace() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; + return res; + } + }; + + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + + /** + * UNIQUE constraint class. + */ + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; + + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} + }; + + struct unindexed_t {}; + + template + struct prefix_t { + using value_type = T; + + value_type value; + }; + + template + struct tokenize_t { + using value_type = T; + + value_type value; + }; + + template + struct content_t { + using value_type = T; + + value_type value; + }; + + template + struct table_content_t { + using mapped_type = T; + }; + + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; + + value_type value; + + operator std::string() const { + return "DEFAULT"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3006019 + /** + * FOREIGN KEY constraint class. + * Cs are columns which has foreign key + * Rs are column which C references to + * Available in SQLite 3.6.19 or higher + */ + + template + struct foreign_key_t; + + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; + + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch (action) { + case foreign_key_action::no_action: + os << "NO ACTION"; + break; + case foreign_key_action::restrict_: + os << "RESTRICT"; + break; + case foreign_key_action::set_null: + os << "SET NULL"; + break; + case foreign_key_action::set_default: + os << "SET DEFAULT"; + break; + case foreign_key_action::cascade: + os << "CASCADE"; + break; + case foreign_key_action::none: + break; + } + return os; + } + + struct on_update_delete_base { + const bool update; // true if update and false if delete + + operator std::string() const { + if (this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; + + /** + * F - foreign key class + */ + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; + + const foreign_key_type& fk; + + on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : + on_update_delete_base{update_}, fk(fk_), _action(action_) {} + + foreign_key_action _action = foreign_key_action::none; + + foreign_key_type no_action() const { + auto res = this->fk; + if (update) { + res.on_update._action = foreign_key_action::no_action; + } else { + res.on_delete._action = foreign_key_action::no_action; + } + return res; + } + + foreign_key_type restrict_() const { + auto res = this->fk; + if (update) { + res.on_update._action = foreign_key_action::restrict_; + } else { + res.on_delete._action = foreign_key_action::restrict_; + } + return res; + } + + foreign_key_type set_null() const { + auto res = this->fk; + if (update) { + res.on_update._action = foreign_key_action::set_null; + } else { + res.on_delete._action = foreign_key_action::set_null; + } + return res; + } + + foreign_key_type set_default() const { + auto res = this->fk; + if (update) { + res.on_update._action = foreign_key_action::set_default; + } else { + res.on_delete._action = foreign_key_action::set_default; + } + return res; + } + + foreign_key_type cascade() const { + auto res = this->fk; + if (update) { + res.on_update._action = foreign_key_action::cascade; + } else { + res.on_delete._action = foreign_key_action::cascade; + } + return res; + } + + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; + + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } + + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; + + /** + * Holds obect type of all referenced columns. + */ + using target_type = same_or_void_t...>; + + /** + * Holds obect type of all source columns. + */ + using source_type = same_or_void_t...>; + + columns_type columns; + references_type references; + + on_update_delete_t on_update; + on_update_delete_t on_delete; + + static_assert(std::tuple_size::value == std::tuple_size::value, + "Columns size must be equal to references tuple"); + static_assert(!std::is_same::value, "All references must have the same type"); + + foreign_key_t(columns_type columns_, references_type references_) : + columns(std::move(columns_)), references(std::move(references_)), + on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + + foreign_key_t(const self& other) : + columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), + on_delete(*this, false, other.on_delete._action) {} + + self& operator=(const self& other) { + this->columns = other.columns; + this->references = other.references; + this->on_update = {*this, true, other.on_update._action}; + this->on_delete = {*this, false, other.on_delete._action}; + return *this; + } + }; + + template + bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + + /** + * Cs can be a class member pointer, a getter function member pointer or setter + * func member pointer + * Available in SQLite 3.6.19 or higher + */ + template + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; + + tuple_type columns; + + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), {std::forward(refs)...}}; + } + + template + foreign_key_t, std::tuple...>> references(Rs... refs) { + return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + } + }; +#endif + + struct collate_constraint_t { + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif + + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } + + static std::string string_from_collate_argument(collate_argument argument) { + switch (argument) { + case collate_argument::binary: + return "BINARY"; + case collate_argument::nocase: + return "NOCASE"; + case collate_argument::rtrim: + return "RTRIM"; + } + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; + } + }; + + template + struct check_t { + using expression_type = T; + + expression_type expression; + }; + + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; + +#if SQLITE_VERSION_NUMBER >= 3031000 + 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 + template + struct generated_always_t : basic_generated_always { + using expression_type = T; + + expression_type expression; + + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} + + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } + + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } + }; +#endif + + struct null_t {}; + + struct not_null_t {}; + } + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of::value; +#else + false; +#endif + + template + struct is_foreign_key : polyfill::bool_constant> {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; + + template + struct is_primary_key : polyfill::bool_constant> {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of::value; +#else + false; +#endif + + template + struct is_generated_always : polyfill::bool_constant> {}; + + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::invoke_t, + check_if_has_template>, + constraints_type_t>, + std::is_base_of>>> { + + static_assert(tuple_has, is_primary_key>::value, + "an unexpected type was passed"); + }; + + template + using is_column_constraint = mpl::invoke_t>, + check_if_is_type, + check_if_is_type, + check_if_is_type>, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if, + check_if_is_type>, + T>; + } + +#if SQLITE_VERSION_NUMBER >= 3031000 + template + internal::generated_always_t generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; + } + + template + internal::generated_always_t as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; + } +#endif + +#if SQLITE_VERSION_NUMBER >= 3006019 + /** + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher + */ + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {{std::forward(columns)...}}; + } +#endif + + /** + * UNIQUE table constraint builder function. + */ + template + internal::unique_t unique(Args... args) { + return {{std::forward(args)...}}; + } + + /** + * UNIQUE column constraint builder function. + */ + inline internal::unique_t<> unique() { + return {{}}; + } + +#if SQLITE_VERSION_NUMBER >= 3009000 + /** + * UNINDEXED column constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#the_unindexed_column_option + */ + inline internal::unindexed_t unindexed() { + return {}; + } + + /** + * prefix=N table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#prefix_indexes + */ + template + internal::prefix_t prefix(T value) { + return {std::move(value)}; + } + + /** + * tokenize='...'' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#tokenizers + */ + template + internal::tokenize_t tokenize(T value) { + return {std::move(value)}; + } + + /** + * content='' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#contentless_tables + */ + template + internal::content_t content(T value) { + return {std::move(value)}; + } + + /** + * content='table' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#external_content_tables + */ + template + internal::table_content_t content() { + return {}; + } +#endif + + /** + * PRIMARY KEY table constraint builder function. + */ + template + internal::primary_key_t primary_key(Cs... cs) { + return {{std::forward(cs)...}}; + } + + /** + * PRIMARY KEY column constraint builder function. + */ + inline internal::primary_key_t<> primary_key() { + return {{}}; + } + + template + internal::default_t default_value(T t) { + return {std::move(t)}; + } + + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; + } + + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; + } + + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } + + template + internal::check_t check(T t) { + return {std::move(t)}; + } + + inline internal::null_t null() { + return {}; + } + + inline internal::not_null_t not_null() { + return {}; + } +} + +// #include "field_printer.h" + +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::shared_ptr, std::unique_ptr +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_std_ptr.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + + /** + * Is used to print members mapped to objects in storage_t::dump member function. + * Other developers can create own specialization to map custom types + */ + template + struct field_printer; + + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_preparable_v`, `is_bindable_v`. + */ + template + struct indirectly_test_printable; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_printable_v{})>>> = true; + + template + struct is_printable : polyfill::bool_constant> {}; + } + + template + struct field_printer> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.str(); + } + }; + + /** + * Upgrade to integer is required when using unsigned char(uint8_t) + */ + template<> + struct field_printer { + std::string operator()(const unsigned char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; + + /** + * Upgrade to integer is required when using signed char(int8_t) + */ + template<> + struct field_printer { + std::string operator()(const signed char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; + + /** + * char is neither signed char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; + + template + struct field_printer> { + std::string operator()(std::string string) const { + return string; + } + }; + + template<> + struct field_printer, void> { + std::string operator()(const std::vector& t) const { + std::stringstream ss; + ss << std::hex; + for (auto c: t) { + ss << c; + } + return ss.str(); + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + template<> + struct field_printer { + std::string operator()(const nullptr_t&) const { + return "NULL"; + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer { + std::string operator()(const std::nullopt_t&) const { + return "NULL"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer, + internal::is_printable>>::value>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if (t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if (t.has_value()) { + return field_printer()(*t); + } else { + return field_printer{}(std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} + +// #include "rowid.h" + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + struct rowid_t { + operator std::string() const { + return "rowid"; + } + }; + + struct oid_t { + operator std::string() const { + return "oid"; + } + }; + + struct _rowid_t { + operator std::string() const { + return "_rowid_"; + } + }; + + template + struct table_rowid_t : public rowid_t { + using type = T; + }; + + template + struct table_oid_t : public oid_t { + using type = T; + }; + template + struct table__rowid_t : public _rowid_t { + using type = T; + }; + + } + + inline internal::rowid_t rowid() { + return {}; + } + + inline internal::oid_t oid() { + return {}; + } + + inline internal::_rowid_t _rowid_() { + return {}; + } + + template + internal::table_rowid_t rowid() { + return {}; + } + + template + internal::table_oid_t oid() { + return {}; + } + + template + internal::table__rowid_t _rowid_() { + return {}; + } +} + +// #include "operators.h" + +#include // std::false_type, std::true_type +#include // std::move + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); + }; + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); +#else + template class C, typename... Ts> + std::true_type is_base_of_template_impl(const C&); + + template class C> + std::false_type is_base_of_template_impl(...); + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#endif + + template class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + } +} + +// #include "tags.h" + +// #include "serialize_result_type.h" + +// #include "functional/cxx_string_view.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; + using serialize_arg_type = std::string_view; +#else + using serialize_result_type = std::string; + using serialize_arg_type = const std::string&; +#endif + } +} + +namespace sqlite_orm { + + namespace internal { + + template + struct binary_operator : Ds... { + using left_type = L; + using right_type = R; + + left_type lhs; + right_type rhs; + + constexpr binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + + template + using is_binary_operator = polyfill::bool_constant>; + + struct conc_string { + serialize_result_type serialize() const { + return "||"; + } + }; + + /** + * Result of concatenation || operator + */ + template + using conc_t = binary_operator; + + struct unary_minus_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of unary minus - operator + */ + template + struct unary_minus_t : unary_minus_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + unary_minus_t(argument_type argument_) : argument(std::move(argument_)) {} + }; + + struct add_string { + serialize_result_type serialize() const { + return "+"; + } + }; + + /** + * Result of addition + operator + */ + template + using add_t = binary_operator; + + struct sub_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of substraction - operator + */ + template + using sub_t = binary_operator; + + struct mul_string { + serialize_result_type serialize() const { + return "*"; + } + }; + + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; + + struct div_string { + serialize_result_type serialize() const { + return "/"; + } + }; + + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; + + struct mod_operator_string { + serialize_result_type serialize() const { + return "%"; + } + }; + + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + serialize_result_type serialize() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + serialize_result_type serialize() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + serialize_result_type serialize() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + serialize_result_type serialize() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + serialize_result_type serialize() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} + }; + + struct assign_string { + serialize_result_type serialize() const { + return "="; + } + }; + + /** + * Result of assign = operator + */ + template + using assign_t = binary_operator; + + /** + * Assign operator traits. Common case + */ + template + struct is_assign_t : public std::false_type {}; + + /** + * Assign operator traits. Specialized case + */ + template + struct is_assign_t> : public std::true_type {}; + } + + /** + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users + */ + template + constexpr internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::unary_minus_t minus(T t) { + return {std::move(t)}; + } + + /** + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + */ + template + constexpr internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users + */ + template + constexpr internal::sub_t sub(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users + */ + template + constexpr internal::mul_t mul(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users + * @note Please notice that ::div function already exists in pure C standard library inside header. + * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. + */ + template + constexpr internal::div_t div(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users + */ + template + constexpr internal::mod_t mod(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } + + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } +} + +// #include "select_constraints.h" + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif +#include // std::enable_if, std::remove_cvref, std::is_convertible, std::is_same, std::is_member_pointer +#include // std::string +#include // std::move +#include // std::tuple, std::get, std::tuple_size +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members + */ + template + struct optional_container { + using type = T; + + type field; + + template + void apply(const L& l) const { + l(this->field); + } + }; + + template<> + struct optional_container { + using type = void; + + template + void apply(const L&) const { + //.. + } + }; + } +} + +// #include "ast/where.h" + +#include // std::false_type, std::true_type +#include // std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../serialize_result_type.h" + +namespace sqlite_orm { + namespace internal { + + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; + } + }; + + /** + * WHERE argument holder. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * Don't construct it manually. Call `where(...)` function instead. + */ + template + struct where_t : where_string { + using expression_type = C; + + expression_type expression; + + constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; + + template + struct is_where : polyfill::bool_constant> {}; + } + + /** + * WHERE clause. Use it to add WHERE conditions wherever you like. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * @example + * // SELECT name + * // FROM letters + * // WHERE id > 3 + * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); + */ + template + constexpr internal::where_t where(C expression) { + return {std::move(expression)}; + } +} + +// #include "ast/group_by.h" + +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; + + args_type args; + expression_type expression; + }; + + /** + * GROUP BY pack holder. + */ + template + struct group_by_t { + using args_type = std::tuple; + + args_type args; + + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; + } + }; + + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + } + + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args... args) { + return {{std::forward(args)...}}; + } +} + +// #include "core_functions.h" + +#include // std::string +#include // std::make_tuple, std::tuple_size +#include // std::forward, std::is_base_of, std::enable_if +#include // std::unique_ptr +#include // std::vector + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl/conditional.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "conditions.h" + +#include // std::string +#include // std::enable_if, std::is_same, std::remove_const +#include // std::vector +#include // std::tuple +#include // std::move, std::forward +#include // std::stringstream +#include // std::flush + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +// #include "type_traits.h" + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + bool fts5_columns = false; + }; + + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + }; + + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; + } + + const storage_type& storage; + }; + } + +} + +// #include "serialize_result_type.h" + +// #include "tags.h" + +// #include "table_reference.h" + +// #include "alias_traits.h" + +// #include "expression.h" + +#include +#include // std::enable_if +#include // std::move, std::forward, std::declval +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tags.h" + +// #include "operators.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct in_t; + + template + struct and_condition_t; + + template + struct or_condition_t; + + /** + * Result of c(...) function. Has operator= overloaded which returns assign_t + */ + template + struct expression_t { + T value; + + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; + } + + assign_t operator=(nullptr_t) const { + return {this->value, nullptr}; + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template + in_t in(Args... args) const { + return {this->value, {std::forward(args)...}, false}; + } + + template + in_t not_in(Args... args) const { + return {this->value, {std::forward(args)...}, true}; + } + + template + and_condition_t and_(R right) const { + return {this->value, std::move(right)}; + } + + template + or_condition_t or_(R right) const { + return {this->value, std::move(right)}; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; + + template + constexpr T get_from_expression(T&& value) { + return std::move(value); + } + + template + constexpr const T& get_from_expression(const T& value) { + return value; + } + + template + constexpr T get_from_expression(expression_t&& expression) { + return std::move(expression.value); + } + + template + constexpr const T& get_from_expression(const expression_t& expression) { + return expression.value; + } + + template + using unwrap_expression_t = decltype(get_from_expression(std::declval())); + } + + /** + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); + */ + template + constexpr internal::expression_t c(T value) { + return {std::move(value)}; + } +} + +// #include "column_pointer.h" + +// #include "tags.h" + +// #include "type_printer.h" + +// #include "literal.h" + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; + + type value; + }; + + } +} + +namespace sqlite_orm { + + namespace internal { + + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; + + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + optional_container off; + + limit_t() = default; + + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} + + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; + + template + struct is_limit : std::false_type {}; + + template + struct is_limit> : std::true_type {}; + + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; + + template + using is_offset = polyfill::is_specialization_of; + + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; + + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + + operator std::string() const { + return collate_constraint_t{this->argument}; + } + }; + + struct named_collate_base { + std::string name; + + operator std::string() const { + return "COLLATE " + this->name; + } + }; + + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; + + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; + + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; + + /** + * Result of not operator + */ + template + struct negated_condition_t : condition_t, negated_condition_string { + using argument_type = C; + + argument_type c; + + constexpr negated_condition_t(argument_type arg) : c(std::move(arg)) {} + }; + + /** + * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type + */ + template + struct binary_condition : condition_t, S { + using left_type = L; + using right_type = R; + using result_type = Res; + + left_type lhs; + right_type rhs; + + constexpr binary_condition() = default; + + constexpr binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + + template + struct is_binary_condition : polyfill::bool_constant> {}; + + struct and_condition_string { + serialize_result_type serialize() const { + return "AND"; + } + }; + + /** + * Result of and operator + */ + template + struct and_condition_t : binary_condition, negatable_t { + using super = binary_condition; + + using super::super; + }; + + struct or_condition_string { + serialize_result_type serialize() const { + return "OR"; + } + }; + + /** + * Result of or operator + */ + template + struct or_condition_t : binary_condition, negatable_t { + using super = binary_condition; + + using super::super; + }; + + struct is_equal_string { + serialize_result_type serialize() const { + return "="; + } + }; + + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } + + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; + + template + struct is_equal_with_table_t : negatable_t { + using left_type = L; + using right_type = R; + + right_type rhs; + + is_equal_with_table_t(right_type rhs) : rhs(std::move(rhs)) {} + }; + + struct is_not_equal_string { + serialize_result_type serialize() const { + return "!="; + } + }; + + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct greater_than_string { + serialize_result_type serialize() const { + return ">"; + } + }; + + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct greater_or_equal_string { + serialize_result_type serialize() const { + return ">="; + } + }; + + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct less_than_string { + serialize_result_type serialize() const { + return "<"; + } + }; + + /** + * < operator object. + */ + template + struct less_than_t : binary_condition, negatable_t { + using self = less_than_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct less_or_equal_string { + serialize_result_type serialize() const { + return "<="; + } + }; + + /** + * <= operator object. + */ + template + struct less_or_equal_t : binary_condition, negatable_t { + using self = less_or_equal_t; + + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct in_base { + bool negative = false; // used in not_in + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif + }; + + /** + * IN operator object. + */ + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; + + L left; // left expression + A argument; // in arg + + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; + + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } + }; + + /** + * IS NULL operator object. + */ + template + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; + + T t; + + is_null_t(T t_) : t(std::move(t_)) {} + }; + + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; + + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; + + T t; + + is_not_null_t(T t_) : t(std::move(t_)) {} + }; + + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; + + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} +#endif + }; + + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; + + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; + + expression_type expression; + + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } + + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } + + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } + + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } + + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } + + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } + + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); + } + }; + + /** + * ORDER BY pack holder. + */ + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; + + args_type args; + + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; + + struct dynamic_order_by_entry_t : order_by_base { + std::string name; + + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + }; + + /** + * C - serializer context class + */ + template + struct dynamic_order_by_t : order_by_string { + using context_t = C; + using entry_t = dynamic_order_by_entry_t; + using const_iterator = typename std::vector::const_iterator; + + dynamic_order_by_t(const context_t& context_) : context(context_) {} + + template + void push_back(order_by_t order_by) { + auto newContext = this->context; + newContext.skip_table_name = 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)); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + } + + protected: + std::vector entries; + context_t context; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction, + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value; + + template + struct is_order_by : polyfill::bool_constant> {}; + + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; + + /** + * BETWEEN operator object. + */ + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; + + expression_type expr; + lower_type b1; + upper_type b2; + + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { + operator std::string() const { + return "LIKE"; + } + }; + + /** + * LIKE operator object. + */ + template + struct like_t : condition_t, like_string, negatable_t { + using self = like_t; + using arg_t = A; + using pattern_t = T; + using escape_t = E; + + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here + + like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} + + template + like_t escape(C c) const { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + } + }; + + struct glob_string { + operator std::string() const { + return "GLOB"; + } + }; + + template + struct glob_t : condition_t, glob_string, negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; + + arg_t arg; + pattern_t pattern; + + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; + + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; + } + }; + + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; + }; + + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } + }; + + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; + }; + + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; + } + }; + + /** + * LEFT JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_join_t : left_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct join_string { + operator std::string() const { + return "JOIN"; + } + }; + + /** + * Simple JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct join_t : join_string { + using type = T; + using on_type = O; + + on_type constraint; + + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; + + /** + * LEFT OUTER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_outer_join_t : left_outer_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct on_string { + operator std::string() const { + return "ON"; + } + }; + + /** + * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN + * T is on type argument. + */ + template + struct on_t : on_string { + using arg_type = T; + + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; + + /** + * USING argument holder. + */ + template + struct using_t { + column_pointer column; + + operator std::string() const { + return "USING"; + } + }; + + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + + /** + * INNER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct inner_join_t : inner_join_string { + using type = T; + using on_type = O; + + on_type constraint; + + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct cast_string { + operator std::string() const { + return "CAST"; + } + }; + + /** + * CAST holder. + * T is a type to cast to + * E is an expression type + * Example: cast(&User::id) + */ + template + struct cast_t : cast_string { + using to_type = T; + using expression_type = E; + + expression_type expression; + + cast_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + struct from_t { + using tuple_type = std::tuple; + }; + + template + using is_from = polyfill::is_specialization_of; + + template + using is_constrained_join = polyfill::is_detected; + } + + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from<"a"_alias.for_>());` + */ + template + auto from() { + return from...>(); + } +#endif + + // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr less_than_t, unwrap_expression_t> operator<(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr greater_than_t, unwrap_expression_t> operator>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr is_equal_t, unwrap_expression_t> operator==(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, std::is_base_of>::value, + bool> = true> + constexpr or_condition_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template< + class L, + class R, + std::enable_if_t, + std::is_base_of, + is_operator_argument, + is_operator_argument>, + // exclude conditions + polyfill::negation, + std::is_base_of>>>::value, + bool> = true> + constexpr conc_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + } + + template + internal::using_t using_(F O::* field) { + return {field}; + } + template + internal::using_t using_(internal::column_pointer field) { + return {std::move(field)}; + } + + template + internal::on_t on(T t) { + return {std::move(t)}; + } + + template + internal::cross_join_t cross_join() { + return {}; + } + + template + internal::natural_join_t natural_join() { + return {}; + } + + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_join(On on) { + return left_join, On>(std::move(on)); + } +#endif + + template + internal::join_t join(O o) { + return {std::move(o)}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto join(On on) { + return join, On>(std::move(on)); + } +#endif + + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_outer_join(On on) { + return left_outer_join, On>(std::move(on)); + } +#endif + + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto inner_join(On on) { + return inner_join, On>(std::move(on)); + } +#endif + + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } + + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } + + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } + + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } + + template + constexpr auto and_(L l, R r) { + using namespace ::sqlite_orm::internal; + return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } + + template + constexpr auto or_(L l, R r) { + using namespace ::sqlite_orm::internal; + return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } + + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } + + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } + + template + internal::dynamic_in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::dynamic_in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::dynamic_in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } + + template + internal::dynamic_in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::dynamic_in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } + + template + constexpr internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::is_equal_with_table_t is_equal(R rhs) { + return {std::move(rhs)}; + } + + template + constexpr internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::less_than_t less_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_than(...)` instead. + */ + template + [[deprecated("Use the accurately named function `less_than(...)` instead")]] internal::less_than_t + lesser_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::less_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::less_or_equal_t less_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_or_equal(...)` instead. + */ + template + [[deprecated("Use the accurately named function `less_or_equal(...)` instead")]] internal::less_or_equal_t + lesser_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + constexpr internal::less_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) + */ + template> = true> + internal::order_by_t order_by(O o) { + return {std::move(o)}; + } + + /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template> = true> + internal::order_by_t> order_by(O o) { + return {{std::move(o)}}; + } + + /** + * ORDER BY column1, column2 + * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) + */ + template + internal::multi_order_by_t multi_order_by(Args... args) { + return {{std::forward(args)...}}; + } + + /** + * ORDER BY column1, column2 + * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member + * function Example: + * auto orderBy = dynamic_order_by(storage); + * if(someCondition) { + * orderBy.push_back(&User::id); + * } else { + * orderBy.push_back(&User::name); + * orderBy.push_back(&User::birthDate); + * } + */ + template + internal::dynamic_order_by_t> + dynamic_order_by(const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); + } + + /** + * X BETWEEN Y AND Z + * Example: storage.select(between(&User::id, 10, 20)) + */ + template + internal::between_t between(A expr, T b1, T b2) { + return {std::move(expr), std::move(b1), std::move(b2)}; + } + + /** + * X LIKE Y + * Example: storage.select(like(&User::name, "T%")) + */ + template + internal::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; + } + + /** + * X GLOB Y + * Example: storage.select(glob(&User::name, "*S")) + */ + template + internal::glob_t glob(A a, T t) { + return {std::move(a), std::move(t)}; + } + + /** + * X LIKE Y ESCAPE Z + * Example: storage.select(like(&User::name, "T%", "%")) + */ + template + internal::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; + } + + /** + * CAST(X AS type). + * Example: cast(&User::id) + */ + template + internal::cast_t cast(E e) { + return {std::move(e)}; + } +} + +// #include "serialize_result_type.h" + +// #include "operators.h" + +// #include "tags.h" + +// #include "table_reference.h" + +// #include "ast/into.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct into_t { + using type = T; + }; + + template + using is_into = polyfill::is_specialization_of; + } + + template + internal::into_t into() { + return {}; + } +} + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + template + struct unique_ptr_result_of {}; + + /** + * Base class for operator overloading + * R - return type + * S - class with operator std::string + * Args - function arguments types + */ + template + struct built_in_function_t : S, arithmetic_t { + using return_type = R; + using string_type = S; + using args_type = std::tuple; + + static constexpr size_t args_size = std::tuple_size::value; + + args_type args; + + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = + is_base_of_template::value; + + template + struct is_built_in_function : polyfill::bool_constant> {}; + + template + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; + + function_type function; + where_expression where; + }; + + template + struct where_t; + + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; + + using super::super; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; + + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } + }; + + struct length_string { + serialize_result_type serialize() const { + return "LENGTH"; + } + }; + + struct abs_string { + serialize_result_type serialize() const { + return "ABS"; + } + }; + + struct lower_string { + serialize_result_type serialize() const { + return "LOWER"; + } + }; + + struct upper_string { + serialize_result_type serialize() const { + return "UPPER"; + } + }; + + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; + + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; + + struct changes_string { + serialize_result_type serialize() const { + return "CHANGES"; + } + }; + + struct trim_string { + serialize_result_type serialize() const { + return "TRIM"; + } + }; + + struct ltrim_string { + serialize_result_type serialize() const { + return "LTRIM"; + } + }; + + struct rtrim_string { + serialize_result_type serialize() const { + return "RTRIM"; + } + }; + + struct hex_string { + serialize_result_type serialize() const { + return "HEX"; + } + }; + + struct quote_string { + serialize_result_type serialize() const { + return "QUOTE"; + } + }; + + struct randomblob_string { + serialize_result_type serialize() const { + return "RANDOMBLOB"; + } + }; + + struct instr_string { + serialize_result_type serialize() const { + return "INSTR"; + } + }; + + struct replace_string { + serialize_result_type serialize() const { + return "REPLACE"; + } + }; + + struct round_string { + serialize_result_type serialize() const { + return "ROUND"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3007016 + struct char_string { + serialize_result_type serialize() const { + return "CHAR"; + } + }; + + struct random_string { + serialize_result_type serialize() const { + return "RANDOM"; + } + }; + +#endif + + struct coalesce_string { + serialize_result_type serialize() const { + return "COALESCE"; + } + }; + + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; + + struct nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; + + struct date_string { + serialize_result_type serialize() const { + return "DATE"; + } + }; + + struct time_string { + serialize_result_type serialize() const { + return "TIME"; + } + }; + + struct datetime_string { + serialize_result_type serialize() const { + return "DATETIME"; + } + }; + + struct julianday_string { + serialize_result_type serialize() const { + return "JULIANDAY"; + } + }; + + struct strftime_string { + serialize_result_type serialize() const { + return "STRFTIME"; + } + }; + + struct zeroblob_string { + serialize_result_type serialize() const { + return "ZEROBLOB"; + } + }; + + struct substr_string { + serialize_result_type serialize() const { + return "SUBSTR"; + } + }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + serialize_result_type serialize() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + serialize_result_type serialize() const { + return "TOTAL"; + } + }; + + struct sum_string { + serialize_result_type serialize() const { + return "SUM"; + } + }; + + struct count_string { + serialize_result_type serialize() const { + return "COUNT"; + } + }; + + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; + + struct avg_string { + serialize_result_type serialize() const { + return "AVG"; + } + }; + + struct max_string { + serialize_result_type serialize() const { + return "MAX"; + } + }; + + struct min_string { + serialize_result_type serialize() const { + return "MIN"; + } + }; + + struct group_concat_string { + serialize_result_type serialize() const { + return "GROUP_CONCAT"; + } + }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; + + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; + + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; + + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; + + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; + + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; + + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; + + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; + + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } + }; + + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; + + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; + + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; + + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; + + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; + } + }; + + struct ln_string { + serialize_result_type serialize() const { + return "LN"; + } + }; + + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; + + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; + + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; + + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; + + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; + + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } + }; + + struct power_string { + serialize_result_type serialize() const { + return "POWER"; + } + }; + + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; + } + }; + + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; + } + }; + + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; + } + }; + + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; + + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; + + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; + + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; + +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; + + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; + + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; + } + }; + + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; + } + }; + + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; + + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; + } + }; + + struct json_set_string { + serialize_result_type serialize() const { + return "JSON_SET"; + } + }; + + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; + + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; + + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } + }; + + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; + + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; + + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; + + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } + }; + + struct json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 + + template + using field_type_or_type_t = polyfill::detected_or_t>; + + template + struct highlight_t { + using table_type = T; + using argument0_type = X; + 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)) {} + }; + } + +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + inline internal::built_in_function_t pi() { + return {{}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pi() { + return {{}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template + internal::built_in_function_t typeof_(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template + internal::built_in_function_t unicode(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length + */ + template + internal::built_in_function_t length(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * ABS(x) function https://sqlite.org/lang_corefunc.html#abs + */ + template + internal::built_in_function_t, internal::abs_string, T> abs(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower + */ + template + internal::built_in_function_t lower(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper + */ + template + internal::built_in_function_t upper(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t last_insert_rowid() { + return {{}}; + } + + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t total_changes() { + return {{}}; + } + + /** + * CHANGES() function https://sqlite.org/lang_corefunc.html#changes + */ + inline internal::built_in_function_t changes() { + return {{}}; + } + + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::built_in_function_t hex(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::built_in_function_t quote(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::built_in_function_t instr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template, internal::is_into>::value == 0, bool> = true> + internal::built_in_function_t replace(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; + } + + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + +#if SQLITE_VERSION_NUMBER >= 3007016 + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + internal::built_in_function_t char_(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random + */ + inline internal::built_in_function_t random() { + return {{}}; + } +#endif + + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + auto coalesce(Args... args) + -> internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename mpl::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr (std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else + template + internal::built_in_function_t nullif(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } +#endif + + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t date(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t time(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + /** + * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t datetime(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + /** + * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t julianday(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + /** + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t strftime(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + /** + * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob + */ + template + internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple{std::forward(n)}}; + } + + /** + * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::built_in_function_t substr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::built_in_function_t substr(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; + } + +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::built_in_function_t soundex(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t total(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t count(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(*) without FROM function. + */ + inline internal::count_asterisk_without_type count() { + return {}; + } + + /** + * COUNT(*) with FROM function. Specified type T will be serialized as + * a from argument. + */ + template + internal::count_asterisk_t count() { + return {}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * COUNT(*) with FROM function. Specified recordset will be serialized as + * a from argument. + */ + template + auto count() { + return count>(); + } +#endif + + /** + * AVG(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::built_in_aggregate_function_t group_concat(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template + internal::built_in_function_t json(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_extract(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_insert(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_replace(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_set(X x, Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_object(Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_patch(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_remove(X x, + Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_remove(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_valid(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_quote(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_array(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_object(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#endif // SQLITE_ENABLE_JSON1 + + // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr unary_minus_t> operator-(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr add_t, unwrap_expression_t> operator+(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr sub_t, unwrap_expression_t> operator-(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr mul_t, unwrap_expression_t> operator*(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr div_t, unwrap_expression_t> operator/(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr bitwise_not_t> operator~(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_left_t, unwrap_expression_t> operator<<(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_right_t, unwrap_expression_t> operator>>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_and_t, unwrap_expression_t> operator&(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_or_t, unwrap_expression_t> operator|(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + } + + template + internal::highlight_t highlight(X x, Y y, Z z) { + return {std::move(x), std::move(y), std::move(z)}; + } +} + +// #include "alias_traits.h" + +// #include "cte_moniker.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#include // std::make_index_sequence +#endif +#include // std::enable_if, std::is_member_pointer, std::is_same, std::is_convertible +#include // std::ignore +#include +#endif + +// #include "functional/cstring_literal.h" + +// #include "alias.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { + + namespace internal { + /** + * A special record set alias that is both, a storage lookup type (mapping type) and an alias. + */ + template + struct cte_moniker + : recordset_alias< + cte_moniker /* refer to self, since a moniker is both, an alias and a mapped type */, + A, + X...> { + /** + * Introduce the construction of a common table expression using this moniker. + * + * The list of explicit columns is optional; + * if provided the number of columns must match the number of columns of the subselect. + * The column names will be merged with the subselect: + * 1. column names of subselect + * 2. explicit columns + * 3. fill in empty column names with column index + * + * Example: + * 1_ctealias()(select(&Object::id)); + * 1_ctealias(&Object::name)(select("object")); + * + * @return A `cte_builder` instance. + * @note (internal): Defined in select_constraints.h in order to keep this member function in the same place as the named factory function `cte()`, + * and to keep the actual creation of the builder in one place. + */ +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires ((is_column_alias_v || std::is_member_pointer_v || + std::same_as> || + std::convertible_to) && + ...) + constexpr auto operator()(ExplicitCols... explicitColumns) const; +#else + template, + std::is_member_pointer, + std::is_same>, + std::is_convertible>...>, + bool> = true> + constexpr auto operator()(ExplicitCols... explicitColumns) const; +#endif + }; + } + + inline namespace literals { + /** + * cte_moniker<'n'> from a numeric literal. + * E.g. 1_ctealias, 2_ctealias + */ + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _ctealias() { + return internal::cte_moniker{}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * cte_moniker<'1'[, ...]> from a string literal. + * E.g. "1"_cte, "2"_cte + */ + template + [[nodiscard]] consteval auto operator"" _cte() { + return internal::explode_into(std::make_index_sequence{}); + } +#endif + } +} +#endif + +// #include "schema/column.h" + +#include // std::tuple +#include // std::string +#include // std::unique_ptr +#include // std::is_same, std::is_member_object_pointer +#include // std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../type_traits.h" + +// #include "../member_traits/member_traits.h" + +// #include "../type_is_nullable.h" + +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + /** + * This is class that tells `sqlite_orm` that type is nullable. Nullable types + * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. + * Default nullability status for all types is `NOT NULL`. So if you want to map + * custom type as `NULL` (for example: boost::optional) you have to create a specialization + * of `type_is_nullable` for your type and derive from `std::true_type`. + */ + template + struct type_is_nullable : std::false_type { + bool operator()(const T&) const { + return true; + } + }; + + /** + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. + */ + template + struct type_is_nullable, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); + } + }; +} + +// #include "../constraints.h" + +namespace sqlite_orm { + + namespace internal { + + struct column_identifier { + + /** + * Column name. + */ + std::string name; + }; + + struct empty_setter {}; + + /* + * Encapsulates object member pointers that are used as column fields, + * and whose object is mapped to storage. + * + * G is a member object pointer or member function pointer + * S is a member function pointer or `empty_setter` + */ + template + struct column_field { + using member_pointer_t = G; + using setter_type = S; + using object_type = member_object_type_t; + using field_type = member_field_type_t; + + /** + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. + */ + const member_pointer_t member_pointer; + + /** + * Setter member function to write a field value + */ + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; + + /** + * Simplified interface for `NOT NULL` constraint + */ + constexpr bool is_not_null() const { + return !type_is_nullable::value; + } + }; + + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + */ + template + struct column_constraints { + using constraints_type = std::tuple; + + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints contain specified type. + */ + template class Trait> + constexpr static bool is() { + return tuple_has::value; + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const; + }; + + /** + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. + */ + template + struct column_t : column_identifier, column_field, column_constraints { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple op) : + column_identifier{std::move(name)}, column_field{memberPointer, setter}, + column_constraints{std::move(op)} {} +#endif + }; + + template + struct column_field_expression { + using type = void; + }; + + template + struct column_field_expression, void> { + using type = typename column_t::member_pointer_t; + }; + + template + using column_field_expression_t = typename column_field_expression::type; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; + + template + using is_column = polyfill::bool_constant>; + + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + } + + /** + * Factory function for a column definition from a member object pointer of the object to be mapped. + */ + template = true> + internal::column_t + make_column(std::string name, M memberPointer, Op... constraints) { + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), memberPointer, {}, std::tuple{std::move(constraints)...}}); + } + + /** + * Factory function for a column definition from "setter" and "getter" member function pointers of the object to be mapped. + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); + } + + /** + * Factory function for a column definition from "getter" and "setter" member function pointers of the object to be mapped. + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); + } +} + +namespace sqlite_orm { + + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using expression_type = T; + + expression_type expression; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + struct distinct_string { + operator std::string() const { + return "DISTINCT"; + } + }; + + /** + * DISCTINCT generic container. + */ + template + struct distinct_t : distinct_string { + using expression_type = T; + + expression_type expression; + + distinct_t(expression_type expression) : expression(std::move(expression)) {} + }; + + struct all_string { + operator std::string() const { + return "ALL"; + } + }; + + /** + * ALL generic container. + */ + template + struct all_t : all_string { + using expression_type = T; + + expression_type expression; + + all_t(expression_type expression) : expression(std::move(expression)) {} + }; + + /** + * Whether a type represents a keyword for a result set modifier (as part of a simple select expression). + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_rowset_deduplicator_v = + polyfill::disjunction, + polyfill::is_specialization_of>::value; + + template + struct is_rowset_deduplicator : polyfill::bool_constant> {}; + + template + struct columns_t { + using columns_type = std::tuple; + + 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 + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; + + template + using is_columns = polyfill::bool_constant>; + + /* + * Captures the type of an aggregate/structure/object and column expressions, such that + * `T` can be constructed in-place as part of a result row. + * `T` must be constructible using direct-list-initialization. + */ + template + struct struct_t { + using columns_type = std::tuple; + + 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 + SQLITE_ORM_INLINE_VAR constexpr bool is_struct_v = polyfill::is_specialization_of::value; + + template + using is_struct = polyfill::bool_constant>; + + /** + * Subselect object type. + */ + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; + + return_type col; + conditions_type conditions; + bool highest_level = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : + col{std::move(col)}, conditions{std::move(conditions)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of::value; + + template + using is_select = polyfill::bool_constant>; + + /** + * Base for UNION, UNION ALL, EXCEPT and INTERSECT + */ + template + struct compound_operator { + using expressions_tuple = std::tuple; + + expressions_tuple compound; + + constexpr compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + iterate_tuple(this->compound, [](auto& expression) { + expression.highest_level = true; + }); + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + + template + using is_compound_operator = polyfill::bool_constant>; + + struct union_base { + bool all = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} +#endif + + operator std::string() const { + if (!this->all) { + return "UNION"; + } else { + return "UNION ALL"; + } + } + }; + + /** + * UNION object type. + */ + template + struct union_t : public compound_operator, union_base { + using typename compound_operator::expressions_tuple; + + constexpr union_t(expressions_tuple compound, bool all) : + compound_operator{std::move(compound)}, union_base{all} {} + }; + + struct except_string { + operator std::string() const { + return "EXCEPT"; + } + }; + + /** + * EXCEPT object type. + */ + template + struct except_t : compound_operator, except_string { + using super = compound_operator; + + using super::super; + }; + + struct intersect_string { + operator std::string() const { + return "INTERSECT"; + } + }; + /** + * INTERSECT object type. + */ + template + struct intersect_t : compound_operator, intersect_string { + using super = compound_operator; + + using super::super; + }; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /* + * Turn explicit columns for a CTE into types that the CTE backend understands + */ + template + struct decay_explicit_column { + using type = T; + }; + template + struct decay_explicit_column> { + using type = alias_holder; + }; + template + struct decay_explicit_column> { + using type = std::string; + }; + template + using decay_explicit_column_t = typename decay_explicit_column::type; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". + */ + struct materialized_t {}; + + /* + * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. + */ + struct not_materialized_t {}; +#endif + + /** + * Monikered (aliased) CTE expression. + */ + template + struct common_table_expression { + using cte_moniker_type = Moniker; + using expression_type = Select; + using explicit_colrefs_tuple = ExplicitCols; + using hints_tuple = Hints; + static constexpr size_t explicit_colref_count = std::tuple_size_v; + + SQLITE_ORM_NOUNIQUEADDRESS hints_tuple hints; + explicit_colrefs_tuple explicitColumns; + expression_type subselect; + + constexpr common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { + this->subselect.highest_level = true; + } + }; + + template + using common_table_expressions = std::tuple; + + template + struct cte_builder { + 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)}; + } + + template = true> + constexpr common_table_expression, select_t> + as(Compound sel) && { + return {std::move(this->explicitColumns), {std::move(sel)}}; + } +#else + template = true> + constexpr common_table_expression, Select> as(Select sel) && { + return {std::move(this->explicitColumns), std::move(sel)}; + } + + template = true> + constexpr common_table_expression, select_t> + as(Compound sel) && { + return {std::move(this->explicitColumns), {std::move(sel)}}; + } +#endif + }; + + /** + * WITH object type - expression with prepended CTEs. + */ + template + struct with_t { + using cte_type = common_table_expressions; + using expression_type = E; + + bool recursiveIndicated; + cte_type cte; + expression_type expression; + + with_t(bool recursiveIndicated, cte_type cte, expression_type expression) : + recursiveIndicated{recursiveIndicated}, cte{std::move(cte)}, expression{std::move(expression)} { + if constexpr (is_select_v) { + this->expression.highest_level = true; + } + } + }; +#endif + + template + struct asterisk_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + asterisk_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct object_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct then_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct simple_case_t { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + }; + + /** + * T is a case expression type + * E is else type (void is ELSE is omitted) + * Args... is a pack of WHEN expressions + */ + template + struct simple_case_builder { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + + template + simple_case_builder> when(W w, then_t t) { + using result_args_type = std::tuple>; + std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); + std::get::value - 1>(result_args) = std::move(newPair); + return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; + } + + simple_case_t end() { + return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; + } + + template + simple_case_builder else_(El el) { + return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; + } + }; + + template, bool> = true> + const T& access_column_expression(const T& expression) { + return expression; + } + + /* + * Access a column expression prefixed by a result set deduplicator (as part of a simple select expression, i.e. distinct, all) + */ + template, bool> = true> + const typename D::expression_type& access_column_expression(const D& modifier) { + return modifier.expression; + } + + template + constexpr void validate_conditions() { + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); + } + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + internal::as_optional_t as_optional(T value) { + return {std::move(value)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + internal::then_t then(T t) { + return {std::move(t)}; + } + + template + internal::simple_case_builder case_(T t) { + return {{std::move(t)}}; + } + + template + internal::simple_case_builder case_() { + return {}; + } + + template + internal::distinct_t distinct(T t) { + return {std::move(t)}; + } + + template + internal::all_t all(T t) { + return {std::move(t)}; + } + + /* + * Combine multiple columns in a tuple. + */ + template + constexpr internal::columns_t columns(Args... args) { + return {{std::forward(args)...}}; + } + + /* + * Construct an unmapped structure ad-hoc from multiple columns. + * `T` must be constructible from the column results using direct-list-initialization. + */ + template + constexpr internal::struct_t struct_(Args... args) { + return {{std::forward(args)...}}; + } + + /** + * Public function for subselect query. Is useful in UNION queries. + */ + template + constexpr internal::select_t select(T t, Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(t), {std::forward(args)...}}; + } + + /** + * Public function for UNION operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp + */ + template + constexpr internal::union_t union_(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}, false}; + } + + /** + * Public function for UNION ALL operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp + */ + template + constexpr internal::union_t union_all(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}, true}; + } + + /** + * Public function for EXCEPT operator. + * Expressions are subselect objects. + * Look through example in examples/except.cpp + */ + template + constexpr internal::except_t except(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}}; + } + + template + constexpr internal::intersect_t intersect(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}}; + } + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if SQLITE_VERSION_NUMBER >= 3035003 +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". + * + * Example: + * 1_ctealias().as(select(1)); + */ + consteval internal::materialized_t materialized() { + return {}; + } + + /* + * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. + * + * Example: + * 1_ctealias().as(select(1)); + */ + consteval internal::not_materialized_t not_materialized() { + return {}; + } +#endif +#endif + + /** + * Introduce the construction of a common table expression using the specified moniker. + * + * The list of explicit columns is optional; + * if provided the number of columns must match the number of columns of the subselect. + * The column names will be merged with the subselect: + * 1. column names of subselect + * 2. explicit columns + * 3. fill in empty column names with column index + * + * Example: + * using cte_1 = decltype(1_ctealias); + * cte()(select(&Object::id)); + * cte(&Object::name)(select("object")); + */ + template, + std::is_member_pointer, + internal::is_column, + std::is_same>, + std::is_convertible>...>, + bool> = true> + constexpr auto cte(ExplicitCols... explicitColumns) { + using namespace ::sqlite_orm::internal; + static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); + 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>>; + return builder_type{{std::move(explicitColumns)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires ((internal::is_column_alias_v || std::is_member_pointer_v || + internal::is_column_v || + std::same_as> || + std::convertible_to) && + ...) + constexpr auto cte(ExplicitCols... explicitColumns) { + using namespace ::sqlite_orm::internal; + 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>>; + return builder_type{{std::move(explicitColumns)...}}; + } +#endif + + namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + template + requires ((is_column_alias_v || std::is_member_pointer_v || + std::same_as> || + std::convertible_to) && + ...) + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + return cte>(std::forward(explicitColumns)...); + } +#else + template + template, + std::is_member_pointer, + std::is_same>, + std::is_convertible>...>, + bool>> + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + return cte>(std::forward(explicitColumns)...); + } +#endif + } + + /** + * With-clause for a tuple of ordinary CTEs. + * + * Despite the missing RECURSIVE keyword, the CTEs can be recursive. + */ + template = true> + internal::with_t with(internal::common_table_expressions ctes, E expression) { + return {false, std::move(ctes), std::move(expression)}; + } + + /** + * With-clause for a tuple of ordinary CTEs. + * + * Despite the missing RECURSIVE keyword, the CTEs can be recursive. + */ + template = true> + internal::with_t, CTEs...> with(internal::common_table_expressions ctes, + Compound sel) { + return {false, std::move(ctes), sqlite_orm::select(std::move(sel))}; + } + + /** + * With-clause for a single ordinary CTE. + * + * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + */ + template = true, + internal::satisfies_not = true> + internal::with_t with(CTE cte, E expression) { + return {false, {std::move(cte)}, std::move(expression)}; + } + + /** + * With-clause for a single ordinary CTE. + * + * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + */ + template = true, + internal::satisfies = true> + internal::with_t, CTE> with(CTE cte, Compound sel) { + return {false, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + } + + /** + * With-clause for a tuple of potentially recursive CTEs. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + */ + template = true> + internal::with_t with_recursive(internal::common_table_expressions ctes, E expression) { + return {true, std::move(ctes), std::move(expression)}; + } + + /** + * With-clause for a tuple of potentially recursive CTEs. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + */ + template = true> + internal::with_t, CTEs...> + with_recursive(internal::common_table_expressions ctes, Compound sel) { + return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; + } + + /** + * With-clause for a single potentially recursive CTE. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + */ + template = true, + internal::satisfies_not = true> + internal::with_t with_recursive(CTE cte, E expression) { + return {true, {std::move(cte)}, std::move(expression)}; + } + + /** + * With-clause for a single potentially recursive CTE. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + */ + template = true, + internal::satisfies = true> + internal::with_t, CTE> with_recursive(CTE cte, Compound sel) { + return {true, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + } +#endif + + /** + * `SELECT * FROM T` expression that fetches results as tuples. + * T is a type mapped to a storage, or an alias of it. + * The `definedOrder` parameter denotes the expected order of result columns. + * The default is the implicit order as returned by SQLite, which may differ from the defined order + * if the schema of a table has been changed. + * By specifying the defined order, the columns are written out in the resulting select SQL string. + * + * In pseudo code: + * select(asterisk(false)) -> SELECT * from User + * select(asterisk(true)) -> SELECT id, name from User + * + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * Example: auto rows = storage.select(asterisk(true)); + * // decltype(rows) is std::vector> + * + * If you need to fetch results as objects instead of tuples please use `object()`. + */ + template + constexpr internal::asterisk_t asterisk(bool definedOrder = false) { + return {definedOrder}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Example: + * constexpr orm_table_alias auto m = "m"_alias.for_(); + * auto reportingTo = + * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); + */ + template + constexpr auto asterisk(bool definedOrder = false) { + return asterisk>(definedOrder); + } +#endif + + /** + * `SELECT * FROM T` expression that fetches results as objects of type T. + * T is a type mapped to a storage, or an alias of it. + * + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order + * Example: auto rows = storage.select(object(true)); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order + * + * If you need to fetch results as tuples instead of objects please use `asterisk()`. + */ + template + constexpr internal::object_t object(bool definedOrder = false) { + return {definedOrder}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + constexpr auto object(bool definedOrder = false) { + return object>(definedOrder); + } +#endif +} + +// #include "core_functions.h" + +// #include "conditions.h" + +// #include "statement_binder.h" + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include // std::default_delete +#include // std::string, std::wstring +#include // std::vector +#include // strncpy, strlen +// #include "functional/cxx_string_view.h" + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // wcsncpy, wcslen +#endif +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "is_std_ptr.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" + +// #include "error_code.h" + +// #include "arithmetic_tag.h" + +#include // std::is_integral + +// #include "functional/mpl/conditional.h" + +namespace sqlite_orm { + + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + using arithmetic_tag_t = + mpl::conditional_t::value, + // Integer class + mpl::conditional_t, + // Floating-point class + real_tag>; +} + +// #include "xdestroy_handling.h" + +#include // std::integral_constant +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template + concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + + /** + * Constrains a deleter to be an integral function constant. + */ + template + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; + }; +#endif + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + /** + * Yield a deleter's function pointer. + */ + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#else + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; + + template + struct is_integral_fp_c : std::false_type {}; + template + struct is_integral_fp_c< + D, + polyfill::void_t>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + + template + struct can_yield_fp : std::false_type {}; + template + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval()), + std::enable_if_t())>>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + + template> + struct yield_fp_of { + using type = void; + }; + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept is_unusable_for_xdestroy = + (!stateless_deleter && (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template + concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + + template + concept needs_xdestroy_proxy = + (stateless_deleter && (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template + requires (!integral_fp_c) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

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

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#endif + } +} + +namespace sqlite_orm { + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept + requires (internal::is_unusable_for_xdestroy) + { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept + requires (internal::needs_xdestroy_proxy) + { + return internal::xdestroy_proxy; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept + requires (internal::yields_xdestroy) + { + return d; + } +#else + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept { + return internal::xdestroy_proxy; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept { + return d; + } +#endif +} + +// #include "pointer_value.h" + +#if SQLITE_VERSION_NUMBER >= 3020000 +#include +#include +#include +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#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 { + template + struct pointer_type { + using value_type = const char[sizeof...(C) + 1]; + static inline constexpr value_type value = {C..., '\0'}; + }; + } + + inline namespace literals { + template + [[nodiscard]] consteval auto operator"" _pointer_type() { + return internal::explode_into(std::make_index_sequence{}); + } + } + + /** @short Specifies that a type is an integral constant string usable as a pointer type. + */ + template + concept orm_pointer_type = requires { + typename T::value_type; + { T::value } -> std::convertible_to; + }; +#endif + + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `"carray"_pointer_type`. + * + */ + template + struct pointer_arg { + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + // note (internal): this is currently a static assertion instead of a type constraint because + // of forward declarations in other places (e.g. function.h) + static_assert(orm_pointer_type, "T must be a pointer type (tag)"); +#else + static_assert(std::is_convertible::value, + "The pointer type (tag) must be convertible to `const char*`"); +#endif + + using tag = T; + using qualified_type = P; + + P* p_; + + P* ptr() const noexcept { + return p_; + } + + operator P*() const noexcept { + return p_; + } + }; + + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pointer_type`. + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template + class pointer_binding { + + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bind_pointer() + template + friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + // Constructing pointer bindings must go through bind_pointer() + template + friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; +#endif + template + friend B bind_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; + + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; + + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; + + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + + ~pointer_binding() { + if (p_) { + if (auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder> + xDestroy((void*)p_); + } + } + } + + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); + } + }; + + /** + * Alias template for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding = pointer_binding; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using pointer_arg_t = pointer_arg; + + template + using pointer_binding_t = pointer_binding; + + /** + * Alias template for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding_t = pointer_binding_t; +#endif +} + +namespace sqlite_orm { + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } + + template + auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bind_pointer(p.release(), p.get_deleter()); + } + + template + auto bind_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept -> B { + return B{p, std::move(d)}; + } + + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(P* p, D d) noexcept { + return bind_pointer(p, std::move(d)); + } + + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(std::unique_ptr p) noexcept { + return bind_pointer(p.release(), p.get_deleter()); + } + + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] B + bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + return bind_pointer(p, std::move(d)); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Wrap a pointer, its type (tag) and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } + + template + auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bind_pointer(p.release(), p.get_deleter()); + } +#endif + + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { + return bind_pointer(p, null_xdestroy_f); + } + + template + B bind_pointer_statically(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bind_pointer(p); + } + + template + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding + statically_bindable_pointer(P* p) noexcept { + return bind_pointer(p, null_xdestroy_f); + } + + template + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B + statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bind_pointer(p); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Wrap a pointer and its type (tag) for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { + return bind_pointer(p, null_xdestroy_f); + } +#endif + + /** + * Forward a pointer value from an argument. + */ + template + auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { + return bind_pointer_statically(pv.ptr()); + } +} +#endif + +namespace sqlite_orm { + + /** + * Helper class used for binding fields to sqlite3 statements. + */ + template + struct statement_binder; + + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_printable_v`, `is_preparable_v`. + */ + template + struct indirectly_test_bindable; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_bindable_v{})>>> = true; + + template + struct is_bindable : polyfill::bool_constant> {}; + } + +#if SQLITE_VERSION_NUMBER >= 3020000 + /** + * Specialization for pointer bindings (part of the 'pointer-passing interface'). + */ + template + struct statement_binder, void> { + using V = pointer_binding; + + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } + + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); + } + }; +#endif + + /** + * Specialization for arithmetic types. + */ + template + struct statement_binder> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + return this->bind(stmt, index, value, tag()); + } + + void result(sqlite3_context* context, const V& value) const { + this->result(context, value, tag()); + } + + private: + using tag = arithmetic_tag_t; + + int bind(sqlite3_stmt* stmt, int index, const V& value, int_or_smaller_tag) const { + return sqlite3_bind_int(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { + sqlite3_result_int(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { + return sqlite3_bind_int64(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, bigint_tag) const { + sqlite3_result_int64(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { + return sqlite3_bind_double(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, real_tag) const { + sqlite3_result_double(context, static_cast(value)); + } + }; + + /** + * Specialization for std::string and C-string. + */ + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto dataCopy = new char[stringData.second + 1]; + constexpr auto deleter = std::default_delete{}; + strncpy(dataCopy, stringData.first, stringData.second + 1); + sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::string_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const char* s) const { + return {s, int(strlen(s))}; + } +#endif + }; + +#ifndef SQLITE_ORM_OMITS_CODECVT + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + std::wstring_convert> converter; + std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); + return statement_binder().bind(stmt, index, utf8Str); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::wstring_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::wstring& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const wchar_t* s) const { + return {s, int(wcslen(s))}; + } +#endif + }; +#endif + + /** + * Specialization for nullptr_t. + */ + template<> + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + return sqlite3_bind_null(stmt, index); + } + + void result(sqlite3_context* context, const nullptr_t&) const { + sqlite3_result_null(context); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Specialization for std::nullopt_t. + */ + template<> + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { + return sqlite3_bind_null(stmt, index); + } + + void result(sqlite3_context* context, const std::nullopt_t&) const { + sqlite3_result_null(context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_binder< + V, + std::enable_if_t::value && + internal::is_bindable>::value>> { + using unqualified_type = std::remove_cv_t; + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + if (value) { + return statement_binder().bind(stmt, index, *value); + } else { + return statement_binder().bind(stmt, index, nullptr); + } + } + }; + + /** + * Specialization for binary data (std::vector). + */ + template<> + struct statement_binder, void> { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { + if (!value.empty()) { + return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); + } else { + return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); + } + } + + void result(sqlite3_context* context, const std::vector& value) const { + if (!value.empty()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_binder && + internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + if (value) { + return statement_binder().bind(stmt, index, *value); + } else { + return statement_binder().bind(stmt, index, std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + namespace internal { + + struct conditional_binder { + sqlite3_stmt* stmt = nullptr; + int index = 1; + + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template = true> + void operator()(const T& t) { + int rc = statement_binder{}.bind(this->stmt, this->index++, t); + if (SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } + } + + template = true> + void operator()(const T&) const {} + }; + + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); + + template = true> + void operator()(const T&) const = delete; + + template + void operator()(const T* value) { + if (!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value); + } + }; + + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; + + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence::value>{}, + std::forward(project)); + } + + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); + } +#else + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + 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); + } + } + + template + void bind(const T* value, size_t idx) const { + if (!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value, idx); + } + }; + + template + using bindable_filter_t = filter_tuple_t; + } +} + +// #include "column_result.h" + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of +#include // std::reference_wrapper + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_fy.h" + +#include + +namespace sqlite_orm { + + namespace internal { + + template + struct tuplify { + using type = std::tuple; + }; + template + struct tuplify> { + using type = std::tuple; + }; + + template + using tuplify_t = typename tuplify::type; + } +} + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "tuple_helper/same_or_void.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +// #include "mapped_type_proxy.h" + +#include // std::remove_const + +// #include "type_traits.h" + +// #include "table_reference.h" + +// #include "alias_traits.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * If T is a table reference or recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, + * otherwise unqualified T. + */ + template + struct mapped_type_proxy : std::remove_const {}; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + struct mapped_type_proxy : R {}; +#endif + + template + struct mapped_type_proxy> : std::remove_const> {}; + + template + using mapped_type_proxy_t = typename mapped_type_proxy::type; + } +} + +// #include "core_functions.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "rowid.h" + +// #include "column_result_proxy.h" + +// #include "type_traits.h" + +// #include "table_reference.h" + +namespace sqlite_orm { + namespace internal { + + /* + * Holder for the type of an unmapped aggregate/structure/object to be constructed ad-hoc from column results. + * `T` must be constructible using direct-list-initialization. + */ + template + struct structure { + using type = T; + }; + } +} + +namespace sqlite_orm { + namespace internal { + + template + struct column_result_proxy : std::remove_const {}; + + /* + * Unwrap `table_reference` + */ + template + struct column_result_proxy> : decay_table_ref

{}; + + /* + * Pass through `structure` + */ + template + struct column_result_proxy> : P {}; + + template + using column_result_proxy_t = typename column_result_proxy::type; + } +} + +// #include "alias.h" + +// #include "cte_types.h" + +#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" + +// #include "tuple_helper/tuple_fy.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { + + namespace internal { + + /** + * Aliased column expression mapped into a CTE, stored as a field in a table column. + */ + template + struct aliased_field { + ~aliased_field() = delete; + aliased_field(const aliased_field&) = delete; + void operator=(const aliased_field&) = delete; + + F field; + }; + + /** + * This class captures various properties and aspects of a subselect's column expression, + * and is used as a proxy in table_t<>. + */ + template + class subselect_mapper { + public: + subselect_mapper() = delete; + + // this type name is used to detect the mapping from moniker to object + using cte_moniker_type = Moniker; + using fields_type = std::tuple; + // this type captures the expressions forming the columns in a subselect; + // it is currently unused, however proves to be useful in compilation errors, + // as it simplifies recognizing errors in column expressions + using expressions_tuple = tuplify_t; + // this type captures column reference expressions specified at CTE construction; + // those are: member pointers, alias holders + using explicit_colrefs_tuple = ExplicitColRefs; + // this type captures column reference expressions from the subselect; + // those are: member pointers, alias holders + using subselect_colrefs_tuple = SubselectColRefs; + // this type captures column reference expressions merged from SubselectColRefs and ExplicitColRefs + using final_colrefs_tuple = FinalColRefs; + }; + } +} +#endif + +// #include "storage_traits.h" + +#include // std::tuple + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "type_traits.h" + +// #include "storage_lookup.h" + +#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void +#include +#include // std::index_sequence, std::make_index_sequence + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + namespace internal { + + template + struct storage_t; + + template + using db_objects_tuple = std::tuple; + + struct basic_table; + struct index_base; + struct base_trigger; + + template + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; + + template + struct is_db_objects : std::false_type {}; + + template + struct is_db_objects> : std::true_type {}; + // note: cannot use `db_objects_tuple` alias template because older compilers have problems + // to match `const db_objects_tuple`. + template + struct is_db_objects> : std::true_type {}; + + /** + * `std::true_type` if given object is mapped, `std::false_type` otherwise. + * + * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + */ + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; + + /** + * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. + */ + template + using lookup_type_matches = object_type_matches; + } + + // pick/lookup metafunctions + namespace internal { + + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - mapped data type + * Seq - index sequence matching the number of DBOs + * DBOs - db_objects_tuple type + */ + template + struct storage_pick_table; + + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - 'table' type, mapped data type + * DBOs - db_objects_tuple type, possibly const-qualified + */ + template + using storage_pick_table_t = typename storage_pick_table::value>, + std::remove_const_t>::type; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template + struct storage_find_table : polyfill::detected {}; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type, possibly const-qualified + * Lookup - mapped data type + */ + template + using storage_find_table_t = typename storage_find_table>::type; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template + struct is_mapped : std::false_type {}; + template + struct is_mapped>> : std::true_type {}; +#else + template> + struct is_mapped : std::true_type {}; + template + struct is_mapped : std::false_type {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} + +// runtime lookup functions +namespace sqlite_orm { + namespace internal { + /** + * Pick the table definition for the specified lookup type from the given tuple of schema objects. + * + * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. + */ + template = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t; + return std::get(dbObjects); + } + + /** + * Return passed in DBOs. + */ + template = true> + decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { + return dbObjects; + } + + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + } +} + +// #include "schema/column.h" + +namespace sqlite_orm { + namespace internal { + + namespace storage_traits { + + /** + * DBO - db object (table) + */ + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; + + template<> + struct storage_mapped_columns_impl { + using type = std::tuple<>; + }; + + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; + + /** + * DBO - db object (table) + */ + template + struct storage_mapped_column_expressions_impl + : tuple_transformer, is_column>, column_field_expression_t> {}; + + template<> + struct storage_mapped_column_expressions_impl { + using type = std::tuple<>; + }; + + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_column_expressions + : storage_mapped_column_expressions_impl> {}; + } + } +} + +// #include "function.h" + +#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 +#endif +#include // std::tuple, std::tuple_size, std::tuple_element +#include // std::min, std::copy_n +#include // std::move, std::forward + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cstring_literal.h" + +// #include "functional/function_traits.h" + +// #include "cxx_type_traits_polyfill.h" + +// #include "mpl.h" + +namespace sqlite_orm { + namespace internal { + /* + * Define nested typenames: + * - return_type + * - arguments_tuple + * - signature_type + */ + template + struct function_traits; + + /* + * A function's return type + */ + template + using function_return_type_t = typename function_traits::return_type; + + /* + * A function's arguments tuple + */ + template class Tuple, + template class ProjectOp = polyfill::type_identity_t> + using function_arguments = typename function_traits::template arguments_tuple; + + /* + * A function's signature + */ + template + using function_signature_type_t = typename function_traits::signature_type; + + template + struct function_traits { + using return_type = R; + + template class Tuple, template class ProjectOp> + using arguments_tuple = Tuple...>; + + using signature_type = R(Args...); + }; + + // non-exhaustive partial specializations of `function_traits` + + template + struct function_traits : function_traits { + using signature_type = R(Args...) const; + }; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct function_traits : function_traits { + using signature_type = R(Args...) noexcept; + }; + + template + struct function_traits : function_traits { + using signature_type = R(Args...) const noexcept; + }; +#endif + + /* + * Pick signature of function pointer + */ + template + struct function_traits : function_traits {}; + + /* + * Pick signature of function reference + */ + template + struct function_traits : function_traits {}; + + /* + * Pick signature of pointer-to-member function + */ + template + struct function_traits : function_traits {}; + } +} + +// #include "type_traits.h" + +// #include "tags.h" + +namespace sqlite_orm { + + struct arg_values; + + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + struct pointer_arg; + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + class pointer_binding; + + namespace internal { + template + using scalar_call_function_t = decltype(&F::operator()); + + template + using aggregate_step_function_t = decltype(&F::step); + + template + using aggregate_fin_function_t = decltype(&F::fin); + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; + + template + struct is_scalar_udf : polyfill::bool_constant> {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v< + F, + polyfill::void_t, + aggregate_fin_function_t, + std::enable_if_t>::value>, + std::enable_if_t>::value>>> = + true; + + template + struct is_aggregate_udf : polyfill::bool_constant> {}; + + template + struct function; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). + */ + template + concept orm_function_sig = std::is_function_v; + + /** @short Specifies that a type is a classic function object. + * + * A classic function object meets the following requirements: + * - defines a single call operator `F::operator()` + * - isn't a traditional sqlite_orm scalar function (having a static `F::name()` function + */ + template + concept orm_classic_function_object = + ((!requires { typename F::is_transparent; }) && (requires { &F::operator(); }) && + /*rule out sqlite_orm scalar function*/ + (!requires { F::name(); })); + + /** @short Specifies that a type is a user-defined scalar function. + * + * `UDF` must meet the following requirements: + * - `UDF::name()` static function + * - `UDF::operator()()` call operator + */ + template + concept orm_scalar_udf = requires { + UDF::name(); + typename internal::scalar_call_function_t; + }; + + /** @short Specifies that a type is a user-defined aggregate function. + * + * `UDF` must meet the following requirements: + * - `UDF::name()` static function + * - `UDF::step()` member function + * - `UDF::fin()` member function + */ + template + concept orm_aggregate_udf = requires { + UDF::name(); + typename internal::aggregate_step_function_t; + typename internal::aggregate_fin_function_t; + requires std::is_member_function_pointer_v>; + requires std::is_member_function_pointer_v>; + }; + + /** @short Specifies that a type is a framed user-defined scalar function. + */ + template + concept orm_scalar_function = (polyfill::is_specialization_of_v, internal::function> && + orm_scalar_udf); + + /** @short Specifies that a type is a framed user-defined aggregate function. + */ + template + concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && + orm_aggregate_udf); + + /** @short Specifies that a type is a framed and quoted user-defined scalar function. + */ + template + concept orm_quoted_scalar_function = requires(const Q& quotedF) { + quotedF.name(); + quotedF.callable(); + }; +#endif + + namespace internal { + template + struct callable_arguments_impl; + + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; + + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires (std::is_function_v) + struct callable_arguments_impl { + using args_tuple = function_arguments; + using return_type = std::decay_t>; + }; +#endif + + template + struct callable_arguments : callable_arguments_impl {}; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Bundle of type and name of a quoted user-defined function. + */ + template + struct udf_holder : private std::string { + using udf_type = UDF; + + using std::string::basic_string; + + const std::string& operator()() const { + return *this; + } + }; +#endif + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Bundle of type and name of a traditional sqlite_orm user-defined function. + */ + template + requires (requires { UDF::name(); }) + struct udf_holder +#else + /* + * Bundle of type and name of a traditional sqlite_orm user-defined function. + */ + template + struct udf_holder +#endif + { + using udf_type = UDF; + + template>::value, bool> = true> + decltype(auto) operator()() const { + return UDF::name(); + } + + template::value, bool> = true> + std::string operator()() const { + return std::string{UDF::name()}; + } + }; + + /* + * Represents a call of a user-defined function. + */ + template + struct function_call { + using udf_type = UDF; + using args_tuple = std::tuple; + + udf_holder name; + args_tuple callArgs; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; + + template + struct unpacked_arg { + using type = T; + }; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; + }; + template + using unpacked_arg_t = typename unpacked_arg::type; + + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; + } + + template + constexpr bool is_same_pvt_v = expected_pointer_value(); + + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v> = true; + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v, pointer_binding, void> = true; + + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_data_type() { + constexpr bool valid = std::is_convertible::value; + static_assert(valid, "Pointer data types of I-th argument do not match"); + return valid; + } + +#if __cplusplus >= 201703L // C++17 or later + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer types (tags) of I-th argument do not match"); + return valid; + } + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag() && + assert_same_pointer_data_type(); +#else + template + constexpr bool assert_same_pointer_tag() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer types (tags) of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag(); +#endif + + // not a pointer value, currently leave it unchecked + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } + + // check the type of pointer values + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + return true; + } + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + using func_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>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); + + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); +#endif + } + + /* + * Note: Currently the number of call arguments is checked and whether the types of pointer values match, + * 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; + constexpr size_t functionParamsCount = std::tuple_size::value; + static_assert(std::is_same>::value || + (callArgsCount == functionParamsCount && + validate_pointer_value_types( + polyfill::index_constant{})), + "Check the number and types of the function call arguments"); + } + + /* + * Generator of a user-defined function call in a sql query expression. + * + * Use the variable template `func<>` to instantiate. + * + * Calling the function captures the parameters in a `function_call` node. + */ + template + struct function { + using udf_type = UDF; + using callable_type = UDF; + + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } + + constexpr auto udf_holder() const { + return internal::udf_holder{}; + } + + // returns a character range + constexpr auto name() const { + return this->udf_holder()(); + } + }; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Generator of a user-defined function call in a sql query expression. + * + * Use the string literal operator template `""_scalar.quote()` to quote + * a freestanding function, stateless lambda or function object. + * + * Calling the function captures the parameters in a `function_call` node. + * + * Internal note: + * 1. Captures and represents a function [pointer or object], especially one without side effects. + * If `F` is a stateless function object, `quoted_scalar_function::callable()` returns the original function object, + * otherwise it is assumed to have possibe side-effects and `quoted_scalar_function::callable()` returns a copy. + * 2. The nested `udf_type` typename is deliberately chosen to be the function signature, + * and will be the abstracted version of the user-defined function. + */ + template + struct quoted_scalar_function { + using udf_type = Sig; + using callable_type = F; + + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } + + /* + * Return original `udf` if stateless or a copy of it otherwise + */ + constexpr decltype(auto) callable() const { + if constexpr (stateless) { + return (this->udf); + } else { + // non-const copy + return F(this->udf); + } + } + + constexpr auto udf_holder() const { + return internal::udf_holder{this->name()}; + } + + constexpr auto name() const { + return this->nme; + } + + template + consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : + udf(std::forward(constructorArgs)...) { + std::copy_n(name, N, this->nme); + } + + F udf; + char nme[N]; + }; + + template + struct quoted_function_builder : cstring_literal { + using cstring_literal::cstring_literal; + + /* + * From a freestanding function, possibly overloaded. + */ + template + [[nodiscard]] consteval auto quote(F* callable) const { + return quoted_scalar_function{this->cstr, std::move(callable)}; + } + + /* + * From a classic function object instance. + */ + template + requires (orm_classic_function_object && (stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(F callable) const { + using Sig = function_signature_type_t; + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::move(callable)}; + } + + /* + * From a function object instance, picking the overloaded call operator. + */ + template + requires ((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(F callable) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::move(callable)}; + } + + /* + * From a classic function object type. + */ + template + requires (stateless || std::copy_constructible) + [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { + using Sig = function_signature_type_t; + return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + } + + /* + * From a function object type, picking the overloaded call operator. + */ + template + requires ((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + } + }; +#endif + } + + /** @short Call a user-defined function. + * + * Note: Currently the number of call arguments is checked and whether the types of pointer values match, + * but other call argument types are not checked against the parameter types of the function. + * + * Example: + * struct IdFunc { int oeprator(int arg)() const { return arg; } }; + * // inline: + * select(func(42)); + * // As this is a variable template, you can frame the user-defined function and define a variable for syntactic sugar and legibility: + * inline constexpr orm_scalar_function auto idfunc = func; + * select(idfunc(42)); + * + */ + template +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + requires (orm_scalar_udf || orm_aggregate_udf) +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::function func{}; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline namespace literals { + /* @short Create a scalar function from a freestanding function, stateless lambda or function object, + * and call such a user-defined function. + * + * If you need to pick a function or method from an overload set, or pick a template function you can + * specify an explicit function signature in the call to `from()`. + * + * Examples: + * // freestanding function from a library + * constexpr orm_quoted_scalar_function auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp); + * // stateless lambda + * constexpr orm_quoted_scalar_function auto is_fatal_error_f = "IS_FATAL_ERROR"_scalar.quote([](unsigned long errcode) { + * return errcode != 0; + * }); + * // function object instance + * constexpr orm_quoted_scalar_function auto equal_to_int_f = "equal_to"_scalar.quote(std::equal_to{}); + * // function object + * constexpr orm_quoted_scalar_function auto equal_to_int_2_f = "equal_to"_scalar.quote>(); + * // pick function object's template call operator + * constexpr orm_quoted_scalar_function auto equal_to_int_3_f = "equal_to"_scalar.quote(std::equal_to{}); + * + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * + * auto rows = storage.select(clamp_int_f(0, 1, 1)); + * auto rows = storage.select(is_fatal_error_f(1)); + * auto rows = storage.select(equal_to_int_f(1, 1)); + * auto rows = storage.select(equal_to_int_2_f(1, 1)); + * auto rows = storage.select(equal_to_int_3_f(1, 1)); + */ + template + [[nodiscard]] consteval auto operator"" _scalar() { + return builder; + } + } +#endif +} + +// #include "ast/special_keywords.h" + +namespace sqlite_orm { + namespace internal { + struct current_time_t {}; + struct current_date_t {}; + struct current_timestamp_t {}; + } + + inline internal::current_time_t current_time() { + return {}; + } + + inline internal::current_date_t current_date() { + return {}; + } + + inline internal::current_timestamp_t current_timestamp() { + return {}; + } +} + +namespace sqlite_orm { + + namespace internal { + + /** + * Obtains the result type of expressions that form the columns of a select statement. + * + * This is a proxy class used to define what type must have result type depending on select + * arguments (member pointer, aggregate functions, etc). Below you can see specializations + * for different types. E.g. specialization for internal::length_t has `type` int cause + * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals + * c++ SELECT return type for T + * DBOs - db_objects_tuple type + * T - C++ type + * SFINAE - sfinae argument + */ + template + struct column_result_t { +#ifdef __FUNCTION__ + // produce an error message that reveals `T` and `DBOs` + static constexpr bool reveal() { + static_assert(polyfill::always_false_v, "T not found in DBOs - " __FUNCTION__); + } + static constexpr bool trigger = reveal(); +#endif + }; + + template + using column_result_of_t = typename column_result_t::type; + + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; + }; + + template + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t> : member_field_type {}; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; + + template + struct column_result_t, S, X, Rest...>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = nullptr_t; + }; + + template + struct column_result_t { + using type = int; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> { + using type = std::string; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + struct column_result_t>, void> { + using table_type = storage_pick_table_t; + using cte_mapper_type = cte_mapper_type_t; + + // lookup ColAlias in the final column references + using colalias_index = + find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, + "No such column mapped into the CTE."); + using type = std::tuple_element_t; + }; +#endif + + template + struct column_result_t, void> + : conc_tuple>>...> {}; + + template + struct column_result_t, void> { + using type = structure>>...>>; + }; + + template + struct column_result_t> : column_result_t {}; + + template + struct column_result_t> { + using type = + polyfill::detected_t>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); + }; + + template + struct column_result_t> { + using type = typename T::result_type; + }; + + template + struct column_result_t, void> { + using type = std::string; + }; + + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t> { + using type = T; + }; + + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t, void> : column_result_t> {}; + + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; + + template + struct column_result_t, void> { + using type = table_reference; + }; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> : column_result_t {}; + } +} + +// #include "mapped_type_proxy.h" + +// #include "sync_schema_result.h" + +#include + +namespace sqlite_orm { + + enum class sync_schema_result { + + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, + + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, + + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, + + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, + + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, + + /** + * old table is dropped and new is recreated. Reasons : + * 1. delete excess columns in the table than storage if preseve = false + * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint + * 3. Reasons 1 and 2 both together + * 4. data_type mismatch between table and storage. + */ + dropped_and_recreated, + }; + + inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { + switch (value) { + case sync_schema_result::new_table_created: + return os << "new table created"; + case sync_schema_result::already_in_sync: + return os << "table and storage is already in sync."; + case sync_schema_result::old_columns_removed: + return os << "old excess columns removed"; + case sync_schema_result::new_columns_added: + return os << "new columns added"; + case sync_schema_result::new_columns_added_and_old_columns_removed: + return os << "old excess columns removed and new columns added"; + case sync_schema_result::dropped_and_recreated: + return os << "old table dropped and recreated"; + } + return os; + } +} + +// #include "table_info.h" + +#include // std::string + +namespace sqlite_orm { + + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), + dflt_value(std::move(dflt_value_)), pk(pk_) {} +#endif + }; + + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_xinfo(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_, + decltype(hidden) hidden_) : + cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), + dflt_value(std::move(dflt_value_)), pk(pk_), hidden{hidden_} {} +#endif + }; +} + +// #include "storage_impl.h" + +#include // std::string + +// #include "functional/static_magic.h" + +// #include "functional/index_sequence_util.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "select_constraints.h" + +// #include "cte_types.h" + +// #include "schema/column.h" + +// #include "schema/table.h" + +#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 + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/static_magic.h" + +// #include "../functional/mpl.h" + +// #include "../functional/index_sequence_util.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../tuple_helper/tuple_iteration.h" + +// #include "../tuple_helper/tuple_transformer.h" + +// #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 "../type_traits.h" + +// #include "../alias_traits.h" + +// #include "../constraints.h" + +// #include "../table_info.h" + +// #include "index.h" + +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../indexed_column.h" + +#include // std::string +#include // std::move + +// #include "ast/where.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} +#endif + + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } + + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } + + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; + + template + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } + + template + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } + + template + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); + } + } + + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) + */ + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } +} + +// #include "../table_type_of.h" + +namespace sqlite_orm { + + namespace internal { + + struct index_base { + std::string name; + bool unique = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} +#endif + }; + + template + struct index_t : index_base { + using elements_type = std::tuple; + using object_type = void; + using table_mapped_type = T; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + index_t(std::string name_, bool unique_, elements_type elements_) : + index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} +#endif + + elements_type elements; + }; + } + + template + internal::index_t()))...> make_index(std::string name, + Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_unique_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } +} + +// #include "column.h" + +namespace sqlite_orm { + + namespace internal { + + template + using is_table_element_or_constraint = mpl::invoke_t, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * A subselect mapper's CTE moniker, void otherwise. + */ + template + using moniker_of_or_void_t = polyfill::detected_or_t; + + /** + * If O is a subselect_mapper then returns its nested type name O::cte_moniker_type, + * otherwise O itself is a regular object type to be mapped. + */ + template + using mapped_object_type_for_t = polyfill::detected_or_t; +#endif + + struct basic_table { + + /** + * Table name. + */ + std::string name; + }; + + /** + * Table definition. + */ + template + struct table_t : basic_table { +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + // this typename is used in contexts where it is known that the 'table' holds a subselect_mapper + // instead of a regular object type + using cte_mapper_type = O; + using cte_moniker_type = moniker_of_or_void_t; + using object_type = mapped_object_type_for_t; +#else + using object_type = O; +#endif + using elements_type = std::tuple; + + static constexpr bool is_without_rowid_v = WithoutRowId; + + using is_without_rowid = polyfill::bool_constant; + + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} +#endif + + table_t without_rowid() const { + return {this->name, this->elements}; + } + + /* + * Returns the number of elements of the specified type. + */ + template class Trait> + static constexpr int count_of() { + using sequence_of = filter_tuple_sequence_t; + return int(sequence_of::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_with() { + using filtered_index_sequence = col_index_sequence_with; + return int(filtered_index_sequence::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_excluding() { + using excluded_col_index_sequence = col_index_sequence_excluding; + return int(excluded_col_index_sequence::size()); + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if (compare_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } + + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with{}, + [&result, &name](auto& column) { + if (column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t, + is_generated_always>; + constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); + result = &std::get(column.constraints).storage; + }); +#else + (void)name; +#endif + return result; + } + + /** + * Call passed lambda with all defined primary keys. + */ + template + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); + }); + return res; + } + + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + if (pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } + + template + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with{}, + call_as_template_base([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); + }); + } + + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if (const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + + /** + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. + */ + template = true> + const std::string* find_column_name(M m) const { + const std::string* res = nullptr; + using field_type = member_field_type_t; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, m](auto& c) { + if (compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + res = &c.name; + } + }); + return res; + } + + /** + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_foreign_key(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, fk_index_sequence{}, lambda); + } + + template + void for_each_foreign_key_to(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + iterate_tuple(this->elements, filtered_index_sequence{}, lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, col_index_sequence{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->template for_each_column_excluding(lambda); + } + + std::vector get_table_info() const; + }; + + template + struct is_table : std::false_type {}; + + template + struct is_table> : std::true_type {}; + + template + struct virtual_table_t : basic_table { + using module_details_type = M; + using object_type = typename module_details_type::object_type; + using elements_type = typename module_details_type::columns_type; + + static constexpr bool is_without_rowid_v = false; + using is_without_rowid = polyfill::bool_constant; + + 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. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + this->module_details.template for_each_column_excluding(lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->module_details.template for_each_column_excluding(lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + this->module_details.for_each_column(lambda); + } + }; + + template + struct is_virtual_table : std::false_type {}; + + template + struct is_virtual_table> : std::true_type {}; + +#if SQLITE_VERSION_NUMBER >= 3009000 + template + struct using_fts5_t { + using object_type = T; + using columns_type = std::tuple; + + columns_type columns; + + using_fts5_t(columns_type columns) : columns(std::move(columns)) {} + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->columns, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->template for_each_column_excluding(lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->columns, col_index_sequence{}, lambda); + } + }; +#endif + + template + bool exists_in_composite_primary_key(const table_t& table, + const column_field& column) { + bool res = false; + table.for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if (compare_any(memberPointer, column.member_pointer) || + compare_any(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; + } + + template + bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, + const column_field& /*column*/) { + return false; + } + } + +#if SQLITE_VERSION_NUMBER >= 3009000 + template>::object_type> + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } + + template + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } +#endif + + /** + * Factory function for a table definition. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::table_t make_table(std::string name, Cs... args) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + internal::table_t make_table(std::string name, Cs... args) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + auto make_table(std::string name, Cs... args) { + return make_table>(std::move(name), std::forward(args)...); + } +#endif + + template + internal::virtual_table_t make_virtual_table(std::string name, M module_details) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); + } +} + +// #include "storage_lookup.h" + +// interface functions +namespace sqlite_orm { + namespace internal { + + template + using tables_index_sequence = filter_tuple_sequence_t; + + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.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); + } + + /** + * Find column name by its type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, F O::* field) { + return pick_table(dbObjects).find_column_name(field); + } + + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { + return cp.field; + } + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Materialize column pointer: + * 3. by moniker and alias_holder<>. + * + * internal note: there's an overload for `find_column_name()` that avoids going through `table_t<>::find_column_name()` + */ + template = true> + 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; + + // lookup ColAlias in the final column references + 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; + } +#endif + + /** + * Find column name by: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Find column name by: + * 3. by moniker and alias_holder<>. + */ + template = true> + 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 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, + "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; + // however we have the column index already. + // lookup column in table_t<>'s elements + constexpr size_t ColIdx = index_sequence_value_at(column_index_sequence{}); + auto& table = pick_table(dboObjects); + return &std::get(table.elements).name; + } +#endif + } +} + +// #include "journal_mode.h" + +#include // std::array +#include // std::string +#include // std::pair +#include // std::ranges::transform +#include // std::toupper + +// #include "serialize_result_type.h" + +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE +#endif + +namespace sqlite_orm { + + /** + * Caps case because of: + * 1) delete keyword; + * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ + enum class journal_mode : signed char { + DELETE = 0, + // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. + DELETE_ = DELETE, + TRUNCATE = 1, + PERSIST = 2, + MEMORY = 3, + WAL = 4, + OFF = 5, + }; + + namespace internal { + + inline const serialize_result_type& journal_mode_to_string(journal_mode value) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + static constexpr std::array idx2str = { +#else + static const std::array idx2str = { +#endif + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return idx2str.at(static_cast(value)); + } + + inline std::pair journal_mode_from_string(std::string string) { + static constexpr std::array journalModes = {{ + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }}; +#if __cpp_lib_ranges >= 201911L + std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { + return std::toupper(c); + }); + if (auto found = std::ranges::find(journalModes, string, journal_mode_to_string); + found != journalModes.end()) SQLITE_ORM_CPP_LIKELY { + return {true, *found}; + } +#else + std::transform(string.begin(), string.end(), string.begin(), [](unsigned char c) noexcept { + return std::toupper(c); + }); + for (auto journalMode: journalModes) { + if (journal_mode_to_string(journalMode) == string) { + return {true, journalMode}; + } + } +#endif + return {false, journal_mode::OFF}; + } + } +} + +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif + +// #include "mapped_view.h" + +#include +#include // std::forward, std::move + +// #include "row_extractor.h" + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // atof, atoi, atoll +#include // strlen +#include // std::system_error +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +#include // std::vector +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include +#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" + +// #include "pointer_value.h" + +// #include "journal_mode.h" + +// #include "locking_mode.h" + +#include // std::array +#include // std::string +#include // std::pair +#include // std::ranges::transform +#include // std::toupper + +// #include "serialize_result_type.h" + +namespace sqlite_orm { + enum class locking_mode : signed char { + NORMAL = 0, + EXCLUSIVE = 1, + }; + + namespace internal { + inline const serialize_result_type& locking_mode_to_string(locking_mode value) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + static constexpr std::array idx2str = { +#else + static const std::array idx2str = { +#endif + "NORMAL", + "EXCLUSIVE", + }; + return idx2str.at(static_cast(value)); + } + + inline std::pair locking_mode_from_string(std::string string) { + static constexpr std::array lockingModes = {{ + locking_mode::NORMAL, + locking_mode::EXCLUSIVE, + }}; + +#if __cpp_lib_ranges >= 201911L + std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { + return std::toupper(c); + }); + if (auto found = std::ranges::find(lockingModes, string, locking_mode_to_string); + found != lockingModes.end()) SQLITE_ORM_CPP_LIKELY { + return {true, *found}; + } +#else + std::transform(string.begin(), string.end(), string.begin(), [](unsigned char c) noexcept { + return std::toupper(c); + }); + for (auto lockingMode: lockingModes) { + if (locking_mode_to_string(lockingMode) == string) { + return {true, lockingMode}; + } + } +#endif + return {false, locking_mode::NORMAL}; + } + } +} +// #include "error_code.h" + +// #include "is_std_ptr.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + + /** + * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. + * + * sqlite_orm provides specializations for known C++ types, users may define their custom specialization + * of this helper. + * + * @note (internal): Since row extractors are used in certain contexts with only one purpose at a time + * (e.g., converting a row result set but not function values or column text), + * there are factory functions that perform conceptual checking that should be used + * instead of directly creating row extractors. + * + * + */ + template + struct row_extractor { + /* + * Called during one-step query execution (one result row) for each column of a result row. + */ + V extract(const char* columnText) const = delete; + + /* + * Called during multi-step query execution (result set) for each column of a result row. + */ + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + + /* + * Called before invocation of user-defined scalar or aggregate functions, + * in order to unbox dynamically typed SQL function values into a tuple of C++ function arguments. + */ + V extract(sqlite3_value* value) const = delete; + }; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept orm_column_text_extractable = requires(const row_extractor& extractor, const char* columnText) { + { extractor.extract(columnText) } -> std::same_as; + }; + + template + concept orm_row_value_extractable = + requires(const row_extractor& extractor, sqlite3_stmt* stmt, int columnIndex) { + { extractor.extract(stmt, columnIndex) } -> std::same_as; + }; + + template + concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { + { extractor.extract(value) } -> std::same_as; + }; +#endif + + namespace internal { + /* + * Make a row extractor to be used for casting SQL column text to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_column_text_extractable) +#endif + row_extractor column_text_extractor() { + return {}; + } + + /* + * Make a row extractor to be used for converting a value from a SQL result row set to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_row_value_extractable) +#endif + row_extractor row_value_extractor() { + return {}; + } + + /* + * Make a row extractor to be used for unboxing a dynamically typed SQL value to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_boxed_value_extractable) +#endif + row_extractor boxed_value_extractor() { + return {}; + } + } + + template + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if (argc) { + const auto rowExtractor = internal::column_text_extractor(); + res = rowExtractor.extract(argv[0]); + } + return 0; + } + +#if SQLITE_VERSION_NUMBER >= 3020000 + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template + struct row_extractor, void> { + using V = pointer_arg; + + V extract(const char* columnText) const = delete; + + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; + + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; +#endif + + /** + * Specialization for arithmetic types. + */ + template + struct row_extractor::value>> { + V extract(const char* columnText) const { + return this->extract(columnText, tag()); + } + + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); + } + + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); + } + + private: + using tag = arithmetic_tag_t; + + V extract(const char* columnText, const int_or_smaller_tag&) const { + return static_cast(atoi(columnText)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } + + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } + + V extract(const char* columnText, const bigint_tag&) const { + return static_cast(atoll(columnText)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } + + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } + + V extract(const char* columnText, const real_tag&) const { + return static_cast(atof(columnText)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { + return static_cast(sqlite3_column_double(stmt, columnIndex)); + } + + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } + }; + + /** + * Specialization for std::string. + */ + template + struct row_extractor::value>> { + T extract(const char* columnText) const { + if (columnText) { + return columnText; + } else { + return {}; + } + } + + T extract(sqlite3_stmt* stmt, int columnIndex) const { + if (auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { + return cStr; + } else { + return {}; + } + } + + T extract(sqlite3_value* value) const { + if (auto cStr = (const char*)sqlite3_value_text(value)) { + return cStr; + } else { + return {}; + } + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring. + */ + template<> + struct row_extractor { + std::wstring extract(const char* columnText) const { + if (columnText) { + std::wstring_convert> converter; + return converter.from_bytes(columnText); + } else { + return {}; + } + } + + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + if (cStr) { + std::wstring_convert> converter; + return converter.from_bytes(cStr); + } else { + return {}; + } + } + + std::wstring extract(sqlite3_value* value) const { + if (auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* columnText) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_column_text_extractable) +#endif + { + if (columnText) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(columnText)); + } else { + return {}; + } + } + + V extract(sqlite3_stmt* stmt, int columnIndex) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_row_value_extractable) +#endif + { + auto type = sqlite3_column_type(stmt, columnIndex); + if (type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(stmt, columnIndex)); + } else { + return {}; + } + } + + V extract(sqlite3_value* value) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_boxed_value_extractable) +#endif + { + auto type = sqlite3_value_type(value); + if (type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(value)); + } else { + return {}; + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* columnText) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_column_text_extractable) +#endif + { + if (columnText) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(columnText)); + } else { + return std::nullopt; + } + } + + V extract(sqlite3_stmt* stmt, int columnIndex) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_row_value_extractable) +#endif + { + auto type = sqlite3_column_type(stmt, columnIndex); + if (type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(stmt, columnIndex)); + } else { + return std::nullopt; + } + } + + V extract(sqlite3_value* value) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (orm_boxed_value_extractable) +#endif + { + auto type = sqlite3_value_type(value); + if (type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(value)); + } else { + return std::nullopt; + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template<> + struct row_extractor { + nullptr_t extract(const char* /*columnText*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor, void> { + std::vector extract(const char* columnText) const { + return {columnText, columnText + (columnText ? strlen(columnText) : 0)}; + } + + std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { + auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); + auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); + return {bytes, bytes + len}; + } + + std::vector extract(sqlite3_value* value) const { + auto bytes = static_cast(sqlite3_value_blob(value)); + auto len = static_cast(sqlite3_value_bytes(value)); + return {bytes, bytes + len}; + } + }; + + /** + * Specialization for locking_mode. + */ + template<> + struct row_extractor { + locking_mode extract(const char* columnText) const { + if (columnText) { + auto resultPair = internal::locking_mode_from_string(columnText); + if (resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } + + locking_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + locking_mode extract(sqlite3_value* value) const = delete; + }; + + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + journal_mode extract(const char* columnText) const { + if (columnText) { + auto resultPair = internal::journal_mode_from_string(columnText); + if (resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } + + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + journal_mode extract(sqlite3_value* value) const = delete; + }; + + namespace internal { + + /* + * Helper to extract a structure from a rowset. + */ + 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. + */ + template + auto make_row_extractor([[maybe_unused]] const DBOs& dbObjects) { + if constexpr (polyfill::is_specialization_of_v || + polyfill::is_specialization_of_v || is_table_reference_v) { + return struct_extractor{dbObjects}; + } else { + 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. + */ + template + struct struct_extractor, DBOs> { + const DBOs& db_objects; + + std::tuple extract(const char* columnText) const = delete; + + // note: expects to be called only from the top level, and therefore discards the index + std::tuple...> extract(sqlite3_stmt* stmt, + int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return {make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } + + // unused to date + std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; + + std::tuple extract(sqlite3_value* value) const = delete; + }; + + /** + * Specialization for an unmapped structure to be constructed ad-hoc from column results. + * + * This plays together with `column_result_of_t`, which returns `struct_t` as `structure` + */ + template + struct struct_extractor>, DBOs> { + const DBOs& db_objects; + + O extract(const char* columnText) const = delete; + + // note: expects to be called only from the top level, and therefore discards the index; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } + + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } + + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } + + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } + + O extract(sqlite3_value* value) const = delete; + }; + } +} + +// #include "mapped_iterator.h" + +#include +#include // std::shared_ptr, std::make_shared +#include // std::move +#include // std::input_iterator_tag +#include // std::system_error +#include // std::bind + +// #include "statement_finalizer.h" + +#include +#include // std::unique_ptr +#include // std::integral_constant + +namespace sqlite_orm { + + /** + * Guard class which finalizes `sqlite3_stmt` in dtor + */ + using statement_finalizer = + std::unique_ptr>; +} + +// #include "error_code.h" + +// #include "object_from_column_builder.h" + +#include +#include // std::is_member_object_pointer +#include // std::move + +// #include "functional/static_magic.h" + +// #include "member_traits/member_traits.h" + +// #include "table_reference.h" + +// #include "row_extractor.h" + +// #include "schema/column.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + 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 + }; + + /** + * Function object for building an object from a result row. + */ + template + struct object_from_column_builder : object_from_column_builder_base { + using object_type = O; + + object_type& object; + + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_, int nextColumnIndex = 0) : + object_from_column_builder_base{stmt_, nextColumnIndex - 1}, object(object_) {} + + template + 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); + } + }; + + /** + * Specialization for a table reference. + * + * This plays together with `column_result_of_t`, which returns `object_t` as `table_referenece` + */ + template + struct struct_extractor, DBOs> { + const DBOs& db_objects; + + O extract(const char* columnText) const = delete; + + // note: expects to be called only from the top level, and therefore discards the index + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = 0; + return this->extract(stmt, columnIndex); + } + + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + O obj; + object_from_column_builder builder{obj, stmt, columnIndex}; + auto& table = pick_table(this->db_objects); + table.for_each_column(builder); + columnIndex = builder.columnIndex; + return obj; + } + + O extract(sqlite3_value* value) const = delete; + }; + } +} + +// #include "storage_lookup.h" + +// #include "util.h" + +#include +#include // std::string +#include // std::move + +// #include "error_code.h" + +namespace sqlite_orm { + + /** + * Escape the provided character in the given string by doubling it. + * @param str A copy of the original string + * @param char2Escape The character to escape + */ + inline std::string sql_escape(std::string str, char char2Escape) { + for (size_t pos = 0; (pos = str.find(char2Escape, pos)) != str.npos; pos += 2) { + str.replace(pos, 1, 2, char2Escape); + } + + return str; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_string_literal(std::string v) { + constexpr char quoteChar = '\''; + return quoteChar + sql_escape(std::move(v), quoteChar) + quoteChar; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_blob_literal(std::string v) { + constexpr char quoteChar = '\''; + return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + } + + /** + * Quote the given identifier using double quotes, + * escape containing double quotes by doubling them. + */ + inline std::string quote_identifier(std::string identifier) { + constexpr char quoteChar = '"'; + return quoteChar + sql_escape(std::move(identifier), quoteChar) + quoteChar; + } + + namespace internal { + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + // note: query is deliberately taken by value, such that it is thrown away early + inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + sqlite3_stmt* stmt; + if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + return stmt; + } + + inline void perform_void_exec(sqlite3* db, const std::string& query) { + int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const char* query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + int rc = sqlite3_exec(db, query, callback, user_data, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + return perform_exec(db, query.c_str(), callback, user_data); + } + + template + void perform_step(sqlite3_stmt* stmt) { + int rc = sqlite3_step(stmt); + if (rc != expected) { + throw_translated_sqlite_error(stmt); + } + } + + template + void perform_step(sqlite3_stmt* stmt, L&& lambda) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + int rc; + do { + switch (rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while (rc != SQLITE_DONE); + } + } +} + +namespace sqlite_orm { + namespace internal { + + /* + * (Legacy) Input iterator over a result set for a mapped object. + */ + template + class mapped_iterator { + public: + using db_objects_type = DBOs; + + using iterator_category = std::input_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = O; + using reference = O&; + using pointer = O*; + + private: + /** + pointer to the db objects. + only null for the default constructed iterator. + */ + const db_objects_type* db_objects = nullptr; + + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr stmt; + + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr current; + + void extract_object() { + this->current = std::make_shared(); + object_from_column_builder builder{*this->current, this->stmt.get()}; + auto& table = pick_table(*this->db_objects); + table.for_each_column(builder); + } + + void step() { + perform_step(this->stmt.get(), std::bind(&mapped_iterator::extract_object, this)); + if (!this->current) { + this->stmt.reset(); + } + } + + void next() { + this->current.reset(); + this->step(); + } + + public: + mapped_iterator() = default; + + mapped_iterator(const db_objects_type& dbObjects, statement_finalizer stmt) : + db_objects{&dbObjects}, stmt{std::move(stmt)} { + this->step(); + } + + mapped_iterator(const mapped_iterator&) = default; + mapped_iterator& operator=(const mapped_iterator&) = default; + mapped_iterator(mapped_iterator&&) = default; + mapped_iterator& operator=(mapped_iterator&&) = default; + + value_type& operator*() const { + if (!this->stmt) SQLITE_ORM_CPP_UNLIKELY { + throw std::system_error{orm_error_code::trying_to_dereference_null_iterator}; + } + return *this->current; + } + + // note: should actually be only present for contiguous iterators + value_type* operator->() const { + return &(this->operator*()); + } + + mapped_iterator& operator++() { + next(); + return *this; + } + + mapped_iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(const mapped_iterator& lhs, const mapped_iterator& rhs) { + return lhs.current == rhs.current; + } + +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator!=(const mapped_iterator& lhs, const mapped_iterator& rhs) { + return !(lhs == rhs); + } +#endif + }; + } +} + +// #include "ast_iterator.h" + +#include // std::vector +#include // std::reference_wrapper + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "conditions.h" + +// #include "alias.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "core_functions.h" + +// #include "prepared_statement.h" + +#include +#include // std::unique_ptr +#include // std::iterator_traits +#include // std::string +#include // std::integral_constant, std::declval +#include // std::move, std::forward, std::pair +#include // std::tuple + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "connection_holder.h" + +#include +#include +#include // std::string + +// #include "error_code.h" + +namespace sqlite_orm { + + namespace internal { + + struct connection_holder { + + connection_holder(std::string filename_) : filename(std::move(filename_)) {} + + void retain() { + if (1 == ++this->_retain_count) { + auto rc = sqlite3_open(this->filename.c_str(), &this->db); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + void release() { + if (0 == --this->_retain_count) { + auto rc = sqlite3_close(this->db); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + sqlite3* get() const { + return this->db; + } + + int retain_count() const { + return this->_retain_count; + } + + const std::string filename; + + protected: + sqlite3* db = nullptr; + std::atomic_int _retain_count{}; + }; + + struct connection_ref { + connection_ref(connection_holder& holder) : holder(&holder) { + this->holder->retain(); + } + + connection_ref(const connection_ref& other) : holder(other.holder) { + this->holder->retain(); + } + + // rebind connection reference + connection_ref& operator=(const connection_ref& other) { + if (other.holder != this->holder) { + this->holder->release(); + this->holder = other.holder; + this->holder->retain(); + } + + return *this; + } + + ~connection_ref() { + this->holder->release(); + } + + sqlite3* get() const { + return this->holder->get(); + } + + private: + connection_holder* holder = nullptr; + }; + } +} + +// #include "select_constraints.h" + +// #include "values.h" + +#include // std::vector +#include // std::tuple +#include // std::forward, std::move + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + using args_tuple = std::tuple; + + args_tuple tuple; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of::value; + + template + using is_values = polyfill::bool_constant>; + + template + struct dynamic_values_t { + std::vector vector; + }; + + } + + template + internal::values_t values(Args... args) { + return {{std::forward(args)...}}; + } + + template + internal::dynamic_values_t values(std::vector vector) { + return {{std::move(vector)}}; + } +} + +// #include "table_reference.h" + +// #include "mapped_type_proxy.h" + +// #include "ast/upsert_clause.h" + +#if SQLITE_VERSION_NUMBER >= 3024000 +#include // std::tuple +#include // std::forward, std::move +#endif + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { +#if SQLITE_VERSION_NUMBER >= 3024000 + template + struct upsert_clause; + + template + struct conflict_target { + using args_tuple = std::tuple; + + args_tuple args; + + upsert_clause> do_nothing() { + return {std::move(this->args), {}}; + } + + template + upsert_clause> do_update(ActionsArgs... actions) { + return {std::move(this->args), {std::forward(actions)...}}; + } + }; + + template + struct upsert_clause, std::tuple> { + using target_args_tuple = std::tuple; + using actions_tuple = std::tuple; + + target_args_tuple target_args; + + actions_tuple actions; + }; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_upsert_clause_v = +#if SQLITE_VERSION_NUMBER >= 3024000 + polyfill::is_specialization_of::value; +#else + false; +#endif + + template + using is_upsert_clause = polyfill::bool_constant>; + } + +#if SQLITE_VERSION_NUMBER >= 3024000 + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template + internal::conflict_target on_conflict(Args... args) { + return {{std::forward(args)...}}; + } +#endif +} + +// #include "ast/set.h" + +#include // std::tuple, std::tuple_size +#include // std::string +#include // std::vector +#include // std::stringstream +#include // std::false_type, std::true_type + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../table_name_collector.h" + +#include // std::set +#include // std::string +#include // std::pair, std::move + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "mapped_type_proxy.h" + +// #include "select_constraints.h" + +// #include "alias.h" + +// #include "core_functions.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector_base { + using table_name_set = std::set>; + + table_name_set table_names; + }; + + template + struct table_name_collector : table_name_collector_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + table_name_collector() = default; + + table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + + template + void operator()(const T&) const {} + + template + void operator()(F O::*) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const column_pointer&) { + auto tableName = lookup_table_name>(this->db_objects); + this->table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const alias_column_t&) { + // note: instead of accessing the column, we are interested in the type the column is aliased into + auto tableName = lookup_table_name>(this->db_objects); + this->table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const count_asterisk_t&) { + auto tableName = lookup_table_name(this->db_objects); + if (!tableName.empty()) { + this->table_names.emplace(std::move(tableName), ""); + } + } + + template + void operator()(const asterisk_t&) { + auto tableName = lookup_table_name>(this->db_objects); + table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const object_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_oid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table__rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const highlight_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + }; + + template = true> + table_name_collector make_table_name_collector(const DBOs& dbObjects) { + return {dbObjects}; + } + + } + +} + +namespace sqlite_orm { + + namespace internal { + + template + void iterate_ast(const T& t, L&& lambda); + + template + struct set_t { + using assigns_type = std::tuple; + + assigns_type assigns; + }; + + template + struct is_set : std::false_type {}; + + template + struct is_set> : std::true_type {}; + + struct dynamic_set_entry { + std::string serialized_value; + }; + + template + struct dynamic_set_t { + using context_t = C; + using entry_t = dynamic_set_entry; + using const_iterator = typename std::vector::const_iterator; + + dynamic_set_t(const context_t& context_) : context(context_), collector(this->context.db_objects) {} + + dynamic_set_t(const dynamic_set_t& other) = default; + dynamic_set_t(dynamic_set_t&& other) = default; + dynamic_set_t& operator=(const dynamic_set_t& other) = default; + dynamic_set_t& operator=(dynamic_set_t&& other) = default; + + template + void push_back(assign_t assign) { + auto newContext = this->context; + newContext.skip_table_name = true; + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_ast(assign.lhs, this->collector); + std::stringstream ss; + ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' + << serialize(assign.rhs, context); + this->entries.push_back({ss.str()}); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + this->collector.table_names.clear(); + } + + std::vector entries; + context_t context; + table_name_collector collector; + }; + + template + struct is_set> : std::true_type {}; + + template + struct is_dynamic_set : std::false_type {}; + + template + struct is_dynamic_set> : std::true_type {}; + } + + /** + * SET keyword used in UPDATE ... SET queries. + * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) + */ + template + internal::set_t set(Args... args) { + using arg_tuple = std::tuple; + static_assert(std::tuple_size::value == + internal::count_tuple::value, + "set function accepts assign operators only"); + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * SET keyword used in UPDATE ... SET queries. It is dynamic version. It means use can add amount of arguments now known at compilation time but known at runtime. + */ + template + internal::dynamic_set_t> dynamic_set(const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); + } +} + +namespace sqlite_orm { + + namespace internal { + + struct prepared_statement_base { + sqlite3_stmt* stmt = nullptr; + connection_ref con; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +#endif + + ~prepared_statement_base() { + sqlite3_finalize(this->stmt); + } + + std::string sql() const { + // note: sqlite3 internally checks for null before calling + // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous + if (const char* sql = sqlite3_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } + +#if SQLITE_VERSION_NUMBER >= 3014000 + std::string expanded_sql() const { + // note: must check return value due to SQLITE_OMIT_TRACE + using char_ptr = std::unique_ptr>; + if (char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { + return sql.get(); + } else { + return {}; + } + } +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) + std::string normalized_sql() const { + if (const char* sql = sqlite3_normalized_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } +#endif + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view column_name(int index) const { + return sqlite3_column_name(stmt, index); + } +#endif + }; + + template + struct prepared_statement_t : prepared_statement_base { + using expression_type = T; + + expression_type expression; + + prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} + + prepared_statement_t(prepared_statement_t&& prepared_stmt) : + prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, + expression(std::move(prepared_stmt.expression)) { + prepared_stmt.stmt = nullptr; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of::value; + + template + struct is_prepared_statement : polyfill::bool_constant> {}; + + /** + * T - type of object to obtain from a database + */ + template + struct get_all_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_all_pointer_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_all_optional_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_all_t { + using set_type = S; + using conditions_type = std::tuple; + + static_assert(is_set::value, "update_all_t must have set or dynamic set as the first argument"); + + set_type set; + conditions_type conditions; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_update_all_v = polyfill::is_specialization_of::value; + + template + using is_update_all = polyfill::bool_constant>; + + template + struct remove_all_t { + using type = T; + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_remove_all_v = polyfill::is_specialization_of::value; + + template + using is_remove_all = polyfill::bool_constant>; + + template + struct get_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct get_pointer_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_optional_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_t { + using type = T; + + type object; + }; + + template + struct remove_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct insert_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of::value; + + template + struct is_insert : polyfill::bool_constant> {}; + + template + struct insert_explicit { + using type = T; + using columns_type = columns_t; + + type obj; + columns_type columns; + }; + + template + struct replace_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of::value; + + template + struct is_replace : polyfill::bool_constant> {}; + + template + struct insert_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = + polyfill::is_specialization_of::value; + + template + struct is_insert_range : polyfill::bool_constant> {}; + + template + struct replace_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = + polyfill::is_specialization_of::value; + + template + struct is_replace_range : polyfill::bool_constant> {}; + + template + struct insert_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of::value; + + template + struct is_insert_raw : polyfill::bool_constant> {}; + + template + struct replace_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of::value; + + template + struct is_replace_raw : polyfill::bool_constant> {}; + + struct default_values_t {}; + + template + using is_default_values = std::is_same; + + enum class conflict_action { + abort, + fail, + ignore, + replace, + rollback, + }; + + struct insert_constraint { + conflict_action action = conflict_action::abort; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + insert_constraint(conflict_action action) : action{action} {} +#endif + }; + + template + using is_insert_constraint = std::is_same; + } + + inline internal::insert_constraint or_rollback() { + return {internal::conflict_action::rollback}; + } + + inline internal::insert_constraint or_replace() { + return {internal::conflict_action::replace}; + } + + inline internal::insert_constraint or_ignore() { + return {internal::conflict_action::ignore}; + } + + inline internal::insert_constraint or_fail() { + return {internal::conflict_action::fail}; + } + + inline internal::insert_constraint or_abort() { + return {internal::conflict_action::abort}; + } + + /** + * Use this function to add `DEFAULT VALUES` modifier to raw `INSERT`. + * + * @example + * ``` + * storage.insert(into(), default_values()); + * ``` + */ + inline internal::default_values_t default_values() { + return {}; + } + + /** + * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), default_values())); + * storage.execute(statement)); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * auto statement = storage.prepare(insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * auto statement2 = storage.prepare(insert(or_rollback(), into(), default_values())); + * auto statement3 = storage.prepare(insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + */ + template + internal::insert_raw_t insert(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_insert_constraint; + using internal::is_into; + using internal::is_select; + using internal::is_upsert_clause; + using internal::is_values; + + constexpr int orArgsCount = count_tuple::value; + static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw insert must have into argument"); + static_assert(intoArgsCount < 2, "Raw insert must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); + + constexpr int upsertClausesCount = count_tuple::value; + static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + + selectsArgsCount + orArgsCount + upsertClausesCount, + "Raw insert has invalid arguments"); + + return {{std::forward(args)...}}; + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), default_values())); + * storage.execute(statement)); + * ``` + */ + template + internal::replace_raw_t replace(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_into; + using internal::is_values; + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw replace must have into argument"); + static_assert(intoArgsCount < 2, "Raw replace must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == + intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, + "Raw replace has invalid arguments"); + + return {{std::forward(args)...}}; + } + + /** + * Create a replace range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::replace_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create a replace range statement. + * Overload of `replace_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::replace_range_t replace_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create an insert range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::insert_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create an insert range statement. + * Overload of `insert_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::insert_range_t insert_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create a replace statement. + * T is an object type mapped to a storage. + * Usage: storage.replace(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.replace(std::ref(myUserInstance)); + */ + template + internal::replace_t replace(T obj) { + return {std::move(obj)}; + } + + /** + * Create an insert statement. + * T is an object type mapped to a storage. + * Usage: storage.insert(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance)); + */ + template + internal::insert_t insert(T obj) { + return {std::move(obj)}; + } + + /** + * Create an explicit insert statement. + * T is an object type mapped to a storage. + * Cols is columns types aparameter pack. Must contain member pointers + * Usage: storage.insert(myUserInstance, columns(&User::id, &User::name)); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); + */ + template + internal::insert_explicit insert(T obj, internal::columns_t cols) { + return {std::move(obj), std::move(cols)}; + } + + /** + * Create a remove statement + * T is an object type mapped to a storage. + * Usage: remove(5); + */ + template + internal::remove_t remove(Ids... ids) { + return {{std::forward(ids)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a remove statement + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * Usage: remove(5); + */ + template + auto remove(Ids... ids) { + return remove>(std::forward(ids)...); + } +#endif + + /** + * Create an update statement. + * T is an object type mapped to a storage. + * Usage: storage.update(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.update(std::ref(myUserInstance)); + */ + template + internal::update_t update(T obj) { + return {std::move(obj)}; + } + + /** + * Create a get statement. + * T is an object type mapped to a storage. + * Usage: get(5); + */ + template + internal::get_t get(Ids... ids) { + return {{std::forward(ids)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * Usage: get(5); + */ + template + auto get(Ids... ids) { + return get>(std::forward(ids)...); + } +#endif + + /** + * Create a get pointer statement. + * T is an object type mapped to a storage. + * Usage: get_pointer(5); + */ + template + internal::get_pointer_t get_pointer(Ids... ids) { + return {{std::forward(ids)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get pointer statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * Usage: get_pointer(5); + */ + template + auto get_pointer(Ids... ids) { + return get_pointer>(std::forward(ids)...); + } +#endif + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get optional statement. + * T is an object type mapped to a storage. + * Usage: get_optional(5); + */ + template + internal::get_optional_t get_optional(Ids... ids) { + return {{std::forward(ids)...}}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get optional statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * Usage: get_optional(5); + */ + template + auto get_optional(Ids... ids) { + return get_optional>(std::forward(ids)...); + } +#endif + + /** + * Create a remove all statement. + * T is an object type mapped to a storage. + * Usage: storage.remove_all(...); + */ + template + internal::remove_all_t remove_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(args)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a remove all statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * Usage: storage.remove_all(...); + */ + template + auto remove_all(Args... args) { + return remove_all>(std::forward(args)...); + } +#endif + + /** + * Create a get all statement. + * T is an explicitly specified object mapped to a storage or a table alias. + * R is a container type. std::vector is default + * Usage: storage.prepare(get_all(...)); + */ + template>, class... Args> + internal::get_all_t get_all(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get all statement. + * `mapped` is an explicitly specified table reference or table alias to be extracted. + * `R` is the container return type, which must have a `R::push_back(T&&)` method, and defaults to `std::vector` + * Usage: storage.get_all(...); + */ + template>, + class... Args> + auto get_all(Args&&... conditions) { + return get_all, R>(std::forward(conditions)...); + } +#endif + + /** + * Create an update all statement. + * Usage: storage.update_all(set(...), ...); + */ + template + internal::update_all_t update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, "first argument in update_all can be either set or dynamic_set"); + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(set), {std::forward(wh)...}}; + } + + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.prepare(get_all_pointer(...)); + */ + template>, class... Args> + internal::get_all_pointer_t get_all_pointer(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get all pointer statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * R is a container return type. std::vector> is default + * Usage: storage.prepare(get_all_pointer(...)); + */ + template>, + class... Args> + auto get_all_pointer(Args... conditions) { + return get_all_pointer, R>(std::forward(conditions)...); + } +#endif + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_optional(...); + */ + template>, class... Args> + internal::get_all_optional_t get_all_optional(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a get all optional statement. + * `table` is an explicitly specified table reference of a mapped object to be extracted. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_optional(...); + */ + template>, + class... Args> + auto get_all_optional(Args&&... conditions) { + return get_all_optional, R>(std::forward(conditions)...); + } +#endif +} + +// #include "values.h" + +// #include "function.h" + +// #include "ast/excluded.h" + +#include // std::move + +namespace sqlite_orm { + namespace internal { + + template + struct excluded_t { + using expression_type = T; + + expression_type expression; + }; + } + + template + internal::excluded_t excluded(T expression) { + return {std::move(expression)}; + } +} + +// #include "ast/upsert_clause.h" + +// #include "ast/where.h" + +// #include "ast/into.h" + +// #include "ast/group_by.h" + +// #include "ast/exists.h" + +#include // std::move + +// #include "../tags.h" + +namespace sqlite_orm { + namespace internal { + + template + struct exists_t : condition_t, negatable_t { + using expression_type = T; + using self = exists_t; + + expression_type expression; + + exists_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + } + + /** + * EXISTS(condition). + * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), + where(exists(select(asterisk(), + where(is_equal(&Customer::grade, 3) and + is_equal(&Agent::code, &Customer::agentCode))))), + order_by(&Agent::comission)); + */ + template + internal::exists_t exists(T expression) { + return {std::move(expression)}; + } +} + +// #include "ast/set.h" + +// #include "ast/match.h" + +#include // std::move + +namespace sqlite_orm { + namespace internal { + + template + struct match_t { + using mapped_type = T; + using argument_type = X; + + argument_type argument; + + match_t(argument_type argument) : argument(std::move(argument)) {} + }; + } + + template + internal::match_t match(X argument) { + return {std::move(argument)}; + } +} + +namespace sqlite_orm { + + namespace internal { + + /** + * ast_iterator accepts any expression and a callable object + * which will be called for any node of provided expression. + * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then + * callable object will be called with 5, &User::id and 10. + * ast_iterator is used in finding literals to be bound to + * a statement, and to collect table names. + * + * Note that not all leaves of the expression tree are always visited: + * Column expressions can be more complex, but are passed as a whole to the callable. + * Examples are `column_pointer<>` and `alias_column_t<>`. + * + * To use `ast_iterator` call `iterate_ast(object, callable);` + * + * `T` is an ast element, e.g. where_t + */ + template + struct ast_iterator { + using node_type = T; + + /** + * L is a callable type. Mostly is a templated lambda + */ + template + void operator()(const T& t, L& lambda) const { + lambda(t); + } + }; + + /** + * Simplified API + */ + template + void iterate_ast(const T& t, L&& lambda) { + ast_iterator iterator; + iterator(t, lambda); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = as_optional_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct ast_iterator, void> { + using node_type = std::reference_wrapper; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = match_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = highlight_t; + + 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); + } + }; + + template + struct ast_iterator, void> { + using node_type = excluded_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.actions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = where_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator< + T, + std::enable_if_t, is_binary_operator>::value>> { + using node_type = T; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.lhs, lambda); + iterate_ast(node.rhs, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_equal_with_table_t; + + template + void operator()(const node_type& node, C& lambda) const { + iterate_ast(node.rhs, lambda); + } + }; + + template + struct ast_iterator, is_struct>::value>> { + using node_type = C; + + template + void operator()(const node_type& cols, L& lambda) const { + iterate_ast(cols.columns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + for (auto& i: vec) { + iterate_ast(i, lambda); + } + } + }; + + template<> + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + lambda(vec); + } + }; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + struct ast_iterator> { + using node_type = CTE; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.subselect, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = With; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.cte, lambda); + iterate_ast(c.expression, lambda); + } + }; +#endif + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.compound, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = into_t; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const { + //.. + } + }; + + template + struct ast_iterator, void> { + using node_type = insert_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = replace_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = select_t; + + template + void operator()(const node_type& sel, L& lambda) const { + iterate_ast(sel.col, lambda); + iterate_ast(sel.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_pointer_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = get_all_optional_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct ast_iterator, void> { + using node_type = update_all_t; + + template + void operator()(const node_type& u, L& lambda) const { + iterate_ast(u.set, lambda); + iterate_ast(u.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = remove_all_t; + + template + void operator()(const node_type& r, L& lambda) const { + iterate_ast(r.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.assigns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.entries, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::tuple; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_tuple(node, [&lambda](auto& v) { + iterate_ast(v, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_with_having; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = cast_t; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = exists_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = like_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + lk.arg3.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = glob_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = between_t; + + template + void operator()(const node_type& b, L& lambda) const { + iterate_ast(b.expr, lambda); + iterate_ast(b.b1, lambda); + iterate_ast(b.b2, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = named_collate; + + template + void operator()(const node_type& col, L& lambda) const { + iterate_ast(col.expr, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = negated_condition_t; + + template + void operator()(const node_type& neg, L& lambda) const { + iterate_ast(neg.c, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_not_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = function_call; + + template + void operator()(const node_type& f, L& lambda) const { + iterate_ast(f.callArgs, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_aggregate_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = filtered_aggregate_function; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.function, lambda); + iterate_ast(node.where, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = Join; + + template + void operator()(const node_type& join, L& lambda) const { + iterate_ast(join.constraint, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = on_t; + + template + void operator()(const node_type& on, L& lambda) const { + iterate_ast(on.arg, lambda); + } + }; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct ast_iterator::value>> { + using node_type = T; + + template + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.column, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = simple_case_t; + + template + void operator()(const node_type& c, L& lambda) const { + c.case_expression.apply([&lambda](auto& c_) { + iterate_ast(c_, lambda); + }); + iterate_tuple(c.args, [&lambda](auto& pair) { + iterate_ast(pair.first, lambda); + iterate_ast(pair.second, lambda); + }); + c.else_expression.apply([&lambda](auto& el) { + iterate_ast(el, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = as_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.tuple, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.vector, lambda); + } + }; + + /** + * Column alias or literal: skipped + */ + template + struct ast_iterator, + polyfill::is_specialization_of, + is_column_alias>::value>> { + using node_type = T; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const {} + }; + + template + struct ast_iterator, void> { + using node_type = order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = collate_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expr, lambda); + } + }; + + } +} + +// #include "prepared_statement.h" + +// #include "connection_holder.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * A C++ view-like class which is returned + * by `storage_t::iterate()` function. This class contains STL functions: + * - size_t size() + * - bool empty() + * - iterator end() + * - iterator begin() + * All these functions are not right const cause all of them may open SQLite connections. + * + * `mapped_view` is also a 'borrowed range', + * meaning that iterators obtained from it are not tied to the lifetime of the view instance. + */ + template + struct mapped_view { + using mapped_type = T; + using storage_type = S; + using db_objects_type = typename S::db_objects_type; + + storage_type& storage; + connection_ref connection; + get_all_t expression; + + mapped_view(storage_type& storage, connection_ref conn, Args&&... args) : + storage(storage), connection(std::move(conn)), expression{{std::forward(args)...}} {} + + size_t size() const { + return this->storage.template count(); + } + + bool empty() const { + return !this->size(); + } + + 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))}; + iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); + return {dbObjects, std::move(stmt)}; + } + + mapped_iterator end() { + return {}; + } + }; + } +} + +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED +template +inline constexpr bool std::ranges::enable_borrowed_range> = true; +#endif + +// #include "result_set_view.h" + +#include +#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 + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "row_extractor.h" + +// #include "result_set_iterator.h" + +#include +#include // std::move +#include // std::input_iterator_tag, std::default_sentinel_t +#include // std::reference_wrapper + +// #include "statement_finalizer.h" + +// #include "row_extractor.h" + +// #include "column_result_proxy.h" + +// #include "util.h" + +#if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) +namespace sqlite_orm::internal { + + template + class result_set_iterator; + +#ifdef SQLITE_ORM_STL_HAS_DEFAULT_SENTINEL + using result_set_sentinel_t = std::default_sentinel_t; +#else + // sentinel + template<> + class result_set_iterator {}; + + using result_set_sentinel_t = result_set_iterator; +#endif + + /* + * Input iterator over a result set for a select statement. + */ + template + class result_set_iterator { + public: + using db_objects_type = DBOs; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + using iterator_concept = std::input_iterator_tag; +#else + using iterator_category = std::input_iterator_tag; +#endif + using difference_type = ptrdiff_t; + using value_type = column_result_proxy_t; + + public: + result_set_iterator(const db_objects_type& dbObjects, statement_finalizer stmt) : + db_objects{dbObjects}, stmt{std::move(stmt)} { + this->step(); + } + result_set_iterator(result_set_iterator&&) = default; + result_set_iterator& operator=(result_set_iterator&&) = default; + result_set_iterator(const result_set_iterator&) = delete; + result_set_iterator& operator=(const result_set_iterator&) = delete; + + /** @pre `*this != std::default_sentinel` */ + value_type operator*() const { + return this->extract(); + } + + result_set_iterator& operator++() { + this->step(); + return *this; + } + + void operator++(int) { + ++*this; + } + + friend bool operator==(const result_set_iterator& it, const result_set_sentinel_t&) noexcept { + return sqlite3_data_count(it.stmt.get()) == 0; + } + + private: + void step() { + perform_step(this->stmt.get(), [](sqlite3_stmt*) {}); + } + + value_type extract() const { + const auto rowExtractor = make_row_extractor(this->db_objects.get()); + return rowExtractor.extract(this->stmt.get(), 0); + } + + private: + std::reference_wrapper db_objects; + statement_finalizer stmt; + }; +} +#endif + +// #include "ast_iterator.h" + +// #include "connection_holder.h" + +// #include "util.h" + +// #include "type_traits.h" + +// #include "storage_lookup.h" + +#if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) +namespace sqlite_orm::internal { + /* + * A C++ view over a result set of a select statement, returned by `storage_t::iterate()`. + * + * `result_set_view` is also a 'borrowed range', + * meaning that iterators obtained from it are not tied to the lifetime of the view instance. + */ + template + struct result_set_view +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + : std::ranges::view_interface> +#endif + { + using db_objects_type = DBOs; + using expression_type = Select; + + result_set_view(const db_objects_type& dbObjects, connection_ref conn, Select expression) : + db_objects{dbObjects}, connection{std::move(conn)}, expression{std::move(expression)} {} + + result_set_view(result_set_view&&) = default; + result_set_view& operator=(result_set_view&&) = default; + result_set_view(const result_set_view&) = default; + result_set_view& operator=(const result_set_view&) = default; + + auto begin() { + const auto& exprDBOs = db_objects_for_expression(this->db_objects.get(), this->expression); + using ExprDBOs = std::remove_cvref_t; + // note: Select can be `select_t` or `with_t` + 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))}; + 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; + // because we cannot select objects/structs from a CTE, passing the permanently defined DBOs are enough. + using iterator_type = result_set_iterator; + return iterator_type{this->db_objects, std::move(stmt)}; + } + + result_set_sentinel_t end() { + return {}; + } + + private: + std::reference_wrapper db_objects; + connection_ref connection; + expression_type expression; + }; +} + +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED +template +inline constexpr bool std::ranges::enable_borrowed_range> = true; +#endif +#endif + +// #include "ast_iterator.h" + +// #include "storage_base.h" + +#include +#include // atoi +#include // std::allocator +#include // std::function, std::bind, std::bind_front +#include // std::string +#include // std::stringstream +#include // std::flush +#include // std::move +#include // std::system_error +#include // std::vector +#include // std::list +#include // std::make_unique, std::unique_ptr +#include // std::map +#include // std::is_same +#include // std::find_if, std::ranges::find + +// #include "functional/cxx_tuple_polyfill.h" + +#include // std::apply; std::tuple_size +#if __cpp_lib_apply < 201603L +#include // std::forward, std::index_sequence, std::make_index_sequence +#endif + +// #include "../functional/cxx_functional_polyfill.h" +// std::invoke + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_apply >= 201603L + using std::apply; +#else + template + decltype(auto) apply(Callable&& callable, Tpl&& tpl, std::index_sequence) { + return polyfill::invoke(std::forward(callable), std::get(std::forward(tpl))...); + } + + template + decltype(auto) apply(Callable&& callable, Tpl&& tpl) { + constexpr size_t size = std::tuple_size>::value; + return apply(std::forward(callable), + std::forward(tpl), + std::make_index_sequence{}); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} +// std::apply +// #include "tuple_helper/tuple_iteration.h" + +// #include "pragma.h" + +#include +#include // atoi +#include // std::string +#include // std::function +#include // std::shared_ptr +#include // std::vector +#include +#include // std::flush + +// #include "error_code.h" + +// #include "row_extractor.h" + +// #include "journal_mode.h" + +// #include "locking_mode.h" + +// #include "connection_holder.h" + +// #include "util.h" + +// #include "serializing_util.h" + +#include // std::index_sequence, std::remove_cvref +#include +#include +#include +#include +#include // std::exchange, std::tuple_size, std::make_index_sequence + +// #include "functional/cxx_type_traits_polyfill.h" +// std::remove_cvref, polyfill::is_detected +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "error_code.h" + +// #include "serializer_context.h" + +// #include "serialize_result_type.h" + +// #include "util.h" + +// #include "schema/column.h" + +namespace sqlite_orm { + namespace internal { + template + struct order_by_t; + + template + auto serialize(const T& t, const Ctx& context); + + template + std::string serialize_order_by(const T&, const Ctx&); + + inline void stream_sql_escaped(std::ostream& os, serialize_arg_type str, char char2Escape) { + for (size_t offset = 0, next; true; offset = next + 1) { + next = str.find(char2Escape, offset); + + if (next == str.npos) SQLITE_ORM_CPP_LIKELY { + os.write(str.data() + offset, str.size() - offset); + break; + } + + os.write(str.data() + offset, next - offset + 1); + os.write(&char2Escape, 1); + } + } + + inline void stream_identifier(std::ostream& ss, + serialize_arg_type qualifier, + serialize_arg_type identifier, + serialize_arg_type alias) { + constexpr char quoteChar = '"'; + constexpr char qualified[] = {quoteChar, '.', '\0'}; + constexpr char aliased[] = {' ', quoteChar, '\0'}; + + // note: In practice, escaping double quotes in identifiers is arguably overkill, + // but since the SQLite grammar allows it, it's better to be safe than sorry. + + if (!qualifier.empty()) { + ss << quoteChar; + stream_sql_escaped(ss, qualifier, quoteChar); + ss << qualified; + } + { + ss << quoteChar; + stream_sql_escaped(ss, identifier, quoteChar); + ss << quoteChar; + } + if (!alias.empty()) { + ss << aliased; + stream_sql_escaped(ss, alias, quoteChar); + ss << quoteChar; + } + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier, const std::string& alias) { + return stream_identifier(ss, "", identifier, alias); + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier) { + return stream_identifier(ss, "", identifier, ""); + } + + template + void stream_identifier(std::ostream& ss, const Tpl& tpl, std::index_sequence) { + static_assert(sizeof...(Is) > 0 && sizeof...(Is) <= 3, ""); + return stream_identifier(ss, std::get(tpl)...); + } + + template>::value, bool> = true> + void stream_identifier(std::ostream& ss, const Tpl& tpl) { + return stream_identifier(ss, tpl, std::make_index_sequence::value>{}); + } + + enum class stream_as { + conditions_tuple, + actions_tuple, + expressions_tuple, + dynamic_expressions, + compound_expressions, + serialized, + identifier, + identifiers, + values_placeholders, + table_columns, + non_generated_columns, + field_values_excluding, + mapped_columns_expressions, + column_constraints, + constraints_tuple, + }; + + template + struct streaming { + template + auto operator()(const Ts&... ts) const { + return std::forward_as_tuple(*this, ts...); + } + + template + constexpr std::index_sequence<1u + Idx...> offset_index(std::index_sequence) const { + return {}; + } + }; + constexpr streaming streaming_conditions_tuple{}; + constexpr streaming streaming_actions_tuple{}; + constexpr streaming streaming_expressions_tuple{}; + constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_compound_expressions{}; + constexpr streaming streaming_serialized{}; + constexpr streaming streaming_identifier{}; + constexpr streaming streaming_identifiers{}; + constexpr streaming streaming_values_placeholders{}; + constexpr streaming streaming_table_column_names{}; + constexpr streaming streaming_non_generated_column_names{}; + constexpr streaming streaming_field_values_excluding{}; + constexpr streaming streaming_mapped_columns_expressions{}; + constexpr streaming streaming_constraints_tuple{}; + constexpr streaming streaming_column_constraints{}; + + // serialize and stream a tuple of condition expressions; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& conditions = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + iterate_tuple(conditions, [&ss, &context](auto& c) { + ss << " " << serialize(c, context); + }); + return ss; + } + + // serialize and stream a tuple of action expressions; + // space-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, T, Ctx> tpl) { + const auto& actions = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + iterate_tuple(actions, [&ss, &context, first = true](auto& action) mutable { + static constexpr std::array sep = {" ", ""}; + ss << sep[std::exchange(first, false)] << serialize(action, context); + }); + return ss; + } + + // serialize and stream a tuple of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& args = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(arg, context); + }); + return ss; + } + + // serialize and stream expressions of a compound statement; + // separated by compound operator + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, T, const std::string&, Ctx> tpl) { + const auto& args = std::get<1>(tpl); + const std::string& opString = std::get<2>(tpl); + auto& context = std::get<3>(tpl); + + iterate_tuple(args, [&ss, &opString, &context, first = true](auto& arg) mutable { + if (!std::exchange(first, false)) { + ss << ' ' << opString << ' '; + } + ss << serialize(arg, context); + }); + return ss; + } + + // serialize and stream multi_order_by arguments; + // comma-separated + template + std::ostream& operator<<( + std::ostream& ss, + std::tuple&, const std::tuple...>&, Ctx> tpl) { + const auto& args = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize_order_by(arg, context); + }); + return ss; + } + + // serialize and stream a vector or any other STL container of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, C, Ctx> tpl) { + const auto& args = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + static constexpr std::array sep = {", ", ""}; + bool first = true; + for (auto& argument: args) { + ss << sep[std::exchange(first, false)] << serialize(argument, context); + } + return ss; + } + + // stream a vector of already serialized strings; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& strings = std::get<1>(tpl); + + static constexpr std::array sep = {", ", ""}; + for (size_t i = 0, first = true; i < strings.size(); ++i) { + ss << sep[std::exchange(first, false)] << strings[i]; + } + return ss; + } + + // stream an identifier described by a variadic string pack, which is one of: + // 1. identifier + // 2. identifier, alias + // 3. qualifier, identifier, alias + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Strings...> tpl) { + stream_identifier(ss, tpl, streaming_identifier.offset_index(std::index_sequence_for{})); + return ss; + } + + // stream a container of identifiers described by a string or a tuple, which is one of: + // 1. identifier + // 1. tuple(identifier) + // 2. tuple(identifier, alias), pair(identifier, alias) + // 3. tuple(qualifier, identifier, alias) + // + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& identifiers = std::get<1>(tpl); + + static constexpr std::array sep = {", ", ""}; + bool first = true; + for (auto& identifier: identifiers) { + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, identifier); + } + return ss; + } + + // stream placeholders as part of a values clause + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Ts...> tpl) { + const size_t& columnsCount = std::get<1>(tpl); + const ptrdiff_t& valuesCount = std::get<2>(tpl); + + if (!valuesCount || !columnsCount) { + return ss; + } + + std::string result; + result.reserve((1 + (columnsCount * 1) + (columnsCount * 2 - 2) + 1) * valuesCount + (valuesCount * 2 - 2)); + + static constexpr std::array sep = {", ", ""}; + for (ptrdiff_t i = 0, first = true; i < valuesCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "("; + for (size_t i = 0, first = true; i < columnsCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "?"; + } + result += ")"; + } + ss << result; + return ss; + } + + // stream a table's column identifiers, possibly qualified; + // comma-separated + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, Table, const std::string&> tpl) { + const auto& table = std::get<1>(tpl); + const std::string& qualifier = std::get<2>(tpl); + + table.for_each_column([&ss, &qualifier, first = true](const column_identifier& column) mutable { + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, qualifier, column.name, std::string{}); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table> tpl) { + const auto& table = std::get<1>(tpl); + + table.template for_each_column_excluding( + [&ss, first = true](const column_identifier& column) mutable { + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, column.name); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, PredFnCls, L, Ctx, Obj> tpl) { + using check_if_excluded = polyfill::remove_cvref_t>; + auto& excluded = std::get<2>(tpl); + auto& context = std::get<3>(tpl); + auto& object = std::get<4>(tpl); + using object_type = polyfill::remove_cvref_t; + auto& table = pick_table(context.db_objects); + + table.template for_each_column_excluding(call_as_template_base( + [&ss, &excluded, &context, &object, first = true](auto& column) mutable { + if (excluded(column)) { + return; + } + + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(column.member_pointer, object), context); + })); + return ss; + } + + // stream a tuple of mapped columns (which are member pointers or column pointers); + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& columns = std::get<1>(tpl); + auto& context = std::get<2>(tpl); + + iterate_tuple(columns, [&ss, &context, first = true](auto& colRef) mutable { + const std::string* columnName = find_column_name(context.db_objects, colRef); + if (!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, *columnName); + }); + return ss; + } + + // serialize and stream a tuple of conditions or hints; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& constraints = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(constraints, [&ss, &context](auto& constraint) mutable { + ss << ' ' << serialize(constraint, context); + }); + return ss; + } + + // serialize and stream a tuple of column constraints; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, + const column_constraints&, + const bool&, + Ctx> tpl) { + const auto& column = std::get<1>(tpl); + const bool& isNotNull = std::get<2>(tpl); + auto& context = std::get<3>(tpl); + + using constraints_tuple = decltype(column.constraints); + iterate_tuple(column.constraints, [&ss, &context](auto& constraint) { + ss << ' ' << serialize(constraint, context); + }); + // add implicit null constraint + if (!context.fts5_columns) { + constexpr bool hasExplicitNullableConstraint = + mpl::invoke_t, check_if_has_type>, + constraints_tuple>::value; + if SQLITE_ORM_CONSTEXPR_IF (!hasExplicitNullableConstraint) { + if (isNotNull) { + ss << " NOT NULL"; + } else { + ss << " NULL"; + } + } + } + + return ss; + } + } +} + +namespace sqlite_orm { + + namespace internal { + struct storage_base; + + template + int getPragmaCallback(void* data, int argc, char** argv, char** x) { + return extract_single_value(data, argc, argv, x); + } + + template<> + inline int getPragmaCallback>(void* data, int argc, char** argv, char**) { + auto& res = *(std::vector*)data; + res.reserve(argc); + 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)); + } + return 0; + } + + struct pragma_t { + using get_connection_t = std::function; + + pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + std::vector module_list() { + return this->get_pragma>("module_list"); + } + + bool recursive_triggers() { + return bool(this->get_pragma("recursive_triggers")); + } + + void recursive_triggers(bool value) { + this->set_pragma("recursive_triggers", int(value)); + } + + void busy_timeout(int value) { + this->set_pragma("busy_timeout", value); + } + + int busy_timeout() { + return this->get_pragma("busy_timeout"); + } + + sqlite_orm::locking_mode locking_mode() { + return this->get_pragma("locking_mode"); + } + + void locking_mode(sqlite_orm::locking_mode value) { + this->set_pragma("locking_mode", value); + } + + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } + + void journal_mode(sqlite_orm::journal_mode value) { + this->journal_mode_ = -1; + this->set_pragma("journal_mode", value); + this->journal_mode_ = static_castjournal_mode_)>(value); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + int application_id() { + return this->get_pragma("application_id"); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + void application_id(int value) { + this->set_pragma("application_id", value); + } + + int synchronous() { + return this->get_pragma("synchronous"); + } + + void synchronous(int value) { + this->synchronous_ = -1; + this->set_pragma("synchronous", value); + this->synchronous_ = value; + } + + int user_version() { + return this->get_pragma("user_version"); + } + + void user_version(int value) { + this->set_pragma("user_version", value); + } + + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); + } + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); + } + + int max_page_count() { + return this->get_pragma("max_page_count"); + } + + void max_page_count(int value) { + this->set_pragma("max_page_count", value); + } + + std::vector integrity_check() { + return this->get_pragma>("integrity_check"); + } + + template + std::vector integrity_check(T table_name) { + std::ostringstream ss; + ss << "integrity_check(" << table_name << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + std::vector integrity_check(int n) { + std::ostringstream ss; + ss << "integrity_check(" << n << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + std::vector quick_check() { + return this->get_pragma>("quick_check"); + } + + // will include generated columns in response as opposed to table_info + std::vector table_xinfo(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::vector result; + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if (argc) { + auto index = 0; + auto cid = atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = atoi(argv[index++]); + auto hidden = atoi(argv[index++]); + res.emplace_back(cid, + std::move(name), + std::move(type), + notnull, + std::move(dflt_value), + pk, + hidden); + } + return 0; + }, + &result); + return result; + } + + std::vector table_info(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + std::vector result; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if (argc) { + auto index = 0; + auto cid = atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = atoi(argv[index++]); + res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); + } + return 0; + }, + &result); + return result; + } + + private: + friend struct storage_base; + + int synchronous_ = -1; + signed char journal_mode_ = -1; // if != -1 stores static_cast(journal_mode) + get_connection_t get_connection; + + template + T get_pragma(const std::string& name) { + auto connection = this->get_connection(); + T result; + perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + return result; + } + + /** + * Yevgeniy Zakharov: I wanted to refactor this function with statements and value bindings + * but it turns out that bindings in pragma statements are not supported. + */ + template + void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << value; + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, sqlite_orm::journal_mode value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << journal_mode_to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, sqlite_orm::locking_mode value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << locking_mode_to_string(value); + 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(); + } + perform_void_exec(db, query); + } + }; + } +} + +// #include "limit_accessor.h" + +#include +#include // std::map +#include // std::function +#include // std::shared_ptr + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + struct limit_accessor { + using get_connection_t = std::function; + + limit_accessor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + int length() { + return this->get(SQLITE_LIMIT_LENGTH); + } + + void length(int newValue) { + this->set(SQLITE_LIMIT_LENGTH, newValue); + } + + int sql_length() { + return this->get(SQLITE_LIMIT_SQL_LENGTH); + } + + void sql_length(int newValue) { + this->set(SQLITE_LIMIT_SQL_LENGTH, newValue); + } + + int column() { + return this->get(SQLITE_LIMIT_COLUMN); + } + + void column(int newValue) { + this->set(SQLITE_LIMIT_COLUMN, newValue); + } + + int expr_depth() { + return this->get(SQLITE_LIMIT_EXPR_DEPTH); + } + + void expr_depth(int newValue) { + this->set(SQLITE_LIMIT_EXPR_DEPTH, newValue); + } + + int compound_select() { + return this->get(SQLITE_LIMIT_COMPOUND_SELECT); + } + + void compound_select(int newValue) { + this->set(SQLITE_LIMIT_COMPOUND_SELECT, newValue); + } + + int vdbe_op() { + return this->get(SQLITE_LIMIT_VDBE_OP); + } + + void vdbe_op(int newValue) { + this->set(SQLITE_LIMIT_VDBE_OP, newValue); + } + + int function_arg() { + return this->get(SQLITE_LIMIT_FUNCTION_ARG); + } + + void function_arg(int newValue) { + this->set(SQLITE_LIMIT_FUNCTION_ARG, newValue); + } + + int attached() { + return this->get(SQLITE_LIMIT_ATTACHED); + } + + void attached(int newValue) { + this->set(SQLITE_LIMIT_ATTACHED, newValue); + } + + int like_pattern_length() { + return this->get(SQLITE_LIMIT_LIKE_PATTERN_LENGTH); + } + + void like_pattern_length(int newValue) { + this->set(SQLITE_LIMIT_LIKE_PATTERN_LENGTH, newValue); + } + + int variable_number() { + return this->get(SQLITE_LIMIT_VARIABLE_NUMBER); + } + + void variable_number(int newValue) { + this->set(SQLITE_LIMIT_VARIABLE_NUMBER, newValue); + } + + int trigger_depth() { + return this->get(SQLITE_LIMIT_TRIGGER_DEPTH); + } + + void trigger_depth(int newValue) { + this->set(SQLITE_LIMIT_TRIGGER_DEPTH, newValue); + } + +#if SQLITE_VERSION_NUMBER >= 3008007 + int worker_threads() { + return this->get(SQLITE_LIMIT_WORKER_THREADS); + } + + void worker_threads(int newValue) { + this->set(SQLITE_LIMIT_WORKER_THREADS, newValue); + } +#endif + + protected: + get_connection_t get_connection; + + friend struct storage_base; + + /** + * Stores limit set between connections. + */ + std::map limits; + + int get(int id) { + auto connection = this->get_connection(); + return sqlite3_limit(connection.get(), id, -1); + } + + void set(int id, int newValue) { + this->limits[id] = newValue; + auto connection = this->get_connection(); + sqlite3_limit(connection.get(), id, newValue); + } + }; + } +} + +// #include "transaction_guard.h" + +#include // std::function +#include // std::move + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Class used as a guard for a transaction. Calls `ROLLBACK` in destructor. + * Has explicit `commit()` and `rollback()` functions. After explicit function is fired + * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to + * make it call `COMMIT` on destroy. + * + * Note: The guard's destructor is explicitly marked as potentially throwing, + * so exceptions that occur during commit or rollback are propagated to the caller. + */ + struct transaction_guard_t { + /** + * This is a public lever to tell a guard what it must do in its destructor + * if `gotta_fire` is true + */ + bool commit_on_destroy = false; + + transaction_guard_t(connection_ref connection_, + std::function commit_func_, + std::function rollback_func_) : + connection(std::move(connection_)), commit_func(std::move(commit_func_)), + rollback_func(std::move(rollback_func_)) {} + + transaction_guard_t(transaction_guard_t&& other) : + commit_on_destroy(other.commit_on_destroy), connection(std::move(other.connection)), + commit_func(std::move(other.commit_func)), rollback_func(std::move(other.rollback_func)), + gotta_fire(other.gotta_fire) { + other.gotta_fire = false; + } + + ~transaction_guard_t() noexcept(false) { + if (this->gotta_fire) { + if (this->commit_on_destroy) { + this->commit_func(); + } else { + this->rollback_func(); + } + } + } + + transaction_guard_t& operator=(transaction_guard_t&&) = delete; + + /** + * Call `COMMIT` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void commit() { + this->gotta_fire = false; + this->commit_func(); + } + + /** + * Call `ROLLBACK` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void rollback() { + this->gotta_fire = false; + this->rollback_func(); + } + + protected: + connection_ref connection; + std::function commit_func; + std::function rollback_func; + bool gotta_fire = true; + }; + } +} + +// #include "row_extractor.h" + +// #include "connection_holder.h" + +// #include "backup.h" + +#include +#include // std::system_error +#include // std::string +#include +#include // std::move, std::exchange + +// #include "error_code.h" + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. + * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class + * to have maximum control on a backup operation. In case you need a single backup in one line you + * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. + */ + struct backup_t { + backup_t(connection_ref to_, + const std::string& zDestName, + connection_ref from_, + const std::string& zSourceName, + std::unique_ptr holder_) : + handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), + holder(std::move(holder_)), to(to_), from(from_) { + if (!this->handle) { + throw std::system_error{orm_error_code::failed_to_init_a_backup}; + } + } + + backup_t(backup_t&& other) : + handle(std::exchange(other.handle, nullptr)), holder(std::move(other.holder)), to(other.to), + from(other.from) {} + + ~backup_t() { + if (this->handle) { + (void)sqlite3_backup_finish(this->handle); + } + } + + /** + * Calls sqlite3_backup_step with pages argument + */ + int step(int pages) { + return sqlite3_backup_step(this->handle, pages); + } + + /** + * Returns sqlite3_backup_remaining result + */ + int remaining() const { + return sqlite3_backup_remaining(this->handle); + } + + /** + * Returns sqlite3_backup_pagecount result + */ + int pagecount() const { + return sqlite3_backup_pagecount(this->handle); + } + + protected: + sqlite3_backup* handle = nullptr; + std::unique_ptr holder; + connection_ref to; + connection_ref from; + }; + } +} + +// #include "function.h" + +// #include "values_to_tuple.h" + +#include +#include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence +#include // std::tuple, std::tuple_size, std::tuple_element + +// #include "functional/cxx_functional_polyfill.h" + +// #include "type_traits.h" + +// #include "row_extractor.h" + +// #include "arg_values.h" + +#include + +// #include "row_extractor.h" + +namespace sqlite_orm { + + /** @short Wrapper around a dynamically typed value object. + */ + struct arg_value { + + arg_value() : arg_value(nullptr) {} + + arg_value(sqlite3_value* value_) : value(value_) {} + + template + T get() const { + const auto rowExtractor = internal::boxed_value_extractor(); + return rowExtractor.extract(this->value); + } + + bool is_null() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_NULL; + } + + bool is_text() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_TEXT; + } + + bool is_integer() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_INTEGER; + } + + bool is_float() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_FLOAT; + } + + bool is_blob() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_BLOB; + } + + bool empty() const { + return this->value == nullptr; + } + + private: + sqlite3_value* value = nullptr; + }; + + struct arg_values { + + struct iterator { + + iterator(const arg_values& container_, int index_) : + container(container_), index(index_), + currentValue(index_ < int(container_.size()) ? container_[index_] : arg_value()) {} + + iterator& operator++() { + ++this->index; + if (this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return *this; + } + + iterator operator++(int) { + auto res = *this; + ++this->index; + if (this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return res; + } + + arg_value operator*() const { + if (this->index < int(this->container.size()) && this->index >= 0) { + return this->currentValue; + } else { + throw std::system_error{orm_error_code::index_is_out_of_bounds}; + } + } + + arg_value* operator->() const { + return &this->currentValue; + } + + bool operator==(const iterator& other) const { + return &other.container == &this->container && other.index == this->index; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + private: + const arg_values& container; + int index = 0; + mutable arg_value currentValue; + }; + + arg_values() : arg_values(0, nullptr) {} + + arg_values(int argsCount_, sqlite3_value** values_) : argsCount(argsCount_), values(values_) {} + + size_t size() const { + return this->argsCount; + } + + bool empty() const { + return 0 == this->argsCount; + } + + arg_value operator[](int index) const { + if (index < this->argsCount && index >= 0) { + sqlite3_value* value = this->values[index]; + return {value}; + } else { + throw std::system_error{orm_error_code::index_is_out_of_bounds}; + } + } + + arg_value at(int index) const { + return this->operator[](index); + } + + iterator begin() const { + return {*this, 0}; + } + + iterator end() const { + return {*this, this->argsCount}; + } + + private: + int argsCount = 0; + sqlite3_value** values = nullptr; + }; +} + +namespace sqlite_orm { + + namespace internal { + + template + struct tuple_from_values { + template> = true> + R operator()(sqlite3_value** values, int /*argsCount*/) const { + return this->create_from(values, std::make_index_sequence::value>{}); + } + + template> = true> + R operator()(sqlite3_value** values, int argsCount) const { + return {arg_values(argsCount, values)}; + } + + private: + template + Tpl create_from(sqlite3_value** values, std::index_sequence) const { + return {this->extract>(values[Idx])...}; + } + + template + T extract(sqlite3_value* value) const { + const auto rowExtractor = boxed_value_extractor(); + return rowExtractor.extract(value); + } + }; + } +} + +// #include "arg_values.h" + +// #include "util.h" + +// #include "xdestroy_handling.h" + +// #include "udf_proxy.h" + +#include +#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 + +// #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. + */ + template + std::pair preallocate_udf_memory() { + std::allocator allocator; + using traits = std::allocator_traits; + + SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + std::allocator allocator; + using traits = std::allocator_traits; + traits::deallocate(allocator, (UDF*)location, 1); + }; + + 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. + */ + template + std::pair obtain_udf_allocator() { + SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 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 { + std::allocator allocator; + using traits = std::allocator_traits; + traits::deallocate(allocator, (UDF*)location, 1); + }; + + return {allocate, deallocate}; + } + + /* + * A deleter that only destroys the application-defined function object. + */ + struct udf_destruct_only_deleter { + template + void operator()(UDF* f) const noexcept { + std::allocator allocator; + using traits = std::allocator_traits; + traits::destroy(allocator, f); + } + }; + + /* + * 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); + using final_call_fn_t = void (*)(void* udfHandle, sqlite3_context* context); + using memory_alloc = std::pair; + using memory_space = std::pair; + + std::string name; + int argumentsCount; + std::function constructAt; + xdestroy_fn_t destroy; + sqlite_func_t func; + final_call_fn_t finalAggregateCall; + + // allocator/deallocator function pair for aggregate UDF + const memory_alloc udfAllocator; + // pointer to preallocated memory space for scalar UDF, already constructed by caller if stateless + const memory_space udfMemorySpace; + + udf_proxy(std::string name, + int argumentsCount, + std::function constructAt, + xdestroy_fn_t destroy, + sqlite_func_t func, + memory_space udfMemorySpace) : + name{std::move(name)}, argumentsCount{argumentsCount}, constructAt{std::move(constructAt)}, + destroy{destroy}, func{func}, finalAggregateCall{nullptr}, udfAllocator{}, + udfMemorySpace{udfMemorySpace} {} + + udf_proxy(std::string name, + int argumentsCount, + std::function constructAt, + xdestroy_fn_t destroy, + sqlite_func_t func, + final_call_fn_t finalAggregateCall, + memory_alloc udfAllocator) : + name{std::move(name)}, argumentsCount{argumentsCount}, constructAt{std::move(constructAt)}, + destroy{destroy}, func{func}, finalAggregateCall{finalAggregateCall}, udfAllocator{udfAllocator}, + udfMemorySpace{} {} + + ~udf_proxy() { + // destruct + if (/*bool aprioriConstructed = */ !constructAt && destroy) { + destroy(udfMemorySpace.first); + } + // deallocate + if (udfMemorySpace.second) { + udfMemorySpace.second(udfMemorySpace.first); + } + } + + udf_proxy(const udf_proxy&) = delete; + udf_proxy& operator=(const udf_proxy&) = delete; + + // convenience accessors for better legibility; + // [`friend` is intentional - it ensures that these are core accessors (only found via ADL), yet still be an out-of-class interface] + + friend void* preallocated_udf_handle(udf_proxy* proxy) { + return proxy->udfMemorySpace.first; + } + + friend void* allocate_udf(udf_proxy* proxy) { + return proxy->udfAllocator.first(); + } + + friend void deallocate_udf(udf_proxy* proxy, void* udfHandle) { + proxy->udfAllocator.second(udfHandle); + } + }; + + // safety net of doing a triple check at runtime + inline void assert_args_count(const udf_proxy* proxy, 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) { + 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 + inline void* ensure_aggregate_udf(sqlite3_context* context, udf_proxy* proxy, int argsCount) { + // reserve memory for storing a void pointer (which is the `udfHandle`, i.e. address of the aggregate function object) + void* ctxMemory = sqlite3_aggregate_context(context, sizeof(void*)); + if (!ctxMemory) SQLITE_ORM_CPP_UNLIKELY { + throw std::bad_alloc(); + } + void*& udfHandle = *static_cast(ctxMemory); + + if (udfHandle) SQLITE_ORM_CPP_LIKELY { + return udfHandle; + } else { + assert_args_count(proxy, argsCount); + udfHandle = allocate_udf(proxy); + // Note on the use of the `udfHandle` pointer after the object construction: + // since we only ever cast between void* and UDF* pointer types and + // only use the memory space for one type during the entire lifetime of a proxy, + // we can use `udfHandle` interconvertibly without laundering its provenance. + proxy->constructAt(udfHandle); + return udfHandle; + } + } + + inline void delete_aggregate_udf(udf_proxy* proxy, void* udfHandle) { + proxy->destroy(udfHandle); + deallocate_udf(proxy, udfHandle); + } + + // Return C pointer to preallocated and a priori constructed UDF + template + inline UDF* + proxy_get_scalar_udf(std::true_type /*is_stateless*/, sqlite3_context* context, int argsCount) noexcept { + proxy_assert_args_count(context, argsCount); + udf_proxy* proxy = static_cast(sqlite3_user_data(context)); + return static_cast(preallocated_udf_handle(proxy)); + } + + // Return unique pointer to newly constructed UDF at preallocated memory space + template + inline auto proxy_get_scalar_udf(std::false_type /*is_stateless*/, sqlite3_context* context, int argsCount) { + proxy_assert_args_count(context, argsCount); + udf_proxy* proxy = static_cast(sqlite3_user_data(context)); + // Note on the use of the `udfHandle` pointer after the object construction: + // since we only ever cast between void* and UDF* pointer types and + // only use the memory space for one type during the entire lifetime of a proxy, + // we can use `udfHandle` interconvertibly without laundering its provenance. + proxy->constructAt(preallocated_udf_handle(proxy)); + return std::unique_ptr{static_cast(preallocated_udf_handle(proxy)), + proxy->destroy}; + } + + // note: may throw `std::bad_alloc` in case memory space for the aggregate function object cannot be allocated + template + inline UDF* proxy_get_aggregate_step_udf(sqlite3_context* context, int argsCount) { + udf_proxy* proxy = static_cast(sqlite3_user_data(context)); + void* udfHandle = ensure_aggregate_udf(context, proxy, argsCount); + return static_cast(udfHandle); + } + + inline void aggregate_function_final_callback(sqlite3_context* context) { + udf_proxy* proxy = static_cast(sqlite3_user_data(context)); + void* udfHandle; + try { + // note: it is possible that the 'step' function was never called + udfHandle = ensure_aggregate_udf(context, proxy, -1); + } catch (const std::bad_alloc&) { + sqlite3_result_error_nomem(context); + return; + } + proxy->finalAggregateCall(udfHandle, context); + delete_aggregate_udf(proxy, udfHandle); + } + } +} + +// #include "serializing_util.h" + +// #include "table_info.h" + +namespace sqlite_orm { + + namespace internal { + + struct storage_base { + using collating_function = std::function; + + std::function on_open; + pragma_t pragma; + limit_accessor limit; + + transaction_guard_t transaction_guard() { + this->begin_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + transaction_guard_t deferred_transaction_guard() { + this->begin_deferred_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + transaction_guard_t immediate_transaction_guard() { + this->begin_immediate_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + transaction_guard_t exclusive_transaction_guard() { + this->begin_exclusive_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + /** + * Drops index with given name. + * Calls `DROP INDEX indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ + void drop_index(const std::string& indexName) { + this->drop_index_internal(indexName, false); + } + + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP INDEX IF EXISTS indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ + void drop_index_if_exists(const std::string& indexName) { + this->drop_index_internal(indexName, true); + } + + /** + * Drops trigger with given name. + * Calls `DROP TRIGGER triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ + void drop_trigger(const std::string& triggerName) { + this->drop_trigger_internal(triggerName, false); + } + + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP TRIGGER IF EXISTS triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ + void drop_trigger_if_exists(const std::string& triggerName) { + this->drop_trigger_internal(triggerName, true); + } + + /** + * `VACUUM` query. + * More info: https://www.sqlite.org/lang_vacuum.html + */ + void vacuum() { + perform_void_exec(this->get_connection().get(), "VACUUM"); + } + + /** + * Drops table with given name. + * Calls `DROP TABLE tableName`. + * 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); + } + + /** + * Drops table with given name if table exists. + * Calls `DROP TABLE IF EXISTS tableName`. + * 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); + } + + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string& from, const std::string& to) { + this->rename_table(this->get_connection().get(), from, to); + } + + protected: + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) + << std::flush; + perform_void_exec(db, ss.str()); + } + + /** + * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. + * Note: table can be not mapped to a storage + * @return true if table with a given name exists in db, false otherwise. + */ + bool table_exists(const std::string& tableName) { + auto con = this->get_connection(); + return this->table_exists(con.get(), tableName); + } + + bool table_exists(sqlite3* db, const std::string& tableName) const { + bool result = false; + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + perform_exec( + db, + ss.str(), + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; + if (argc) { + res = !!atoi(argv[0]); + } + return 0; + }, + &result); + return result; + } + + void add_generated_cols(std::vector& columnsToAdd, + const std::vector& storageTableInfo) { + // iterate through storage columns + for (const table_xinfo& storageColumnInfo: storageTableInfo) { + if (storageColumnInfo.hidden) { + columnsToAdd.push_back(&storageColumnInfo); + } + } + } + + public: + /** + * sqlite3_changes function. + */ + int changes() { + auto con = this->get_connection(); + return sqlite3_changes(con.get()); + } + + /** + * sqlite3_total_changes function. + */ + int total_changes() { + auto con = this->get_connection(); + return sqlite3_total_changes(con.get()); + } + + int64 last_insert_rowid() { + auto con = this->get_connection(); + return sqlite3_last_insert_rowid(con.get()); + } + + int busy_timeout(int ms) { + auto con = this->get_connection(); + return sqlite3_busy_timeout(con.get(), ms); + } + + /** + * Returns libsqlite3 version, not sqlite_orm + */ + std::string libversion() { + return sqlite3_libversion(); + } + + bool transaction(const std::function& f) { + auto guard = this->transaction_guard(); + return guard.commit_on_destroy = f(); + } + + std::string current_time() { + auto con = this->get_connection(); + return this->current_time(con.get()); + } + + std::string current_date() { + auto con = this->get_connection(); + return this->current_date(con.get()); + } + + std::string current_timestamp() { + auto con = this->get_connection(); + return this->current_timestamp(con.get()); + } + +#if SQLITE_VERSION_NUMBER >= 3007010 + /** + * \fn db_release_memory + * \brief Releases freeable memory of database. It is function can/should be called periodically by + * application, if application has less memory usage constraint. \note sqlite3_db_release_memory added + * in 3.7.10 https://sqlite.org/changes.html + */ + int db_release_memory() { + auto con = this->get_connection(); + return sqlite3_db_release_memory(con.get()); + } +#endif + + /** + * Returns existing permanent table names in database. Doesn't check storage itself - works only with + * actual database. + * @return Returns list of tables in database. + */ + std::vector table_names() { + auto con = this->get_connection(); + std::vector tableNames; + using data_t = std::vector; + perform_exec( + con.get(), + "SELECT name FROM sqlite_master WHERE type='table'", + [](void* data, int argc, char** argv, char** /*columnName*/) -> int { + auto& tableNames_ = *(data_t*)data; + for (int i = 0; i < argc; ++i) { + if (argv[i]) { + tableNames_.emplace_back(argv[i]); + } + } + return 0; + }, + &tableNames); + tableNames.shrink_to_fit(); + return tableNames; + } + + /** + * Call it once during storage lifetime to make it keeping its connection opened till dtor call. + * By default if storage is not in-memory it calls `sqlite3_open` only when the connection is really + * 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` + * 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()); + } + } + + /** + * Create an application-defined scalar SQL function. + * Can be called at any time 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. + * If `F` is a stateless function object, an instance of the function object is created once, otherwise + * an instance of the function object is repeatedly recreated for each result row, + * ensuring that the calculations always start with freshly initialized values. + * + * T - function class. T must have operator() overload and static name function like this: + * ``` + * struct SqrtFunction { + * + * double operator()(double arg) const { + * return std::sqrt(arg); + * } + * + * static const char *name() { + * return "SQRT"; + * } + * }; + * ``` + */ + template + void create_scalar_function(Args&&... constructorArgs) { + static_assert(is_scalar_udf_v, "F must be a scalar function"); + + this->create_scalar_function_impl( + udf_holder{}, +#ifdef SQLITE_ORM_PACK_EXPANSION_IN_INIT_CAPTURE_SUPPORTED + /* constructAt */ [... constructorArgs = std::move(constructorArgs)](void* location) { +#else + /* constructAt */ + [constructorArgs...](void* location) { +#endif + std::allocator allocator; + using traits = std::allocator_traits; + traits::construct(allocator, (F*)location, constructorArgs...); + }); + } + +#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. + * + * 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. + * If `F` is a stateless function object, an instance of the function object is created once, otherwise + * an instance of the function object is repeatedly recreated for each result row, + * ensuring that the calculations always start with freshly initialized values. + */ + template + void create_scalar_function(Args&&... constructorArgs) { + return this->create_scalar_function>(std::forward(constructorArgs)...); + } + + /** + * Create an application-defined scalar function. + * Can be called at any time 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 + void create_scalar_function() { + using Sig = auto_udf_type_t; + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->scalarFunctions.emplace_back( + std::string{quotedF.name()}, + argsCount, + /* constructAt = */ + nullptr, + /* destroy = */ + nullptr, + /* call = */ + [](sqlite3_context* context, int argsCount, sqlite3_value** values) { + proxy_assert_args_count(context, argsCount); + args_tuple argsTuple = tuple_from_values{}(values, argsCount); + auto result = polyfill::apply(quotedF.callable(), std::move(argsTuple)); + statement_binder().result(context, result); + }, + /* finalCall = */ + nullptr, + std::pair{nullptr, null_xdestroy_f}); + + if (this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + try_to_create_scalar_function(db, this->scalarFunctions.back()); + } + } +#endif + + /** + * Create an application-defined aggregate SQL function. + * Can be called at any time 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. + * An instance of the function object is repeatedly recreated for each result row, + * ensuring that the calculations always start with freshly initialized values. + * + * T - function class. T must have step member function, fin member function and static name function like this: + * ``` + * struct MeanFunction { + * double total = 0; + * int count = 0; + * + * void step(double value) { + * total += value; + * ++count; + * } + * + * int fin() const { + * return total / count; + * } + * + * static std::string name() { + * return "MEAN"; + * } + * }; + * ``` + */ + template + void create_aggregate_function(Args&&... constructorArgs) { + static_assert(is_aggregate_udf_v, "F must be an aggregate function"); + + this->create_aggregate_function_impl( + udf_holder{}, /* constructAt = */ +#ifdef SQLITE_ORM_PACK_EXPANSION_IN_INIT_CAPTURE_SUPPORTED + /* constructAt */ [... constructorArgs = std::move(constructorArgs)](void* location) { +#else + /* constructAt */ + [constructorArgs...](void* location) { +#endif + std::allocator allocator; + using traits = std::allocator_traits; + traits::construct(allocator, (F*)location, constructorArgs...); + }); + } + +#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. + * + * 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. + * An instance of the function object is repeatedly recreated for each result row, + * ensuring that the calculations always start with freshly initialized values. + */ + template + void create_aggregate_function(Args&&... constructorArgs) { + return this->create_aggregate_function>(std::forward(constructorArgs)...); + } +#endif + + /** + * Delete a scalar function you created before. + * Can be called at any time no matter whether the database connection is open or not. + */ + template + void delete_scalar_function() { + static_assert(is_scalar_udf_v, "F must be a scalar function"); + udf_holder udfName; + this->delete_function_impl(udfName(), this->scalarFunctions); + } + +#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. + */ + template + void delete_scalar_function() { + this->delete_function_impl(f.name(), this->scalarFunctions); + } + + /** + * Delete a quoted scalar function you created before. + * Can be called at any time no matter whether the database connection is open or not. + */ + template + void delete_scalar_function() { + this->delete_function_impl(quotedF.name(), this->scalarFunctions); + } +#endif + + /** + * Delete aggregate function you created before. + * Can be called at any time no matter whether the database connection is open or not. + */ + template + void delete_aggregate_function() { + static_assert(is_aggregate_udf_v, "F must be an aggregate function"); + udf_holder udfName; + this->delete_function_impl(udfName(), this->aggregateFunctions); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void delete_aggregate_function() { + this->delete_function_impl(f.name(), this->aggregateFunctions); + } +#endif + + template + void create_collation() { + collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { + C collatingObject; + return collatingObject(leftLength, lhs, rightLength, rhs); + }; + std::stringstream ss; + ss << C::name() << std::flush; + this->create_collation(ss.str(), std::move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + const auto functionExists = bool(f); + collating_function* function = nullptr; + if (functionExists) { + function = &(collatingFunctions[name] = std::move(f)); + } + + // create collations if db is open + if (this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + int rc = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + function, + functionExists ? collate_callback : nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + if (!functionExists) { + collatingFunctions.erase(name); + } + } + + template + void delete_collation() { + std::stringstream ss; + ss << C::name() << std::flush; + this->create_collation(ss.str(), {}); + } + + void begin_transaction() { + this->begin_transaction_internal("BEGIN TRANSACTION"); + } + + void begin_deferred_transaction() { + this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); + } + + void begin_immediate_transaction() { + this->begin_transaction_internal("BEGIN IMMEDIATE TRANSACTION"); + } + + void begin_exclusive_transaction() { + this->begin_transaction_internal("BEGIN EXCLUSIVE TRANSACTION"); + } + + void commit() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "COMMIT"); + this->connection->release(); + if (this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void rollback() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "ROLLBACK"); + this->connection->release(); + if (this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void backup_to(const std::string& filename) { + auto backup = this->make_backup_to(filename); + backup.step(-1); + } + + void backup_to(storage_base& other) { + auto backup = this->make_backup_to(other); + backup.step(-1); + } + + void backup_from(const std::string& filename) { + auto backup = this->make_backup_from(filename); + backup.step(-1); + } + + void backup_from(storage_base& other) { + auto backup = this->make_backup_from(other); + backup.step(-1); + } + + backup_t make_backup_to(const std::string& filename) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", std::move(holder)}; + } + + backup_t make_backup_to(storage_base& other) { + return {other.get_connection(), "main", this->get_connection(), "main", {}}; + } + + backup_t make_backup_from(const std::string& filename) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", std::move(holder)}; + } + + backup_t make_backup_from(storage_base& other) { + return {this->get_connection(), "main", other.get_connection(), "main", {}}; + } + + const std::string& filename() const { + return this->connection->filename; + } + + /** + * Checks whether connection to database is opened right now. + * Returns always `true` for in memory databases. + */ + bool is_opened() const { + return this->connection->retain_count() > 0; + } + + /* + * returning false when there is a transaction in place + * otherwise true; function is not const because it has to call get_connection() + */ + bool get_autocommit() { + auto con = this->get_connection(); + return sqlite3_get_autocommit(con.get()); + } + + int busy_handler(std::function handler) { + _busy_handler = std::move(handler); + if (this->is_opened()) { + if (_busy_handler) { + return sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } else { + return sqlite3_busy_handler(this->connection->get(), nullptr, nullptr); + } + } else { + return SQLITE_OK; + } + } + + protected: + storage_base(std::string filename, int foreignKeysCount) : + pragma(std::bind(&storage_base::get_connection, this)), + limit(std::bind(&storage_base::get_connection, this)), + inMemory(filename.empty() || filename == ":memory:"), + connection(std::make_unique(std::move(filename))), + cachedForeignKeysCount(foreignKeysCount) { + if (this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + storage_base(const storage_base& other) : + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), + connection(std::make_unique(other.connection->filename)), + cachedForeignKeysCount(other.cachedForeignKeysCount) { + if (this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + ~storage_base() { + if (this->isOpenedForever) { + this->connection->release(); + } + if (this->inMemory) { + this->connection->release(); + } + } + + void begin_transaction_internal(const std::string& query) { + this->connection->retain(); + if (1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + sqlite3* db = this->connection->get(); + perform_void_exec(db, query); + } + + connection_ref get_connection() { + connection_ref res{*this->connection}; + if (1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + return res; + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + void foreign_keys(sqlite3* db, bool value) { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + perform_void_exec(db, ss.str()); + } + + bool foreign_keys(sqlite3* db) { + bool result = false; + perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + return result; + } + +#endif + void on_open_internal(sqlite3* db) { + +#if SQLITE_VERSION_NUMBER >= 3006019 + if (this->cachedForeignKeysCount) { + this->foreign_keys(db, true); + } +#endif + if (this->pragma.synchronous_ != -1) { + this->pragma.synchronous(this->pragma.synchronous_); + } + + if (this->pragma.journal_mode_ != -1) { + this->pragma.set_pragma("journal_mode", static_cast(this->pragma.journal_mode_), db); + } + + for (auto& p: this->collatingFunctions) { + 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); + } + } + + for (auto& p: this->limit.limits) { + sqlite3_limit(db, p.first, p.second); + } + + if (_busy_handler) { + sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } + + for (auto& udfProxy: this->scalarFunctions) { + try_to_create_scalar_function(db, udfProxy); + } + + for (auto& udfProxy: this->aggregateFunctions) { + try_to_create_aggregate_function(db, udfProxy); + } + + if (this->on_open) { + this->on_open(db); + } + } + + template + void create_scalar_function_impl(udf_holder udfName, std::function constructAt) { + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + using is_stateless = std::is_empty; + auto udfMemorySpace = preallocate_udf_memory(); + if SQLITE_ORM_CONSTEXPR_IF (is_stateless::value) { + constructAt(udfMemorySpace.first); + } + this->scalarFunctions.emplace_back( + udfName(), + argsCount, + is_stateless::value ? nullptr : std::move(constructAt), + /* destroy = */ + obtain_xdestroy_for(udf_destruct_only_deleter{}), + /* call = */ + [](sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto udfPointer = proxy_get_scalar_udf(is_stateless{}, context, argsCount); + args_tuple argsTuple = tuple_from_values{}(values, argsCount); + auto result = polyfill::apply(*udfPointer, std::move(argsTuple)); + statement_binder().result(context, result); + }, + udfMemorySpace); + + if (this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + try_to_create_scalar_function(db, this->scalarFunctions.back()); + } + } + + template + void create_aggregate_function_impl(udf_holder udfName, + std::function constructAt) { + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->aggregateFunctions.emplace_back( + udfName(), + argsCount, + std::move(constructAt), + /* destroy = */ + obtain_xdestroy_for(udf_destruct_only_deleter{}), + /* step = */ + [](sqlite3_context* context, int argsCount, sqlite3_value** values) { + F* udfPointer; + try { + udfPointer = proxy_get_aggregate_step_udf(context, argsCount); + } catch (const std::bad_alloc&) { + sqlite3_result_error_nomem(context); + return; + } + args_tuple argsTuple = tuple_from_values{}(values, argsCount); +#if __cpp_lib_bind_front >= 201907L + std::apply(std::bind_front(&F::step, udfPointer), std::move(argsTuple)); +#else + polyfill::apply( + [udfPointer](auto&&... args) { + udfPointer->step(std::forward(args)...); + }, + std::move(argsTuple)); +#endif + }, + /* finalCall = */ + [](void* udfHandle, sqlite3_context* context) { + F& udf = *static_cast(udfHandle); + auto result = udf.fin(); + statement_binder().result(context, result); + }, + obtain_udf_allocator()); + + if (this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + try_to_create_aggregate_function(db, this->aggregateFunctions.back()); + } + } + + void delete_function_impl(const std::string& name, std::list& functions) const { +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(functions, name, &udf_proxy::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { + return udfProxy.name == name; + }); +#endif + if (it != functions.end()) { + if (this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + int rc = sqlite3_create_function_v2(db, + name.c_str(), + it->argumentsCount, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + it = functions.erase(it); + } else { + throw std::system_error{orm_error_code::function_not_found}; + } + } + + 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); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + 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); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); + } + } + + std::string current_time(sqlite3* db) { + std::string result; + 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); + return result; + } + + std::string current_timestamp(sqlite3* db) { + std::string result; + perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + return result; + } + + void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { + std::stringstream ss; + ss << "DROP TABLE"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << streaming_identifier(tableName) << std::flush; + perform_void_exec(db, ss.str()); + } + + void drop_index_internal(const std::string& indexName, bool ifExists) { + std::stringstream ss; + ss << "DROP INDEX"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger_internal(const std::string& triggerName, bool ifExists) { + std::stringstream ss; + ss << "DROP TRIGGER"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + static int + collate_callback(void* argument, int leftLength, const void* lhs, int rightLength, const void* rhs) { + auto& function = *(collating_function*)argument; + return function(leftLength, lhs, rightLength, rhs); + } + + static int busy_handler_callback(void* selfPointer, int triesCount) { + auto& storage = *static_cast(selfPointer); + if (storage._busy_handler) { + return storage._busy_handler(triesCount); + } else { + return 0; + } + } + + bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) const { + bool notEqual = false; + + // iterate through storage columns + for (size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + table_xinfo& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + const std::string& columnName = storageColumnInfo.name; + + // search for a column in db with the same name +#if __cpp_lib_ranges >= 201911L + auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); +#else + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { + return ti.name == columnName; + }); +#endif + if (dbColumnInfoIt != dbTableInfo.end()) { + auto& dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && + dbColumnInfo.pk == storageColumnInfo.pk && + (dbColumnInfo.hidden == 0) == (storageColumnInfo.hidden == 0); + if (!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; + } + + const bool inMemory; + bool isOpenedForever = false; + std::unique_ptr connection; + std::map collatingFunctions; + const int cachedForeignKeysCount; + std::function _busy_handler; + std::list scalarFunctions; + std::list aggregateFunctions; + }; + } +} + +// #include "prepared_statement.h" + +// #include "expression_object_type.h" + +#include // std::decay, std::remove_reference +#include // std::reference_wrapper + +// #include "type_traits.h" + +// #include "prepared_statement.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct expression_object_type; + + template + using expression_object_type_t = typename expression_object_type::type; + + template + using statement_object_type_t = expression_object_type_t>>; + + template + struct expression_object_type, void> : value_unref_type {}; + + template + struct expression_object_type, void> : value_unref_type {}; + + template + struct expression_object_type> { + using type = object_type_t; + }; + + template + struct expression_object_type, void> : value_unref_type {}; + + template + struct expression_object_type, void> : value_unref_type {}; + + template + struct expression_object_type> { + using type = object_type_t; + }; + + template + struct expression_object_type, void> : value_unref_type {}; + + template + struct get_ref_t { + + template + auto& operator()(O& t) const { + return t; + } + }; + + template + struct get_ref_t> { + + template + auto& operator()(O& t) const { + return t.get(); + } + }; + + template + auto& get_ref(T& t) { + using arg_type = std::decay_t; + get_ref_t g; + return g(t); + } + + template + struct get_object_t; + + template + struct get_object_t : get_object_t {}; + + template + auto& get_object(T& t) { + using expression_type = std::decay_t; + get_object_t obj; + return obj(t); + } + + template + struct get_object_t> { + using expression_type = replace_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = insert_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = update_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + } +} + +// #include "statement_serializer.h" + +#include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction +#include // std::stringstream +#include // std::string +#include // std::vector +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +#include +#include +#include // std::list +// #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/mpl.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + +// #include "ast/group_by.h" + +// #include "ast/into.h" + +// #include "ast/match.h" + +// #include "ast/rank.h" + +namespace sqlite_orm { + namespace internal { + struct rank_t {}; + } + + inline internal::rank_t rank() { + return {}; + } +} + +// #include "ast/special_keywords.h" + +// #include "core_functions.h" + +// #include "constraints.h" + +// #include "conditions.h" + +// #include "indexed_column.h" + +// #include "function.h" + +// #include "prepared_statement.h" + +// #include "rowid.h" + +// #include "pointer_value.h" + +// #include "type_printer.h" + +// #include "field_printer.h" + +// #include "literal.h" + +// #include "expression.h" + +// #include "table_name_collector.h" + +// #include "column_names_getter.h" + +#include // std::is_base_of +#include // std::string +#include // std::vector +#include // std::reference_wrapper +#include +#include // std::move + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "mapped_type_proxy.h" + +// #include "alias_traits.h" + +// #include "select_constraints.h" + +// #include "storage_lookup.h" +// pick_table +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + auto serialize(const T& t, const Ctx& context); + + template + std::vector& collect_table_column_names(std::vector& collectedExpressions, + bool definedOrder, + const Ctx& context) { + if (definedOrder) { + auto& table = pick_table>(context.db_objects); + collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); + table.for_each_column([qualified = !context.skip_table_name, + &tableName = table.name, + &collectedExpressions](const column_identifier& column) { + if (is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + + quote_identifier(column.name)); + } else if (qualified) { + collectedExpressions.push_back(quote_identifier(tableName) + "." + + quote_identifier(column.name)); + } else { + collectedExpressions.push_back(quote_identifier(column.name)); + } + }); + } else { + collectedExpressions.reserve(collectedExpressions.size() + 1); + if (is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); + } else if (!context.skip_table_name) { + const basic_table& table = pick_table>(context.db_objects); + collectedExpressions.push_back(quote_identifier(table.name) + ".*"); + } else { + collectedExpressions.emplace_back("*"); + } + } + + return collectedExpressions; + } + + /** @short Column expression collector. + */ + struct column_names_getter { + /** + * The default implementation simply serializes the passed argument. + */ + template + std::vector& operator()(const E& t, const Ctx& context) { + auto columnExpression = serialize(t, context); + if (columnExpression.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); + this->collectedExpressions.push_back(std::move(columnExpression)); + return this->collectedExpressions; + } + + template + std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { + return (*this)(expression.get(), context); + } + + template + std::vector& operator()(const asterisk_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const object_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const columns_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if (tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + template + std::vector& operator()(const struct_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if (tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + std::vector collectedExpressions; + }; + + template + std::vector get_column_names(const T& expression, const Ctx& context) { + column_names_getter serializer; + return serializer(access_column_expression(expression), context); + } + } +} + +// #include "cte_column_names_collector.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#include // std::reference_wrapper +#include +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +// #include "error_code.h" + +// #include "alias.h" + +// #include "select_constraints.h" + +// #include "serializer_context.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { + namespace internal { + // collecting column names utilizes the statement serializer + template + auto serialize(const T& t, const Ctx& context); + + inline void unquote_identifier(std::string& identifier) { + if (!identifier.empty()) { + constexpr char quoteChar = '"'; + constexpr char sqlEscaped[] = {quoteChar, quoteChar}; + identifier.erase(identifier.end() - 1); + identifier.erase(identifier.begin()); + for (size_t pos = 0; (pos = identifier.find(sqlEscaped, pos, 2)) != identifier.npos; ++pos) { + identifier.erase(pos, 1); + } + } + } + + inline void unquote_or_erase(std::string& name) { + constexpr char quoteChar = '"'; + if (name.front() == quoteChar) { + unquote_identifier(name); + } else { + // unaliased expression - see 3. below + name.clear(); + } + } + + template + struct cte_column_names_collector { + using expression_type = T; + + // Compound statements are never passed in by db_objects_for_expression() + static_assert(!is_compound_operator_v); + + template + std::vector operator()(const expression_type& t, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + std::string columnName = serialize(t, newContext); + if (columnName.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + unquote_or_erase(columnName); + return {std::move(columnName)}; + } + }; + + template + std::vector get_cte_column_names(const T& t, const Ctx& context) { + cte_column_names_collector collector; + return collector(access_column_expression(t), context); + } + + template + struct cte_column_names_collector> { + using expression_type = As; + + template + std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { + return {alias_extractor>::extract()}; + } + }; + + template + struct cte_column_names_collector> { + using expression_type = Wrapper; + + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return get_cte_column_names(expression.get(), context); + } + }; + + template + struct cte_column_names_collector> { + using expression_type = Asterisk; + using T = typename Asterisk::type; + + template + std::vector operator()(const expression_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::vector columnNames; + columnNames.reserve(size_t(table.template count_of())); + + table.for_each_column([&columnNames](const column_identifier& column) { + columnNames.push_back(column.name); + }); + return columnNames; + } + }; + + // No CTE for object expressions. + template + struct cte_column_names_collector> { + static_assert(polyfill::always_false_v, "Selecting an object in a subselect is not allowed."); + }; + + // No CTE for object expressions. + template + struct cte_column_names_collector> { + static_assert(polyfill::always_false_v, "Repacking columns in a subselect is not allowed."); + }; + + template + struct cte_column_names_collector> { + using expression_type = Columns; + + template + std::vector operator()(const expression_type& cols, const Ctx& context) const { + std::vector columnNames; + columnNames.reserve(size_t(cols.count)); + auto newContext = context; + newContext.skip_table_name = true; + iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { + using value_type = polyfill::remove_cvref_t; + + if constexpr (polyfill::is_specialization_of_v) { + columnNames.push_back(alias_extractor>::extract()); + } else { + std::string columnName = serialize(m, newContext); + if (!columnName.empty()) { + columnNames.push_back(std::move(columnName)); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + unquote_or_erase(columnNames.back()); + } + }); + return columnNames; + } + }; + + template = true> + std::vector + collect_cte_column_names(const E& sel, const ExplicitColRefs& explicitColRefs, const Ctx& context) { + // 1. determine column names from subselect + 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()) { + throw std::system_error{orm_error_code::column_not_found}; + } + + size_t idx = 0; + iterate_tuple(explicitColRefs, [&idx, &columnNames, &context](auto& colRef) { + using ColRef = polyfill::remove_cvref_t; + + if constexpr (polyfill::is_specialization_of_v) { + columnNames[idx] = alias_extractor>::extract(); + } else if constexpr (std::is_member_pointer::value) { + using O = table_type_of_t; + if (auto* columnName = find_column_name(context.db_objects, colRef)) { + columnNames[idx] = *columnName; + } else { + // relaxed: allow any member pointer as column reference + columnNames[idx] = typeid(ColRef).name(); + } + } else if constexpr (polyfill::is_specialization_of_v) { + columnNames[idx] = colRef.name; + } else if constexpr (std::is_same_v) { + if (!colRef.empty()) { + columnNames[idx] = colRef; + } + } else if constexpr (std::is_same_v>) { + if (columnNames[idx].empty()) { + columnNames[idx] = std::to_string(idx + 1); + } + } else { + static_assert(polyfill::always_false_v, "Invalid explicit column reference specified"); + } + ++idx; + }); + } + + // 3. fill in blanks with numerical column identifiers + { + for (size_t i = 0, n = columnNames.size(); i < n; ++i) { + if (columnNames[i].empty()) { + columnNames[i] = std::to_string(i + 1); + } + } + } + + return columnNames; + } + } +} +#endif + +// #include "order_by_serializer.h" + +#include // std::string +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializer; + + template + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer serializer; + return serializer(t, context); + } + + template + struct order_by_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + + ss << serialize(orderBy.expression, newContext); + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch (orderBy.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + return ss.str(); + } + }; + + template + struct order_by_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx&) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + int index = 0; + for (const dynamic_order_by_entry_t& entry: orderBy) { + if (index > 0) { + ss << ", "; + } + + ss << entry.name; + if (!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; + } + switch (entry.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + ++index; + }; + return ss.str(); + } + }; + + } +} + +// #include "serializing_util.h" + +// #include "serialize_result_type.h" + +// #include "statement_binder.h" + +// #include "values.h" + +// #include "table_type_of.h" + +// #include "util.h" + +// #include "error_code.h" + +// #include "schema/triggers.h" + +#include +#include +#include +#include + +// #include "../optional_container.h" + +// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? +// (Could be implemented with a normal trigger that insert or update an internal table and then retreive +// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? + +namespace sqlite_orm { + namespace internal { + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; + + /** + * This class is an intermediate SQLite trigger, to be used with + * `make_trigger` to create a full trigger. + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statements + */ + template + struct partial_trigger_t { + using statements_type = std::tuple; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + statements_type statements; + + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + + partial_trigger_t& end() { + return *this; + } + }; + + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; + + /** + * This class represent a SQLite trigger + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statments + */ + template + struct trigger_t : base_trigger { + using object_type = void; + using elements_type = typename partial_trigger_t::statements_type; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + trigger_t(std::string name, T trigger_base, elements_type statements) : + base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} +#endif + }; + + /** + * Base of a trigger. Contains the trigger type/timming and the table type + * T is the table type + * W is `when` expression type + * Type is the trigger base type (type+timing) + */ + template + struct trigger_base_t { + using table_type = T; + using when_type = W; + using trigger_type_base = Type; + + /** + * Contains the trigger type and timing + */ + trigger_type_base type_base; + /** + * Value used to determine if we execute the trigger on each row or on each statement + * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description + * so this value is more of a placeholder for a later update) + */ + bool do_for_each_row = false; + /** + * When expression (if any) + * If a WHEN expression is specified, the trigger will only execute + * if the expression evaluates to true when the trigger is fired + */ + optional_container container_when; + + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} + + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; + } + + template + trigger_base_t when(WW expression) { + trigger_base_t res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } + + template + partial_trigger_t, S...> begin(S... statements) { + return {*this, std::forward(statements)...}; + } + }; + + /** + * Contains the trigger type and timing + */ + struct trigger_type_base_t { + /** + * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF + * the statement that fired it. + */ + trigger_timing timing; + /** + * The type of the statement that would cause the trigger to fire. + * Can be DELETE, INSERT, or UPDATE. + */ + trigger_type type; + + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} + + template + trigger_base_t on() { + return {*this}; + } + }; + + /** + * Special case for UPDATE OF (columns) + * Contains the trigger type and timing + */ + template + struct trigger_update_type_t : trigger_type_base_t { + using columns_type = std::tuple; + + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; + + trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : + trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} + + template + trigger_base_t> on() { + return {*this}; + } + }; + + struct trigger_timing_t { + trigger_timing timing; + + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; + } + + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; + } + + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } + + template + trigger_update_type_t update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward(columns)...}; + } + }; + + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; + + type_t type = type_t::ignore; + std::string message; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} +#endif + }; + + template + struct new_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct old_t { + using expression_type = T; + + expression_type expression; + }; + } // NAMESPACE internal + + /** + * NEW.expression function used within TRIGGER expressions + */ + template + internal::new_t new_(T expression) { + return {std::move(expression)}; + } + + /** + * OLD.expression function used within TRIGGER expressions + */ + template + internal::old_t old(T expression) { + return {std::move(expression)}; + } + + /** + * RAISE(IGNORE) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_ignore() { + return {internal::raise_t::type_t::ignore, {}}; + } + + /** + * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_rollback(std::string message) { + return {internal::raise_t::type_t::rollback, std::move(message)}; + } + + /** + * RAISE(ABORT, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_abort(std::string message) { + return {internal::raise_t::type_t::abort, std::move(message)}; + } + + /** + * RAISE(FAIL, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_fail(std::string message) { + return {internal::raise_t::type_t::fail, std::move(message)}; + } + + template + internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::move(part.base), std::move(part.statements)}); + } + + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } + + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } + + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} + +// #include "schema/column.h" + +// #include "schema/index.h" + +// #include "schema/table.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct statement_serializer; + + template + auto serialize(const T& t, const Ctx& context) { + statement_serializer serializer; + return serializer(t, context); + } + + /** + * Serializer for bindable types. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& statement, const Ctx& context) const { + if (context.replace_bindable_with_question) { + return "?"; + } else { + return this->do_serialize(statement); + } + } + + private: + template::value && !std::is_base_of::value +#ifndef SQLITE_ORM_OMITS_CODECVT + && !std::is_base_of::value +#endif + , + bool> = true> + std::string do_serialize(const X& c) const { + static_assert(std::is_same::value, ""); + + // implementation detail: utilizing field_printer + return field_printer{}(c); + } + + std::string do_serialize(const std::string& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const char* c) const { + return quote_string_literal(c); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const wchar_t* c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c)); + } +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string do_serialize(const std::string_view& c) const { + return quote_string_literal(std::string(c)); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring_view& c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c.data(), c.data() + c.size())); + } +#endif +#endif + /** + * Specialization for binary data (std::vector). + */ + std::string do_serialize(const std::vector& t) const { + return quote_blob_literal(field_printer>{}(t)); + } + +#if SQLITE_VERSION_NUMBER >= 3020000 + template + std::string do_serialize(const pointer_binding&) const { + // always serialize null (security reasons) + return field_printer{}(nullptr); + } +#endif + }; + + template + struct statement_serializer, void> { + using statement_type = table_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + return this->serialize(statement, context, statement.name); + } + + template + auto serialize(const statement_type& statement, const Ctx& context, const std::string& tableName) { + std::stringstream ss; + ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" + << streaming_expressions_tuple(statement.elements, context) << ")"; + if (statement_type::is_without_rowid_v) { + ss << " WITHOUT ROWID"; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = current_time_t; + + template + std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) { + return "CURRENT_TIME"; + } + }; + + template<> + struct statement_serializer { + using statement_type = current_date_t; + + template + std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) { + return "CURRENT_DATE"; + } + }; + + template<> + struct statement_serializer { + using statement_type = current_timestamp_t; + + template + std::string operator()(const statement_type& /*statement*/, const Ctx& /*context*/) { + return "CURRENT_TIMESTAMP"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = highlight_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + auto& tableName = lookup_table_name(context.db_objects); + ss << "HIGHLIGHT (" << streaming_identifier(tableName); + ss << ", " << serialize(statement.argument0, context); + ss << ", " << serialize(statement.argument1, context); + ss << ", " << serialize(statement.argument2, context) << ")"; + return ss.str(); + } + }; + + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + + template + struct statement_serializer, void> { + using statement_type = filtered_aggregate_function; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + ss << serialize(statement.function, context); + ss << " FILTER (WHERE " << serialize(statement.where, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = excluded_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "excluded."; + if (auto* columnName = find_column_name(context.db_objects, statement.expression)) { + ss << streaming_identifier(*columnName); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = as_optional_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.expression, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + return serialize(s.get(), context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(T::get()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = match_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << streaming_identifier(table.name) << " MATCH " << serialize(statement.argument, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = column_alias; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(statement_type::get()); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = polyfill::remove_cvref_t; + auto needParenthesis = std::is_member_pointer::value; + ss << ' '; + if (needParenthesis) { + ss << '('; + } + ss << serialize(value, context); + if (needParenthesis) { + ss << ')'; + } + }); + ss << ' ' << "DO"; + if (std::tuple_size::value == 0) { + ss << " NOTHING"; + } else { + auto updateContext = context; + updateContext.use_parentheses = false; + ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = built_in_function_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> + : statement_serializer, void> {}; + + template + struct statement_serializer, void> { + using statement_type = function_call; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + stream_identifier(ss, "", statement.name(), ""); + ss << "(" << streaming_expressions_tuple(statement.callArgs, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = as_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.expression, context) + " AS " << streaming_identifier(alias_extractor::extract()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + if (!context.skip_table_name) { + ss << streaming_identifier(alias_extractor::extract()) << "."; + } + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer< + E, + std::enable_if_t, is_column_pointer>::value>> { + using statement_type = E; + + template + std::string operator()(const statement_type& e, const Ctx& context) const { + std::stringstream ss; + if (auto* columnName = find_column_name(context.db_objects, e)) { + ss << streaming_identifier( + !context.skip_table_name ? lookup_table_name>(context.db_objects) : "", + *columnName, + ""); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = rank_t; + + template + serialize_result_type operator()(const statement_type& /*statement*/, const Ctx&) const { + return "rank"; + } + }; + + template<> + struct statement_serializer { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + return static_cast(statement); + } + }; + + template<> + struct statement_serializer { + using statement_type = oid_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + return static_cast(statement); + } + }; + + template<> + struct statement_serializer<_rowid_t, void> { + using statement_type = _rowid_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + return static_cast(statement); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(statement); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(statement); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(statement); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_equal_with_table_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + const auto tableName = lookup_table_name(context.db_objects); + ss << streaming_identifier(tableName); + ss << " = "; + ss << serialize(statement.rhs, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type& c, const Ctx&) const { + std::stringstream ss; + auto functionName = c.serialize(); + ss << functionName << "(*)"; + return ss.str(); + } + }; + + // note (internal): this is a serializer for the deduplicator in an aggregate function; + // the result set deduplicators in a simple-select are treated by the select serializer. + template + struct statement_serializer, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + // DISTINCT introduces no parentheses + auto subCtx = context; + subCtx.use_parentheses = false; + + std::stringstream ss; + auto expr = serialize(c.expression, subCtx); + ss << static_cast(c) << " " << expr; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; + return ss.str(); + } + }; + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if SQLITE_VERSION_NUMBER >= 3035003 +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template<> + struct statement_serializer { + using statement_type = materialized_t; + + template + std::string_view operator()(const statement_type& /*statement*/, const Ctx& /*context*/) const { + return "MATERIALIZED"; + } + }; + + template<> + struct statement_serializer { + using statement_type = not_materialized_t; + + template + std::string_view operator()(const statement_type& /*statement*/, const Ctx& /*context*/) const { + return "NOT MATERIALIZED"; + } + }; +#endif +#endif + + template + struct statement_serializer> { + using statement_type = CTE; + + template + std::string operator()(const statement_type& cte, const Ctx& context) const { + // A CTE always starts a new 'highest level' context + Ctx cteContext = context; + cteContext.use_parentheses = false; + + std::stringstream ss; + ss << streaming_identifier(alias_extractor>::extract()); + { + std::vector columnNames = + collect_cte_column_names(get_cte_driving_subselect(cte.subselect), + cte.explicitColumns, + context); + ss << '(' << streaming_identifiers(columnNames) << ')'; + } + ss << " AS" << streaming_constraints_tuple(cte.hints, context) << " (" + << serialize(cte.subselect, cteContext) << ')'; + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = With; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + Ctx tupleContext = context; + tupleContext.use_parentheses = false; + + std::stringstream ss; + ss << "WITH"; + if (c.recursiveIndicated) { + ss << " RECURSIVE"; + } + ss << " " << serialize(c.cte, tupleContext); + ss << " " << serialize(c.expression, context); + return ss.str(); + } + }; +#endif + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << streaming_compound_expressions(c.compound, static_cast(c), context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << "CASE "; + c.case_expression.apply([&ss, context](auto& c_) { + ss << serialize(c_, context) << " "; + }); + iterate_tuple(c.args, [&ss, context](auto& pair) { + ss << "WHEN " << serialize(pair.first, context) << " "; + ss << "THEN " << serialize(pair.second, context) << " "; + }); + c.else_expression.apply([&ss, context](auto& el) { + ss << "ELSE " << serialize(el, context) << " "; + }); + ss << "END"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer< + T, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = T; + + template + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + + std::stringstream ss; + ss << expression.serialize(); + if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.argument), subCtx); + if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + + std::stringstream ss; + ss << static_cast(expression) << " "; + if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.c), subCtx); + if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer< + T, + std::enable_if_t, is_binary_operator>::value>> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesizeLeft = is_binary_condition>::value || + is_binary_operator>::value; + constexpr bool parenthesizeRight = is_binary_condition>::value || + is_binary_operator>::value; + + std::stringstream ss; + if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + ss << "("; + } + ss << serialize(statement.lhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + ss << ")"; + } + ss << " " << statement.serialize() << " "; + if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + ss << "("; + } + ss << serialize(statement.rhs, subCtx); + if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer< + dynamic_in_t, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = dynamic_in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if (!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + if (is_compound_operator::value) { + ss << '('; + } + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(statement.argument, newContext); + if (is_compound_operator::value) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer< + dynamic_in_t, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = dynamic_in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if (!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " (" << streaming_dynamic_expressions(statement.argument, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if (!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + using args_type = std::tuple; + constexpr bool theOnlySelect = + std::tuple_size::value == 1 && is_select>::value; + if (!theOnlySelect) { + ss << "("; + } + ss << streaming_expressions_tuple(statement.argument, context); + if (!theOnlySelect) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + c.arg3.apply([&ss, &context](auto& value) { + ss << " ESCAPE " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.expr, context); + ss << expr << " " << static_cast(c) << " "; + ss << serialize(c.b1, context); + ss << " AND "; + ss << serialize(c.b2, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.use_parentheses = true; + ss << "EXISTS " << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_clause_t; + + template + serialize_result_type operator()(const statement_type& statement, const Ctx&) const { + switch (statement) { + case conflict_clause_t::rollback: + return "ROLLBACK"; + case conflict_clause_t::abort: + return "ABORT"; + case conflict_clause_t::fail: + return "FAIL"; + case conflict_clause_t::ignore: + return "IGNORE"; + case conflict_clause_t::replace: + return "REPLACE"; + } + return {}; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.as_base(), context) + " AUTOINCREMENT"; + } + }; + + template<> + struct statement_serializer { + using statement_type = null_t; + + template + serialize_result_type operator()(const statement_type& /*statement*/, const Ctx& /*context*/) const { + return "NULL"; + } + }; + + template<> + struct statement_serializer { + using statement_type = not_null_t; + + template + serialize_result_type operator()(const statement_type& /*statement*/, const Ctx& /*context*/) const { + return "NOT NULL"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "PRIMARY KEY"; + switch (statement.options.asc_option) { + case statement_type::order_by::ascending: + ss << " ASC"; + break; + case statement_type::order_by::descending: + ss << " DESC"; + break; + default: + break; + } + if (statement.options.conflict_clause_is_on) { + ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); + } + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if (columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = unique_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if (columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; + } + return ss.str(); + } + }; + +#if SQLITE_VERSION_NUMBER >= 3009000 + template<> + struct statement_serializer { + using statement_type = unindexed_t; + + template + serialize_result_type operator()(const statement_type& /*statement*/, const Ctx& /*context*/) const { + return "UNINDEXED"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = prefix_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "prefix=" << serialize(statement.value, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = tokenize_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "tokenize = " << serialize(statement.value, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = content_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "content=" << serialize(statement.value, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_content_t; + + template + std::string operator()(const statement_type& /*statement*/, const Ctx& context) const { + using mapped_type = typename statement_type::mapped_type; + + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "content=" << streaming_identifier(table.name); + return ss.str(); + } + }; +#endif + + template<> + struct statement_serializer { + using statement_type = collate_constraint_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + return static_cast(statement); + } + }; + + template + struct statement_serializer, void> { + using statement_type = default_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return static_cast(statement) + " (" + serialize(statement.value, context) + ")"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3006019 + template + struct statement_serializer, std::tuple>, void> { + using statement_type = foreign_key_t, std::tuple>; + + template + std::string operator()(const statement_type& fk, const Ctx& context) const { + std::stringstream ss; + ss << "FOREIGN KEY(" << streaming_mapped_columns_expressions(fk.columns, context) << ") REFERENCES "; + { + using references_type_t = typename statement_type::references_type; + using first_reference_t = std::tuple_element_t<0, references_type_t>; + using first_reference_mapped_type = table_type_of_t; + auto refTableName = lookup_table_name(context.db_objects); + ss << streaming_identifier(refTableName); + } + ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; + if (fk.on_update) { + ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; + } + if (fk.on_delete) { + ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; + } + return ss.str(); + } + }; +#endif + + template + struct statement_serializer, void> { + using statement_type = check_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CHECK (" << serialize(statement.expression, context) << ")"; + return ss.str(); + } + }; +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct statement_serializer, void> { + using statement_type = generated_always_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (statement.full) { + ss << "GENERATED ALWAYS "; + } + ss << "AS ("; + ss << serialize(statement.expression, context) << ")"; + switch (statement.storage) { + case basic_generated_always::storage_type::not_specified: + //.. + break; + case basic_generated_always::storage_type::virtual_: + ss << " VIRTUAL"; + break; + case basic_generated_always::storage_type::stored: + ss << " STORED"; + break; + } + return ss.str(); + } + }; +#endif + template + struct statement_serializer, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type& column, const Ctx& context) const { + std::stringstream ss; + ss << streaming_identifier(column.name); + if (!context.fts5_columns) { + ss << " " << type_printer>>().print(); + } + ss << streaming_column_constraints( + call_as_template_base(polyfill::identity{})(column), + column.is_not_null(), + context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_all_t; + + template + std::string operator()(const statement_type& rem, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) + << streaming_conditions_tuple(rem.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")" + << " VALUES (" + << streaming_field_values_excluding(check_if{}, + empty_callable, // don't exclude + context, + get_ref(statement.object)) + << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type& ins, const Ctx& context) const { + constexpr size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + ss << "(" << streaming_mapped_columns_expressions(ins.columns.columns, context) << ") " + << "VALUES ("; + iterate_tuple(ins.columns.columns, + [&ss, &context, &object = get_ref(ins.obj), first = true](auto& memberPointer) mutable { + using member_pointer_type = std::remove_reference_t; + static_assert(!is_setter_v, + "Unable to use setter within insert explicit"); + + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(memberPointer, object), context); + }); + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = update_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(table.name) << " SET "; + table.template for_each_column_excluding>( + [&table, &ss, &context, &object = get_ref(statement.object), first = true](auto& column) mutable { + if (exists_in_composite_primary_key(table, column)) { + return; + } + + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + ss << " WHERE "; + table.for_each_column( + [&table, &context, &ss, &object = get_ref(statement.object), first = true](auto& column) mutable { + if (!column.template is() && !exists_in_composite_primary_key(table, column)) { + return; + } + + static constexpr std::array sep = {" AND ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_set_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + std::stringstream ss; + ss << "SET "; + int index = 0; + for (const dynamic_set_entry& entry: statement) { + if (index > 0) { + ss << ", "; + } + ss << entry.serialized_value; + ++index; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = set_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "SET "; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' + << value.serialize() << ' ' << serialize(value.rhs, context); + }); + return ss.str(); + } + }; + + template + std::set> collect_table_names(const set_t& set, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_tuple(set.assigns, [&collector](const auto& assignmentOperator) { + iterate_ast(assignmentOperator.lhs, collector); + }); + return std::move(collector.table_names); + } + + template + const std::set>& collect_table_names(const dynamic_set_t& set, + const Ctx&) { + return set.collector.table_names; + } + + template = true> + std::set> collect_table_names(const T& sel, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + iterate_ast(sel, collector); + return std::move(collector.table_names); + } + + template + struct statement_serializer, void> { + using statement_type = update_all_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + const auto& tableNames = collect_table_names(statement.set, context); + if (tableNames.empty()) { + throw std::system_error{orm_error_code::no_tables_specified}; + } + const std::string& tableName = tableNames.begin()->first; + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(tableName) << ' ' << serialize(statement.set, context) + << streaming_conditions_tuple(statement.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if (exists_in_composite_primary_key(table, column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if (columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES"; + if (columnNamesCount) { + ss << " (" + << streaming_field_values_excluding( + mpl::conjunction>, + mpl::disjunction_fn>{}, + [&table](auto& column) { + return exists_in_composite_primary_key(table, column); + }, + context, + get_ref(statement.object)) + << ")"; + } + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = into_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "INTO " << streaming_identifier(table.name); + return ss.str(); + } + }; + + template + struct statement_serializer, is_struct>::value>> { + using statement_type = C; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; + + std::stringstream ss; + if (context.use_parentheses) { + ss << '('; + } + ss << streaming_serialized(get_column_names(statement, subCtx)); + if (context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer< + T, + std::enable_if_t, is_replace_raw>::value>> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (is_insert_raw::value) { + ss << "INSERT"; + } else { + ss << "REPLACE"; + } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = polyfill::remove_cvref_t; + ss << ' '; + if (is_columns::value) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if (is_values::value || is_select::value) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); + } + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) << " " + << "WHERE "; + std::vector idsStrings; + idsStrings.reserve(std::tuple_size::value); + iterate_tuple(statement.ids, [&idsStrings, &context](auto& idValue) { + idsStrings.push_back(serialize(idValue, context)); + }); + table.for_each_primary_key_column([&table, &ss, &idsStrings, index = 0](auto& memberPointer) mutable { + auto* columnName = table.find_column_name(memberPointer); + if (!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + static constexpr std::array sep = {" AND ", ""}; + ss << sep[index == 0] << streaming_identifier(*columnName) << " = " << idsStrings[index]; + ++index; + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type& rep, const Ctx& context) const { + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")"; + const auto valuesCount = std::distance(rep.range.first, rep.range.second); + const auto columnsCount = table.template count_of_columns_excluding(); + ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = expression_object_type_t; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if (exists_in_composite_primary_key(table, column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t valuesCount = std::distance(statement.range.first, statement.range.second); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if (columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES "; + if (columnNamesCount) { + ss << streaming_values_placeholders(columnNamesCount, valuesCount); + } else if (valuesCount != 1) { + throw std::system_error{orm_error_code::cannot_use_default_value}; + } + return ss.str(); + } + }; + + template + std::string serialize_get_all_impl(const T& getAll, const Ctx& context) { + using table_type = type_t; + using mapped_type = mapped_type_proxy_t; + + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, alias_extractor::as_qualifier(table)) + << " FROM " << streaming_identifier(table.name, alias_extractor::as_alias()) + << streaming_conditions_tuple(getAll.conditions, context); + return ss.str(); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializer, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_all_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + std::string serialize_get_impl(const T&, const Ctx& context) { + using primary_type = type_t; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, std::string{}) << " FROM " + << streaming_identifier(table.name) << " WHERE "; + + auto primaryKeyColumnNames = table.primary_key_column_names(); + if (primaryKeyColumnNames.empty()) { + throw std::system_error{orm_error_code::table_has_no_primary_key_column}; + } + + for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + if (i > 0) { + ss << " AND "; + } + ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; + } + return ss.str(); + } + + template + struct statement_serializer, void> { + using statement_type = get_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_action; + + template + serialize_result_type operator()(const statement_type& statement, const Ctx&) const { + switch (statement) { + case conflict_action::replace: + return "REPLACE"; + case conflict_action::abort: + return "ABORT"; + case conflict_action::fail: + return "FAIL"; + case conflict_action::ignore: + return "IGNORE"; + case conflict_action::rollback: + return "ROLLBACK"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << "OR " << serialize(statement.action, context); + return ss.str(); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializer, void> { + using statement_type = select_t; + using return_type = typename statement_type::return_type; + + template + std::string operator()(const statement_type& sel, Ctx context) const { + context.skip_table_name = false; + // subqueries should always use parentheses in column names + auto subCtx = context; + subCtx.use_parentheses = true; + + std::stringstream ss; + if (!is_compound_operator::value) { + if (!sel.highest_level && context.use_parentheses) { + ss << "("; + } + ss << "SELECT "; + call_if_constexpr>( + // note: make use of implicit to-string conversion + [&ss](std::string keyword) { + ss << keyword << ' '; + }, + sel.col); + } + + 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) { + auto tableNames = collect_table_names(sel, context); + using joins_index_sequence = filter_tuple_sequence_t; + // deduplicate table names of constrained join statements + iterate_tuple(sel.conditions, joins_index_sequence{}, [&tableNames, &context](auto& join) { + using original_join_type = typename std::remove_reference_t::type; + using cross_join_type = mapped_type_proxy_t; + std::pair tableNameWithAlias{ + lookup_table_name(context.db_objects), + alias_extractor::as_alias()}; + tableNames.erase(tableNameWithAlias); + }); + if (!tableNames.empty() && !is_compound_operator::value) { + ss << " FROM " << streaming_identifiers(tableNames); + } + } + ss << streaming_conditions_tuple(sel.conditions, context); + if (!is_compound_operator::value) { + if (!sel.highest_level && context.use_parentheses) { + ss << ")"; + } + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = indexed_column_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << serialize(statement.column_or_expression, context); + if (!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; + } + if (statement._order) { + switch (statement._order) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + } + return ss.str(); + } + }; + +#if SQLITE_VERSION_NUMBER >= 3009000 + template + struct statement_serializer, void> { + using statement_type = using_fts5_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "USING FTS5("; + auto subContext = context; + subContext.fts5_columns = true; + ss << streaming_expressions_tuple(statement.columns, subContext) << ")"; + return ss.str(); + } + }; +#endif + + template + struct statement_serializer, void> { + using statement_type = virtual_table_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE VIRTUAL TABLE IF NOT EXISTS "; + ss << streaming_identifier(statement.name) << ' '; + ss << serialize(statement.module_details, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = index_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + if (statement.unique) { + ss << "UNIQUE "; + } + using indexed_type = typename statement_type::table_mapped_type; + ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " + << streaming_identifier(lookup_table_name(context.db_objects)); + std::vector columnNames; + std::string whereString; + iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { + using value_type = polyfill::remove_cvref_t; + if (!is_where::value) { + auto newContext = context; + newContext.use_parentheses = false; + auto whereString = serialize(value, newContext); + columnNames.push_back(std::move(whereString)); + } else { + auto columnName = serialize(value, context); + whereString = std::move(columnName); + } + }); + ss << " (" << streaming_serialized(columnNames) << ")"; + if (!whereString.empty()) { + ss << ' ' << whereString; + } + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = From; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + std::stringstream ss; + ss << "FROM "; + iterate_tuple([&context, &ss, first = true](auto* dummyItem) mutable { + using table_type = std::remove_pointer_t; + + static constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::as_alias()); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = old_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "OLD."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = new_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "NEW."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = raise_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + switch (statement.type) { + case raise_t::type_t::ignore: + return "RAISE(IGNORE)"; + + case raise_t::type_t::rollback: + return "RAISE(ROLLBACK, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::abort: + return "RAISE(ABORT, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::fail: + return "RAISE(FAIL, " + serialize(statement.message, context) + ")"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_timing; + + template + serialize_result_type operator()(const statement_type& statement, const Ctx&) const { + switch (statement) { + case trigger_timing::trigger_before: + return "BEFORE"; + case trigger_timing::trigger_after: + return "AFTER"; + case trigger_timing::trigger_instead_of: + return "INSTEAD OF"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type; + + template + serialize_result_type operator()(const statement_type& statement, const Ctx&) const { + switch (statement) { + case trigger_type::trigger_delete: + return "DELETE"; + case trigger_type::trigger_insert: + return "INSERT"; + case trigger_type::trigger_update: + return "UPDATE"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " " << serialize(statement.type, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_update_type_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " UPDATE OF " + << streaming_mapped_columns_expressions(statement.columns, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.type_base, context); + ss << " ON " << streaming_identifier(lookup_table_name(context.db_objects)); + if (statement.do_for_each_row) { + ss << " FOR EACH ROW"; + } + statement.container_when.apply([&ss, &context](auto& value) { + ss << " WHEN " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + + ss << "TRIGGER IF NOT EXISTS " << streaming_identifier(statement.name) << " " + << serialize(statement.base, context); + ss << " BEGIN "; + iterate_tuple(statement.elements, [&ss, &context](auto& element) { + using element_type = polyfill::remove_cvref_t; + if (is_select::value) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(element, newContext); + } else { + ss << serialize(element, context); + } + ss << ";"; + }); + ss << " END"; + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = where_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); + ss << '(' << whereString << ')'; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + ss << serialize_order_by(orderBy, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + return serialize_order_by(orderBy, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " " << streaming_expressions_tuple(orderBy.args, context); + return ss.str(); + } + }; + + template + struct statement_serializer< + Join, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>(context.db_objects)); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>>(context.db_objects), + alias_extractor>::as_alias()) + << " " << serialize(join.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = on_t; + + template + std::string operator()(const statement_type& on, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(on) << " " << serialize(on.arg, newContext) << " "; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_with_having; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " + << serialize(statement.expression, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); + return ss.str(); + } + }; + + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializer, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type& limt, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = false; + std::stringstream ss; + ss << static_cast(limt) << " "; + if (HO) { + if (OI) { + limt.off.apply([&newContext, &ss](auto& value) { + ss << serialize(value, newContext); + }); + ss << ", "; + ss << serialize(limt.lim, newContext); + } else { + ss << serialize(limt.lim, newContext) << " OFFSET "; + limt.off.apply([&newContext, &ss](auto& value) { + ss << serialize(value, newContext); + }); + } + } else { + ss << serialize(limt.lim, newContext); + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = default_values_t; + + template + serialize_result_type operator()(const statement_type&, const Ctx&) const { + return "DEFAULT VALUES"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = using_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = std::tuple; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (context.use_parentheses) { + ss << '('; + } + ss << streaming_expressions_tuple(statement, context); + if (context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + Ctx tupleContext = context; + tupleContext.use_parentheses = true; + ss << streaming_expressions_tuple(statement.tuple, tupleContext); + } + if (context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if (context.use_parentheses) { + ss << '('; + } + ss << "VALUES " << streaming_dynamic_expressions(statement.vector, context); + if (context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + } +} + +// #include "serializer_context.h" + +// #include "schema/triggers.h" + +// #include "object_from_column_builder.h" + +// #include "row_extractor.h" + +// #include "schema/table.h" + +// #include "schema/column.h" + +// #include "schema/index.h" + +// #include "cte_storage.h" + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#include +#include +#endif + +// #include "tuple_helper/tuple_fy.h" + +// #include "table_type_of.h" + +// #include "column_result.h" + +// #include "select_constraints.h" + +// #include "schema/table.h" + +// #include "alias.h" + +// #include "cte_types.h" + +// #include "cte_column_names_collector.h" + +// #include "column_expression.h" + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic +#include // std::tuple +#include // std::reference_wrapper + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "type_traits.h" + +// #include "select_constraints.h" + +// #include "alias.h" + +// #include "storage_traits.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct column_expression_type; + + /** + * Obains the expressions that form the columns of a subselect statement. + */ + template + using column_expression_of_t = typename column_expression_type::type; + + /** + * Identity. + */ + template + struct column_expression_type { + using type = E; + }; + + /** + * Resolve column alias. + * as_t -> as_t + */ + template + struct column_expression_type> { + using type = as_t, column_expression_of_t>>; + }; + + /** + * Resolve reference wrapper. + * reference_wrapper -> T& + */ + template + struct column_expression_type, void> + : std::add_lvalue_reference> {}; + + // No CTE for object expression. + template + struct column_expression_type, void> { + static_assert(polyfill::always_false_v, "Selecting an object in a subselect is not allowed."); + }; + + /** + * Resolve all columns of a mapped object or CTE. + * asterisk_t -> tuple + */ + template + struct column_expression_type, + std::enable_if_t>, + is_cte_moniker>::value>> + : storage_traits::storage_mapped_column_expressions {}; + + template + struct add_column_alias { + template + using apply_t = alias_column_t; + }; + /** + * Resolve all columns of an aliased object. + * asterisk_t -> tuple...> + */ + template + struct column_expression_type, match_if> + : tuple_transformer>::type, + add_column_alias::template apply_t> {}; + + /** + * Resolve multiple columns. + * columns_t -> tuple + */ + template + struct column_expression_type, void> { + using type = std::tuple>...>; + }; + + /** + * Resolve multiple columns. + * struct_t -> tuple + */ + template + struct column_expression_type, void> { + using type = std::tuple>...>; + }; + + /** + * Resolve column(s) of subselect. + * select_t -> ColExpr, tuple + */ + template + struct column_expression_type> : column_expression_type {}; + } +} + +// #include "storage_lookup.h" + +namespace sqlite_orm { + namespace internal { + +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + // F = field_type + template + struct create_cte_mapper { + using type = subselect_mapper; + }; + + // std::tuple + template + struct create_cte_mapper> { + using type = subselect_mapper; + }; + + template + using create_cte_mapper_t = + typename create_cte_mapper:: + type; + + // aliased column expressions, explicit or implicitly numbered + template = true> + static 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); + } + + // F O::* + template = true> + static auto make_cte_column(std::string name, const ColRef& finalColRef) { + using object_type = table_type_of_t; + using column_type = column_t; + + return column_type{std::move(name), finalColRef, empty_setter{}}; + } + + /** + * Concatenate newly created tables with given DBOs, forming a new set of DBOs. + */ + template + auto db_objects_cat(const DBOs& dbObjects, std::index_sequence, CTETables&&... cteTables) { + return std::tuple{std::forward(cteTables)..., get(dbObjects)...}; + } + + /** + * Concatenate newly created tables with given DBOs, forming a new set of DBOs. + */ + template + auto db_objects_cat(const DBOs& dbObjects, CTETables&&... cteTables) { + return db_objects_cat(dbObjects, + std::make_index_sequence>{}, + std::forward(cteTables)...); + } + + /** + * This function returns the expression contained in a subselect that is relevant for + * creating the definition of a CTE table. + * Because CTEs can recursively refer to themselves in a compound statement, parsing + * the whole compound statement would lead to compiler errors if a column_pointer<> + * can't be resolved. Therefore, at the time of building a CTE table, we are only + * interested in the column results of the left-most select expression. + */ + template + decltype(auto) get_cte_driving_subselect(const Select& subSelect); + + /** + * Return given select expression. + */ + template + decltype(auto) get_cte_driving_subselect(const Select& subSelect) { + return subSelect; + } + + /** + * Return left-most select expression of compound statement. + */ + template, bool> = true> + decltype(auto) get_cte_driving_subselect(const select_t& subSelect) { + return std::get<0>(subSelect.col.compound); + } + + /** + * Return a tuple of member pointers of all columns + */ + template + auto get_table_columns_fields(const C& coldef, std::index_sequence) { + return std::make_tuple(get(coldef).member_pointer...); + } + + // any expression -> numeric column alias + template>, bool> = true> + auto extract_colref_expressions(const DBOs& /*dbObjects*/, const E& /*col*/, std::index_sequence = {}) + -> std::tuple())>> { + return {}; + } + + // expression_t<> + template + auto + extract_colref_expressions(const DBOs& dbObjects, const expression_t& col, std::index_sequence s = {}) { + return extract_colref_expressions(dbObjects, col.value, s); + } + + // F O::* (field/getter) -> field/getter + template + auto extract_colref_expressions(const DBOs& /*dbObjects*/, F O::* col, std::index_sequence = {}) { + return std::make_tuple(col); + } + + // as_t<> (aliased expression) -> alias_holder + template + std::tuple> extract_colref_expressions(const DBOs& /*dbObjects*/, + const as_t& /*col*/, + std::index_sequence = {}) { + return {}; + } + + // alias_holder<> (colref) -> alias_holder + template + std::tuple> extract_colref_expressions(const DBOs& /*dbObjects*/, + const alias_holder& /*col*/, + std::index_sequence = {}) { + return {}; + } + + // column_pointer<> + template + auto extract_colref_expressions(const DBOs& dbObjects, + const column_pointer& col, + std::index_sequence s = {}) { + return extract_colref_expressions(dbObjects, col.field, s); + } + + // column expression tuple + template + auto extract_colref_expressions(const DBOs& dbObjects, + const std::tuple& cols, + std::index_sequence) { + return std::tuple_cat(extract_colref_expressions(dbObjects, get(cols), std::index_sequence{})...); + } + + // columns_t<> + template + auto extract_colref_expressions(const DBOs& dbObjects, const columns_t& cols) { + return extract_colref_expressions(dbObjects, cols.columns, std::index_sequence_for{}); + } + + // asterisk_t<> -> fields + template + auto extract_colref_expressions(const DBOs& dbObjects, const asterisk_t& /*col*/) { + using table_type = storage_pick_table_t; + using elements_t = typename table_type::elements_type; + using column_idxs = filter_tuple_sequence_t; + + auto& table = pick_table(dbObjects); + return get_table_columns_fields(table.elements, column_idxs{}); + } + + template + void extract_colref_expressions(const DBOs& /*dbObjects*/, const select_t& /*subSelect*/) = delete; + + template, bool> = true> + void extract_colref_expressions(const DBOs& /*dbObjects*/, const Compound& /*subSelect*/) = delete; + + /* + * Depending on ExplicitColRef's type returns either the explicit column reference + * or the expression's column reference otherwise. + */ + template + auto determine_cte_colref(const DBOs& /*dbObjects*/, + const SubselectColRef& subselectColRef, + const ExplicitColRef& explicitColRef) { + if constexpr (polyfill::is_specialization_of_v) { + return explicitColRef; + } else if constexpr (std::is_member_pointer::value) { + return explicitColRef; + } else if constexpr (std::is_base_of_v) { + return explicitColRef.member_pointer; + } else if constexpr (std::is_same_v) { + return subselectColRef; + } else if constexpr (std::is_same_v>) { + return subselectColRef; + } else { + static_assert(polyfill::always_false_v, "Invalid explicit column reference specified"); + } + } + + template + auto determine_cte_colrefs([[maybe_unused]] const DBOs& dbObjects, + const SubselectColRefs& subselectColRefs, + [[maybe_unused]] const ExplicitColRefs& explicitColRefs, + std::index_sequence) { + if constexpr (std::tuple_size_v != 0) { + static_assert( + (!is_builtin_numeric_column_alias_v< + alias_holder_type_or_none_t>> && + ...), + "Numeric column aliases are reserved for referencing columns locally within a single CTE."); + + return std::tuple{ + determine_cte_colref(dbObjects, get(subselectColRefs), get(explicitColRefs))...}; + } else { + return subselectColRefs; + } + } + + template + auto make_cte_table_using_column_indices(const DBOs& /*dbObjects*/, + std::string tableName, + std::vector columnNames, + const ColRefs& finalColRefs, + std::index_sequence) { + return make_table( + std::move(tableName), + make_cte_column>(std::move(columnNames.at(CIs)), + get(finalColRefs))...); + } + + template + auto make_cte_table(const DBOs& dbObjects, const CTE& cte) { + using cte_type = CTE; + + auto subSelect = get_cte_driving_subselect(cte.subselect); + + using subselect_type = decltype(subSelect); + using column_results = column_result_of_t; + using index_sequence = std::make_index_sequence>>; + static_assert(cte_type::explicit_colref_count == 0 || + cte_type::explicit_colref_count == index_sequence::size(), + "Number of explicit columns of common table expression doesn't match the number of columns " + "in the subselect."); + + std::string tableName = alias_extractor>::extract(); + auto subselectColRefs = extract_colref_expressions(dbObjects, subSelect.col); + const auto& finalColRefs = + determine_cte_colrefs(dbObjects, subselectColRefs, cte.explicitColumns, index_sequence{}); + + serializer_context context{dbObjects}; + std::vector columnNames = collect_cte_column_names(subSelect, cte.explicitColumns, context); + + using mapper_type = create_cte_mapper_t, + typename cte_type::explicit_colrefs_tuple, + column_expression_of_t, + decltype(subselectColRefs), + polyfill::remove_cvref_t, + column_results>; + return make_cte_table_using_column_indices(dbObjects, + std::move(tableName), + std::move(columnNames), + finalColRefs, + index_sequence{}); + } + + template + decltype(auto) make_recursive_cte_db_objects(const DBOs& dbObjects, + const common_table_expressions& cte, + std::index_sequence) { + auto tbl = make_cte_table(dbObjects, get(cte)); + + if constexpr (sizeof...(In) > 0) { + return make_recursive_cte_db_objects( + // Because CTEs can depend on their predecessor we recursively pass in a new set of DBOs + db_objects_cat(dbObjects, std::move(tbl)), + cte, + std::index_sequence{}); + } else { + return db_objects_cat(dbObjects, std::move(tbl)); + } + } + + /** + * Return new DBOs for CTE expressions. + */ + template = true> + decltype(auto) db_objects_for_expression(DBOs& dbObjects, const with_t& e) { + return make_recursive_cte_db_objects(dbObjects, e.cte, std::index_sequence_for{}); + } +#endif + } +} + +// #include "util.h" + +// #include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_printable_v`, `is_bindable_v`. + */ + template + struct indirectly_test_preparable; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v< + S, + E, + polyfill::void_t().prepare(std::declval()))>>> = + true; + + /** + * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` + * function. + */ + template + struct storage_t : storage_base { + using self = storage_t; + using db_objects_type = db_objects_tuple; + + /** + * @param filename database filename. + * @param dbObjects db_objects_tuple + */ + storage_t(std::string filename, db_objects_type dbObjects) : + storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + + storage_t(const storage_t&) = default; + + private: + db_objects_type db_objects; + + /** + * Obtain a storage_t's const db_objects_tuple. + * + * @note Historically, `serializer_context_builder` was declared friend, along with + * a few other library stock objects, in order to limit access to the db_objects_tuple. + * However, one could gain access to a storage_t's db_objects_tuple through + * `serializer_context_builder`, hence leading the whole friend declaration mambo-jumbo + * ad absurdum. + * Providing a free function is way better and cleaner. + * + * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. + */ + friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + return storage.db_objects; + } + + template + void create_table(sqlite3* db, const std::string& tableName, const Table& table) { + using context_t = serializer_context; + + context_t context{this->db_objects}; + statement_serializer serializer; + std::string sql = serializer.serialize(table, context, tableName); + perform_void_exec(db, std::move(sql)); + } + + /** + * Copies sourceTableName to another table with name: destinationTableName + * Performs INSERT INTO %destinationTableName% () SELECT %table.column_names% FROM %sourceTableName% + */ + template + void copy_table(sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const; + +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + perform_void_exec(db, ss.str()); + } +#endif + + template + void drop_create_with_loss(sqlite3* db, const Table& table) { + // eliminated all transaction handling + this->drop_table_internal(db, table.name, false); + this->create_table(db, table.name, table); + } + + template + void backup_table(sqlite3* db, const Table& table, const std::vector& columnsToIgnore) { + + // here we copy source table to another with a name with '_backup' suffix, but in case table with such + // a name already exists we append suffix 1, then 2, etc until we find a free name.. + auto backupTableName = table.name + "_backup"; + if (this->table_exists(db, backupTableName)) { + int suffix = 1; + do { + std::stringstream ss; + ss << suffix << std::flush; + auto anotherBackupTableName = backupTableName + ss.str(); + if (!this->table_exists(db, anotherBackupTableName)) { + backupTableName = std::move(anotherBackupTableName); + break; + } + ++suffix; + } while (true); + } + this->create_table(db, backupTableName, table); + + this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); + + this->drop_table_internal(db, table.name, false); + + this->rename_table(db, backupTableName, table.name); + } + + template + void assert_mapped_type() const { + static_assert(tuple_has_type::value, + "type is not mapped to storage"); + } + + 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 col_index_sequence = filter_tuple_sequence_t; + using pk_index_sequence = filter_tuple_sequence_t; + using pkcol_index_sequence = col_index_sequence_with; + constexpr size_t dedicatedPrimaryKeyColumnsCount = + nested_tuple_size_for_t::value; + + constexpr size_t primaryKeyColumnsCount = + dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size(); + constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount; + static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated"); + static_assert( + nonPrimaryKeysColumnsCount > 0, + "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); +#endif + } + + template, + std::enable_if_t = true> + void assert_insertable_type() const {} + + template, + std::enable_if_t = true> + void assert_insertable_type() const { + using elements_type = elements_type_t
; + using pkcol_index_sequence = col_index_sequence_with; + static_assert( + count_filtered_tuple::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert(count_filtered_tuple::template fn, + pkcol_index_sequence>::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); + } + + template + auto& get_table() const { + return pick_table(this->db_objects); + } + + template + auto& get_table() { + return pick_table(this->db_objects); + } + + public: + template, class... Args> + mapped_view iterate(Args&&... args) { + this->assert_mapped_type(); + + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto iterate(Args&&... args) { + return this->iterate(std::forward(args)...); + } +#endif + +#if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) + template +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + requires (is_select_v