#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 "sqlite3.h" #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 // 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" #include // #include "cxx_universal.h" // #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; } 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 } #pragma once namespace sqlite_orm { namespace internal { enum class collate_argument { binary, nocase, rtrim, }; } } #pragma once #include // std::system_error #include // std::ostream #include // std::string #include // std::tuple #include // std::is_base_of, std::false_type, std::true_type // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl.h" /* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, 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_universal.h" // ::size_t // #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/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 "../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 // #include "../functional/cxx_universal.h" // ::size_t // #include "../functional/mpl/conditional.h" // #include "../functional/index_sequence_util.h" #include // std::index_sequence // #include "../functional/cxx_universal.h" // ::size_t 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 size_t 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 "type_traits.h" // #include "collate_argument.h" // #include "error_code.h" #include "sqlite3.h" #include // std::error_code, std::system_error #include // std::string #include #include // std::ostringstream #include namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @note We don't bother listing all possible values; this also allows for compatibility with 'Construction rules for enum class values (P0138R2)' */ enum class sqlite_errc {}; enum class orm_error_code { not_found = 1, type_is_not_mapped_to_storage, trying_to_dereference_null_iterator, too_many_tables_specified, incorrect_set_fields_specified, column_not_found, table_has_no_primary_key_column, cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, incorrect_order, cannot_use_default_value, arguments_count_does_not_match, function_not_found, index_is_out_of_bounds, value_is_null, no_tables_specified, }; } namespace std { template<> struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; template<> struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } namespace sqlite_orm { class orm_error_category : public std::error_category { public: const char* name() const noexcept override final { return "ORM error"; } std::string message(int c) const override final { switch(static_cast(c)) { case orm_error_code::not_found: return "Not found"; case orm_error_code::type_is_not_mapped_to_storage: return "Type is not mapped to storage"; case orm_error_code::trying_to_dereference_null_iterator: return "Trying to dereference null iterator"; case orm_error_code::too_many_tables_specified: return "Too many tables specified"; case orm_error_code::incorrect_set_fields_specified: return "Incorrect set fields specified"; case orm_error_code::column_not_found: return "Column not found"; case orm_error_code::table_has_no_primary_key_column: return "Table has no primary key column"; case orm_error_code::cannot_start_a_transaction_within_a_transaction: return "Cannot start a transaction within a transaction"; case orm_error_code::no_active_transaction: return "No active transaction"; case orm_error_code::invalid_collate_argument_enum: return "Invalid collate_argument enum"; case orm_error_code::failed_to_init_a_backup: return "Failed to init a backup"; case orm_error_code::unknown_member_value: return "Unknown member value"; case orm_error_code::incorrect_order: return "Incorrect order"; case orm_error_code::cannot_use_default_value: return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; case orm_error_code::arguments_count_does_not_match: return "Arguments count does not match"; case orm_error_code::function_not_found: return "Function not found"; case orm_error_code::index_is_out_of_bounds: return "Index is out of bounds"; case orm_error_code::value_is_null: return "Value is null"; case orm_error_code::no_tables_specified: return "No tables specified"; default: return "unknown error"; } } }; class sqlite_error_category : public std::error_category { public: const char* name() const noexcept override final { return "SQLite error"; } std::string message(int c) const override final { return sqlite3_errstr(c); } }; inline const orm_error_category& get_orm_error_category() { static orm_error_category res; return res; } inline const sqlite_error_category& get_sqlite_error_category() { static sqlite_error_category res; return res; } inline std::error_code make_error_code(sqlite_errc ev) noexcept { return {static_cast(ev), get_sqlite_error_category()}; } inline std::error_code make_error_code(orm_error_code ev) noexcept { return {static_cast(ev), get_orm_error_category()}; } template std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; (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 "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 "type_printer.h" #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector // #include "functional/cxx_optional.h" // #include "cxx_core_features.h" #if SQLITE_ORM_HAS_INCLUDE() #include #endif #if __cpp_lib_optional >= 201606L #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" // #include "is_std_ptr.h" #include #include namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). */ template struct is_std_ptr : std::false_type {}; template struct is_std_ptr> : std::true_type { using element_type = typename std::shared_ptr::element_type; static std::shared_ptr make(std::remove_cv_t&& v) { return std::make_shared(std::move(v)); } }; template struct is_std_ptr> : std::true_type { using element_type = typename std::unique_ptr::element_type; static auto make(std::remove_cv_t&& v) { return std::make_unique(std::move(v)); } }; } namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) */ template struct type_printer {}; struct integer_printer { const std::string& print() const { static const std::string res = "INTEGER"; return res; } }; struct text_printer { const std::string& print() const { static const std::string res = "TEXT"; return res; } }; struct real_printer { const std::string& print() const { static const std::string res = "REAL"; return res; } }; struct blob_printer { const std::string& print() const { static const std::string res = "BLOB"; return res; } }; // Note: char, unsigned/signed char are used for storing integer values, not char values. template struct type_printer>, std::is_integral>::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 {}; } 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)...}}; } }; #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 {}; } } #pragma once #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr // #include "functional/cxx_optional.h" // #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. * Default nullability status for all types is `NOT NULL`. So if you want to map * custom type as `NULL` (for example: boost::optional) you have to create a 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); } }; } #pragma once #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 "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_universal.h" // #include "../functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type template struct object_field_type {}; template using object_field_type_t = typename object_field_type::type; template struct object_field_type : std::enable_if::value, F> {}; // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) template struct getter_field_type {}; template using getter_field_type_t = typename getter_field_type::type; template struct getter_field_type : getter_field_type {}; template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; #ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; #endif // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) template struct setter_field_type {}; template using setter_field_type_t = typename setter_field_type::type; template struct setter_field_type : setter_field_type {}; template struct setter_field_type : polyfill::remove_cvref {}; #ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct setter_field_type : polyfill::remove_cvref {}; #endif template struct is_getter : std::false_type {}; template struct is_getter>> : std::true_type {}; template SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; template struct is_setter : std::false_type {}; template struct is_setter>> : std::true_type {}; template SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; template struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; template using member_field_type_t = typename member_field_type::type; template struct member_object_type {}; template struct member_object_type : polyfill::type_identity {}; template using member_object_type_t = typename member_object_type::type; } } 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; } 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 "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; 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 add_string { serialize_result_type serialize() const { return "+"; } }; /** * Result of addition + operator */ template using add_t = binary_operator; struct sub_string { serialize_result_type serialize() const { return "-"; } }; /** * Result of substitute - operator */ template using sub_t = binary_operator; struct mul_string { serialize_result_type serialize() const { return "*"; } }; /** * Result of multiply * operator */ template using mul_t = binary_operator; struct div_string { serialize_result_type serialize() const { return "/"; } }; /** * Result of divide / operator */ template using div_t = binary_operator; struct mod_operator_string { serialize_result_type serialize() const { return "%"; } }; /** * Result of mod % operator */ template using mod_t = binary_operator; struct bitwise_shift_left_string { serialize_result_type serialize() const { return "<<"; } }; /** * Result of bitwise shift left << operator */ template using bitwise_shift_left_t = binary_operator; struct bitwise_shift_right_string { serialize_result_type serialize() const { return ">>"; } }; /** * Result of bitwise shift right >> operator */ template using bitwise_shift_right_t = binary_operator; struct bitwise_and_string { serialize_result_type serialize() const { return "&"; } }; /** * Result of bitwise and & operator */ template using bitwise_and_t = binary_operator; struct bitwise_or_string { serialize_result_type serialize() const { return "|"; } }; /** * Result of bitwise or | operator */ template using bitwise_or_t = binary_operator; struct bitwise_not_string { serialize_result_type serialize() const { return "~"; } }; /** * Result of bitwise not ~ operator */ template struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { using argument_type = T; argument_type argument; bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} }; struct assign_string { serialize_result_type serialize() const { return "="; } }; /** * Result of assign = operator */ template using assign_t = binary_operator; /** * Assign operator traits. Common case */ template struct is_assign_t : public std::false_type {}; /** * Assign operator traits. Specialized case */ template struct is_assign_t> : public std::true_type {}; } /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users */ template internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users */ template internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users * @note Please notice that ::div function already exists in pure C standard library inside header. * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. */ template internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ template internal::mod_t mod(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_and_t bitwise_and(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_or_t bitwise_or(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } template internal::assign_t assign(L l, R r) { return {std::move(l), std::move(r)}; } } #pragma once #include // std::tuple #include // std::string #include // std::unique_ptr #include // std::is_same, std::is_member_object_pointer #include // std::move // #include "../functional/cxx_universal.h" // #include "../functional/cxx_type_traits_polyfill.h" // #include "../tuple_helper/tuple_traits.h" // #include "../tuple_helper/tuple_filter.h" // #include "../type_traits.h" // #include "../member_traits/member_traits.h" // #include "../type_is_nullable.h" // #include "../constraints.h" namespace sqlite_orm { namespace internal { struct column_identifier { /** * Column name. */ std::string name; }; struct empty_setter {}; /* * Encapsulates object member pointers that are used as column fields, * and whose object is mapped to storage. * * G is a member object pointer or member function pointer * S is a member function pointer or `empty_setter` */ template struct column_field { using member_pointer_t = G; using setter_type = S; using object_type = member_object_type_t; using field_type = member_field_type_t; /** * Member pointer used to read a field value. * If it is a object member pointer it is also used to write a field value. */ const member_pointer_t member_pointer; /** * Setter member function to write a field value */ SQLITE_ORM_NOUNIQUEADDRESS const setter_type setter; /** * Simplified interface for `NOT NULL` constraint */ constexpr bool is_not_null() const { return !type_is_nullable::value; } }; /* * Encapsulates a tuple of column constraints. * * Op... is a constraints pack, e.g. primary_key_t, unique_t etc */ template struct column_constraints { using constraints_type = std::tuple; SQLITE_ORM_NOUNIQUEADDRESS constraints_type constraints; /** * Checks whether contraints 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)...}}); } } #pragma once #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 "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "is_base_of_template.h" // #include "type_traits.h" // #include "collate_argument.h" // #include "constraints.h" // #include "optional_container.h" namespace sqlite_orm { namespace internal { /** * This is a cute class which allows storing something or nothing * depending on template argument. Useful for optional class members */ template struct optional_container { using type = T; type field; template void apply(const L& l) const { l(this->field); } }; template<> struct optional_container { using type = void; template void apply(const L&) const { //.. } }; } } // #include "serializer_context.h" namespace sqlite_orm { namespace internal { struct serializer_context_base { bool replace_bindable_with_question = false; bool skip_table_name = true; bool use_parentheses = true; 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 // std::remove_const, std::type_identity #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif // #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 } // #include "alias_traits.h" #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" // #include "table_reference.h" namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ struct alias_tag {}; namespace internal { template SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; template 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 "expression.h" #include #include // std::enable_if #include // std::move, std::forward, std::declval // #include "functional/cxx_optional.h" // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.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 T get_from_expression(T value) { return std::move(value); } template T get_from_expression(expression_t expression) { return std::move(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 internal::expression_t c(T value) { return {std::move(value)}; } } // #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 } // #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 { C c; negated_condition_t(C c_) : c(std::move(c_)) {} }; /** * Base class for binary conditions * L is left argument type * R is right argument type * S is 'string' class (a class which has cast to `std::string` operator) * Res is result type */ template struct binary_condition : condition_t, S { using left_type = L; using right_type = R; using result_type = Res; left_type lhs; right_type rhs; binary_condition() = default; 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 { 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 { 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 = true; auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, std::move(order_by._collate_argument)); } const_iterator begin() const { return this->entries.begin(); } const_iterator end() const { return this->entries.end(); } void clear() { this->entries.clear(); } protected: std::vector entries; context_t context; }; template SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = polyfill::disjunction, 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> negated_condition_t operator!(T arg) { return {std::move(arg)}; } template, std::is_base_of, is_operator_argument, is_operator_argument>::value, bool> = true> 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> 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> 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> 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> 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> 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> 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> 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> 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 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 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 internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_equal_with_table_t is_equal(R rhs) { return {std::move(rhs)}; } template internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template internal::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 internal::less_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template 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 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)}; } } #pragma once #include // std::enable_if, std::is_same, std::conditional #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 // #include "cxx_universal.h" // ::size_t #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 "table_type_of.h" // #include "tags.h" // #include "column_pointer.h" 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 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 } #pragma once #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if #include // std::unique_ptr #include // std::vector // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl/conditional.h" // #include "is_base_of_template.h" // #include "tuple_helper/tuple_traits.h" // #include "conditions.h" // #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, std::is_base_of, is_operator_argument, is_operator_argument>::value, bool> = true> 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> 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> 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> 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> mod_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)}; } } #pragma once #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif #include // std::remove_const #include // std::string #include // std::move #include // std::tuple, std::get, std::tuple_size // #include "functional/cxx_optional.h" // #include "functional/cxx_universal.h" // ::size_t // #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 // 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_universal.h" // ::size_t // #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 // #include "../functional/cxx_universal.h" // ::size_t namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template 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 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 "optional_container.h" // #include "ast/where.h" #include // std::false_type, std::true_type #include // std::move // #include "../functional/cxx_universal.h" // #include "../functional/cxx_type_traits_polyfill.h" // #include "../serialize_result_type.h" namespace sqlite_orm { namespace internal { struct where_string { serialize_result_type serialize() const { return "WHERE"; } }; /** * WHERE argument holder. * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc * Don't construct it manually. Call `where(...)` function instead. */ template struct where_t : where_string { using expression_type = C; expression_type expression; where_t(expression_type expression_) : expression(std::move(expression_)) {} }; template SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::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 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 "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/cxx_universal.h" // #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) && ...) auto operator()(ExplicitCols... explicitColumns) const; #else template, std::is_member_pointer, std::is_same>, std::is_convertible>...>, bool> = true> 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 namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct as_optional_t { using value_type = T; value_type value; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED struct distinct_string { operator std::string() const { return "DISTINCT"; } }; /** * DISCTINCT generic container. */ template struct distinct_t : distinct_string { using value_type = T; value_type value; distinct_t(value_type value_) : value(std::move(value_)) {} }; struct all_string { operator std::string() const { return "ALL"; } }; /** * ALL generic container. */ template struct all_t : all_string { T value; all_t(T value_) : value(std::move(value_)) {} }; template struct columns_t { using columns_type = std::tuple; columns_type columns; bool distinct = false; static constexpr int count = std::tuple_size::value; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED columns_t(columns_type columns) : columns{std::move(columns)} {} #endif }; template SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::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; bool distinct = false; 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; 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; 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; 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> common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> common_table_expression, select_t> as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #else template = true> common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> 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 /** * Generic way to get DISTINCT value from any type. */ template bool get_distinct(const T&) { return false; } template bool get_distinct(const columns_t& cols) { return cols.distinct; } template bool get_distinct(const struct_t& cols) { return cols.distinct; } template struct asterisk_t { using type = T; bool defined_order = false; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED asterisk_t(bool definedOrder) : defined_order{definedOrder} {} #endif }; template struct object_t { using type = T; bool defined_order = false; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED object_t(bool definedOrder) : defined_order{definedOrder} {} #endif }; template struct then_t { using expression_type = T; expression_type expression; }; template struct simple_case_t { using return_type = R; using case_expression_type = T; using args_type = std::tuple; using else_expression_type = E; optional_container case_expression; args_type args; optional_container else_expression; }; /** * T is a case expression type * E is else type (void is ELSE is omitted) * Args... is a pack of WHEN expressions */ template struct simple_case_builder { using return_type = R; using case_expression_type = T; using args_type = std::tuple; using else_expression_type = E; optional_container case_expression; args_type args; optional_container else_expression; template simple_case_builder> when(W w, then_t t) { using result_args_type = std::tuple>; std::pair newPair{std::move(w), std::move(t.expression)}; result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); std::get::value - 1>(result_args) = std::move(newPair); return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; } simple_case_t end() { return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; } template simple_case_builder else_(El el) { return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; } }; template void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template internal::as_optional_t as_optional(T value) { return {std::move(value)}; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template internal::then_t then(T t) { return {std::move(t)}; } template internal::simple_case_builder case_(T t) { return {{std::move(t)}}; } template internal::simple_case_builder case_() { return {}; } template internal::distinct_t distinct(T t) { return {std::move(t)}; } template internal::all_t all(T t) { return {std::move(t)}; } template internal::columns_t distinct(internal::columns_t cols) { cols.distinct = true; return cols; } /* * Combine multiple columns in a tuple. */ template constexpr internal::columns_t columns(Args... args) { return {std::make_tuple(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::make_tuple(std::forward(args)...)}; } /** * Public function for subselect query. Is useful in UNION queries. */ template internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), {std::forward(args)...}}; } /** * Public function for UNION operator. * Expressions are subselect objects. * Look through example in examples/union.cpp */ template 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 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 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 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)); */ inline 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)); */ inline 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> 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) && ...) 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) && ...) 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>> 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 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 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 internal::object_t object(bool definedOrder = false) { return {definedOrder}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto object(bool definedOrder = false) { return object>(definedOrder); } #endif } #pragma once #include // std::string // #include "functional/cxx_universal.h" namespace sqlite_orm { struct table_info { int cid = 0; std::string name; std::string type; bool notnull = false; std::string dflt_value; int pk = 0; #if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, decltype(notnull) notnull_, decltype(dflt_value) dflt_value_, decltype(pk) pk_) : cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), pk(pk_) {} #endif }; struct table_xinfo { int cid = 0; std::string name; std::string type; bool notnull = false; std::string dflt_value; int pk = 0; int hidden = 0; // different than 0 => generated_always_as() - TODO verify #if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) table_xinfo(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, decltype(notnull) notnull_, decltype(dflt_value) dflt_value_, decltype(pk) pk_, decltype(hidden) hidden_) : cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), pk(pk_), hidden{hidden_} {} #endif }; } #pragma once #include #include #include #include // #include "../functional/cxx_universal.h" // #include "../optional_container.h" // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? // (Could be implemented with a normal trigger that insert or update an internal table and then retreive // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) // It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? namespace sqlite_orm { namespace internal { enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; /** * This class is an intermediate SQLite trigger, to be used with * `make_trigger` to create a full trigger. * T is the base of the trigger (contains its type, timing and associated table) * S is the list of trigger statements */ template struct partial_trigger_t { using statements_type = std::tuple; /** * Base of the trigger (contains its type, timing and associated table) */ T base; /** * Statements of the triggers (to be executed when the trigger fires) */ statements_type statements; partial_trigger_t(T trigger_base, S... statements) : base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} partial_trigger_t& end() { return *this; } }; struct base_trigger { /** * Name of the trigger */ std::string name; }; /** * This class represent a SQLite trigger * T is the base of the trigger (contains its type, timing and associated table) * S is the list of trigger statments */ template struct trigger_t : base_trigger { using object_type = void; using elements_type = typename partial_trigger_t::statements_type; /** * Base of the trigger (contains its type, timing and associated table) */ T base; /** * Statements of the triggers (to be executed when the trigger fires) */ elements_type elements; #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED trigger_t(std::string name, T trigger_base, elements_type statements) : base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} #endif }; /** * Base of a trigger. Contains the trigger type/timming and the table type * T is the table type * W is `when` expression type * Type is the trigger base type (type+timing) */ template struct trigger_base_t { using table_type = T; using when_type = W; using trigger_type_base = Type; /** * Contains the trigger type and timing */ trigger_type_base type_base; /** * Value used to determine if we execute the trigger on each row or on each statement * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description * so this value is more of a placeholder for a later update) */ bool do_for_each_row = false; /** * When expression (if any) * If a WHEN expression is specified, the trigger will only execute * if the expression evaluates to true when the trigger is fired */ optional_container container_when; trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} trigger_base_t& for_each_row() { this->do_for_each_row = true; return *this; } template trigger_base_t when(WW expression) { trigger_base_t res(this->type_base); res.container_when.field = std::move(expression); return res; } template partial_trigger_t, S...> begin(S... statements) { return {*this, std::forward(statements)...}; } }; /** * Contains the trigger type and timing */ struct trigger_type_base_t { /** * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF * the statement that fired it. */ trigger_timing timing; /** * The type of the statement that would cause the trigger to fire. * Can be DELETE, INSERT, or UPDATE. */ trigger_type type; trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} template trigger_base_t on() { return {*this}; } }; /** * Special case for UPDATE OF (columns) * Contains the trigger type and timing */ template struct trigger_update_type_t : trigger_type_base_t { using columns_type = std::tuple; /** * Contains the columns the trigger is watching. Will only * trigger if one of theses columns is updated. */ columns_type columns; trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} template trigger_base_t> on() { return {*this}; } }; struct trigger_timing_t { trigger_timing timing; trigger_type_base_t delete_() { return {timing, trigger_type::trigger_delete}; } trigger_type_base_t insert() { return {timing, trigger_type::trigger_insert}; } trigger_type_base_t update() { return {timing, trigger_type::trigger_update}; } template trigger_update_type_t update_of(Cs... columns) { return {timing, trigger_type::trigger_update, std::forward(columns)...}; } }; struct raise_t { enum class type_t { ignore, rollback, abort, fail, }; type_t type = type_t::ignore; std::string message; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} #endif }; template struct new_t { using expression_type = T; expression_type expression; }; template struct old_t { using expression_type = T; expression_type expression; }; } // NAMESPACE internal /** * NEW.expression function used within TRIGGER expressions */ template internal::new_t new_(T expression) { return {std::move(expression)}; } /** * OLD.expression function used within TRIGGER expressions */ template internal::old_t old(T expression) { return {std::move(expression)}; } /** * RAISE(IGNORE) expression used within TRIGGER expressions */ inline internal::raise_t raise_ignore() { return {internal::raise_t::type_t::ignore, {}}; } /** * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions */ inline internal::raise_t raise_rollback(std::string message) { return {internal::raise_t::type_t::rollback, std::move(message)}; } /** * RAISE(ABORT, %message%) expression used within TRIGGER expressions */ inline internal::raise_t raise_abort(std::string message) { return {internal::raise_t::type_t::abort, std::move(message)}; } /** * RAISE(FAIL, %message%) expression used within TRIGGER expressions */ inline internal::raise_t raise_fail(std::string message) { return {internal::raise_t::type_t::fail, std::move(message)}; } template internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( return {std::move(name), std::move(part.base), std::move(part.statements)}); } inline internal::trigger_timing_t before() { return {internal::trigger_timing::trigger_before}; } inline internal::trigger_timing_t after() { return {internal::trigger_timing::trigger_after}; } inline internal::trigger_timing_t instead_of() { return {internal::trigger_timing::trigger_instead_of}; } } #pragma once #include "sqlite3.h" #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_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/cxx_functional_polyfill.h" // #include "is_std_ptr.h" // #include "tuple_helper/tuple_filter.h" // #include "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_universal.h" // #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; } namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_CONCEPTS_SUPPORTED /** * Constrains a deleter to be state-less. */ template concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; /** * Constrains a deleter to be an integral function constant. */ template concept integral_fp_c = requires { typename D::value_type; D::value; requires std::is_function_v>; }; /** * Constrains a deleter to be or to yield a function pointer. */ template concept yields_fp = requires(D d) { // yielding function pointer by using the plus trick { +d }; requires std::is_function_v>; }; #endif #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; } } #pragma once #include "sqlite3.h" #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if #include // atof, atoi, atoll #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert #include // std::codecvt_utf8_utf16 #endif #include // std::vector #include // strlen #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_universal.h" // #include "functional/cxx_functional_polyfill.h" // #include "functional/static_magic.h" #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant #endif #include // std::forward namespace sqlite_orm { // got from here // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { // 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 "tuple_helper/tuple_transformer.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 "arithmetic_tag.h" // #include "pointer_value.h" // #include "journal_mode.h" #include // std::back_inserter #include // std::string #include // std::unique_ptr #include // std::array #include // std::transform #include // std::toupper #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") #undef DELETE #endif namespace sqlite_orm { /** * Caps case because of: * 1) delete keyword; * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling */ enum class journal_mode : signed char { DELETE = 0, // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. DELETE_ = DELETE, TRUNCATE = 1, PERSIST = 2, MEMORY = 3, WAL = 4, OFF = 5, }; namespace internal { inline const std::string& to_string(journal_mode j) { static std::string res[] = { "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF", }; return res[static_cast(j)]; } inline std::unique_ptr journal_mode_from_string(const std::string& str) { std::string upper_str; std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { return static_cast(std::toupper(static_cast(c))); }); static std::array all = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, journal_mode::MEMORY, journal_mode::WAL, journal_mode::OFF, }}; for(auto j: all) { if(to_string(j) == upper_str) { return std::make_unique(j); } } return {}; } } } #if defined(_WINNT_) #pragma pop_macro("DELETE") #endif // #include "error_code.h" // #include "is_std_ptr.h" // #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 journal_mode. */ template<> struct row_extractor { journal_mode extract(const char* columnText) const { if(columnText) { if(auto res = internal::journal_mode_from_string(columnText)) { return std::move(*res); } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); return this->extract(cStr); } 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; }; } } #pragma once #include namespace sqlite_orm { enum class sync_schema_result { /** * created new table, table with the same tablename did not exist */ new_table_created, /** * table schema is the same as storage, nothing to be done */ already_in_sync, /** * removed excess columns in table (than storage) without dropping a table */ old_columns_removed, /** * lacking columns in table (than storage) added without dropping a table */ new_columns_added, /** * both old_columns_removed and new_columns_added */ new_columns_added_and_old_columns_removed, /** * old table is dropped and new is recreated. Reasons : * 1. delete excess columns in the table than storage if preseve = false * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint * 3. Reasons 1 and 2 both together * 4. data_type mismatch between table and storage. */ dropped_and_recreated, }; inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { switch(value) { case sync_schema_result::new_table_created: return os << "new table created"; case sync_schema_result::already_in_sync: return os << "table and storage is already in sync."; case sync_schema_result::old_columns_removed: return os << "old excess columns removed"; case sync_schema_result::new_columns_added: return os << "new columns added"; case sync_schema_result::new_columns_added_and_old_columns_removed: return os << "old excess columns removed and new columns added"; case sync_schema_result::dropped_and_recreated: return os << "old table dropped and recreated"; } return os; } } #pragma once #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string #include // std::forward // #include "../functional/cxx_universal.h" // #include "../tuple_helper/tuple_traits.h" // #include "../indexed_column.h" #include // std::string #include // std::move // #include "functional/cxx_universal.h" // #include "ast/where.h" namespace sqlite_orm { namespace internal { template struct indexed_column_t { using column_type = C; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED indexed_column_t(column_type _column_or_expression) : column_or_expression(std::move(_column_or_expression)) {} #endif column_type column_or_expression; std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); res._collation_name = std::move(name); return res; } indexed_column_t asc() { auto res = std::move(*this); res._order = 1; return res; } indexed_column_t desc() { auto res = std::move(*this); res._order = -1; return res; } }; template indexed_column_t make_indexed_column(C col) { return {std::move(col)}; } template where_t make_indexed_column(where_t wher) { return std::move(wher); } template indexed_column_t make_indexed_column(indexed_column_t col) { return std::move(col); } } /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) */ template internal::indexed_column_t indexed_column(C column_or_expression) { return {std::move(column_or_expression)}; } } // #include "../table_type_of.h" namespace sqlite_orm { namespace internal { struct index_base { std::string name; bool unique = false; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} #endif }; template struct index_t : index_base { using elements_type = std::tuple; using object_type = void; using table_mapped_type = T; #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED index_t(std::string name_, bool unique_, elements_type elements_) : index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} #endif elements_type elements; }; } template internal::index_t()))...> make_index(std::string name, Cols... cols) { using cols_tuple = std::tuple; static_assert(internal::count_tuple::value <= 1, "amount of where arguments can be 0 or 1"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } template internal::index_t>>, decltype(internal::make_indexed_column(std::declval()))...> make_index(std::string name, Cols... cols) { using cols_tuple = std::tuple; static_assert(internal::count_tuple::value <= 1, "amount of where arguments can be 0 or 1"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } template internal::index_t>>, decltype(internal::make_indexed_column(std::declval()))...> make_unique_index(std::string name, Cols... cols) { using cols_tuple = std::tuple; static_assert(internal::count_tuple::value <= 1, "amount of where arguments can be 0 or 1"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } } #pragma once #include // std::string namespace sqlite_orm { namespace internal { struct rowid_t { operator std::string() const { return "rowid"; } }; struct oid_t { operator std::string() const { return "oid"; } }; struct _rowid_t { operator std::string() const { return "_rowid_"; } }; template struct table_rowid_t : public rowid_t { using type = T; }; template struct table_oid_t : public oid_t { using type = T; }; template struct table__rowid_t : public _rowid_t { using type = T; }; } inline internal::rowid_t rowid() { return {}; } inline internal::oid_t oid() { return {}; } inline internal::_rowid_t _rowid_() { return {}; } template internal::table_rowid_t rowid() { return {}; } template internal::table_oid_t oid() { return {}; } template internal::table__rowid_t _rowid_() { return {}; } } #pragma once #include // std::string #include // std::remove_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_universal.h" // ::size_t // #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 "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)}); } } #pragma once #include // std::string // #include "functional/cxx_universal.h" // ::size_t // #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" #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" #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; } } #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_lookup.h" #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void #include #include // std::index_sequence, std::make_index_sequence // #include "functional/cxx_universal.h" // ::size_t // #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); } } // 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 } } #pragma once #include // std::string // #include "constraints.h" // #include "serializer_context.h" // #include "storage_lookup.h" namespace sqlite_orm { namespace internal { template auto serialize(const T& t, const C& context); /** * Serialize default value of a column's default valu */ template std::string serialize_default_value(const default_t& dft) { db_objects_tuple<> dbObjects; serializer_context> context{dbObjects}; return serialize(dft.value, context); } } } #pragma once #include "sqlite3.h" #include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared #include // std::system_error #include // std::string #include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type #include // std::identity #include // std::stringstream #include // std::map #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple, std::tie #include // std::forward, std::pair #include // std::for_each, std::ranges::for_each // #include "functional/cxx_optional.h" // #include "functional/cxx_universal.h" // #include "functional/cxx_functional_polyfill.h" // #include "functional/static_magic.h" // #include "functional/mpl.h" // #include "tuple_helper/tuple_traits.h" // #include "tuple_helper/tuple_filter.h" // #include "tuple_helper/tuple_transformer.h" // #include "tuple_helper/tuple_iteration.h" // #include "type_traits.h" // #include "alias.h" // #include "error_code.h" // #include "type_printer.h" // #include "constraints.h" // #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_universal.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 "operators.h" // #include "select_constraints.h" // #include "core_functions.h" // #include "conditions.h" // #include "statement_binder.h" // #include "column_result.h" #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper // #include "functional/cxx_universal.h" // ::nullptr_t // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl.h" // #include "tuple_helper/tuple_traits.h" // #include "tuple_helper/tuple_fy.h" // #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 "alias.h" // #include "cte_types.h" // #include "storage_traits.h" #include // std::tuple // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_filter.h" // #include "tuple_helper/tuple_transformer.h" // #include "type_traits.h" // #include "storage_lookup.h" // #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_universal.h" // ::size_t, ::nullptr_t // #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 = 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 "table_info.h" // #include "storage_impl.h" // #include "journal_mode.h" // #include "mapped_view.h" #include "sqlite3.h" #include // std::forward, std::move // #include "row_extractor.h" // #include "mapped_iterator.h" #include "sqlite3.h" #include // std::shared_ptr, std::make_shared #include // std::move #include // std::input_iterator_tag #include // std::system_error #include // std::bind // #include "functional/cxx_universal.h" // ::ptrdiff_t // #include "statement_finalizer.h" #include "sqlite3.h" #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 "sqlite3.h" #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 "sqlite3.h" #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 "sqlite3.h" #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_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/cxx_functional_polyfill.h" // #include "tuple_helper/tuple_traits.h" // #include "connection_holder.h" #include "sqlite3.h" #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 // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { template struct values_t { using args_tuple = std::tuple; args_tuple tuple; }; template SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of::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; iterate_ast(assign, this->collector); std::stringstream ss; ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' << serialize(assign.rhs, context); this->entries.push_back({ss.str()}); } const_iterator begin() const { return this->entries.begin(); } const_iterator end() const { return this->entries.end(); } void clear() { this->entries.clear(); this->collector.table_names.clear(); } std::vector entries; context_t context; table_name_collector collector; }; template struct is_set> : std::true_type {}; template struct is_dynamic_set : std::false_type {}; template struct is_dynamic_set> : std::true_type {}; } /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) */ template internal::set_t set(Args... args) { using arg_tuple = std::tuple; static_assert(std::tuple_size::value == internal::count_tuple::value, "set function accepts assign operators only"); return {std::make_tuple(std::forward(args)...)}; } /** * SET keyword used in UPDATE ... SET queries. It is dynamic version. It means use can add amount of arguments now known at compilation time but known at runtime. */ template internal::dynamic_set_t> dynamic_set(const S& storage) { internal::serializer_context_builder builder(storage); return builder(); } } namespace sqlite_orm { namespace internal { struct prepared_statement_base { sqlite3_stmt* stmt = nullptr; connection_ref con; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} #endif ~prepared_statement_base() { sqlite3_finalize(this->stmt); } std::string sql() const { // note: sqlite3 internally checks for null before calling // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous if(const char* sql = sqlite3_sql(this->stmt)) { return sql; } else { return {}; } } #if SQLITE_VERSION_NUMBER >= 3014000 std::string expanded_sql() const { // note: must check return value due to SQLITE_OMIT_TRACE using char_ptr = std::unique_ptr>; if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { return sql.get(); } else { return {}; } } #endif #if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) std::string normalized_sql() const { if(const char* sql = sqlite3_normalized_sql(this->stmt)) { return sql; } else { return {}; } } #endif #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED std::string_view column_name(int index) const { return sqlite3_column_name(stmt, index); } #endif }; template struct prepared_statement_t : prepared_statement_base { using expression_type = T; expression_type expression; prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} prepared_statement_t(prepared_statement_t&& prepared_stmt) : prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, expression(std::move(prepared_stmt.expression)) { prepared_stmt.stmt = nullptr; } }; template SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = polyfill::is_specialization_of::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 struct remove_all_t { using type = T; using conditions_type = std::tuple; conditions_type conditions; }; template struct get_t { using type = T; using ids_type = std::tuple; ids_type ids; }; template struct get_pointer_t { using type = T; using ids_type = std::tuple; ids_type ids; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct get_optional_t { using type = T; using ids_type = std::tuple; ids_type ids; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct update_t { using type = T; type object; }; template struct remove_t { using type = T; using ids_type = std::tuple; ids_type ids; }; template struct insert_t { using type = T; type object; }; template SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of::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" 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.value, lambda); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct ast_iterator, void> { using node_type = std::reference_wrapper; template void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.get(), lambda); } }; template struct ast_iterator, void> { using node_type = 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.value, lambda); } }; template struct ast_iterator, void> { using node_type = all_t; template void operator()(const node_type& a, L& lambda) const { iterate_ast(a.value, lambda); } }; template struct ast_iterator, void> { using node_type = bitwise_not_t; template void operator()(const node_type& a, L& lambda) const { iterate_ast(a.argument, lambda); } }; template struct ast_iterator, void> { using node_type = values_t; template void operator()(const node_type& node, L& lambda) const { iterate_ast(node.tuple, lambda); } }; template struct ast_iterator, void> { using node_type = dynamic_values_t; template void operator()(const node_type& node, L& lambda) const { iterate_ast(node.vector, lambda); } }; /** * Column alias or literal: skipped */ template struct ast_iterator, polyfill::is_specialization_of, is_column_alias>::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 "sqlite3.h" #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 "sqlite3.h" #include // std::move #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper // #include "functional/cxx_universal.h" // ::ptrdiff_t // #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 "sqlite3.h" #include // std::allocator #include // std::function, std::bind, std::bind_front #include // std::string #include // std::stringstream #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_universal.h" // ::size_t // #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_universal.h" // ::size_t // #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 "sqlite3.h" #include // std::string #include // std::function #include // std::shared_ptr #include // std::vector #include // #include "error_code.h" // #include "row_extractor.h" // #include "journal_mode.h" // #include "connection_holder.h" // #include "util.h" // #include "serializing_util.h" #include // std::index_sequence #include #include #include #include #include // std::exchange, std::tuple_size // #include "functional/cxx_universal.h" // ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_iteration.h" // #include "error_code.h" // #include "serializer_context.h" // #include "serialize_result_type.h" // #include "util.h" namespace sqlite_orm { namespace internal { template struct order_by_t; template auto serialize(const T& t, const C& 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 { 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 { 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 { 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); 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); 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); 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)); 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 { 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 { 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; } 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}; } 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::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } void journal_mode(sqlite_orm::journal_mode value) { this->_journal_mode = -1; this->set_pragma("journal_mode", value); this->_journal_mode = static_cast_journal_mode)>(value); } /** * https://www.sqlite.org/pragma.html#pragma_application_id */ int application_id() { return this->get_pragma("application_id"); } /** * https://www.sqlite.org/pragma.html#pragma_application_id */ void application_id(int value) { this->set_pragma("application_id", value); } int synchronous() { return this->get_pragma("synchronous"); } void synchronous(int value) { this->_synchronous = -1; this->set_pragma("synchronous", value); this->_synchronous = value; } int user_version() { return this->get_pragma("user_version"); } void user_version(int value) { this->set_pragma("user_version", value); } int auto_vacuum() { return this->get_pragma("auto_vacuum"); } void auto_vacuum(int value) { this->set_pragma("auto_vacuum", value); } std::vector integrity_check() { return this->get_pragma>("integrity_check"); } template std::vector integrity_check(T table_name) { std::ostringstream ss; ss << "integrity_check(" << table_name << ")" << std::flush; return this->get_pragma>(ss.str()); } std::vector integrity_check(int n) { std::ostringstream ss; ss << "integrity_check(" << n << ")" << std::flush; return this->get_pragma>(ss.str()); } 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 = std::atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; bool notnull = !!std::atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; auto pk = std::atoi(argv[index++]); auto hidden = std::atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk, hidden); } return 0; }, &result); return result; } std::vector table_info(const std::string& tableName) const { auto connection = this->get_connection(); std::ostringstream ss; ss << "PRAGMA " "table_info(" << streaming_identifier(tableName) << ")" << std::flush; std::vector result; perform_exec( connection.get(), ss.str(), [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if(argc) { auto index = 0; auto cid = std::atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; bool notnull = !!std::atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; auto pk = std::atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); } return 0; }, &result); return result; } private: friend struct storage_base; int _synchronous = -1; signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; template T get_pragma(const std::string& name) { auto connection = this->get_connection(); T result; perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); return result; } /** * Yevgeniy Zakharov: I wanted to refactor this function with statements and value bindings * but it turns out that bindings in pragma statements are not supported. */ template void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { auto con = this->get_connection(); if(!db) { db = con.get(); } std::stringstream ss; ss << "PRAGMA " << name << " = " << value << std::flush; perform_void_exec(db, ss.str()); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { auto con = this->get_connection(); if(!db) { db = con.get(); } std::stringstream ss; ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; perform_void_exec(db, ss.str()); } }; } } // #include "limit_accessor.h" #include "sqlite3.h" #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 "sqlite3.h" #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 "sqlite3.h" #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_universal.h" // ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "type_traits.h" // #include "row_extractor.h" // #include "arg_values.h" #include "sqlite3.h" // #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 "sqlite3.h" #include // assert #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" 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)}; } void drop_index(const std::string& indexName) { std::stringstream ss; ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } void drop_trigger(const std::string& triggerName) { std::stringstream ss; ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } void vacuum() { perform_void_exec(this->get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ void drop_table(const std::string& tableName) { this->drop_table_internal(this->get_connection().get(), tableName); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { this->rename_table(this->get_connection().get(), from, to); } protected: void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { std::stringstream ss; ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) << std::flush; perform_void_exec(db, ss.str()); } /** * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. * Note: table can be not mapped to a storage * @return true if table with a given name exists in db, false otherwise. */ bool table_exists(const std::string& tableName) { auto con = this->get_connection(); return this->table_exists(con.get(), tableName); } bool table_exists(sqlite3* db, const std::string& tableName) const { bool result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << 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 = !!std::atoi(argv[0]); } return 0; }, &result); return result; } void add_generated_cols(std::vector& columnsToAdd, const std::vector& storageTableInfo) { // iterate through storage columns for(const table_xinfo& storageColumnInfo: storageTableInfo) { if(storageColumnInfo.hidden) { columnsToAdd.push_back(&storageColumnInfo); } } } public: /** * sqlite3_changes function. */ int changes() { auto con = this->get_connection(); return sqlite3_changes(con.get()); } /** * sqlite3_total_changes function. */ int total_changes() { auto con = this->get_connection(); return sqlite3_total_changes(con.get()); } int64 last_insert_rowid() { auto con = this->get_connection(); return sqlite3_last_insert_rowid(con.get()); } int busy_timeout(int ms) { auto con = this->get_connection(); return sqlite3_busy_timeout(con.get(), ms); } /** * Returns libsqlite3 version, not sqlite_orm */ std::string libversion() { return sqlite3_libversion(); } bool transaction(const std::function& f) { auto guard = this->transaction_guard(); return guard.commit_on_destroy = f(); } std::string current_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) { std::stringstream ss; ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { auto& f = *(collating_function*)arg; return f(leftLen, lhs, rightLen, rhs); } static int busy_handler_callback(void* selfPointer, int triesCount) { auto& storage = *static_cast(selfPointer); if(storage._busy_handler) { return storage._busy_handler(triesCount); } else { return 0; } } bool calculate_remove_add_columns(std::vector& columnsToAdd, std::vector& storageTableInfo, std::vector& dbTableInfo) const { bool notEqual = false; // iterate through storage columns for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); ++storageColumnInfoIndex) { // get storage's column info 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::stringstream #include // std::string #include // std::enable_if, std::remove_pointer #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_universal.h" // #include "functional/cxx_functional_polyfill.h" // #include "functional/mpl.h" // #include "tuple_helper/tuple_filter.h" // #include "ast/upsert_clause.h" // #include "ast/excluded.h" // #include "ast/group_by.h" // #include "ast/into.h" // #include "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 "schema/column.h" // #include "indexed_column.h" // #include "function.h" // #include "prepared_statement.h" // #include "rowid.h" // #include "pointer_value.h" // #include "type_printer.h" // #include "field_printer.h" // #include "literal.h" // #include "table_name_collector.h" // #include "column_names_getter.h" #include // std::is_base_of #include // std::string #include // std::vector #include // std::reference_wrapper #include #include // std::move // #include "tuple_helper/tuple_traits.h" // #include "tuple_helper/tuple_iteration.h" // #include "error_code.h" // #include "mapped_type_proxy.h" // #include "alias_traits.h" // #include "select_constraints.h" // #include "storage_lookup.h" // pick_table // #include "serializer_context.h" // #include "util.h" namespace sqlite_orm { namespace internal { template auto serialize(const T& t, const C& 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& t, const Ctx& context) { column_names_getter serializer; return serializer(t, 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_universal.h" // #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 C& 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(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 "schema/triggers.h" // #include "table_type_of.h" // #include "schema/index.h" // #include "schema/table.h" // #include "util.h" // #include "error_code.h" namespace sqlite_orm { namespace internal { template struct statement_serializer; template auto serialize(const T& t, const C& 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.value, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializer, void> { using statement_type = std::reference_wrapper; template std::string operator()(const statement_type& s, const Ctx& context) const { return serialize(s.get(), context); } }; template struct statement_serializer, void> { using statement_type = alias_holder; template std::string operator()(const statement_type&, const Ctx&) { std::stringstream ss; ss << streaming_identifier(T::get()); return ss.str(); } }; template struct statement_serializer, void> { using statement_type = 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 = std::decay_t; auto needParenthesis = std::is_member_pointer::value; ss << ' '; if(needParenthesis) { ss << '('; } ss << serialize(value, context); if(needParenthesis) { ss << ')'; } }); ss << ' ' << "DO"; if(std::tuple_size::value == 0) { ss << " NOTHING"; } else { auto updateContext = context; updateContext.use_parentheses = false; ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); } return ss.str(); } }; template struct statement_serializer, void> { using statement_type = built_in_function_t; template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; 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(); } }; template struct statement_serializer, void> { using statement_type = distinct_t; template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } }; template struct statement_serializer, void> { using statement_type = all_t; template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } }; template struct statement_serializer, void> { using statement_type = cast_t; template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << static_cast(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; return ss.str(); } }; #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, void> { using statement_type = bitwise_not_t; template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << statement.serialize() << " "; auto cString = serialize(statement.argument, context); ss << " (" << cString << " )"; return ss.str(); } }; template struct statement_serializer, void> { using statement_type = negated_condition_t; template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.c, context); ss << " (" << cString << " )"; return ss.str(); } }; template struct statement_serializer< 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 std::decay_t::references_type; using first_reference_t = std::tuple_element_t<0, references_type_t>; using first_reference_mapped_type = table_type_of_t; auto refTableName = lookup_table_name(context.db_objects); ss << streaming_identifier(refTableName); } ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; if(fk.on_update) { ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; } if(fk.on_delete) { ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; } return ss.str(); } }; #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::decay_t; static_assert(!is_setter_v, "Unable to use setter within insert explicit"); constexpr std::array sep = {", ", ""}; ss << sep[std::exchange(first, false)] << serialize(polyfill::invoke(memberPointer, object), context); }); ss << ")"; return ss.str(); } }; template struct statement_serializer, void> { using statement_type = update_t; template std::string operator()(const statement_type& statement, const Ctx& context) const { using 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; } 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; } constexpr std::array sep = {" AND ", ""}; ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " << serialize(polyfill::invoke(column.member_pointer, object), context); }); return ss.str(); } }; template struct statement_serializer, void> { using statement_type = dynamic_set_t; template std::string operator()(const statement_type& statement, const Ctx&) const { std::stringstream ss; ss << "SET "; int index = 0; for(const dynamic_set_entry& entry: statement) { if(index > 0) { ss << ", "; } ss << entry.serialized_value; ++index; } return ss.str(); } }; template struct statement_serializer, void> { using statement_type = set_t; template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "SET "; auto leftContext = context; leftContext.skip_table_name = true; iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { constexpr std::array sep = {", ", ""}; ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' << value.serialize() << ' ' << serialize(value.rhs, context); }); return ss.str(); } }; template std::set> collect_table_names(const set_t& set, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); iterate_ast(set, collector); return std::move(collector.table_names); } template const std::set>& collect_table_names(const dynamic_set_t& set, const Ctx&) { return set.collector.table_names; } template = true> std::set> collect_table_names(const T& sel, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); iterate_ast(sel.col, collector); iterate_ast(sel.conditions, collector); return std::move(collector.table_names); } template 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::decay_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 = std::decay_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}; } 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::decay_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; 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 "; } if(get_distinct(sel.col)) { ss << static_cast(distinct(0)) << " "; } 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::decay_t::type; using cross_join_type = mapped_type_proxy_t; std::pair tableNameWithAlias{ lookup_table_name(context.db_objects), alias_extractor::as_alias()}; tableNames.erase(tableNameWithAlias); }); if(!tableNames.empty() && !is_compound_operator::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 << " DESC"; break; case 1: ss << " ASC"; break; default: throw std::system_error{orm_error_code::incorrect_order}; } } 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 std::decay_t::table_mapped_type; ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " << streaming_identifier(lookup_table_name(context.db_objects)); std::vector columnNames; std::string whereString; iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { using value_type = std::decay_t; if(!is_where::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; 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 = std::decay_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 "functional/cxx_universal.h" // ::size_t // #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 table_type = std::decay_t; 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); this->create_table(db, table.name, table); } template void backup_table(sqlite3* db, const Table& table, const std::vector& columnsToIgnore) { // here we copy source table to another with a name with '_backup' suffix, but in case table with such // a name already exists we append suffix 1, then 2, etc until we find a free name.. auto backupTableName = table.name + "_backup"; if(this->table_exists(db, backupTableName)) { int suffix = 1; do { std::stringstream ss; ss << suffix << std::flush; auto anotherBackupTableName = backupTableName + ss.str(); if(!this->table_exists(db, anotherBackupTableName)) { backupTableName = std::move(anotherBackupTableName); break; } ++suffix; } while(true); } this->create_table(db, backupTableName, table); this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); this->drop_table_internal(db, table.name); this->rename_table(db, backupTableName, table.name); } template void assert_mapped_type() const { 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