From 8de20397c66fc5da943365782484177b3b3f168b Mon Sep 17 00:00:00 2001 From: Vittorio Romeo Date: Sun, 19 Mar 2023 03:30:50 +0100 Subject: [PATCH] Update ORM --- public/sqlite_orm/sqlite_orm.h | 13742 +++++++++++++---------- src/SSVOpenHexagon/Online/Database.cpp | 50 +- 2 files changed, 7888 insertions(+), 5904 deletions(-) diff --git a/public/sqlite_orm/sqlite_orm.h b/public/sqlite_orm/sqlite_orm.h index 67df39aaf..e5135a2c7 100644 --- a/public/sqlite_orm/sqlite_orm.h +++ b/public/sqlite_orm/sqlite_orm.h @@ -1,35 +1,352 @@ #pragma once #if defined(_MSC_VER) -#if defined(min) __pragma(push_macro("min")) #undef min -#define __RESTORE_MIN__ -#endif -#if defined(max) - __pragma(push_macro("max")) +__pragma(push_macro("max")) #undef max -#define __RESTORE_MAX__ -#endif #endif // defined(_MSC_VER) +#pragma once + +#include // std::enable_if, std::is_same -#include // due to #166 +// #include "functional/cxx_type_traits_polyfill.h" -#if __cplusplus >= 201703L // use of C++17 or higher -// Enables use of std::optional in SQLITE_ORM. -#define SQLITE_ORM_OPTIONAL_SUPPORTED -#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#include + +// #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 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" + +#ifdef __has_cpp_attribute +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L +#endif + +#ifdef __has_include +#define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) +#else +#define SQLITE_ORM_HAS_INCLUDE(file) 0L +#endif + +#if __cpp_aggregate_nsdmi >= 201304L +#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#endif + +#if __cpp_constexpr >= 201304L +#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_noexcept_function_type >= 201510L #define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif + +#if __cpp_aggregate_bases >= 201603L +#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED +#endif + +#if __cpp_fold_expressions >= 201603L +#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_if_constexpr >= 201606L +#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VAR inline +#else +#define SQLITE_ORM_INLINE_VAR +#endif + +#if __cpp_generic_lambdas >= 201707L +#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +#else +#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 __cpp_consteval >= 201811L +#define SQLITE_ORM_CONSTEVAL consteval +#else +#define SQLITE_ORM_CONSTEVAL constexpr +#endif + +#if __cpp_aggregate_paren_init >= 201902L +#define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED +#endif + +#if __cpp_concepts >= 201907L +#define SQLITE_ORM_CONCEPTS_SUPPORTED +#endif + +// #include "cxx_compiler_quirks.h" + +#ifdef __clang__ +#define SQLITE_ORM_DO_PRAGMA(...) _Pragma(#__VA_ARGS__) +#endif + +#ifdef __clang__ +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic push) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic ignored warnoption) \ + __VA_ARGS__ \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic pop) + +#else +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) __VA_ARGS__ +#endif + +// clang has the bad habit of diagnosing missing brace-init-lists when constructing aggregates with base classes. +// This is a false positive, since the C++ standard is quite clear that braces for nested or base objects may be omitted, +// see https://en.cppreference.com/w/cpp/language/aggregate_initialization: +// "The braces around the nested initializer lists may be elided (omitted), +// in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, +// and the subsequent initializer clauses are used to initialize the following members of the object." +// In this sense clang should only warn about missing field initializers. +// Because we know what we are doing, we suppress the diagnostic message +#define SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(...) SQLITE_ORM_CLANG_SUPPRESS("-Wmissing-braces", __VA_ARGS__) + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +#endif + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_void_t >= 201411L + using std::void_t; +#else + template + using void_t = void; +#endif + +#if __cpp_lib_bool_constant >= 201505L + using std::bool_constant; +#else + template + using bool_constant = std::integral_constant; +#endif + +#if __cpp_lib_logical_traits >= 201510L && __cpp_lib_type_trait_variable_templates >= 201510L + using std::conjunction; + using std::conjunction_v; + using std::disjunction; + using std::disjunction_v; + using std::negation; + using std::negation_v; +#else + template + struct conjunction : std::true_type {}; + template + struct conjunction : B1 {}; + template + struct conjunction : std::conditional_t, B1> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool conjunction_v = conjunction::value; + + template + struct disjunction : std::false_type {}; + template + struct disjunction : B1 {}; + template + struct disjunction : std::conditional_t> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool disjunction_v = disjunction::value; + + template + struct negation : bool_constant {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool negation_v = negation::value; +#endif + +#if __cpp_lib_remove_cvref >= 201711L + using std::remove_cvref, std::remove_cvref_t; +#else + template + struct remove_cvref : std::remove_cv> {}; + + template + using remove_cvref_t = typename remove_cvref::type; +#endif + +#if __cpp_lib_type_identity >= 201806L + using std::type_identity, std::type_identity_t; +#else + template + struct type_identity { + using type = T; + }; + + template + using type_identity_t = typename type_identity::type; +#endif + +#if 0 // __cpp_lib_detect >= 0L // library fundamentals TS v2, [meta.detect] + using std::nonesuch; + using std::detector; + using std::is_detected, std::is_detected_v; + using std::detected, std::detected_t; + using std::detected_or, std::detected_or_t; +#else + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + template class Op, class... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template class Op, class... Args> + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + + template class Op, class... Args> + using is_detected = typename detector::value_t; + + template class Op, class... Args> + using detected = detector; + + template class Op, class... Args> + using detected_t = typename detector::type; + + template class Op, class... Args> + using detected_or = detector; + + template class Op, class... Args> + using detected_or_t = typename detected_or::type; + + template class Op, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected::value; +#endif + +#if 0 // proposed but not pursued + using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v; +#else + // is_specialization_of: https://github.com/cplusplus/papers/issues/812 + + template class Primary> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false; + + template class Primary, class... Types> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v, Primary> = true; + + template class Primary> + struct is_specialization_of : bool_constant> {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool always_false_v = false; + + template + using index_constant = std::integral_constant; + } + } + + namespace polyfill = internal::polyfill; +} + +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template + using is_any_of = polyfill::disjunction...>; + + // enable_if for types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; + + // enable_if for types + template class Op, class... Args> + using match_if_not = std::enable_if_t>>; + + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t>; + + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; + + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; + + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = std::enable_if_t, bool>; + } + + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; + + template + using field_type_t = typename T::field_type; + + template + using constraints_type_t = typename T::constraints_type; + + template + using object_type_t = typename T::object_type; + + template + using elements_type_t = typename T::elements_type; + + template + using target_type_t = typename T::target_type; + } +} #pragma once +#include #include // std::error_code, std::system_error #include // std::string -#include #include #include // std::ostringstream +#include + +namespace sqlite_orm { - 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, @@ -50,7 +367,18 @@ __pragma(push_macro("min")) 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 { @@ -97,6 +425,10 @@ namespace sqlite_orm { 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"; } @@ -124,6 +456,14 @@ namespace sqlite_orm { 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; @@ -135,310 +475,487 @@ namespace sqlite_orm { template [[noreturn]] void throw_error(sqlite3* db, T&&... args) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - get_error_message(db, std::forward(args)...)); + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; } -} -namespace std { - template<> - struct is_error_code_enum : std::true_type {}; + 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); + } - inline std::error_code make_error_code(sqlite_orm::orm_error_code errorCode) { - return std::error_code(static_cast(errorCode), sqlite_orm::get_orm_error_category()); + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); } } #pragma once -#include // std::tuple, std::get, std::tuple_element, std::tuple_size -#include // std::false_type, std::true_type +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" -// #include "static_magic.h" +// #include "cxx_core_features.h" -#include // std::false_type, std::true_type, std::integral_constant +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif -namespace sqlite_orm { +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { +// #include "functional/cxx_type_traits_polyfill.h" - static inline decltype(auto) empty_callable() { - static auto res = [](auto&&...) {}; - return (res); - } +// #include "type_traits.h" - template - decltype(auto) static_if(std::true_type, const T& t, const F&) { - return (t); - } +// #include "is_std_ptr.h" - template - decltype(auto) static_if(std::false_type, const T&, const F& f) { - return (f); - } +#include +#include - template - decltype(auto) static_if(const T& t, const F& f) { - return static_if(std::integral_constant{}, t, f); - } +namespace sqlite_orm { - template - decltype(auto) static_if(const T& t) { - return static_if(std::integral_constant{}, t, empty_callable()); + /** + * 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 - using static_not = std::integral_constant; - } + 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 { - // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type - namespace tuple_helper { + /** + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) + */ + template + struct type_printer {}; - /** - * HAS_TYPE type trait - */ - template - struct has_type; + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; - template - struct has_type> : std::false_type {}; + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; - template - struct has_type> : has_type> {}; + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; - template - struct has_type> : std::true_type {}; + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; - template - using tuple_contains_type = typename has_type::type; + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>>> : integer_printer { + }; - /** - * HAS_SOME_TYPE type trait - */ - template class TT, typename Tuple> - struct has_some_type; + template + struct type_printer::value>> : real_printer {}; - template class TT> - struct has_some_type> : std::false_type {}; + template + struct type_printer, + std::is_base_of, + std::is_base_of>>> : text_printer {}; - template class TT, typename U, typename... Ts> - struct has_some_type> : has_some_type> {}; + template + struct type_printer::value>> : type_printer {}; - template class TT, typename T, typename... Ts> - struct has_some_type, Ts...>> : std::true_type {}; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif - template class TT, typename Tuple> - using tuple_contains_some_type = typename has_some_type::type; + template<> + struct type_printer, void> : blob_printer {}; +} +#pragma once - template - struct iterator_impl { +namespace sqlite_orm { - template - void operator()(const std::tuple& tuple, const L& lambda, bool reverse = true) { - if(reverse) { - lambda(std::get(tuple)); - iterator_impl()(tuple, lambda, reverse); - } else { - iterator_impl()(tuple, lambda, reverse); - lambda(std::get(tuple)); - } - } + namespace internal { - template - void operator()(const L& lambda) { - iterator_impl()(lambda); - lambda((const typename std::tuple_element>::type*)nullptr); - } + enum class collate_argument { + binary, + nocase, + rtrim, }; + } - template - struct iterator_impl<0, Args...> { - - template - void operator()(const std::tuple& tuple, const L& lambda, bool /*reverse*/ = true) { - lambda(std::get<0>(tuple)); - } +} +#pragma once - template - void operator()(const L& lambda) { - lambda((const typename std::tuple_element<0, std::tuple>::type*)nullptr); - } - }; +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple, std::make_tuple +#include // std::is_base_of, std::false_type, std::true_type - template - struct iterator_impl { +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +/* + * Symbols for 'template metaprogramming' (compile-time template programming), + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * + * Currently, the focus is on facilitating advanced type filtering, + * such as filtering columns by constraints having various traits. + * Hence it contains only a very small subset of a full MPL. + * + * Two key concepts are critical to understanding: + * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn" + * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. + * + * Conventions: + * - "Fn" is the name for a metafunction template template parameter. + * - "FnCls" is the name for a metafunction class template parameter. + * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). + */ - template - void operator()(const std::tuple<>&, const L&, bool /*reverse*/ = true) { - //.. - } +#include // std::false_type, std::true_type - template - void operator()(const L&) { - //.. - } - }; +// #include "cxx_universal.h" - template - struct iterator_impl2; +// #include "cxx_type_traits_polyfill.h" - template<> - struct iterator_impl2<> { +namespace sqlite_orm { + namespace internal { + namespace mpl { + template class Fn> + struct indirectly_test_metafunction; - template - void operator()(const L&) const { - //.. - } - }; + /* + * Determines whether a class template has a nested metafunction `fn`. + * + * Implementation note: the technique of specialiazing on the inline variable must come first because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_metafunction_class_v>> = + true; - template - struct iterator_impl2 { + template + struct is_metafunction_class : polyfill::bool_constant> {}; - template - void operator()(const L& lambda) const { - lambda((const H*)nullptr); - iterator_impl2{}(lambda); - } - }; + /* + * Invoke metafunction. + */ + template class Fn, class... Args> + using invoke_fn_t = typename Fn::type; - template - struct iterator; +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Op, class... Args> + struct wrap_op { + using type = Op; + }; - template - struct iterator> { + /* + * Invoke metafunction operation. + * + * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail + * if alias template `Op` has a dependent expression in it. + */ + template class Op, class... Args> + using invoke_op_t = typename wrap_op::type; +#else + /* + * Invoke metafunction operation. + */ + template class Op, class... Args> + using invoke_op_t = Op; +#endif - template - void operator()(const L& lambda) const { - iterator_impl2{}(lambda); - } - }; - } + /* + * Invoke metafunction class by invoking its nested metafunction. + */ + template + using invoke_t = typename FnCls::template fn::type; - namespace internal { + /* + * Instantiate metafunction class' nested metafunction. + */ + template + using instantiate = typename FnCls::template fn; - // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer - template - auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence) { - return (f.*functionPointer)(std::get(t)...); - } + /* + * Wrap given type such that `typename T::type` is valid. + */ + template + struct type_wrap : polyfill::type_identity {}; + template + struct type_wrap> : T {}; - template - auto call(Function& f, FunctionPointer functionPointer, Tuple t) { - static constexpr auto size = std::tuple_size::value; - return call_impl(f, functionPointer, move(t), std::make_index_sequence{}); - } + /* + * Turn metafunction into a metafunction class. + * + * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). + * This is necessary because `fn` is a proxy to the originally quoted metafunction, + * and the instantiation of the metafunction might be an invalid expression. + */ + template class Fn> + struct quote_fn { + template class, class...> + struct invoke_fn; + + template class F, class... Args> + struct invoke_fn>, F, Args...> { + using type = type_wrap>; + }; - template - auto call(Function& f, Tuple t) { - return call(f, &Function::operator(), move(t)); - } + template + using fn = typename invoke_fn::type; + }; - template - void move_tuple_impl(L& lhs, R& rhs) { - std::get(lhs) = std::move(std::get(rhs)); - internal::static_if{}>([](auto& l, auto& r) { - move_tuple_impl(l, r); - })(lhs, rhs); - } + /* + * Indirection wrapper for higher-order metafunctions, + * specialized on the argument indexes where metafunctions appear. + */ + template + struct higherorder; + + template<> + struct higherorder<0u> { + /* + * Turn higher-order metafunction into a metafunction class. + */ + template class Fn, class... Args2> class HigherFn> + struct quote_fn { + template + struct fn : HigherFn {}; + }; + }; - template - void move_tuple(L& lhs, R& rhs) { - using bool_type = std::integral_constant; - static_if([](auto& l, auto& r) { - move_tuple_impl(l, r); - })(lhs, rhs); - } + /* + * Metafunction class that extracts the nested metafunction of its metafunction class argument, + * quotes the extracted metafunction and passes it on to the next metafunction class + * (kind of the inverse of quoting). + */ + template + struct pass_extracted_fn_to { + template + struct fn : FnCls::template fn {}; + + // extract, quote, pass on + template class Fn, class... Args> + struct fn> : FnCls::template fn> {}; + }; - template - void iterate_tuple(const std::tuple& tuple, const L& lambda) { - using tuple_type = std::tuple; - tuple_helper::iterator_impl::value - 1, Args...>()(tuple, lambda, false); - } + /* + * Metafunction class that invokes the specified metafunction operation, + * and passes its result on to the next metafunction class. + */ + template class Op, class FnCls> + struct pass_result_to { + // call Op, pass on its result + template + struct fn : FnCls::template fn> {}; + }; - template - void iterate_tuple(const L& lambda) { - tuple_helper::iterator{}(lambda); - } + /* + * Bind arguments at the front of a metafunction class. + * Metafunction class equivalent to std::bind_front(). + */ + template + struct bind_front { + template + struct fn : FnCls::template fn {}; + }; - template - using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + /* + * Bind arguments at the back of a metafunction class. + * Metafunction class equivalent to std::bind_back() + */ + template + struct bind_back { + template + struct fn : FnCls::template fn {}; + }; - template - struct conc_tuple { - using type = tuple_cat_t; - }; - } -} -#pragma once + /* + * Metafunction class equivalent to polyfill::always_false. + * It ignores arguments passed to the metafunction, + * and always returns the given type. + */ + template + struct always { + template + struct fn : type_wrap {}; + }; -#include // std::tuple -#include // std::enable_if + /* + * Unary metafunction class equivalent to std::type_identity. + */ + struct identity { + template + struct fn : type_wrap {}; + }; -namespace sqlite_orm { - namespace internal { + /* + * Metafunction class equivalent to std::negation. + */ + template + struct not_ { + template + struct fn : polyfill::negation> {}; + }; - template class C, class SFINAE = void> - struct find_in_tuple; + /* + * Metafunction class equivalent to std::conjunction + */ + template + struct conjunction { + template + struct fn : polyfill::conjunction...> {}; + }; - template class C> - struct find_in_tuple, C, void> { - using type = void; - }; + /* + * Metafunction class equivalent to std::disjunction. + */ + template + struct disjunction { + template + struct fn : polyfill::disjunction...> {}; + }; - template class C> - struct find_in_tuple, C, typename std::enable_if::value>::type> { - using type = H; - }; +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + /* + * Metafunction equivalent to std::conjunction. + */ + template class... TraitFn> + using conjunction_fn = conjunction...>; - template class C> - struct find_in_tuple, C, typename std::enable_if::value>::type> { - using type = typename find_in_tuple, C>::type; - }; - } -} -#pragma once + /* + * Metafunction equivalent to std::disjunction. + */ + template class... TraitFn> + using disjunction_fn = disjunction...>; +#else + template class... TraitFn> + struct conjunction_fn : conjunction...> {}; -#include // std::tuple + template class... TraitFn> + struct disjunction_fn : disjunction...> {}; +#endif -namespace sqlite_orm { - namespace internal { + /* + * Convenience template alias for binding arguments at the front of a metafunction. + */ + template class Fn, class... Bound> + using bind_front_fn = bind_front, Bound...>; - template class F> - struct tuple_transformer; + /* + * Convenience template alias for binding arguments at the back of a metafunction. + */ + template class Fn, class... Bound> + using bind_back_fn = bind_back, Bound...>; - template class F> - struct tuple_transformer, F> { - using type = std::tuple::type...>; - }; + /* + * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + */ + template class Fn, class... Args2> class HigherFn, + template + class BoundFn, + class... Bound> + using bind_front_higherorder_fn = + bind_front::quote_fn, quote_fn, Bound...>; + } } -} -#pragma once -#include // std::tuple + namespace mpl = internal::mpl; -namespace sqlite_orm { + // convenience metafunction classes namespace internal { + /* + * Trait metafunction class that checks if a type has the specified trait. + */ + template class TraitFn> + using check_if = mpl::quote_fn; - template class C> - struct count_tuple; + /* + * Trait metafunction class that checks if a type doesn't have the specified trait. + */ + template class TraitFn> + using check_if_not = mpl::not_>; - template class C> - struct count_tuple, C> { - static constexpr const int value = 0; - }; + /* + * Trait metafunction class that checks if a type is the same as the specified type. + */ + template + using check_if_is_type = mpl::bind_front_fn; - template class C> - struct count_tuple, C> { - static constexpr const int value = C::value + count_tuple, C>::value; - }; + /* + * Trait metafunction class that checks if a type's template matches the specified template + * (similar to `is_specialization_of`). + */ + template class Template> + using check_if_is_template = + mpl::pass_extracted_fn_to>>; } } -#pragma once + +// #include "tuple_helper/same_or_void.h" namespace sqlite_orm { namespace internal { @@ -456,465 +973,238 @@ namespace sqlite_orm { using type = A; }; - template - struct same_or_void { - using type = void; - }; - template struct same_or_void { using type = A; }; template - struct same_or_void { - using type = typename same_or_void::type; - }; + struct same_or_void : same_or_void {}; } } -#pragma once -#include // std::string -#include // std::shared_ptr, std::unique_ptr -#include // std::vector -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "tuple_helper/tuple_traits.h" -namespace sqlite_orm { +#include // std::is_same +#include - /** - * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) - */ - template - struct type_printer; +// #include "../functional/cxx_type_traits_polyfill.h" - struct integer_printer { - inline const std::string& print() { - static const std::string res = "INTEGER"; - return res; - } - }; +// #include "../functional/mpl.h" - struct text_printer { - inline const std::string& print() { - static const std::string res = "TEXT"; - return res; - } - }; +namespace sqlite_orm { + namespace internal { + /* + * Higher-order trait metafunction that checks whether a tuple contains a type with given trait. + */ + template class TraitFn, class Tuple> + struct tuple_has {}; + template class TraitFn, class... Types> + struct tuple_has> : polyfill::disjunction...> {}; - struct real_printer { - inline const std::string& print() { - static const std::string res = "REAL"; - return res; - } - }; + /* + * Trait metafunction class that checks whether a tuple contains a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has = mpl::bind_front_higherorder_fn; - struct blob_printer { - inline const std::string& print() { - static const std::string res = "BLOB"; - return res; - } - }; + /* + * Trait metafunction class that checks whether a tuple doesn't contain a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has_not = mpl::not_>; - // Note unsigned/signed char and simple char used for storing integer values, not char values. - template<> - struct type_printer : public integer_printer {}; + /* + * Metafunction class that checks whether a tuple contains given type. + */ + template + using check_if_tuple_has_type = mpl::bind_front_higherorder_fn::template fn>; - template<> - struct type_printer : public integer_printer {}; + /* + * Metafunction class that checks whether a tuple contains a given template. + * + * Note: we are using 2 small tricks: + * 1. A template template parameter can be treated like a metafunction, so we can just "quote" a 'primary' + * template into the MPL system (e.g. `std::vector`). + * 2. This metafunction class does the opposite of the trait function `is_specialization`: + * `is_specialization` tries to instantiate the primary template template parameter using the + * template parameters of a template type, then compares both instantiated types. + * Here instead, `pass_extracted_fn_to` extracts the template template parameter from a template type, + * then compares the resulting template template parameters. + */ + template class Primary> + using check_if_tuple_has_template = + mpl::bind_front_higherorder_fn::template fn>; + } +} +// #include "tuple_helper/tuple_filter.h" - template<> - struct type_printer : public integer_printer {}; +#include // std::integral_constant, std::index_sequence, std::conditional, std::declval +#include // std::tuple - template<> - struct type_printer : public integer_printer {}; +// #include "../functional/cxx_universal.h" - template<> - struct type_printer : public integer_printer {}; +// #include "../functional/index_sequence_util.h" - template<> - struct type_printer : public integer_printer {}; +#include // std::index_sequence, std::make_index_sequence - template<> - struct type_printer : public integer_printer {}; +// #include "../functional/cxx_universal.h" - template<> - struct type_printer : public integer_printer {}; +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#include +#endif - template<> - struct type_printer : public integer_printer {}; +namespace sqlite_orm { + namespace internal { + /** + * Get the first value of an index_sequence. + */ + template + SQLITE_ORM_CONSTEVAL size_t first_index_sequence_value(std::index_sequence) { + return I; + } - template<> - struct type_printer : public integer_printer {}; +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + /** + * Reorder the values of an index_sequence according to the positions from a second sequence. + */ + template + SQLITE_ORM_CONSTEVAL auto reorder_index_sequence(std::index_sequence, + std::index_sequence) { + constexpr std::array values{Value...}; + return std::index_sequence{}; + } - template<> - struct type_printer : public integer_printer {}; + template + SQLITE_ORM_CONSTEVAL std::index_sequence reorder_index_sequence(std::index_sequence, + std::index_sequence) { + return {}; + } - template<> - struct type_printer : public integer_printer {}; + inline SQLITE_ORM_CONSTEVAL std::index_sequence<> reorder_index_sequence(std::index_sequence<>, + std::index_sequence<>) { + return {}; + } - template<> - struct type_printer : public text_printer {}; + /** + * Reverse the values of an index_sequence. + */ + template + SQLITE_ORM_CONSTEVAL auto reverse_index_sequence(std::index_sequence) { + return reorder_index_sequence(std::index_sequence{}, std::make_index_sequence{}); + } +#endif - template<> - struct type_printer : public text_printer {}; + template + struct flatten_idxseq { + using type = std::index_sequence<>; + }; - template<> - struct type_printer : public text_printer {}; + template + struct flatten_idxseq> { + using type = std::index_sequence; + }; - template<> - struct type_printer : public real_printer {}; + template + struct flatten_idxseq, std::index_sequence, Seq...> + : flatten_idxseq, Seq...> {}; - template<> - struct type_printer : public real_printer {}; + template + using flatten_idxseq_t = typename flatten_idxseq::type; + } +} - template - struct type_printer, void> : public type_printer {}; +namespace sqlite_orm { + namespace internal { - template - struct type_printer, void> : public type_printer {}; + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct type_printer, void> : public type_printer {}; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct conc_tuple { + using type = tuple_cat_t; + }; - template<> - struct type_printer, void> : public blob_printer {}; -} -#pragma once + template + struct tuple_from_index_sequence; -namespace sqlite_orm { + template + struct tuple_from_index_sequence> { + using type = std::tuple...>; + }; - namespace internal { + template + using tuple_from_index_sequence_t = typename tuple_from_index_sequence::type; - enum class collate_argument { - binary, - nocase, - rtrim, - }; - } - -} -#pragma once - -#include // std::string -#include // std::tuple, std::make_tuple -#include // std::stringstream -#include // std::is_base_of, std::false_type, std::true_type -#include // std::ostream - -// #include "table_type.h" - -#include // std::enable_if, std::is_member_pointer, std::is_member_function_pointer - -// #include "member_traits/getter_traits.h" - -// #include "getters.h" - -namespace sqlite_orm { - namespace internal { - - template - using getter_by_value_const = T (O::*)() const; - - template - using getter_by_value = T (O::*)(); - - template - using getter_by_ref_const = T& (O::*)() const; - - template - using getter_by_ref = T& (O::*)(); - - template - using getter_by_const_ref_const = const T& (O::*)() const; - - template - using getter_by_const_ref = const T& (O::*)(); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - using getter_by_value_const_noexcept = T (O::*)() const noexcept; - - template - using getter_by_value_noexcept = T (O::*)() noexcept; - - template - using getter_by_ref_const_noexcept = T& (O::*)() const noexcept; - - template - using getter_by_ref_noexcept = T& (O::*)() noexcept; - - template - using getter_by_const_ref_const_noexcept = const T& (O::*)() const noexcept; - - template - using getter_by_const_ref_noexcept = const T& (O::*)() noexcept; -#endif - } -} - -namespace sqlite_orm { - namespace internal { - - template - struct getter_traits; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; -#endif - } -} - -// #include "member_traits/setter_traits.h" - -// #include "setters.h" - -namespace sqlite_orm { - namespace internal { - - template - using setter_by_value = void (O::*)(T); - - template - using setter_by_ref = void (O::*)(T&); - - template - using setter_by_const_ref = void (O::*)(const T&); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - using setter_by_value_noexcept = void (O::*)(T) noexcept; - - template - using setter_by_ref_noexcept = void (O::*)(T&) noexcept; - - template - using setter_by_const_ref_noexcept = void (O::*)(const T&) noexcept; -#endif - } -} - -namespace sqlite_orm { - namespace internal { - - template - struct setter_traits; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; + template class Pred, template class Proj, class Seq> + struct filter_tuple_sequence; - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct setter_traits> { - using object_type = O; - using field_type = T; +#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 - struct setter_traits> { - using object_type = O; - using field_type = T; + template class Pred> + struct tuple_seq_single::value>> { + using type = std::index_sequence; }; - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>, Pred>::type...> {}; #endif - } -} - -// #include "member_traits/is_getter.h" -#include // std::false_type, std::true_type - -// #include "getters.h" - -namespace sqlite_orm { - namespace internal { - - template - struct is_getter : std::false_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; + template + class Pred, + template class Proj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_t = tuple_from_index_sequence_t>; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t> + struct count_tuple : std::integral_constant::size()> {}; - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; -#endif + /* + * Count a tuple, picking only those elements specified in the index sequence. + * + * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems + * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + class Pred, + class Seq, + template class FilterProj = polyfill::type_identity_t> + struct count_filtered_tuple + : std::integral_constant::size()> {}; } } -// #include "member_traits/is_setter.h" - -// #include "setters.h" - -namespace sqlite_orm { - namespace internal { - - template - struct is_setter : std::false_type {}; - - template - struct is_setter> : std::true_type {}; +// #include "type_traits.h" - template - struct is_setter> : std::true_type {}; - - template - struct is_setter> : std::true_type {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct is_setter> : std::true_type {}; +// #include "collate_argument.h" - template - struct is_setter> : std::true_type {}; +// #include "error_code.h" - template - struct is_setter> : std::true_type {}; -#endif - } -} +// #include "table_type_of.h" namespace sqlite_orm { @@ -928,38 +1218,29 @@ namespace sqlite_orm { * T - member pointer * `type` is a type which is mapped. * E.g. - * - `table_type::type` is `User` - * - `table_type::type` is `User` - * - `table_type::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` */ - template - struct table_type; + template + struct table_type_of; template - struct table_type::value && - !std::is_member_function_pointer::value>::type> { + struct table_type_of { using type = O; }; - template - struct table_type::value>::type> { - using type = typename getter_traits::object_type; - }; - - template - struct table_type::value>::type> { - using type = typename setter_traits::object_type; - }; - template - struct table_type, void> { + struct table_type_of> { using type = T; }; + + template + using table_type_of_t = typename table_type_of::type; } } -// #include "tuple_helper/tuple_helper.h" +// #include "type_printer.h" namespace sqlite_orm { @@ -968,11 +1249,14 @@ namespace sqlite_orm { /** * AUTOINCREMENT constraint class. */ - struct autoincrement_t { + struct autoincrement_t {}; - operator std::string() const { - return "AUTOINCREMENT"; - } + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, }; struct primary_key_base { @@ -981,48 +1265,85 @@ namespace sqlite_orm { 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; + }; - order_by asc_option = order_by::unspecified; + template + struct primary_key_with_autoincrement { + using primary_key_type = T; - operator std::string() const { - std::string res = "PRIMARY KEY"; - switch(this->asc_option) { - case order_by::ascending: - res += " ASC"; - break; - case order_by::descending: - res += " DESC"; - break; - default: - break; - } - return res; - } + primary_key_type primary_key; + + primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {} }; /** * PRIMARY KEY constraint class. * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when - * used withen `make_column` function. + * used within `make_column` function. */ template struct primary_key_t : primary_key_base { + using self = primary_key_t; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple; columns_tuple columns; - primary_key_t(decltype(columns) c) : columns(move(c)) {} + primary_key_t(decltype(columns) columns) : columns(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; + } - primary_key_t asc() const { + self on_conflict_ignore() const { auto res = *this; - res.asc_option = order_by::ascending; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; return res; } - primary_key_t desc() const { + self on_conflict_replace() const { auto res = *this; - res.asc_option = order_by::descending; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; return res; } }; @@ -1083,22 +1404,22 @@ namespace sqlite_orm { inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { switch(action) { - case decltype(action)::no_action: + case foreign_key_action::no_action: os << "NO ACTION"; break; - case decltype(action)::restrict_: + case foreign_key_action::restrict_: os << "RESTRICT"; break; - case decltype(action)::set_null: + case foreign_key_action::set_null: os << "SET NULL"; break; - case decltype(action)::set_default: + case foreign_key_action::set_default: os << "SET DEFAULT"; break; - case decltype(action)::cascade: + case foreign_key_action::cascade: os << "CASCADE"; break; - case decltype(action)::none: + case foreign_key_action::none: break; } return os; @@ -1181,7 +1502,7 @@ namespace sqlite_orm { } operator bool() const { - return this->_action != decltype(this->_action)::none; + return this->_action != foreign_key_action::none; } }; @@ -1199,12 +1520,12 @@ namespace sqlite_orm { /** * Holds obect type of all referenced columns. */ - using target_type = typename same_or_void::type...>::type; + using target_type = typename same_or_void...>::type; /** * Holds obect type of all source columns. */ - using source_type = typename same_or_void::type...>::type; + using source_type = typename same_or_void...>::type; columns_type columns; references_type references; @@ -1231,14 +1552,6 @@ namespace sqlite_orm { this->on_delete = {*this, false, other.on_delete._action}; return *this; } - - template - void for_each_column(const L&) {} - - template - constexpr bool has_every() const { - return false; - } }; template @@ -1258,8 +1571,6 @@ namespace sqlite_orm { tuple_type columns; - foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} - template foreign_key_t, std::tuple> references(Rs... refs) { return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; @@ -1268,81 +1579,146 @@ namespace sqlite_orm { #endif struct collate_constraint_t { - internal::collate_argument argument = internal::collate_argument::binary; - - collate_constraint_t(internal::collate_argument argument_) : argument(argument_) {} + 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 { - std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); - return res; + return "COLLATE " + this->string_from_collate_argument(this->argument); } - static std::string string_from_collate_argument(internal::collate_argument argument) { + static std::string string_from_collate_argument(collate_argument argument) { switch(argument) { - case decltype(argument)::binary: + case collate_argument::binary: return "BINARY"; - case decltype(argument)::nocase: + case collate_argument::nocase: return "NOCASE"; - case decltype(argument)::rtrim: + case collate_argument::rtrim: return "RTRIM"; } - throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; } }; - struct check_string { - operator std::string() const { - return "CHECK"; - } + template + struct check_t { + using expression_type = T; + + expression_type expression; + }; + + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; + + bool full = true; + storage_type storage = storage_type::not_specified; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} +#endif }; template - struct check_t : check_string { + struct generated_always_t : basic_generated_always { using expression_type = T; expression_type expression; - check_t(expression_type expression_) : expression(std::move(expression_)) {} + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} + + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } + + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } }; + } + + namespace internal { + template - struct is_constraint : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v; - template<> - struct is_constraint : std::true_type {}; + template + using is_foreign_key = polyfill::bool_constant>; + + template + struct is_primary_key : std::false_type {}; template - struct is_constraint> : std::true_type {}; + struct is_primary_key> : std::true_type {}; - template - struct is_constraint> : std::true_type {}; + template + struct is_primary_key> : std::true_type {}; template - struct is_constraint> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; - template - struct is_constraint> : std::true_type {}; + template + using is_generated_always = polyfill::is_specialization_of; - template<> - struct is_constraint : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always::value; template - struct is_constraint> : std::true_type {}; + using is_autoincrement = std::is_same; - template - struct constraints_size; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_autoincrement_v = is_autoincrement::value; - template<> - struct constraints_size<> { - static constexpr const int value = 0; - }; + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::instantiate, + check_if_tuple_has_template, + check_if_tuple_has_template>, + constraints_type_t>, + std::is_base_of>>> { - template - struct constraints_size { - static constexpr const int value = is_constraint::value + constraints_size::value; + static_assert(tuple_has>::value, "an unexpected type was passed"); }; + + template + using is_constraint = + mpl::instantiate, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, +#if SQLITE_VERSION_NUMBER >= 3031000 + check_if, +#endif + // dummy tail because of SQLITE_VERSION_NUMBER checks above + mpl::always>, + T>; + } + +#if SQLITE_VERSION_NUMBER >= 3031000 + template + internal::generated_always_t generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; } + template + internal::generated_always_t as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; + } +#endif #if SQLITE_VERSION_NUMBER >= 3006019 /** @@ -1367,7 +1743,11 @@ namespace sqlite_orm { return {{}}; } - inline internal::autoincrement_t autoincrement() { + /** + * AUTOINCREMENT keyword. [Deprecation notice] Use `primary_key().autoincrement()` instead of using this function. + * This function will be removed in 1.9 + */ + [[deprecated("Use primary_key().autoincrement()` instead")]] inline internal::autoincrement_t autoincrement() { return {}; } @@ -1401,59 +1781,14 @@ namespace sqlite_orm { internal::check_t check(T t) { return {std::move(t)}; } - - namespace internal { - - /** - * FOREIGN KEY traits. Common case - */ - template - struct is_foreign_key : std::false_type {}; - - /** - * FOREIGN KEY traits. Specialized case - */ - template - struct is_foreign_key> : std::true_type {}; - - /** - * PRIMARY KEY traits. Common case - */ - template - struct is_primary_key : public std::false_type {}; - - /** - * PRIMARY KEY traits. Specialized case - */ - template - struct is_primary_key> : public std::true_type {}; - - /** - * PRIMARY KEY INSERTABLE traits. - */ - template - struct is_primary_key_insertable { - using field_type = typename T::field_type; - using constraints_type = typename T::constraints_type; - - static_assert((tuple_helper::tuple_contains_type, constraints_type>::value), - "an unexpected type was passed"); - - static constexpr bool value = - (tuple_helper::tuple_contains_some_type::value || - tuple_helper::tuple_contains_type::value || - std::is_base_of>::value); - }; - } - } #pragma once -#include // std::false_type, std::true_type +#include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -1464,144 +1799,74 @@ namespace sqlite_orm { * custom type as `NULL` (for example: boost::optional) you have to create a specialiation * of type_is_nullable for your type and derive from `std::true_type`. */ - template - struct type_is_nullable : public std::false_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::shared_ptr is nullable in sqlite_orm. - */ - template - struct type_is_nullable> : public std::true_type { - bool operator()(const std::shared_ptr& t) const { - return static_cast(t); - } - }; - - /** - * This is a specialization for std::unique_ptr. std::unique_ptr is nullable too. + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. */ template - struct type_is_nullable> : public std::true_type { - bool operator()(const std::unique_ptr& t) const { - return static_cast(t); - } - }; - + struct type_is_nullable - struct type_is_nullable> : public std::true_type { - bool operator()(const std::optional& t) const { - return t.has_value(); + polyfill::is_specialization_of, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); } }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED } #pragma once -#include // std::unique_ptr -#include // std::string -#include // std::stringstream - -// #include "constraints.h" +#include // std::false_type, std::true_type +#include // std::move +// #include "functional/cxx_optional.h" -// #include "serializator_context.h" +// #include "tags.h" namespace sqlite_orm { - namespace internal { + struct negatable_t {}; - struct serializator_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - - template - const std::string* column_name(F O::*) const { - return nullptr; - } - }; - - template - struct serializator_context : serializator_context_base { - using impl_type = I; - - const impl_type& impl; - - serializator_context(const impl_type& impl_) : impl(impl_) {} - - template - const std::string* column_name(F O::*m) const { - return this->impl.column_name(m); - } - }; - - template - struct serializator_context_builder { - using storage_type = S; - using impl_type = typename storage_type::impl_type; - - serializator_context_builder(const storage_type& storage_) : storage(storage_) {} - - serializator_context operator()() const { - return {this->storage.impl}; - } - - const storage_type& storage; - }; - + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; } - } -namespace sqlite_orm { - - namespace internal { - - /** - * This class is used in tuple interation to know whether tuple constains `default_value_t` - * constraint class and what it's value if it is - */ - struct default_value_extractor { +// #include "serialize_result_type.h" - template - std::unique_ptr operator()(const A&) { - return {}; - } +// #include "functional/cxx_string_view.h" - template - std::unique_ptr operator()(const default_t& t) { - serializator_context_base context; - return std::make_unique(serialize(t.value, context)); - } - }; +// #include "cxx_core_features.h" - } +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif -} -#pragma once +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif -#include // std::false_type, std::true_type -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::nullopt -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -// #include "tags.h" +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string +#endif namespace sqlite_orm { namespace internal { - struct negatable_t {}; - - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators - */ - struct condition_t {}; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif } } @@ -1626,7 +1891,7 @@ namespace sqlite_orm { }; struct conc_string { - operator std::string() const { + serialize_result_type serialize() const { return "||"; } }; @@ -1638,7 +1903,7 @@ namespace sqlite_orm { using conc_t = binary_operator; struct add_string { - operator std::string() const { + serialize_result_type serialize() const { return "+"; } }; @@ -1650,7 +1915,7 @@ namespace sqlite_orm { using add_t = binary_operator; struct sub_string { - operator std::string() const { + serialize_result_type serialize() const { return "-"; } }; @@ -1662,7 +1927,7 @@ namespace sqlite_orm { using sub_t = binary_operator; struct mul_string { - operator std::string() const { + serialize_result_type serialize() const { return "*"; } }; @@ -1674,7 +1939,7 @@ namespace sqlite_orm { using mul_t = binary_operator; struct div_string { - operator std::string() const { + serialize_result_type serialize() const { return "/"; } }; @@ -1686,7 +1951,7 @@ namespace sqlite_orm { using div_t = binary_operator; struct mod_operator_string { - operator std::string() const { + serialize_result_type serialize() const { return "%"; } }; @@ -1698,7 +1963,7 @@ namespace sqlite_orm { using mod_t = binary_operator; struct bitwise_shift_left_string { - operator std::string() const { + serialize_result_type serialize() const { return "<<"; } }; @@ -1710,7 +1975,7 @@ namespace sqlite_orm { using bitwise_shift_left_t = binary_operator; struct bitwise_shift_right_string { - operator std::string() const { + serialize_result_type serialize() const { return ">>"; } }; @@ -1722,7 +1987,7 @@ namespace sqlite_orm { using bitwise_shift_right_t = binary_operator; struct bitwise_and_string { - operator std::string() const { + serialize_result_type serialize() const { return "&"; } }; @@ -1734,7 +1999,7 @@ namespace sqlite_orm { using bitwise_and_t = binary_operator; struct bitwise_or_string { - operator std::string() const { + serialize_result_type serialize() const { return "|"; } }; @@ -1746,7 +2011,7 @@ namespace sqlite_orm { using bitwise_or_t = binary_operator; struct bitwise_not_string { - operator std::string() const { + serialize_result_type serialize() const { return "~"; } }; @@ -1764,7 +2029,7 @@ namespace sqlite_orm { }; struct assign_string { - operator std::string() const { + serialize_result_type serialize() const { return "="; } }; @@ -1879,292 +2144,248 @@ namespace sqlite_orm { #include // std::tuple #include // std::string #include // std::unique_ptr -#include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer +#include // std::is_same, std::is_member_object_pointer -// #include "type_is_nullable.h" +// #include "functional/cxx_universal.h" -// #include "tuple_helper/tuple_helper.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "default_value_extractor.h" +// #include "tuple_helper/tuple_traits.h" -// #include "constraints.h" +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" // #include "member_traits/member_traits.h" -#include // std::enable_if +#include // std::enable_if, std::is_function, std::true_type, std::false_type -// #include "is_field_member_pointer.h" +// #include "../functional/cxx_universal.h" -#include // std::false_type, std::true_type, std::is_member_pointer, std::is_member_function_pointer +// #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 - struct is_field_member_pointer : std::false_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 is_field_member_pointer::value && - !std::is_member_function_pointer::value>::type> - : std::true_type {}; - } -} + struct getter_field_type {}; -// #include "is_getter.h" + template + using getter_field_type_t = typename getter_field_type::type; -// #include "field_member_traits.h" + template + struct getter_field_type : getter_field_type {}; -#include // std::enable_if + template + struct getter_field_type : polyfill::remove_cvref {}; -// #include "is_field_member_pointer.h" + template + struct getter_field_type : polyfill::remove_cvref {}; -namespace sqlite_orm { - namespace internal { +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; - template - struct field_member_traits; + template + struct getter_field_type : polyfill::remove_cvref {}; +#endif - template - struct field_member_traits::value>::type> { - using object_type = O; - using field_type = F; - }; - } -} + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; -// #include "is_setter.h" + template + using setter_field_type_t = typename setter_field_type::type; -// #include "getter_traits.h" + template + struct setter_field_type : setter_field_type {}; -// #include "setter_traits.h" + template + struct setter_field_type : polyfill::remove_cvref {}; -namespace sqlite_orm { - namespace internal { +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif template - struct member_traits; + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; template - struct member_traits::value>::type> { - using object_type = typename field_member_traits::object_type; - using field_type = typename field_member_traits::field_type; - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + template + struct is_setter : std::false_type {}; template - struct member_traits::value>::type> { - using object_type = typename getter_traits::object_type; - using field_type = typename getter_traits::field_type; - }; + struct is_setter>> : std::true_type {}; template - struct member_traits::value>::type> { - using object_type = typename setter_traits::object_type; - using field_type = typename setter_traits::field_type; - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; + + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; + + template + using member_field_type_t = typename member_field_type::type; + + template + struct member_object_type {}; + + template + struct member_object_type : polyfill::type_identity {}; + + template + using member_object_type_t = typename member_object_type::type; } } +// #include "type_is_nullable.h" + +// #include "constraints.h" + namespace sqlite_orm { namespace internal { - struct basic_column { + struct column_identifier { /** - * Column name. Specified during construction in `make_column`. + * Column name. */ - const std::string name; + std::string name; }; - /** - * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage - * O is a mapped class, e.g. User - * T is a mapped class'es field type, e.g. &User::name - * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc + 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_t : basic_column { - using object_type = O; - using field_type = T; - using constraints_type = std::tuple; - using member_pointer_t = field_type object_type::*; - using getter_type = G; + 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/write member - */ - member_pointer_t member_pointer /* = nullptr*/; - - /** - * Getter member function pointer to get a value. If member_pointer is null than - * `getter` and `setter` must be not null - */ - getter_type getter /* = nullptr*/; - - /** - * Setter member function + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. */ - setter_type setter /* = nullptr*/; + const member_pointer_t member_pointer; /** - * Constraints tuple + * Setter member function to write a field value */ - constraints_type constraints; - - column_t(std::string name_, - member_pointer_t member_pointer_, - getter_type getter_, - setter_type setter_, - constraints_type constraints_) : - basic_column{move(name_)}, - member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; /** * Simplified interface for `NOT NULL` constraint */ - bool not_null() const { + constexpr bool is_not_null() const { return !type_is_nullable::value; } + }; - template - constexpr bool has() const { - return tuple_helper::tuple_contains_type::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; - template - constexpr bool has_every() const { - if(has() && has()) { - return true; - } else { - return has_every(); - } + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints are of trait `Trait` + */ + template class Trait> + constexpr bool is() const { + return tuple_has::value; } - template - constexpr bool has_every() const { - return has(); + constexpr bool is_generated() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + return is(); +#else + return false; +#endif } /** * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr */ - std::unique_ptr default_value() const { - std::unique_ptr res; - iterate_tuple(this->constraints, [&res](auto& v) { - auto dft = internal::default_value_extractor()(v); - if(dft) { - res = std::move(dft); - } - }); - return res; - } + std::unique_ptr default_value() const; }; - // we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list" - namespace sfinae { - /** - * Column with insertable primary key traits. Common case. - */ - template - struct is_column_with_insertable_primary_key : public std::false_type {}; - - /** - * Column with insertable primary key traits. Specialized case case. - */ - template - struct is_column_with_insertable_primary_key< - column_t, - typename std::enable_if<(tuple_helper::tuple_contains_type< - primary_key_t<>, - typename column_t::constraints_type>::value)>::type> { - using column_type = column_t; - static constexpr bool value = is_primary_key_insertable::value; - }; - - /** - * Column with noninsertable primary key traits. Common case. - */ - template - struct is_column_with_noninsertable_primary_key : public std::false_type {}; - - /** - * Column with noninsertable primary key traits. Specialized case case. - */ - template - struct is_column_with_noninsertable_primary_key< - column_t, - typename std::enable_if<(tuple_helper::tuple_contains_type< - primary_key_t<>, - typename column_t::constraints_type>::value)>::type> { - using column_type = column_t; - static constexpr bool value = !is_primary_key_insertable::value; - }; - - } - - /** - * Column traits. Common case. - */ - template - struct is_column : public std::false_type {}; - - /** - * Column traits. Specialized case case. - */ - template - struct is_column> : public std::true_type {}; - /** - * Column with insertable primary key traits. + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. */ - template - struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key {}; + 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{move(name)}, column_field{memberPointer, setter}, column_constraints{ + move(op)} {} +#endif + }; - /** - * Column with noninsertable primary key traits. - */ template - struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v; template - struct column_field_type { - using type = void; - }; - - template - struct column_field_type> { - using type = typename column_t::field_type; - }; + using is_column = polyfill::bool_constant>; - template - struct column_constraints_type { - using type = std::tuple<>; - }; + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; - template - struct column_constraints_type> { - using type = typename column_t::constraints_type; - }; + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; } /** * Column builder function. You should use it to create columns instead of constructor */ - template::value>::type, - class... Op> - internal::column_t - make_column(const std::string& name, T O::*m, Op... constraints) { - static_assert(internal::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - static_assert(internal::is_field_member_pointer::value, - "second argument expected as a member field pointer, not member function pointer"); - return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; + template = true> + internal::column_t make_column(std::string name, M m, Op... constraints) { + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), m, {}, std::make_tuple(constraints...)}); } /** @@ -2172,21 +2393,15 @@ namespace sqlite_orm { */ template::value>::type, - typename = typename std::enable_if::value>::type, - class... Op> - internal::column_t::object_type, - typename internal::setter_traits::field_type, - G, - S, - Op...> - make_column(const std::string& name, S setter, G getter, Op... constraints) { - static_assert(std::is_same::field_type, - typename internal::getter_traits::field_type>::value, + class... Op, + internal::satisfies = 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(internal::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), getter, setter, std::make_tuple(constraints...)}); } /** @@ -2195,34 +2410,34 @@ namespace sqlite_orm { */ template::value>::type, - typename = typename std::enable_if::value>::type, - class... Op> - internal::column_t::object_type, - typename internal::setter_traits::field_type, - G, - S, - Op...> - make_column(const std::string& name, G getter, S setter, Op... constraints) { - static_assert(std::is_same::field_type, - typename internal::getter_traits::field_type>::value, + class... Op, + internal::satisfies = 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(internal::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; - } + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), getter, setter, std::make_tuple(constraints...)}); + } } #pragma once +#include // std::wstring_convert #include // std::string #include // std::stringstream #include // std::vector -#include // std::nullptr_t #include // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_std_ptr.h" namespace sqlite_orm { @@ -2230,12 +2445,26 @@ 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 { + template + struct field_printer; + + namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v{})>> = true + // Also see implementation note for `is_bindable_v` + ; + template + using is_printable = polyfill::bool_constant>; + } + + template + struct field_printer::value>> { std::string operator()(const T& t) const { - std::stringstream stream; - stream << t; - return stream.str(); + std::stringstream ss; + ss << t; + return ss.str(); } }; @@ -2245,9 +2474,9 @@ namespace sqlite_orm { template<> struct field_printer { std::string operator()(const unsigned char& t) const { - std::stringstream stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; @@ -2257,28 +2486,28 @@ namespace sqlite_orm { template<> struct field_printer { std::string operator()(const signed char& t) const { - std::stringstream stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; /** - * char is neigher signer char nor unsigned char so it has its own specialization + * 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 stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; - template<> - struct field_printer { - std::string operator()(const std::string& t) const { - return t; + template + struct field_printer::value>> { + std::string operator()(std::string string) const { + return string; } }; @@ -2293,10 +2522,21 @@ namespace sqlite_orm { return ss.str(); } }; - +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer::value>> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT template<> - struct field_printer { - std::string operator()(const std::nullptr_t&) const { + struct field_printer { + std::string operator()(const nullptr_t&) const { return "null"; } }; @@ -2309,35 +2549,34 @@ namespace sqlite_orm { }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct field_printer, void> { - std::string operator()(const std::shared_ptr& t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer()(nullptr); - } - } - }; + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; - template - struct field_printer, void> { - std::string operator()(const std::unique_ptr& t) const { + std::string operator()(const T& t) const { if(t) { - return field_printer()(*t); + return field_printer()(*t); } else { - return field_printer()(nullptr); + return field_printer{}(nullptr); } } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct field_printer, void> { - std::string operator()(const std::optional& t) const { + 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); + return field_printer()(*t); } else { - return field_printer()(nullptr); + return field_printer{}(std::nullopt); } } }; @@ -2351,6 +2590,12 @@ namespace sqlite_orm { #include // std::tuple, std::tuple_size #include // std::stringstream +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + // #include "collate_argument.h" // #include "constraints.h" @@ -2389,9 +2634,55 @@ namespace sqlite_orm { } } +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + }; + + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + }; + + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; + } + + const storage_type& storage; + }; + + } + +} + // #include "tags.h" // #include "expression.h" + +#include +#include // std::move, std::forward +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + // #include "operators.h" namespace sqlite_orm { @@ -2418,7 +2709,7 @@ namespace sqlite_orm { return {this->value, std::move(r)}; } - assign_t operator=(std::nullptr_t) const { + assign_t operator=(nullptr_t) const { return {this->value, nullptr}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -2468,6 +2759,26 @@ namespace sqlite_orm { } } +// #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 { @@ -2508,10 +2819,7 @@ namespace sqlite_orm { }; template - struct is_offset : std::false_type {}; - - template - struct is_offset> : std::true_type {}; + using is_offset = polyfill::is_specialization_of; /** * Collated something @@ -2659,10 +2967,8 @@ namespace sqlite_orm { template named_collate collate() const { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - return {*this, std::move(name)}; + ss << C::name() << std::flush; + return {*this, ss.str()}; } }; @@ -2809,13 +3115,9 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - operator std::string() const { - if(!this->negative) { - return "IN"; - } else { - return "NOT IN"; - } - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif }; /** @@ -2881,10 +3183,12 @@ namespace sqlite_orm { 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(move(_collate_argument_)) {} +#endif }; struct order_by_string { @@ -2905,13 +3209,13 @@ namespace sqlite_orm { order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} - self asc() { + self asc() const { auto res = *this; res.asc_desc = 1; return res; } - self desc() { + self desc() const { auto res = *this; res.asc_desc = -1; return res; @@ -2919,22 +3223,19 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = - collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::binary); + 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(sqlite_orm::internal::collate_argument::nocase); + 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(sqlite_orm::internal::collate_argument::rtrim); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } @@ -2947,10 +3248,8 @@ namespace sqlite_orm { template self collate() const { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - return this->collate(move(name)); + ss << C::name() << std::flush; + return this->collate(ss.str()); } }; @@ -2963,7 +3262,7 @@ namespace sqlite_orm { args_type args; - multi_order_by_t(args_type&& args_) : args(std::move(args_)) {} + multi_order_by_t(args_type args_) : args{std::move(args_)} {} }; struct dynamic_order_by_entry_t : order_by_base { @@ -2974,7 +3273,7 @@ namespace sqlite_orm { }; /** - * C - serializator context class + * C - serializer context class */ template struct dynamic_order_by_t : order_by_string { @@ -3010,39 +3309,13 @@ namespace sqlite_orm { }; template - struct is_order_by : std::false_type {}; - - template - struct is_order_by> : std::true_type {}; - - template - struct is_order_by> : std::true_type {}; - - template - struct is_order_by> : std::true_type {}; - - struct group_by_string { - operator std::string() const { - return "GROUP BY"; - } - }; - - /** - * GROUP BY pack holder. - */ - template - struct group_by_t : group_by_string { - using args_type = std::tuple; - args_type args; - - group_by_t(args_type&& args_) : args(std::move(args_)) {} - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction_v, + polyfill::is_specialization_of, + polyfill::is_specialization_of>; template - struct is_group_by : std::false_type {}; - - template - struct is_group_by> : std::true_type {}; + using is_order_by = polyfill::bool_constant>; struct between_string { operator std::string() const { @@ -3230,9 +3503,9 @@ namespace sqlite_orm { /** * USING argument holder. */ - template + template struct using_t { - F O::*column = nullptr; + column_pointer column; operator std::string() const { return "USING"; @@ -3260,47 +3533,6 @@ namespace sqlite_orm { inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct exists_string { - operator std::string() const { - return "EXISTS"; - } - }; - - template - struct exists_t : condition_t, exists_string, internal::negatable_t { - using type = T; - using self = exists_t; - - type t; - - exists_t(T t_) : t(std::move(t_)) {} - }; - - struct having_string { - operator std::string() const { - return "HAVING"; - } - }; - - /** - * HAVING holder. - * T is having argument type. - */ - template - struct having_t : having_string { - using type = T; - - type t; - - having_t(type t_) : t(std::move(t_)) {} - }; - - template - struct is_having : std::false_type {}; - - template - struct is_having> : std::true_type {}; - struct cast_string { operator std::string() const { return "CAST"; @@ -3329,10 +3561,7 @@ namespace sqlite_orm { }; template - struct is_from : std::false_type {}; - - template - struct is_from> : std::true_type {}; + using is_from = polyfill::is_specialization_of; } /** @@ -3345,7 +3574,7 @@ namespace sqlite_orm { return {}; } - template::value>::type> + template = true> internal::negated_condition_t operator!(T arg) { return {std::move(arg)}; } @@ -3504,8 +3733,12 @@ namespace sqlite_orm { } template - internal::using_t using_(F O::*p) { - return {std::move(p)}; + internal::using_t using_(F O::*p) { + return {p}; + } + template + internal::using_t using_(internal::column_pointer cp) { + return {std::move(cp)}; } template @@ -3553,9 +3786,8 @@ namespace sqlite_orm { return {std::move(lim)}; } - template - typename std::enable_if::value, internal::limit_t>::type limit(O off, - T lim) { + template = true> + internal::limit_t limit(O off, T lim) { return {std::move(lim), {std::move(off)}}; } @@ -3566,8 +3798,9 @@ namespace sqlite_orm { template::value || - std::is_base_of::value>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> auto operator&&(L l, R r) { using internal::get_from_expression; return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); @@ -3581,8 +3814,9 @@ namespace sqlite_orm { template::value || - std::is_base_of::value>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> auto operator||(L l, R r) { using internal::get_from_expression; return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); @@ -3695,14 +3929,28 @@ namespace sqlite_orm { } /** - * ORDER BY column - * Example: storage.select(&User::name, order_by(&User::id)) + * 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 + 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()) @@ -3725,21 +3973,12 @@ namespace sqlite_orm { * } */ template - internal::dynamic_order_by_t> + internal::dynamic_order_by_t> dynamic_order_by(const S& storage) { - internal::serializator_context_builder builder(storage); + internal::serializer_context_builder builder(storage); return builder(); } - /** - * GROUP BY column. - * Example: storage.get_all(group_by(&Employee::name)) - */ - template - internal::group_by_t group_by(Args&&... args) { - return {std::make_tuple(std::forward(args)...)}; - } - /** * X BETWEEN Y AND Z * Example: storage.select(between(&User::id, 10, 20)) @@ -3776,28 +4015,6 @@ namespace sqlite_orm { return {std::move(a), std::move(t), {std::move(e)}}; } - /** - * 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 t) { - return {std::move(t)}; - } - - /** - * HAVING(expression). - * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); - */ - template - internal::having_t having(T t) { - return {std::move(t)}; - } - /** * CAST(X AS type). * Example: cast(&User::id) @@ -3816,7 +4033,7 @@ namespace sqlite_orm { namespace sqlite_orm { /** - * This is base class for every class which is used as a custom table alias. + * This is base class for every class which is used as a custom table alias, column alias or expression alias. * For more information please look through self_join.cpp example */ struct alias_tag {}; @@ -3825,7 +4042,7 @@ namespace sqlite_orm { /** * This is a common built-in class used for custom single character table aliases. - * Also you can use language aliases `alias_a`, `alias_b` etc. instead + * For convenience there exist type aliases `alias_a`, `alias_b`, ... */ template struct table_alias : alias_tag { @@ -3845,28 +4062,21 @@ namespace sqlite_orm { using column_type = C; column_type column; - - alias_column_t(){}; - - alias_column_t(column_type column_) : column(std::move(column_)) {} }; template - struct alias_extractor; - - template - struct alias_extractor::value>::type> { + struct alias_extractor { static std::string get() { - std::stringstream ss; - ss << T::get(); - return ss.str(); + return {}; } }; template - struct alias_extractor::value>::type> { + struct alias_extractor::value>> { static std::string get() { - return {}; + std::stringstream ss; + ss << T::get(); + return ss.str(); } }; @@ -3881,6 +4091,17 @@ namespace sqlite_orm { expression_type expression; }; + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + */ + template + struct column_alias : alias_tag { + static std::string get() { + return std::string(1u, A); + } + }; + template struct alias_holder { using type = T; @@ -3960,6 +4181,16 @@ namespace sqlite_orm { using alias_y = internal::table_alias; template using alias_z = internal::table_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; } #pragma once @@ -4072,9 +4303,9 @@ namespace sqlite_orm { #include // std::unique_ptr #include // std::vector -// #include "conditions.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "operators.h" +// #include "conditions.h" // #include "is_base_of_template.h" @@ -4088,48 +4319,58 @@ namespace sqlite_orm { * 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 */ -#if defined(_MSC_VER) - template class Base, typename Derived> +#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::true_type test(const Base&); static constexpr std::false_type test(...); - - using type = decltype(test(std::declval())); }; - template class Base> - using is_base_of_template = typename is_base_of_template_impl::type; - + 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*); + 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())); + 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 "tuple_helper/tuple_filter.h" + // #include "serialize_result_type.h" -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // string_view -#else -#include // std::string -#endif +// #include "operators.h" + +// #include "ast/into.h" + +// #include "../functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - using serialize_result_type = std::string_view; -#else - using serialize_result_type = std::string; -#endif + + template + struct into_t { + using type = T; + }; + + template + using is_into = polyfill::is_specialization_of; + } + + template + internal::into_t into() { + return {}; } } @@ -4140,9 +4381,6 @@ namespace sqlite_orm { namespace internal { - template - struct is_into; - template struct unique_ptr_result_of {}; @@ -4158,13 +4396,37 @@ namespace sqlite_orm { using string_type = S; using args_type = std::tuple; - static constexpr const size_t args_size = std::tuple_size::value; + 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 + 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"; @@ -4301,6 +4563,12 @@ namespace sqlite_orm { } }; + struct nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; + struct date_string { serialize_result_type serialize() const { return "DATE"; @@ -4375,6 +4643,11 @@ namespace sqlite_orm { 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)}; + } }; /** @@ -4689,6 +4962,9 @@ namespace sqlite_orm { } }; #endif // SQLITE_ENABLE_JSON1 + + template + using field_type_or_type_t = polyfill::detected_or_t>; } /** @@ -4696,48 +4972,42 @@ namespace sqlite_orm { */ template::value>::type> + std::enable_if_t, bool> = true> internal::lesser_than_t operator<(F f, R r) { return {std::move(f), std::move(r)}; } template::value>::type> + std::enable_if_t, bool> = true> internal::lesser_or_equal_t operator<=(F f, R r) { return {std::move(f), std::move(r)}; } template::value>::type> + std::enable_if_t, bool> = true> internal::greater_than_t operator>(F f, R r) { return {std::move(f), std::move(r)}; } template::value>::type> + std::enable_if_t, bool> = true> internal::greater_or_equal_t operator>=(F f, R r) { return {std::move(f), std::move(r)}; } template::value>::type> + std::enable_if_t, bool> = true> internal::is_equal_t operator==(F f, R r) { return {std::move(f), std::move(r)}; } template::value>::type> + std::enable_if_t, bool> = true> internal::is_not_equal_t operator!=(F f, R r) { return {std::move(f), std::move(r)}; } @@ -5734,10 +6004,11 @@ namespace sqlite_orm { /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ - template - typename std::enable_if, internal::is_into>::value == 0, - internal::built_in_function_t>::type - replace(X x, Y y, Z z) { + 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)}}; } @@ -5779,18 +6050,68 @@ namespace sqlite_orm { /** * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ - template - internal::built_in_function_t coalesce(Args... args) { + template + auto coalesce(Args... args) + -> internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { return {std::make_tuple(std::forward(args)...)}; } /** * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull */ + template + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename std::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else template - internal::built_in_function_t ifnull(X x, Y y) { + 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 @@ -5861,7 +6182,7 @@ namespace sqlite_orm { * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex */ template - internal::core_function_t soundex(X x) { + internal::built_in_function_t soundex(X x) { return {std::tuple{std::forward(x)}}; } #endif @@ -5870,7 +6191,7 @@ namespace sqlite_orm { * TOTAL(X) aggregate function. */ template - internal::built_in_function_t total(X x) { + internal::built_in_aggregate_function_t total(X x) { return {std::tuple{std::forward(x)}}; } @@ -5878,7 +6199,7 @@ namespace sqlite_orm { * SUM(X) aggregate function. */ template - internal::built_in_function_t, internal::sum_string, X> sum(X x) { + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { return {std::tuple{std::forward(x)}}; } @@ -5886,7 +6207,7 @@ namespace sqlite_orm { * COUNT(X) aggregate function. */ template - internal::built_in_function_t count(X x) { + internal::built_in_aggregate_function_t count(X x) { return {std::tuple{std::forward(x)}}; } @@ -5910,7 +6231,7 @@ namespace sqlite_orm { * AVG(X) aggregate function. */ template - internal::built_in_function_t avg(X x) { + internal::built_in_aggregate_function_t avg(X x) { return {std::tuple{std::forward(x)}}; } @@ -5918,7 +6239,7 @@ namespace sqlite_orm { * MAX(X) aggregate function. */ template - internal::built_in_function_t, internal::max_string, X> max(X x) { + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { return {std::tuple{std::forward(x)}}; } @@ -5926,15 +6247,35 @@ namespace sqlite_orm { * MIN(X) aggregate function. */ template - internal::built_in_function_t, internal::min_string, X> min(X x) { + 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_function_t group_concat(X x) { + internal::built_in_aggregate_function_t group_concat(X x) { return {std::tuple{std::forward(x)}}; } @@ -5942,7 +6283,7 @@ namespace sqlite_orm { * GROUP_CONCAT(X, Y) aggregate function. */ template - internal::built_in_function_t group_concat(X x, Y y) { + 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 @@ -6070,45 +6411,45 @@ namespace sqlite_orm { #endif // SQLITE_ENABLE_JSON1 template::value + - std::is_base_of::value > - 0)>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> internal::add_t operator+(L l, R r) { return {std::move(l), std::move(r)}; } template::value + - std::is_base_of::value > - 0)>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> internal::sub_t operator-(L l, R r) { return {std::move(l), std::move(r)}; } template::value + - std::is_base_of::value > - 0)>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> internal::mul_t operator*(L l, R r) { return {std::move(l), std::move(r)}; } template::value + - std::is_base_of::value > - 0)>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> internal::div_t operator/(L l, R r) { return {std::move(l), std::move(r)}; } template::value + - std::is_base_of::value > - 0)>::type> + std::enable_if_t, + std::is_base_of>, + bool> = true> internal::mod_t operator%(L l, R r) { return {std::move(l), std::move(r)}; } @@ -6119,26 +6460,13 @@ namespace sqlite_orm { namespace internal { - /** - * Cute class used to compare setters/getters and member pointers with each other. - */ template - struct typed_comparator { - bool operator()(const L&, const R&) const { - return false; - } - }; - + bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { + return false; + } template - struct typed_comparator { - bool operator()(const O& lhs, const O& rhs) const { - return lhs == rhs; - } - }; - - template - bool compare_any(const L& lhs, const R& rhs) { - return typed_comparator()(lhs, rhs); + bool compare_any(const O& lhs, const O& rhs) { + return lhs == rhs; } } } @@ -6147,35 +6475,28 @@ namespace sqlite_orm { #include // std::string #include // std::declval #include // std::tuple, std::get, std::tuple_size -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" // #include "is_base_of_template.h" -// #include "tuple_helper/tuple_helper.h" +// #include "tuple_helper/tuple_filter.h" // #include "optional_container.h" // #include "ast/where.h" -// #include "../serialize_result_type.h" +#include // std::false_type, std::true_type +#include // std::move -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // string_view -#else -#include // std::string -#endif +// #include "../functional/cxx_universal.h" -namespace sqlite_orm { - namespace internal { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - using serialize_result_type = std::string_view; -#else - using serialize_result_type = std::string; -#endif - } -} +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../serialize_result_type.h" namespace sqlite_orm { namespace internal { @@ -6201,10 +6522,10 @@ namespace sqlite_orm { }; template - struct is_where : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of_v; template - struct is_where> : std::true_type {}; + using is_where = polyfill::bool_constant>; } /** @@ -6222,6 +6543,83 @@ namespace sqlite_orm { } } +// #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 {move(this->args), std::move(expression)}; + } + }; + + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + + /** + * HAVING holder. + * T is having argument type. + */ + template + struct having_t { + using expression_type = T; + + expression_type expression; + }; + + template + using is_having = polyfill::is_specialization_of; + } + + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args&&... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * [Deprecation notice]: this function is deprecated and will be removed in v1.9. Please use `group_by(...).having(...)` instead. + * + * HAVING(expression). + * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template + [[deprecated("Use group_by(...).having(...) instead")]] internal::having_t having(T expression) { + return {std::move(expression)}; + } +} + +// #include "core_functions.h" + namespace sqlite_orm { namespace internal { @@ -6275,28 +6673,24 @@ namespace sqlite_orm { columns_type columns; bool distinct = false; - static constexpr const int count = std::tuple_size::value; + static constexpr int count = std::tuple_size::value; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{move(columns)} {} +#endif }; template - struct is_columns : std::false_type {}; - - template - struct is_columns> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of_v; - struct set_string { - operator std::string() const { - return "SET"; - } - }; + template + using is_columns = polyfill::bool_constant>; template - struct set_t : set_string { + struct set_t { using assigns_type = std::tuple; assigns_type assigns; - - set_t(assigns_type assigns_) : assigns(move(assigns_)) {} }; /** @@ -6353,13 +6747,17 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : col{std::move(col)}, conditions{move(conditions)} {} +#endif }; template - struct is_select : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of_v; - template - struct is_select> : std::true_type {}; + template + using is_select = polyfill::bool_constant>; /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT @@ -6381,6 +6779,10 @@ namespace sqlite_orm { struct union_base { bool all = false; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} +#endif + operator std::string() const { if(!this->all) { return "UNION"; @@ -6398,10 +6800,10 @@ namespace sqlite_orm { using left_type = typename compound_operator::left_type; using right_type = typename compound_operator::right_type; - union_t(left_type l, right_type r, decltype(all) all_) : - compound_operator(std::move(l), std::move(r)), union_base{all_} {} + union_t(left_type l, right_type r, bool all_) : + compound_operator{std::move(l), std::move(r)}, union_base{all_} {} - union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} + union_t(left_type l, right_type r) : union_t{std::move(l), std::move(r), false} {} }; struct except_string { @@ -6455,11 +6857,23 @@ namespace sqlite_orm { 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 @@ -6501,8 +6915,7 @@ namespace sqlite_orm { 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::move(std::make_tuple(newPair))); + 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)}; } @@ -6638,33 +7051,51 @@ namespace sqlite_orm { } /** - * SELECT * FROM T function. - * T is typed mapped to a storage. - * Example: auto rows = storage.select(asterisk()); - * // decltype(rows) is std::vector> - * If you need to fetch result as objects not tuple please use `object` instead. + * `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() { - return {}; + internal::asterisk_t asterisk(bool definedOrder = false) { + return {definedOrder}; } /** - * SELECT * FROM T function. - * T is typed mapped to a storage. - * Example: auto rows = storage.select(object()); - * // decltype(rows) is std::vector - * If you need to fetch result as tuples not objects please use `asterisk` instead. + * `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() { - return {}; + internal::object_t object(bool definedOrder = false) { + return {definedOrder}; } } #pragma once #include // std::string +// #include "functional/cxx_universal.h" + namespace sqlite_orm { struct table_info { @@ -6675,6 +7106,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -6683,97 +7115,932 @@ namespace sqlite_orm { decltype(pk) pk_) : cid(cid_), name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(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(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), + pk(pk_), hidden{hidden_} {} +#endif + }; } #pragma once -#include // std::unique_ptr -#include -#include // std::integral_constant +#include +#include +#include +#include -namespace sqlite_orm { +// #include "functional/cxx_universal.h" - /** - * Guard class which finalizes `sqlite3_stmt` in dtor - */ - using statement_finalizer = - std::unique_ptr>; +// #include "optional_container.h" -} -#pragma once +// 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 }; - /** - * Helper classes used by statement_binder and row_extractor. - */ - struct int_or_smaller_tag {}; - struct bigint_tag {}; - struct real_tag {}; - - template - struct arithmetic_tag { - using type = std::conditional_t::value, - // Integer class - std::conditional_t, - // Floating-point class - real_tag>; - }; + /** + * 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; - template - using arithmetic_tag_t = typename arithmetic_tag::type; -} -#pragma once + /** + * 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; -#include -#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type -#include // std::string, std::wstring -#ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::codecvt_utf8_utf16 -#endif // SQLITE_ORM_OMITS_CODECVT -#include // std::vector -#include // std::nullptr_t -#include // std::declval -#include // std::wstring_convert -#include // ::strncpy, ::strlen + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} -// #include "is_std_ptr.h" + 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{move(name)}, base(std::move(trigger_base)), elements(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{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, 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, 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, move(message)}; + } + + template + internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), std::move(part.base), std::move(part.statements)}); + } + + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } + + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } + + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} +#pragma once + +#include +#include // std::unique_ptr +#include // std::integral_constant namespace sqlite_orm { /** - * Specialization for optional type (std::shared_ptr / std::unique_ptr). + * Guard class which finalizes `sqlite3_stmt` in dtor */ - template - struct is_std_ptr : std::false_type {}; + using statement_finalizer = + std::unique_ptr>; +} +#pragma once +#include - template - struct is_std_ptr> : std::true_type { - using element_type = T; +namespace sqlite_orm { + + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + using arithmetic_tag_t = + std::conditional_t::value, + // Integer class + std::conditional_t, + // Floating-point class + real_tag>; +} +#pragma once + +#include +#include +#include + +// #include "functional/cxx_universal.h" + +// #include "xdestroy_handling.h" + +#include // std::integral_constant +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template + concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + + /** + * Constrains a deleter to be an integral function constant. + */ + template + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; + }; +#endif + +#if __cpp_lib_concepts >= 201907L + /** + * Yield a deleter's function pointer. + */ + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#else + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; + + template + struct is_integral_fp_c : std::false_type {}; + template + struct is_integral_fp_c< + D, + polyfill::void_t>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + + template + struct can_yield_fp : std::false_type {}; + template + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval()), + std::enable_if_t())>>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + + template> + struct yield_fp_of { + using type = void; + }; + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; + +#if __cpp_lib_concepts >= 201907L + template + concept is_unusable_for_xdestroy = (!stateless_deleter && + (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template + concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + + template + concept needs_xdestroy_proxy = (stateless_deleter && + (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template + requires(!integral_fp_c) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

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

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#endif + } +} + +namespace sqlite_orm { + +#if __cpp_lib_concepts >= 201907L + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::is_unusable_for_xdestroy) + { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::needs_xdestroy_proxy) + { + return internal::xdestroy_proxy; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + requires(internal::yields_xdestroy) + { + return d; + } +#else + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + return internal::xdestroy_proxy; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + return d; + } +#endif +} + +namespace sqlite_orm { + + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pvt_name`. + * + */ + template + struct pointer_arg { + + static_assert(std::is_convertible::value, + "`std::integral_constant<>` must be convertible to `const char*`"); - static std::shared_ptr make(const T& v) { - return std::make_shared(v); + using tag = T; + P* p_; + + P* ptr() const noexcept { + return p_; + } + + operator P*() const noexcept { + return p_; } }; - template - struct is_std_ptr> : std::true_type { - using element_type = T; + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template + class pointer_binding { + + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bindable_pointer() + template + friend auto bindable_pointer(P2*, D2) noexcept -> pointer_binding; + template + friend B bindable_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; - static std::unique_ptr make(const T& v) { - return std::make_unique(v); + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; + + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; + + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + + ~pointer_binding() { + if(p_) { + if(auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder> + xDestroy((void*)p_); + } + } + } + + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); } }; + + /** + * Template alias for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding = pointer_binding; } +namespace sqlite_orm { + + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_pointer(P* p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } + + template + auto bindable_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bindable_pointer(p.release(), p.get_deleter()); + } + + template + B bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + return B{p, std::move(d)}; + } + + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_pointer(P* p) noexcept -> static_pointer_binding { + return bindable_pointer(p, null_xdestroy_f); + } + + template + B statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bindable_pointer(p); + } + + /** + * Forward a pointer value from an argument. + */ + template + auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { + return statically_bindable_pointer(pv.ptr()); + } +} +#pragma once + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include // std::default_delete +#include // std::string, std::wstring +#include // std::vector +#include // ::strncpy, ::strlen +// #include "functional/cxx_string_view.h" + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // ::wcsncpy, ::wcslen +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +#if __cpp_lib_invoke < 201411L +// #include "cxx_type_traits_polyfill.h" + +#endif +// #include "../member_traits/member_traits.h" + +namespace sqlite_orm { + namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang < 11 says C++20, but comes w/o std::identity. + // Another way of detection would be the constrained algorithms feature macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && (!__clang_major__ || __clang_major__ >= 11) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } + + using is_transparent = int; + }; +#endif + +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward(object).*callable; + } + + // pointer-to-member-function+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward(object).*callable)(std::forward(args)...); + } + + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template>, + std::reference_wrapper>>, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { + return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); + } + + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "is_std_ptr.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "error_code.h" + +// #include "arithmetic_tag.h" + +// #include "xdestroy_handling.h" + +// #include "pointer_value.h" + namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. */ template - struct statement_binder : std::false_type {}; + struct statement_binder; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v())>> = true + // note : msvc 14.0 needs the parentheses constructor, otherwise `is_bindable` isn't recognised. + // The strangest thing is that this is mutually exclusive with `is_printable_v`. + ; + + template + using is_bindable = polyfill::bool_constant>; + + } + + /** + * Specialization for 'pointer-passing interface'. + */ + template + struct statement_binder, void> { + using V = pointer_binding; + + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } + + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); + } + }; /** * Specialization for arithmetic types. @@ -6792,27 +8059,27 @@ namespace sqlite_orm { private: using tag = arithmetic_tag_t; - int bind(sqlite3_stmt* stmt, int index, const V& value, const int_or_smaller_tag&) const { + 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, const int_or_smaller_tag&) const { + 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, const bigint_tag&) const { + 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, const bigint_tag&) const { + 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, const real_tag&) const { + 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, const real_tag&) const { + void result(sqlite3_context* context, const V& value, real_tag) const { sqlite3_result_double(context, static_cast(value)); } }; @@ -6821,69 +8088,94 @@ namespace sqlite_orm { * Specialization for std::string and C-string. */ template - struct statement_binder< - V, - std::enable_if_t::value || std::is_same::value>> { + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { int bind(sqlite3_stmt* stmt, int index, const V& value) const { auto stringData = this->string_data(value); - return sqlite3_bind_text(stmt, index, std::get<0>(stringData), std::get<1>(stringData), SQLITE_TRANSIENT); + 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 stringDataLength = std::get<1>(stringData); - auto dataCopy = new char[stringDataLength + 1]; - auto stringChars = std::get<0>(stringData); - ::strncpy(dataCopy, stringChars, stringDataLength + 1); - sqlite3_result_text(context, dataCopy, stringDataLength, [](void* pointer) { - auto charPointer = (char*)pointer; - delete[] charPointer; - }); + 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: - std::tuple string_data(const std::string& s) const { +#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::tuple string_data(const char* s) const { - auto length = int(::strlen(s)); - return {s, length}; + std::pair string_data(const char* s) const { + return {s, int(::strlen(s))}; } +#endif }; #ifndef SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::wstring and C-wstring. - */ template - struct statement_binder< - V, - std::enable_if_t::value || std::is_same::value>> { + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); std::wstring_convert> converter; - std::string utf8Str = converter.to_bytes(value); + 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 { - sqlite3_result_text16(context, (const void*)value.data(), int(value.length()), nullptr); + 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 // SQLITE_ORM_OMITS_CODECVT +#endif /** - * Specialization for std::nullptr_t. + * Specialization for nullptr_t. */ template<> - struct statement_binder { - int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { + 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 std::nullptr_t&) const { + void result(sqlite3_context* context, const nullptr_t&) const { sqlite3_result_null(context); } }; @@ -6905,28 +8197,22 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct statement_binder::value>> { - using value_type = typename is_std_ptr::element_type; + struct statement_binder< + V, + std::enable_if_t::value && internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { - return statement_binder().bind(stmt, index, *value); - } else { - return statement_binder().bind(stmt, index, nullptr); - } - } - - void result(sqlite3_context* context, const V& value) const { - if(value) { - statement_binder().result(context, value); + return statement_binder().bind(stmt, index, *value); } else { - statement_binder().result(context, nullptr); + return statement_binder().bind(stmt, index, nullptr); } } }; /** - * Specialization for optional type (std::vector). + * Specialization for binary data (std::vector). */ template<> struct statement_binder, void> { @@ -6948,93 +8234,106 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct statement_binder, void> { - using value_type = T; + template + struct statement_binder && + internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; - int bind(sqlite3_stmt* stmt, int index, const std::optional& value) const { + int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { - return statement_binder().bind(stmt, index, *value); + return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, std::nullopt); } } - - void result(sqlite3_context* context, const std::optional& value) const { - if(value) { - statement_binder().result(context, value); - } else { - statement_binder().result(context, std::nullopt); - } - } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED namespace internal { - template - using is_bindable = std::integral_constant>::value>; - - struct conditional_binder_base { + struct conditional_binder { sqlite3_stmt* stmt = nullptr; - int& index; - - conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} - }; - - template - struct conditional_binder; - - template - struct conditional_binder : conditional_binder_base { + int index = 1; - using conditional_binder_base::conditional_binder_base; + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - int operator()(const T& t) const { - return statement_binder().bind(this->stmt, this->index++, t); + 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(stmt); + } } + + template = true> + void operator()(const T&) const {} }; - template - struct conditional_binder : conditional_binder_base { - using conditional_binder_base::conditional_binder_base; + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); - int operator()(const T&) const { - return SQLITE_OK; + 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); } }; - template class F, class SFINAE = void> - struct tuple_filter_single; - - template class F> - struct tuple_filter_single::value>::type> { - using type = std::tuple; - }; + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; - template class F> - struct tuple_filter_single::value>::type> { - using type = std::tuple<>; - }; + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - template class F> - struct tuple_filter; + template + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence::value>{}, + std::forward(project)); + } - template class F> - struct tuple_filter, F> { - using type = typename conc_tuple::type...>::type; - }; + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); + } +#else + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + this->bind(polyfill::invoke(project, std::get(tpl)), I); + (*this)(tpl, std::index_sequence{}, std::forward(project)); + } - template - struct bindable_filter_single : tuple_filter_single {}; + template + void operator()(const Tpl&, std::index_sequence<>, Projection) const {} +#endif - template - struct bindable_filter; + 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(stmt); + } + } - template - struct bindable_filter> { - using type = typename conc_tuple::type...>::type; + 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 @@ -7042,36 +8341,50 @@ namespace sqlite_orm { #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if #include // atof, atoi, atoll +#include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert, std::codecvt_utf8_utf16 #endif // SQLITE_ORM_OMITS_CODECVT #include // std::vector #include // strlen +#include #include // std::copy #include // std::back_inserter #include // std::tuple, std::tuple_size, std::tuple_element +// #include "functional/cxx_universal.h" + // #include "arithmetic_tag.h" +// #include "pointer_value.h" + // #include "journal_mode.h" +#include // std::back_inserter #include // std::string #include // std::unique_ptr #include // std::array #include // std::transform #include // std::toupper -namespace sqlite_orm { - -/** - * Caps case cause of 1) delete keyword; 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling - */ -#ifdef DELETE +#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, @@ -7116,8 +8429,14 @@ namespace sqlite_orm { } } +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif + // #include "error_code.h" +// #include "is_std_ptr.h" + namespace sqlite_orm { /** @@ -7128,15 +8447,45 @@ namespace sqlite_orm { template struct row_extractor { // used in sqlite3_exec (select) - V extract(const char* row_value) const; + V extract(const char* row_value) const = delete; // used in sqlite_column (iteration, get_all) - V extract(sqlite3_stmt* stmt, int columnIndex) const; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; // used in user defined functions - V extract(sqlite3_value* value) const; + V extract(sqlite3_value* value) const = delete; }; + template + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if(argc) { + res = row_extractor{}.extract(argv[0]); + } + return 0; + } + + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template + struct row_extractor, void> { + using V = pointer_arg; + + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; + + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; + /** * Specialization for arithmetic types. */ @@ -7260,11 +8609,11 @@ namespace sqlite_orm { template struct row_extractor::value>> { - using value_type = typename is_std_ptr::element_type; + using unqualified_type = std::remove_cv_t; V extract(const char* row_value) const { if(row_value) { - return is_std_ptr::make(row_extractor().extract(row_value)); + return is_std_ptr::make(row_extractor().extract(row_value)); } else { return {}; } @@ -7273,7 +8622,7 @@ namespace sqlite_orm { V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { - return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); + return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); } else { return {}; } @@ -7282,7 +8631,7 @@ namespace sqlite_orm { V extract(sqlite3_value* value) const { auto type = sqlite3_value_type(value); if(type != SQLITE_NULL) { - return is_std_ptr::make(row_extractor().extract(value)); + return is_std_ptr::make(row_extractor().extract(value)); } else { return {}; } @@ -7290,73 +8639,71 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct row_extractor, void> { - using value_type = T; + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; - std::optional extract(const char* row_value) const { + V extract(const char* row_value) const { if(row_value) { - return std::make_optional(row_extractor().extract(row_value)); + return std::make_optional(row_extractor().extract(row_value)); } else { return std::nullopt; } } - std::optional extract(sqlite3_stmt* stmt, int columnIndex) const { + V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { - return std::make_optional(row_extractor().extract(stmt, columnIndex)); + return std::make_optional(row_extractor().extract(stmt, columnIndex)); } else { return std::nullopt; } } - std::optional extract(sqlite3_value* value) const { - auto type = sqlite3_value_type(value); - if(type != SQLITE_NULL) { - return std::make_optional(row_extractor().extract(value)); - } else { - return std::nullopt; - } + V extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return std::make_optional(row_extractor().extract(value)); + } else { + return std::nullopt; + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template<> + struct row_extractor { + nullptr_t extract(const char* /*row_value*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_value*) const { + return nullptr; } }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** * Specialization for std::vector. */ template<> struct row_extractor> { std::vector extract(const char* row_value) const { - if(row_value) { - auto len = ::strlen(row_value); - return this->go(row_value, len); - } else { - return {}; - } + return {row_value, row_value + (row_value ? ::strlen(row_value) : 0)}; } std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); - return this->go(bytes, len); + 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 this->go(bytes, len); - } - - protected: - std::vector go(const char* bytes, size_t len) const { - if(len) { - std::vector res; - res.reserve(len); - std::copy(bytes, bytes + len, std::back_inserter(res)); - return res; - } else { - return {}; - } + return {bytes, bytes + len}; } }; @@ -7364,40 +8711,22 @@ namespace sqlite_orm { struct row_extractor> { std::tuple extract(char** argv) const { - std::tuple res; - this->extract::value>(res, argv); - return res; + return this->extract(argv, std::make_index_sequence{}); } std::tuple extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { - std::tuple res; - this->extract::value>(res, stmt); - return res; + return this->extract(stmt, std::make_index_sequence{}); } protected: - template::type* = nullptr> - void extract(std::tuple& t, sqlite3_stmt* stmt) const { - using tuple_type = typename std::tuple_element>::type; - std::get(t) = row_extractor().extract(stmt, I - 1); - this->extract(t, stmt); - } - - template::type* = nullptr> - void extract(std::tuple&, sqlite3_stmt*) const { - //.. - } - - template::type* = nullptr> - void extract(std::tuple& t, char** argv) const { - using tuple_type = typename std::tuple_element>::type; - std::get(t) = row_extractor().extract(argv[I - 1]); - this->extract(t, argv); + template + std::tuple extract(sqlite3_stmt* stmt, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(stmt, Idx)...}; } - template::type* = nullptr> - void extract(std::tuple&, char**) const { - //.. + template + std::tuple extract(char** argv, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(argv[Idx])...}; } }; @@ -7411,10 +8740,10 @@ namespace sqlite_orm { if(auto res = internal::journal_mode_from_string(row_value)) { return std::move(*res); } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } @@ -7428,44 +8757,130 @@ namespace sqlite_orm { #include #include // std::string -#include // std::system_error, std::error_code +#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(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} + 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(move(identifier), quoteChar) + quoteChar; + } + namespace internal { - inline void perform_step(sqlite3* db, sqlite3_stmt* stmt) { - auto rc = sqlite3_step(stmt); - if(rc == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + // 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; } - static void perform_void_exec(sqlite3* db, const std::string& query) { + 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 std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } - template - inline auto call_insert_impl_and_catch_constraint_failed(const T& insert_impl) { - try { - return insert_impl(); - } catch(const std::system_error& e) { - if(e.code() == std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) { - std::stringstream ss; - ss << "Attempting to execute 'insert' request resulted in an error like \"" << e.what() - << "\". Perhaps ordinary 'insert' is not acceptable for this table and you should try " - "'replace' or 'insert' with explicit column listing?"; - throw std::system_error(e.code(), ss.str()); + 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); } - throw; } } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + int rc; + do { + switch(rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while(rc != SQLITE_DONE); + } } } #pragma once @@ -7531,13 +8946,22 @@ namespace sqlite_orm { } #pragma once -#include // std::tuple, std::make_tuple +#include // std::tuple, std::make_tuple, std::declval #include // std::string #include // std::forward +// #include "functional/cxx_universal.h" + +// #include "tuple_helper/tuple_filter.h" + // #include "indexed_column.h" #include // std::string +#include // std::move + +// #include "functional/cxx_universal.h" + +// #include "ast/where.h" namespace sqlite_orm { @@ -7547,8 +8971,10 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED indexed_column_t(column_type _column_or_expression) : column_or_expression(std::move(_column_or_expression)) {} +#endif column_type column_or_expression; std::string _collation_name; @@ -7574,29 +9000,19 @@ namespace sqlite_orm { }; template - struct indexed_column_maker { - using type = indexed_column_t; - - indexed_column_t operator()(C col) const { - return {std::move(col)}; - } - }; + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } template - struct indexed_column_maker> { - using type = indexed_column_t; - - indexed_column_t operator()(indexed_column_t col) const { - return std::move(col); - } - }; + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } template - auto make_indexed_column(C col) { - indexed_column_maker maker; - return maker(std::move(col)); + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); } - } /** @@ -7617,6 +9033,10 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{move(name)}, unique{unique} {} +#endif }; template @@ -7624,27 +9044,39 @@ namespace sqlite_orm { using elements_type = std::tuple; using object_type = void; +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED index_t(std::string name_, bool unique_, elements_type elements_) : index_base{move(name_), unique_}, elements(move(elements_)) {} +#endif elements_type elements; }; } template - internal::index_t::type...> make_index(const std::string& name, - Cols... cols) { - return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; + 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 {move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } template - internal::index_t::type...> make_unique_index(const std::string& name, - Cols... cols) { - return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; + internal::index_t()))...> + 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 {move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } } #pragma once +#include // std::enable_if, std::is_base_of, std::remove_const + // #include "alias.h" namespace sqlite_orm { @@ -7653,17 +9085,18 @@ namespace sqlite_orm { /** * If T is alias than mapped_type_proxy::type is alias::type - * otherwise T is T. + * otherwise T is unqualified T. */ - template - struct mapped_type_proxy { - using type = T; - }; + template + struct mapped_type_proxy : std::remove_const {}; template - struct mapped_type_proxy::value>::type> { + struct mapped_type_proxy::value>> { using type = typename T::type; }; + + template + using mapped_type_proxy_t = typename mapped_type_proxy::type; } } #pragma once @@ -7737,10 +9170,16 @@ namespace sqlite_orm { } #pragma once -#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::tuple #include // std::reference_wrapper +// #include "functional/cxx_universal.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + // #include "core_functions.h" // #include "select_constraints.h" @@ -7754,398 +9193,236 @@ namespace sqlite_orm { // #include "column.h" // #include "storage_traits.h" -#include // std::is_same, std::enable_if, std::true_type, std::false_type, std::integral_constant -#include // std::tuple - -namespace sqlite_orm { - - namespace internal { - - template - struct storage_impl; - - template - struct table_t; - - template - struct foreign_key_t; - - namespace storage_traits { - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template - struct type_is_mapped_impl; - - /** - * S - storage - * T - mapped or not mapped data type - */ - template - struct type_is_mapped : type_is_mapped_impl {}; - - /** - * Final specialisation - */ - template - struct type_is_mapped_impl, T, void> : std::false_type {}; - - template - struct type_is_mapped_impl< - S, - T, - typename std::enable_if::value>::type> - : std::true_type {}; - - template - struct type_is_mapped_impl< - S, - T, - typename std::enable_if::value>::type> - : type_is_mapped_impl {}; - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template - struct storage_columns_count_impl; - - /** - * S - storage - * T - mapped or not mapped data type - */ - template - struct storage_columns_count : storage_columns_count_impl {}; - - /** - * Final specialisation - */ - template - struct storage_columns_count_impl, T, void> : std::integral_constant {}; - - template - struct storage_columns_count_impl< - S, - T, - typename std::enable_if::value>::type> - : std::integral_constant {}; - - template - struct storage_columns_count_impl< - S, - T, - typename std::enable_if::value>::type> - : storage_columns_count_impl {}; - - /** - * T - table type. - */ - template - struct table_types; - - /** - * type is std::tuple of field types of mapped colums. - */ - template - struct table_types> { - using args_tuple = std::tuple; - using columns_tuple = typename tuple_filter::type; - - using type = typename tuple_transformer::type; - }; - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template - struct storage_mapped_columns_impl; - /** - * S - storage - * T - mapped or not mapped data type - */ - template - struct storage_mapped_columns : storage_mapped_columns_impl {}; +#include // std::tuple - /** - * Final specialisation - */ - template - struct storage_mapped_columns_impl, T, void> { - using type = std::tuple<>; - }; +// #include "functional/cxx_type_traits_polyfill.h" - template - struct storage_mapped_columns_impl< - S, - T, - typename std::enable_if::value>::type> { - using table_type = typename S::table_type; - using type = typename table_types::type; - }; +// #include "tuple_helper/tuple_filter.h" - template - struct storage_mapped_columns_impl< - S, - T, - typename std::enable_if::value>::type> - : storage_mapped_columns_impl {}; +// #include "tuple_helper/tuple_transformer.h" - /** - * C is any column type: column_t or constraint type - * O - object type references in FOREIGN KEY - */ - template - struct column_foreign_keys_count : std::integral_constant {}; +#include // std::tuple - template - struct column_foreign_keys_count, O> { - using target_type = typename foreign_key_t::target_type; +// #include "../functional/mpl.h" - static constexpr const int value = std::is_same::value ? 1 : 0; - }; +namespace sqlite_orm { + namespace internal { - /** - * O - object type references in FOREIGN KEY - * Cs - column types which are stored in table_t::columns_type - */ - template - struct table_foreign_keys_count_impl; + template class Op> + struct tuple_transformer; - template - struct table_foreign_keys_count_impl { - static constexpr const int value = 0; - }; + template class Op> + struct tuple_transformer, Op> { + using type = std::tuple...>; + }; - template - struct table_foreign_keys_count_impl { - static constexpr const int value = - column_foreign_keys_count::value + table_foreign_keys_count_impl::value; - }; + /* + * Transform specified tuple. + * + * `Op` is a metafunction operation. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; + } +} - /** - * T is table_t type - * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) - */ - template - struct table_foreign_keys_count; +// #include "type_traits.h" - template - struct table_foreign_keys_count, O> { - using table_type = table_t; +// #include "storage_lookup.h" - static constexpr const int value = table_foreign_keys_count_impl::value; - }; +#include // std::true_type, std::false_type, std::remove_const, std::enable_if +#include +#include // std::index_sequence - /** - * S - storage class - * O - type mapped to S - */ - template - struct storage_foreign_keys_count_impl; +// #include "functional/cxx_universal.h" - template - struct storage_foreign_keys_count_impl, O> : std::integral_constant {}; +// #include "functional/cxx_type_traits_polyfill.h" - template - struct storage_foreign_keys_count_impl, O> { - static constexpr const int value = table_foreign_keys_count::value + - storage_foreign_keys_count_impl, O>::value; - }; +// #include "type_traits.h" - /** - * S - storage class - * O - type mapped to S - * This class tells how many types mapped to S have foreign keys to O - */ - template - struct storage_foreign_keys_count { - using impl_type = typename S::impl_type; +namespace sqlite_orm { + namespace internal { - static constexpr const int value = storage_foreign_keys_count_impl::value; - }; + template + struct storage_t; - /** - * C is any table element type: column_t or constraint type - * O - object type references in FOREIGN KEY - */ - template - struct column_fk_references { - using type = std::tuple<>; - }; + template + using db_objects_tuple = std::tuple; - template - struct column_foreign_keys { - using type = std::tuple<>; - }; + template + struct is_storage : std::false_type {}; - template - struct column_foreign_keys< - foreign_key_t, - O, - typename std::enable_if::target_type>::value>::type> { - using type = std::tuple>; - }; + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; - template - struct column_foreign_keys< - foreign_key_t, - O, - typename std::enable_if::target_type>::value>::type> { - using type = std::tuple<>; - }; + template + struct is_db_objects : std::false_type {}; - template - struct column_fk_references< - foreign_key_t, - O, - typename std::enable_if::target_type>::value>::type> { - using target_type = typename foreign_key_t::source_type; + template + struct is_db_objects> : std::true_type {}; + template + struct is_db_objects> : std::true_type {}; - using type = std::tuple; - }; + /** + * 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>> {}; - template - struct column_fk_references< - foreign_key_t, - O, - typename std::enable_if::target_type>::value>::type> { - using type = std::tuple<>; - }; + /** + * std::true_type if given lookup type (object) is mapped, std::false_type otherwise. + */ + template + struct lookup_type_matches : polyfill::disjunction> {}; + } - /** - * O - object type references in FOREIGN KEY - * Cs - column types which are stored in table_t::columns_type - */ - template - struct table_fk_references_impl; + // pick/lookup metafunctions + namespace internal { - template - struct table_foreign_keys_impl; + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; - template - struct table_fk_references_impl { - using type = std::tuple<>; - }; + /** + * 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 table_foreign_keys_impl { - using type = std::tuple<>; - }; + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; - template - struct table_fk_references_impl { - using head_tuple = typename column_fk_references::type; - using tail_tuple = typename table_fk_references_impl::type; - using type = typename conc_tuple::type; - }; + /** + * 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; - template - struct table_foreign_keys_impl { - using head_tuple = typename column_foreign_keys::type; - using tail_tuple = typename table_foreign_keys_impl::type; - using type = typename conc_tuple::type; - }; + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template + struct storage_find_table : polyfill::detected_or {}; - /** - * T is table_t type - * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) - */ - template - struct table_fk_references; + /** + * 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 - struct table_foreign_keys; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} - template - struct table_fk_references, O> { - using table_type = table_t; +// 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); + } + } +} - using type = typename table_fk_references_impl::type; - }; +namespace sqlite_orm { - template - struct table_foreign_keys, O> { - using table_type = table_t; + namespace internal { - using type = typename table_foreign_keys_impl::type; - }; + namespace storage_traits { /** - * S - storage class - * O - type mapped to S + * DBO - db object (table) */ - template - struct storage_fk_references_impl; - - template - struct storage_foreign_keys_impl; + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; - template - struct storage_fk_references_impl, O> { - using type = std::tuple<>; - }; - - template - struct storage_foreign_keys_impl, O> { + template<> + struct storage_mapped_columns_impl { using type = std::tuple<>; }; - template - struct storage_fk_references_impl, O> { - using head_tuple = typename table_fk_references::type; - using tail_tuple = typename storage_fk_references_impl, O>::type; - using type = typename conc_tuple::type; - }; - - template - struct storage_foreign_keys_impl, O> { - using head_tuple = typename table_foreign_keys::type; - using tail_tuple = typename storage_foreign_keys_impl, O>::type; - using type = typename conc_tuple::type; - }; - /** - * S - storage class - * O - type mapped to S - * type holds `std::tuple` with types that has references to O as foreign keys + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type */ - template - struct storage_fk_references { - using impl_type = typename S::impl_type; - - using type = typename storage_fk_references_impl::type; - }; - - template - struct storage_foreign_keys { - using impl_type = typename S::impl_type; - - using type = typename storage_foreign_keys_impl::type; - }; - + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; } } } // #include "function.h" -#include // std::string #include +#include +#include // std::string #include // std::tuple #include // std::function +#include // std::min +#include // std::move, std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { struct arg_values; + template + struct pointer_arg; + template + class pointer_binding; + namespace internal { - struct function_base { + struct user_defined_function_base { using func_call = std::function< void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; using final_call = std::function; @@ -8155,101 +9432,67 @@ namespace sqlite_orm { std::function create; void (*destroy)(int*) = nullptr; - function_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + user_defined_function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : name(move(name_)), argumentsCount(argumentsCount_), create(move(create_)), destroy(destroy_) {} +#endif }; - struct scalar_function_t : function_base { + struct user_defined_scalar_function_t : user_defined_function_base { func_call run; - scalar_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - function_base{move(name_), argumentsCount_, move(create_), destroy_}, + user_defined_scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + user_defined_function_base{move(name_), argumentsCount_, move(create_), destroy_}, run(move(run_)) {} }; - struct aggregate_function_t : function_base { + struct user_defined_aggregate_function_t : user_defined_function_base { func_call step; final_call finalCall; - aggregate_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - function_base{move(name_), argumentsCount_, move(create_), destroy_}, + user_defined_aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + user_defined_function_base{move(name_), argumentsCount_, move(create_), destroy_}, step(move(step_)), finalCall(move(finalCall_)) {} }; - // got it from here https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature - template - struct is_scalar_function_impl { - - template - struct SFINAE; - - template - struct SFINAE {}; - - template - static char test(SFINAE*); - - template - static int test(...); - - static constexpr bool has = sizeof(test(0)) == sizeof(char); - }; - template - struct is_aggregate_function_impl { - - template - struct SFINAE; - - template - struct SFINAE {}; - - template - static char test(SFINAE*); - - template - static int test(...); - - template - static char test2(SFINAE*); - - template - static int test2(...); - - static constexpr bool has = sizeof(test(0)) == sizeof(char); - static constexpr bool has2 = sizeof(test2(0)) == sizeof(char); - static constexpr bool has_both = has && has2; - }; + using scalar_call_function_t = decltype(&F::operator()); template - struct is_scalar_function : std::integral_constant::has> {}; + using aggregate_step_function_t = decltype(&F::step); template - struct is_aggregate_function : std::integral_constant::has_both> {}; + using aggregate_fin_function_t = decltype(&F::fin); + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; template - struct scalar_run_member_pointer { - using type = decltype(&F::operator()); - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v>> = + true; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; template - struct aggregate_run_member_pointer { - using step_type = decltype(&F::step); - using fin_type = decltype(&F::fin); - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + F, + polyfill::void_t, + aggregate_fin_function_t, + std::enable_if_t>::value>, + std::enable_if_t>::value>>> = + true; template struct member_function_arguments; @@ -8257,37 +9500,34 @@ namespace sqlite_orm { template struct member_function_arguments { using member_function_type = R (O::*)(Args...) const; - using tuple_type = std::tuple::type...>; + using tuple_type = std::tuple...>; using return_type = R; }; template struct member_function_arguments { using member_function_type = R (O::*)(Args...); - using tuple_type = std::tuple::type...>; + using tuple_type = std::tuple...>; using return_type = R; }; - template + template struct callable_arguments_impl; template - struct callable_arguments_impl { - using function_member_pointer = typename scalar_run_member_pointer::type; - using args_tuple = typename member_function_arguments::tuple_type; - using return_type = typename member_function_arguments::return_type; + struct callable_arguments_impl>> { + using args_tuple = typename member_function_arguments>::tuple_type; + using return_type = typename member_function_arguments>::return_type; }; template - struct callable_arguments_impl { - using step_function_member_pointer = typename aggregate_run_member_pointer::step_type; - using fin_function_member_pointer = typename aggregate_run_member_pointer::fin_type; - using args_tuple = typename member_function_arguments::tuple_type; - using return_type = typename member_function_arguments::return_type; + struct callable_arguments_impl>> { + using args_tuple = typename member_function_arguments>::tuple_type; + using return_type = typename member_function_arguments>::return_type; }; template - struct callable_arguments : callable_arguments_impl::value> {}; + struct callable_arguments : callable_arguments_impl {}; template struct function_call { @@ -8296,6 +9536,93 @@ namespace sqlite_orm { args_tuple args; }; + + template + struct unpacked_arg { + using type = T; + }; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; + }; + template + using unpacked_arg_t = typename unpacked_arg::type; + + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; + } + + template + constexpr bool is_same_pvt_v = expected_pointer_value(); + + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v> = true; + +#if __cplusplus >= 201703L // using C++17 or higher + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#else + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#endif + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + return true; + } + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + using func_arg_t = std::tuple_element_t; + using passed_arg_t = unpacked_arg_t>; + +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + constexpr bool valid = validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); + + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); +#endif + } } /** @@ -8307,10 +9634,12 @@ namespace sqlite_orm { using function_args_tuple = typename internal::callable_arguments::args_tuple; constexpr auto argsCount = std::tuple_size::value; constexpr auto functionArgsCount = std::tuple_size::value; - static_assert( - (argsCount == functionArgsCount && !std::is_same>::value) || - std::is_same>::value, - "Arguments amount does not match"); + static_assert((argsCount == functionArgsCount && + !std::is_same>::value && + internal::validate_pointer_value_types( + polyfill::index_constant(functionArgsCount, argsCount) - 1>{})) || + std::is_same>::value, + "Number of arguments does not match"); return {std::make_tuple(std::forward(args)...)}; } @@ -8326,1043 +9655,949 @@ namespace sqlite_orm { * 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 + template struct column_result_t; + template + using column_result_of_t = typename column_result_t::type; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { - using type = std::optional::type>; + template + struct column_result_t, void> { + using type = std::optional>; }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - struct column_result_t::value && - !std::is_member_function_pointer::value>::type> { - using type = F; + template + struct column_result_t, void> { + using type = std::optional; }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = bool; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = bool; }; - /** - * Common case for all getter types. Getter types are defined in column.h file - */ - template - struct column_result_t::value>::type> { - using type = typename getter_traits::field_type; - }; + template + struct column_result_t> : member_field_type {}; - /** - * Common case for all setter types. Setter types are defined in column.h file - */ - template - struct column_result_t::value>::type> { - using type = typename setter_traits::field_type; + template + struct column_result_t, void> { + using type = R; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = R; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = typename callable_arguments::return_type; }; - template - struct column_result_t, S, X>, void> { - using type = std::unique_ptr::type>; + template + struct column_result_t, S, X, Rest...>, void> { + using type = std::unique_ptr>; }; - template - struct column_result_t, void> { - using type = int; + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; }; - template - struct column_result_t { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { - using type = typename column_result_t::type; + template + struct column_result_t { + using type = nullptr_t; }; - template - struct column_result_t, void> { - using type = typename column_result_t::type; + template + struct column_result_t { + using type = int; }; - template - struct column_result_t, void> { + 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> { + template + struct column_result_t, void> { using type = double; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = double; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = double; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = double; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = double; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int; }; - template - struct column_result_t { + template + struct column_result_t { using type = int64; }; - template - struct column_result_t { + template + struct column_result_t { using type = int64; }; - template - struct column_result_t { + template + struct column_result_t { using type = int64; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int64; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int64; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = int64; }; - template - struct column_result_t, void> { - using type = typename column_result_t::type; - }; + template + struct column_result_t, void> : column_result_t {}; - template - struct column_result_t> : column_result_t {}; + template + struct column_result_t, void> : column_result_t {}; - template - struct column_result_t, void> { - using type = std::tuple::type>::type...>; + template + struct column_result_t, void> { + using type = std::tuple>...>; }; - template - struct column_result_t> : column_result_t {}; + template + struct column_result_t> : column_result_t {}; - template - struct column_result_t::value>::type> { - using left_type = typename T::left_type; - using right_type = typename T::right_type; - using left_result = typename column_result_t::type; - using right_result = typename column_result_t::type; + template + struct column_result_t>> { + using left_result = column_result_of_t; + using right_result = column_result_of_t; static_assert(std::is_same::value, "Compound subselect queries must return same types"); using type = left_result; }; - template - struct column_result_t::value>::type> { + template + struct column_result_t>> { using type = typename T::result_type; }; /** * Result for the most simple queries like `SELECT 1` */ - template - struct column_result_t::value>::type> { + template + struct column_result_t> { using type = T; }; /** * Result for the most simple queries like `SELECT 'ototo'` */ - template - struct column_result_t { + template + struct column_result_t { using type = std::string; }; - template - struct column_result_t { + template + struct column_result_t { using type = std::string; }; - template - struct column_result_t, void> : column_result_t::type, void> {}; + template + struct column_result_t, void> : column_result_t> {}; - template - struct column_result_t, void> { - using type = typename storage_traits::storage_mapped_columns::type; - }; + template + struct column_result_t, match_if_not> + : storage_traits::storage_mapped_columns {}; - template - struct column_result_t, void> { + template + struct column_result_t, match_if> + : storage_traits::storage_mapped_columns> {}; + + template + struct column_result_t, void> { using type = T; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = T; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = R; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = bool; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = bool; }; - template - struct column_result_t, void> { + template + struct column_result_t, void> { using type = bool; }; - template - struct column_result_t, void> : column_result_t {}; + template + struct column_result_t, void> : column_result_t {}; } } #pragma once #include // std::string -#include // std::remove_reference, std::is_same, std::is_base_of +#include // std::remove_reference, std::is_same, std::decay #include // std::vector #include // std::tuple_size, std::tuple_element -#include // std::reverse, std::find_if +#include // std::forward, std::move -// #include "column_result.h" +// #include "functional/cxx_universal.h" -// #include "static_magic.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "typed_comparator.h" +// #include "functional/cxx_functional_polyfill.h" -// #include "constraints.h" +// #include "functional/static_magic.h" -// #include "tuple_helper/tuple_helper.h" +#include // std::false_type, std::true_type, std::integral_constant +#include // std::forward -// #include "table_info.h" +namespace sqlite_orm { -// #include "type_printer.h" + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { -// #include "column.h" + template + decltype(auto) empty_callable() { + static auto res = [](auto&&...) -> R { + return R(); + }; + return (res); + } -namespace sqlite_orm { +#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); + } + } - namespace internal { + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable(); + } + } - struct basic_table { + 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); + } - /** - * Table name. - */ - std::string name; - }; + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } - /** - * Table class. - */ - template - struct table_t : basic_table { - using super = basic_table; - using object_type = T; - using elements_type = std::tuple; + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } - static constexpr const int elements_count = static_cast(std::tuple_size::value); - static constexpr const bool is_without_rowid = WithoutRowId; + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable()); + } - elements_type elements; + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } - table_t(std::string name_, elements_type elements_) : super{move(name_)}, elements{move(elements_)} {} +} - table_t without_rowid() const { - return {this->name, this->elements}; - } +// #include "functional/mpl.h" - /** - * Function used to get field value from object by mapped member pointer/setter/getter - */ - template - const F* get_object_field_pointer(const object_type& obj, C c) const { - const F* res = nullptr; - this->for_each_column_with_field_type([&res, &c, &obj](auto& col) { - using column_type = typename std::remove_reference::type; - using member_pointer_t = typename column_type::member_pointer_t; - using getter_type = typename column_type::getter_type; - using setter_type = typename column_type::setter_type; - // Make static_if have at least one input as a workaround for GCC bug: - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 - if(!res) { - static_if{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.member_pointer, c_)) { - res = &(obj.*col.member_pointer); - } - })(c); - } - if(!res) { - static_if{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.getter, c_)) { - res = &((obj).*(col.getter))(); - } - })(c); - } - if(!res) { - static_if{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.setter, c_)) { - res = &((obj).*(col.getter))(); - } - })(c); - } - }); - return res; - } +// #include "functional/index_sequence_util.h" - template - bool exists_in_composite_primary_key(const C& column) const { - auto res = false; - this->for_each_primary_key([&column, &res](auto& primaryKey) { - iterate_tuple(primaryKey.columns, [&res, &column](auto& value) { - if(!res) { - if(column.member_pointer) { - res = compare_any(value, column.member_pointer); - } else { - res = compare_any(value, column.getter) || compare_any(value, column.setter); - } - } - }); - }); - return res; - } +// #include "tuple_helper/tuple_filter.h" - /** - * Calls **l** with every primary key dedicated constraint - */ - template - void for_each_primary_key(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay::type; - static_if{}>(lambda)(element); - }); - } +// #include "tuple_helper/tuple_traits.h" - std::vector composite_key_columns_names() const { - std::vector res; - this->for_each_primary_key([this, &res](auto& c) { - res = this->composite_key_columns_names(c); - }); - return res; - } +// #include "tuple_helper/tuple_iteration.h" - std::vector primary_key_column_names() const { - std::vector res; - this->for_each_column_with>([&res](auto& c) { - res.push_back(c.name); - }); - if(!res.size()) { - res = this->composite_key_columns_names(); - } - return res; - } +#include // std::tuple, std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move - template - std::vector composite_key_columns_names(const primary_key_t& pk) const { - std::vector res; - using pk_columns_tuple = decltype(pk.columns); - res.reserve(std::tuple_size::value); - iterate_tuple(pk.columns, [this, &res](auto& v) { - if(auto columnName = this->find_column_name(v)) { - res.push_back(*columnName); - } else { - res.push_back({}); - } - }); - return res; - } +// #include "../functional/cxx_universal.h" - /** - * Searches column name by class member pointer passed as the first argument. - * @return column name or empty string if nothing found. - */ - template::value && - !std::is_member_function_pointer::value>::type> - const std::string* find_column_name(F O::*m) const { - const std::string* res = nullptr; - this->template for_each_column_with_field_type([&res, m](auto& c) { - if(c.member_pointer == m) { - res = &c.name; - } - }); - return res; - } +// #include "../functional/cxx_type_traits_polyfill.h" - /** - * Searches column name by class getter function member pointer passed as first argument. - * @return column name or empty string if nothing found. - */ - template - const std::string* find_column_name(G getter, - typename std::enable_if::value>::type* = nullptr) const { - const std::string* res = nullptr; - using field_type = typename getter_traits::field_type; - this->template for_each_column_with_field_type([&res, getter](auto& c) { - if(compare_any(c.getter, getter)) { - res = &c.name; - } - }); - return res; - } +// #include "../functional/cxx_functional_polyfill.h" - /** - * Searches column name by class setter function member pointer passed as first argument. - * @return column name or empty string if nothing found. - */ - template - const std::string* find_column_name(S setter, - typename std::enable_if::value>::type* = nullptr) const { - const std::string* res = nullptr; - using field_type = typename setter_traits::field_type; - this->template for_each_column_with_field_type([&res, setter](auto& c) { - if(compare_any(c.setter, setter)) { - res = &c.name; - } - }); - return res; - } +// #include "../functional/index_sequence_util.h" - int count_columns_amount() const { - auto res = 0; - this->for_each_column([&res](auto&) { - ++res; - }); - return res; - } +namespace sqlite_orm { + namespace internal { - /** - * Iterates all columns and fires passed lambda. Lambda must have one and only templated argument. Otherwise - * code will not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To - * iterate columns with table constraints use iterate_tuple(columns, ...) instead. L is lambda type. Do - * not specify it explicitly. - * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} - */ - template - void for_each_column(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay::type; - static_if{}>(lambda)(element); - }); - } + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template + auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence) { + return (f.*functionPointer)(std::get(move(t))...); + } - template - void for_each_foreign_key(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay::type; - static_if{}>(lambda)(element); - }); - } + template + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + constexpr size_t size = std::tuple_size::value; + return call_impl(f, functionPointer, move(t), std::make_index_sequence{}); + } - template - void for_each_column_with_field_type(const L& lambda) const { - this->for_each_column([&lambda](auto& column) { - using column_type = typename std::decay::type; - using field_type = typename column_field_type::type; - static_if{}>(lambda)(column); - }); + template + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), move(t)); + } + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + iterate_tuple(tpl, reverse_index_sequence(std::index_sequence{}), std::forward(lambda)); + } else { + (lambda(std::get(tpl)), ...); } + } +#else + template + void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - /** - * Iterates all columns that have specified constraints and fires passed lambda. - * Lambda must have one and only templated argument Otherwise code will not compile. - * L is lambda type. Do not specify it explicitly. - * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} - */ - template - void for_each_column_with(const L& lambda) const { - this->for_each_column([&lambda](auto& column) { - using tuple_helper::tuple_contains_type; - using column_type = typename std::decay::type; - using constraints_type = typename column_constraints_type::type; - static_if{}>(lambda)(column); - }); + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr(reversed) { +#else + if(reversed) { +#endif + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); } + } +#endif + template + void iterate_tuple(const Tpl& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence::value>{}, + std::forward(lambda)); + } - std::vector get_table_info() const { - std::vector res; - res.reserve(size_t(this->elements_count)); - this->for_each_column([&res](auto& col) { - std::string dft; - using field_type = typename std::decay::type::field_type; - if(auto d = col.default_value()) { - dft = *d; - } - table_info i{ - -1, - col.name, - type_printer().print(), - col.not_null(), - dft, - col.template has>(), - }; - res.emplace_back(i); - }); - auto compositeKeyColumnNames = this->composite_key_columns_names(); - for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { - auto& columnName = compositeKeyColumnNames[i]; - auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info& ti) { - return ti.name == columnName; - }); - if(it != res.end()) { - it->pk = static_cast(i + 1); - } - } - return res; +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(std::index_sequence, L&& lambda) { + lambda((std::tuple_element_t*)nullptr); + iterate_tuple(std::index_sequence{}, std::forward(lambda)); + } +#endif + template + void iterate_tuple(L&& lambda) { + iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); + } + + template + R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + template + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } + + template class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); } }; - } - - /** - * Function used for table creation. Do not use table constructor - use this function - * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). - */ - template>::type::object_type> - internal::table_t make_table(const std::string& name, Cs... args) { - return {name, std::make_tuple(std::forward(args)...)}; - } - template - internal::table_t make_table(const std::string& name, Cs... args) { - return {name, std::make_tuple(std::forward(args)...)}; + /* + * 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)}; + } } } -#pragma once - -#include // std::string -#include -#include // std::nullptr_t -#include // std::system_error, std::error_code -#include // std::stringstream -#include // std::atoi -#include // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type -#include // std::pair, std::make_pair -#include // std::vector -#include // std::find_if -#include // std::type_index - -// #include "error_code.h" -// #include "statement_finalizer.h" +// #include "member_traits/member_traits.h" -// #include "row_extractor.h" +// #include "typed_comparator.h" -// #include "util.h" +// #include "type_traits.h" // #include "constraints.h" -// #include "select_constraints.h" - -// #include "field_printer.h" - // #include "table_info.h" -// #include "sync_schema_result.h" - -// #include "field_value_holder.h" - -#include // std::enable_if - // #include "column.h" namespace sqlite_orm { - namespace internal { - template - struct field_value_holder; + namespace internal { - template - struct field_value_holder::returns_lvalue>::type> { - using type = typename getter_traits::field_type; + struct basic_table { - const type& value; + /** + * Table name. + */ + std::string name; }; - template - struct field_value_holder::returns_lvalue>::type> { - using type = typename getter_traits::field_type; - - type value; - }; - } -} + /** + * Table definition. + */ + template + struct table_t : basic_table { + using object_type = T; + using elements_type = std::tuple; -namespace sqlite_orm { + static constexpr bool is_without_rowid_v = WithoutRowId; + using is_without_rowid = polyfill::bool_constant; - namespace internal { + elements_type elements; - struct storage_impl_base { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : basic_table{move(name_)}, elements{move(elements_)} {} +#endif - bool table_exists(const std::string& tableName, sqlite3* db) const { - auto result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" - << "table" - << "' AND name = '" << tableName << "'"; - auto query = ss.str(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char** /*azColName*/) -> int { - auto& res = *(bool*)data; - if(argc) { - res = !!std::atoi(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return result; + table_t without_rowid() const { + return {this->name, this->elements}; } - void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; - perform_void_exec(db, ss.str()); + /** + * Returns foreign keys count in table definition + */ + constexpr int foreign_keys_count() const { +#if SQLITE_VERSION_NUMBER >= 3006019 + using fk_index_sequence = filter_tuple_sequence_t; + return int(fk_index_sequence::size()); +#else + return 0; +#endif } - static bool calculate_remove_add_columns(std::vector& columnsToAdd, - std::vector& storageTableInfo, - std::vector& dbTableInfo) { - bool notEqual = false; - - // iterate through storage columns - for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); - ++storageColumnInfoIndex) { - - // get storage's column info - auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto& columnName = storageColumnInfo.name; + /** + * 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; + } - // search for a column in db eith the same name - auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { - return ti.name == columnName; - }); - if(dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = - dbColumnInfo.name == storageColumnInfo.name && - dbColumnInfo.notnull == storageColumnInfo.notnull && - (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && - dbColumnInfo.pk == storageColumnInfo.pk; - if(!columnsAreEqual) { - notEqual = true; - break; - } - dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); - --storageColumnInfoIndex; - } else { - columnsToAdd.push_back(&storageColumnInfo); - } - } - return notEqual; + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with{}, + [&result, &name](auto& column) { + if(column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t, + is_generated_always>; + constexpr size_t opIndex = first_index_sequence_value(generated_op_index_sequence{}); + result = &get(column.constraints).storage; + }); +#endif + return result; } - std::vector get_table_info(const std::string& tableName, sqlite3* db) const { - std::vector result; - auto query = "PRAGMA table_info('" + tableName + "')"; - auto rc = sqlite3_exec( - db, - query.c_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.push_back(table_info(cid, name, type, notnull, dflt_value, pk)); + template + bool exists_in_composite_primary_key(const column_field& column) const { + bool res = false; + this->for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if(compare_any(memberPointer, column.member_pointer) || + compare_any(memberPointer, column.setter)) { + res = true; } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return result; + }); + }); + return res; } - }; - /** - * This is a generic implementation. Used as a tail in storage_impl inheritance chain - */ - template - struct storage_impl; + /** + * 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); + } - template - struct storage_impl : public storage_impl { - using table_type = H; - using super = storage_impl; + 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; + } - storage_impl(H h, Ts... ts) : super(std::forward(ts)...), table(std::move(h)) {} + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; - table_type table; + 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(const L& l) { - this->super::for_each(l); - l(*this); + 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); + }); } -#if SQLITE_VERSION_NUMBER >= 3006019 + 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; + } + }); + } /** - * Returns foreign keys count in table definition + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. */ - int foreign_keys_count() { - auto res = 0; - iterate_tuple(this->table.elements, [&res](auto& c) { - if(is_foreign_key::type>::value) { - ++res; - } - }); + 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; } -#endif - /** - * Is used to get column name by member pointer to a base class. - * Main difference between `column_name` and `column_name_simple` is that - * `column_name` has SFINAE check for type equality but `column_name_simple` has not. + * Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints. */ - template - const std::string* column_name_simple(F O::*m) const { - return this->table.find_column_name(m); + constexpr int non_generated_columns_count() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + using non_generated_col_index_sequence = + col_index_sequence_excluding; + return int(non_generated_col_index_sequence::size()); +#else + return this->count_columns_amount(); +#endif } /** - * Cute function used to find column name by its type and member pointer. Uses SFINAE to - * skip inequal type O. + * Counts and returns amount of columns. Skips constraints. */ - template - const std::string* column_name(F O::*m, - typename std::enable_if::value>::type* = nullptr) const { - return this->table.find_column_name(m); + constexpr int count_columns_amount() const { + using col_index_sequence = filter_tuple_sequence_t; + return int(col_index_sequence::size()); } /** - * Opposite version of function defined above. Just calls same function in superclass. + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` */ - template - const std::string* - column_name(F O::*m, typename std::enable_if::value>::type* = nullptr) const { - return this->super::column_name(m); + 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 - const std::string* column_name(const column_pointer& c, - typename std::enable_if::value>::type* = nullptr) const { - return this->column_name_simple(c.field); + 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); } - template - const std::string* - column_name(const column_pointer& c, - typename std::enable_if::value>::type* = nullptr) const { - return this->super::column_name(c); + /** + * 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); } - template - const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { - return *this; + /** + * 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); } - template - const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { - return this->super::template get_impl(); + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->for_each_column_excluding(lambda); } - template - auto& get_impl(typename std::enable_if::value>::type* = nullptr) { - return *this; - } + std::vector get_table_info() const; + }; - template - auto& get_impl(typename std::enable_if::value>::type* = nullptr) { - return this->super::template get_impl(); - } + template + struct is_table : std::false_type {}; - template - const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { - return &this->table; - } + template + struct is_table> : std::true_type {}; + } - template - const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { - return this->super::template find_table(); - } + /** + * Factory function for a table definition. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), std::make_tuple(std::forward(args)...)}); + } - std::string find_table_name(std::type_index ti) const { - std::type_index thisTypeIndex{typeid(typename H::object_type)}; - if(thisTypeIndex == ti) { - return this->table.name; - } else { - return this->super::find_table_name(ti); - } - } + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), std::make_tuple(std::forward(args)...)}); + } +} +#pragma once - void add_column(const table_info& ti, sqlite3* db) const { - std::stringstream ss; - ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name; - ss << " " << ti.type; - if(ti.pk) { - ss << " PRIMARY KEY"; - } - if(ti.notnull) { - ss << " NOT NULL"; - } - if(ti.dflt_value.length()) { - ss << " DEFAULT " << ti.dflt_value; - } - perform_void_exec(db, ss.str()); - } +#include // std::tuple_size +#include // std::type_index +#include // std::string +#include // std::is_same, std::decay, std::make_index_sequence, std::index_sequence - /** - * Copies current table to another table with a given **name**. - * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; - */ - void - copy_table(sqlite3* db, const std::string& name, const std::vector& columnsToIgnore) const { - std::stringstream ss; - std::vector columnNames; - this->table.for_each_column([&columnNames, &columnsToIgnore](auto& c) { - auto& columnName = c.name; - auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), - columnsToIgnore.end(), - [&columnName](auto tableInfoPointer) { - return columnName == tableInfoPointer->name; - }); - if(columnToIgnoreIt == columnsToIgnore.end()) { - columnNames.emplace_back(columnName); - } - }); - auto columnNamesCount = columnNames.size(); - ss << "INSERT INTO " << name << " ("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << columnNames[i]; - if(i < columnNamesCount - 1) { - ss << ","; - } - ss << " "; - } - ss << ") "; - ss << "SELECT "; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << columnNames[i]; - if(i < columnNamesCount - 1) { - ss << ", "; - } - } - ss << " FROM '" << this->table.name << "' "; - perform_void_exec(db, ss.str()); - } +// #include "functional/cxx_universal.h" - sync_schema_result schema_status(sqlite3* db, bool preserve) const { +// #include "functional/static_magic.h" - auto res = sync_schema_result::already_in_sync; +// #include "tuple_helper/tuple_iteration.h" - // first let's see if table with such name exists.. - auto gottaCreateTable = !this->table_exists(this->table.name, db); - if(!gottaCreateTable) { +// #include "type_traits.h" - // get table info provided in `make_table` call.. - auto storageTableInfo = this->table.get_table_info(); +// #include "select_constraints.h" + +// #include "storage_lookup.h" + +// interface functions +namespace sqlite_orm { + namespace internal { + + template + using tables_index_sequence = filter_tuple_sequence_t; - // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = this->get_table_info(this->table.name, db); + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.foreign_keys_count(); + }); + return res; + } + + template = true> + auto lookup_table(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) { + return &pick_table(dbObjects); + }, + empty_callable())(dbObjects); + } + + template = true> + std::string find_table_name(const DBOs& dbObjects, const std::type_index& ti) { + std::string res; + iterate_tuple(dbObjects, tables_index_sequence{}, [&ti, &res](const auto& table) { + using table_type = std::decay_t; + if(ti == typeid(object_type_t)) { + res = table.name; + } + }); + return res; + } + + template = true> + std::string lookup_table_name(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) { + 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); + } - // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { + return cp.field; + } - if(this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { - gottaCreateTable = true; - } + /** + * Find column name by: + * 1. by explicit object type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } + } +} +#pragma once - if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are - // excess columns at the db.. - if(dbTableInfo.size() > 0) { - // extra table columns than storage columns - if(!preserve) { - gottaCreateTable = true; - } else { - res = decltype(res)::old_columns_removed; - } - } - } - if(gottaCreateTable) { - res = decltype(res)::dropped_and_recreated; - } else { - if(columnsToAdd.size()) { - // extra storage columns than table columns - for(auto columnPointer: columnsToAdd) { - if(columnPointer->notnull && columnPointer->dflt_value.empty()) { - gottaCreateTable = true; - break; - } - } - if(!gottaCreateTable) { - if(res == decltype(res)::old_columns_removed) { - res = decltype(res)::new_columns_added_and_old_columns_removed; - } else { - res = decltype(res)::new_columns_added; - } - } else { - res = decltype(res)::dropped_and_recreated; - } - } else { - if(res != decltype(res)::old_columns_removed) { - res = decltype(res)::already_in_sync; - } - } - } - } else { - res = decltype(res)::new_table_created; - } - return res; - } +#include // std::string - private: - using self = storage_impl; - }; +// #include "constraints.h" - template<> - struct storage_impl<> : storage_impl_base { +// #include "serializer_context.h" - std::string find_table_name(std::type_index) const { - return {}; - } +// #include "storage_lookup.h" - template - void for_each(const L&) {} +namespace sqlite_orm { - int foreign_keys_count() { - return 0; - } + namespace internal { - template - const void* find_table() const { - return nullptr; - } - }; + template + std::string serialize(const T& t, const serializer_context& context); + /** + * Serialize default value of a column's default valu + */ template - struct is_storage_impl : std::false_type {}; + std::string serialize_default_value(const default_t& dft) { + db_objects_tuple<> dbObjects; + serializer_context> context{dbObjects}; + return serialize(dft.value, context); + } - template - struct is_storage_impl> : std::true_type {}; } + } #pragma once -#include // std::unique/shared_ptr, std::make_unique/shared -#include // std::string #include +#include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared +#include // std::system_error +#include // std::string #include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type -#include // std::ptrdiff_t -#include // std::input_iterator_tag, std::iterator_traits, std::distance -#include // std::function +#include // std::identity #include // std::stringstream #include // std::map #include // std::vector -#include // std::tuple_size, std::tuple, std::make_tuple +#include // std::tuple_size, std::tuple, std::make_tuple, std::tie #include // std::forward, std::pair -#include // std::find +#include // std::for_each, std::ranges::for_each +// #include "functional/cxx_optional.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/static_magic.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" // #include "alias.h" // #include "row_extractor_builder.h" +// #include "functional/cxx_universal.h" + // #include "row_extractor.h" // #include "mapped_row_extractor.h" @@ -9372,6 +10607,9 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" #include +#include // std::is_member_object_pointer + +// #include "functional/static_magic.h" // #include "row_extractor.h" @@ -9381,7 +10619,11 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; - mutable int index = 0; + int index = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_from_column_builder_base(sqlite3_stmt* stmt) : stmt{stmt} {} +#endif }; /** @@ -9396,18 +10638,18 @@ namespace sqlite_orm { object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} - template - void operator()(const C& c) const { - using field_type = typename C::field_type; - auto value = row_extractor().extract(this->stmt, this->index++); - if(c.member_pointer) { - this->object.*c.member_pointer = std::move(value); - } else { - ((this->object).*(c.setter))(std::move(value)); - } + template + void operator()(const column_field& column) { + auto value = row_extractor>().extract(this->stmt, this->index++); + static_if::value>( + [&value, &object = this->object](const auto& column) { + object.*column.member_pointer = std::move(value); + }, + [&value, &object = this->object](const auto& column) { + (object.*column.setter)(std::move(value)); + })(column); } }; - } } @@ -9418,25 +10660,23 @@ namespace sqlite_orm { /** * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. * Main difference from regular `row_extractor` is that this class takes table info which is required - * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * for constructing objects by member pointers. To construct please use `make_row_extractor()`. * Type arguments: * V is value type just like regular `row_extractor` has * T is table info class `table_t` */ - template + template struct mapped_row_extractor { - using table_info_t = T; + using table_type = Table; - mapped_row_extractor(const table_info_t& tableInfo_) : tableInfo(tableInfo_) {} - - V extract(sqlite3_stmt* stmt, int /*columnIndex*/) { + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { V res; object_from_column_builder builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } - const table_info_t& tableInfo; + const table_type& tableInfo; }; } @@ -9447,37 +10687,15 @@ namespace sqlite_orm { namespace internal { - /** - * This builder is used to construct different row extractors depending on type. - * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and - * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns - * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. - */ - template - struct row_extractor_builder; - - template - struct row_extractor_builder { - - row_extractor operator()(const I* /*tableInfo*/) const { - return {}; - } - }; - - template - struct row_extractor_builder { - - mapped_row_extractor operator()(const I* tableInfo) const { - return {*tableInfo}; - } - }; - - template - auto make_row_extractor(const I* tableInfo) { - using builder_t = row_extractor_builder; - return builder_t{}(tableInfo); + template + row_extractor make_row_extractor(nullptr_t) { + return {}; } + template + mapped_row_extractor make_row_extractor(const Table* table) { + return {*table}; + } } } @@ -9486,12 +10704,8 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper/tuple_helper.h" - // #include "constraints.h" -// #include "type_is_nullable.h" - // #include "field_printer.h" // #include "rowid.h" @@ -9518,35 +10732,28 @@ namespace sqlite_orm { // #include "journal_mode.h" -// #include "field_value_holder.h" - // #include "view.h" -#include // std::shared_ptr +#include #include // std::string #include // std::forward, std::move -#include -#include // std::system_error #include // std::tuple, std::make_tuple // #include "row_extractor.h" -// #include "statement_finalizer.h" - // #include "error_code.h" // #include "iterator.h" -#include // std::shared_ptr, std::unique_ptr, std::make_shared #include +#include // std::shared_ptr, std::unique_ptr, std::make_shared #include // std::decay #include // std::move -#include // std::ptrdiff_t #include // std::input_iterator_tag #include // std::system_error -#include // std::make_error_code +#include // std::bind -// #include "row_extractor.h" +// #include "functional/cxx_universal.h" // #include "statement_finalizer.h" @@ -9554,6 +10761,10 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" +// #include "storage_lookup.h" + +// #include "util.h" + namespace sqlite_orm { namespace internal { @@ -9565,15 +10776,13 @@ namespace sqlite_orm { protected: /** - * The double-indirection is so that copies of the iterator - * share the same sqlite3_stmt from a sqlite3_prepare_v2() - * call. When one finishes iterating it the pointer - * inside the shared_ptr is nulled out in all copies. + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. */ - std::shared_ptr stmt; + std::shared_ptr stmt; // only null for the default constructed iterator - view_type* view; + view_type* view = nullptr; /** * shared_ptr is used over unique_ptr here @@ -9582,29 +10791,37 @@ namespace sqlite_orm { std::shared_ptr current; void extract_value() { - auto& storage = this->view->storage; - auto& impl = storage.template get_impl(); + auto& dbObjects = obtain_db_objects(this->view->storage); this->current = std::make_shared(); - object_from_column_builder builder{*this->current, this->stmt->get()}; - impl.table.for_each_column(builder); + object_from_column_builder builder{*this->current, this->stmt.get()}; + pick_table(dbObjects).for_each_column(builder); + } + + void next() { + this->current.reset(); + if(sqlite3_stmt* stmt = this->stmt.get()) { + perform_step(stmt, std::bind(&iterator_t::extract_value, this)); + if(!this->current) { + this->stmt.reset(); + } + } } public: - using difference_type = std::ptrdiff_t; + using difference_type = ptrdiff_t; using pointer = value_type*; using reference = value_type&; using iterator_category = std::input_iterator_tag; - iterator_t() : view(nullptr){}; + iterator_t(){}; - iterator_t(sqlite3_stmt* stmt_, view_type& view_) : - stmt(std::make_shared(stmt_)), view(&view_) { + iterator_t(statement_finalizer stmt_, view_type& view_) : stmt{move(stmt_)}, view{&view_} { next(); } const value_type& operator*() const { if(!this->stmt || !this->current) { - throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); + throw std::system_error{orm_error_code::trying_to_dereference_null_iterator}; } return *this->current; } @@ -9613,29 +10830,6 @@ namespace sqlite_orm { return &(this->operator*()); } - private: - void next() { - this->current.reset(); - if(this->stmt) { - auto statementPointer = this->stmt->get(); - auto ret = sqlite3_step(statementPointer); - switch(ret) { - case SQLITE_ROW: - this->extract_value(); - break; - case SQLITE_DONE: - this->stmt.reset(); - break; - default: { - auto db = this->view->connection.get(); - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - } - - public: iterator_t& operator++() { next(); return *this; @@ -9661,29 +10855,38 @@ namespace sqlite_orm { #include // std::vector #include // std::reference_wrapper +// #include "tuple_helper/tuple_iteration.h" + // #include "conditions.h" // #include "select_constraints.h" // #include "operators.h" -// #include "tuple_helper/tuple_helper.h" - // #include "core_functions.h" // #include "prepared_statement.h" #include +#include // std::unique_ptr #include // std::iterator_traits #include // std::string -#include // std::true_type, std::false_type +#include // std::integral_constant, std::declval #include // std::pair +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + // #include "connection_holder.h" #include +#include #include // std::string -#include // std::system_error // #include "error_code.h" @@ -9696,23 +10899,19 @@ namespace sqlite_orm { connection_holder(std::string filename_) : filename(move(filename_)) {} void retain() { - ++this->_retain_count; - if(1 == this->_retain_count) { + if(1 == ++this->_retain_count) { auto rc = sqlite3_open(this->filename.c_str(), &this->db); if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), - sqlite3_errmsg(this->db)); + throw_translated_sqlite_error(db); } } } void release() { - --this->_retain_count; - if(0 == this->_retain_count) { + if(0 == --this->_retain_count) { auto rc = sqlite3_close(this->db); if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), - sqlite3_errmsg(this->db)); + throw_translated_sqlite_error(db); } } } @@ -9729,7 +10928,7 @@ namespace sqlite_orm { protected: sqlite3* db = nullptr; - int _retain_count = 0; + std::atomic_int _retain_count{}; }; struct connection_ref { @@ -9764,9 +10963,12 @@ namespace sqlite_orm { // #include "values.h" #include // std::vector -#include #include // std::tuple -#include // std::false_type, std::true_type +#include // std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -9780,10 +10982,10 @@ namespace sqlite_orm { }; template - struct is_values : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of_v; - template - struct is_values> : std::true_type {}; + template + using is_values = polyfill::bool_constant>; template struct dynamic_values_t { @@ -9804,6 +11006,69 @@ namespace sqlite_orm { } +// #include "ast/upsert_clause.h" + +#include // std::tuple, std::make_tuple +#include // std::false_type, std::true_type +#include // std::forward, std::move + +// #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 {move(this->args), {}}; + } + + template + upsert_clause> do_update(ActionsArgs... actions) { + return {move(this->args), {std::make_tuple(std::forward(actions)...)}}; + } + }; + + template + struct upsert_clause, std::tuple> { + using target_args_tuple = std::tuple; + using actions_tuple = std::tuple; + + target_args_tuple target_args; + + actions_tuple actions; + }; + + template + using is_upsert_clause = polyfill::is_specialization_of; + } + + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template + internal::conflict_target on_conflict(Args... args) { + return {std::tuple(std::forward(args)...)}; + } +#endif +} + namespace sqlite_orm { namespace internal { @@ -9812,20 +11077,19 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +#endif + ~prepared_statement_base() { - if(this->stmt) { - sqlite3_finalize(this->stmt); - this->stmt = nullptr; - } + sqlite3_finalize(this->stmt); } std::string sql() const { - if(this->stmt) { - if(auto res = sqlite3_sql(this->stmt)) { - return res; - } else { - return {}; - } + // 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 {}; } @@ -9833,14 +11097,10 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3014000 std::string expanded_sql() const { - if(this->stmt) { - if(auto res = sqlite3_expanded_sql(this->stmt)) { - std::string result = res; - sqlite3_free(res); - return result; - } else { - return {}; - } + // 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 {}; } @@ -9848,34 +11108,43 @@ namespace sqlite_orm { #endif #if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) std::string normalized_sql() const { - if(this->stmt) { - if(auto res = sqlite3_normalized_sql(this->stmt)) { - return res; - } else { - return {}; - } + 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 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(T t_, sqlite3_stmt* stmt_, connection_ref con_) : - prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} + 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 - struct is_prepared_statement : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of_v; template - struct is_prepared_statement> : std::true_type {}; + using is_prepared_statement = polyfill::bool_constant>; /** * T - type of object to obtain from a database @@ -9962,7 +11231,7 @@ namespace sqlite_orm { struct update_t { using type = T; - type obj; + type object; }; template @@ -9977,14 +11246,14 @@ namespace sqlite_orm { struct insert_t { using type = T; - type obj; + type object; }; template - struct is_insert : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of_v; template - struct is_insert> : std::true_type {}; + using is_insert = polyfill::bool_constant>; template struct insert_explicit { @@ -9999,20 +11268,19 @@ namespace sqlite_orm { struct replace_t { using type = T; - type obj; + type object; }; template - struct is_replace : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of_v; template - struct is_replace> : std::true_type {}; + using is_replace = polyfill::bool_constant>; - template + template struct insert_range_t { using iterator_type = It; - using container_object_type = typename std::iterator_traits::value_type; - using transformer_type = L; + using transformer_type = Projection; using object_type = O; std::pair range; @@ -10020,16 +11288,15 @@ namespace sqlite_orm { }; template - struct is_insert_range : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = polyfill::is_specialization_of_v; - template - struct is_insert_range> : std::true_type {}; + template + using is_insert_range = polyfill::bool_constant>; - template + template struct replace_range_t { using iterator_type = It; - using container_object_type = typename std::iterator_traits::value_type; - using transformer_type = L; + using transformer_type = Projection; using object_type = O; std::pair range; @@ -10037,10 +11304,10 @@ namespace sqlite_orm { }; template - struct is_replace_range : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = polyfill::is_specialization_of_v; - template - struct is_replace_range> : std::true_type {}; + template + using is_replace_range = polyfill::bool_constant>; template struct insert_raw_t { @@ -10050,10 +11317,10 @@ namespace sqlite_orm { }; template - struct is_insert_raw : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of_v; - template - struct is_insert_raw> : std::true_type {}; + template + using is_insert_raw = polyfill::bool_constant>; template struct replace_raw_t { @@ -10063,36 +11330,17 @@ namespace sqlite_orm { }; template - struct is_replace_raw : std::false_type {}; - - template - struct is_replace_raw> : std::true_type {}; - - template - struct into_t { - using type = T; - }; - - template - struct is_into : std::false_type {}; - - template - struct is_into> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of_v; - struct default_transformer { - - template - const T& operator()(const T& object) const { - return object; - } - }; + template + using is_replace_raw = polyfill::bool_constant>; struct default_values_t {}; template - using is_default_values = std::is_same; + using is_default_values = std::is_same; - enum class insert_constraint { + enum class conflict_action { abort, fail, ignore, @@ -10100,31 +11348,36 @@ namespace sqlite_orm { rollback, }; - template - using is_insert_constraint = std::is_same; + struct insert_constraint { + conflict_action action = conflict_action::abort; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + insert_constraint(conflict_action action) : action{action} {} +#endif + }; template - struct is_upsert_clause; + using is_insert_constraint = std::is_same; } inline internal::insert_constraint or_rollback() { - return internal::insert_constraint::rollback; + return {internal::conflict_action::rollback}; } inline internal::insert_constraint or_replace() { - return internal::insert_constraint::replace; + return {internal::conflict_action::replace}; } inline internal::insert_constraint or_ignore() { - return internal::insert_constraint::ignore; + return {internal::conflict_action::ignore}; } inline internal::insert_constraint or_fail() { - return internal::insert_constraint::fail; + return {internal::conflict_action::fail}; } inline internal::insert_constraint or_abort() { - return internal::insert_constraint::abort; + return {internal::conflict_action::abort}; } /** @@ -10139,11 +11392,6 @@ namespace sqlite_orm { return {}; } - template - internal::into_t into() { - 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. @@ -10286,7 +11534,8 @@ namespace sqlite_orm { } /** - * Create a replace range statement + * Create a replace range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. * * @example * ``` @@ -10295,33 +11544,33 @@ namespace sqlite_orm { * auto statement = storage.prepare(replace_range(users.begin(), users.end())); * storage.execute(statement); * ``` - */ - template - internal::replace_range_t::value_type> - replace_range(It from, It to) { - return {{std::move(from), std::move(to)}}; - } - - /** - * Create an replace range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, - * optionals or whatever. * @example * ``` * std::vector> userPointers; * userPointers.push_back(std::make_unique(1, "Eneli")); - * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { - * return *userPointer; - * })); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); * storage.execute(statement); * ``` */ - template - internal::replace_range_t replace_range(It from, It to, L transformer) { - return {{std::move(from), std::move(to)}, std::move(transformer)}; + 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 + * 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; @@ -10329,30 +11578,29 @@ namespace sqlite_orm { * auto statement = storage.prepare(insert_range(users.begin(), users.end())); * storage.execute(statement); * ``` - */ - template - internal::insert_range_t::value_type> - insert_range(It from, It to) { - return {{std::move(from), std::move(to)}, internal::default_transformer{}}; - } - - /** - * Create an insert range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, - * optionals or whatever. * @example * ``` * std::vector> userPointers; * userPointers.push_back(std::make_unique(1, "Eneli")); - * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { - * return *userPointer; - * })); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); * storage.execute(statement); * ``` */ - template - internal::insert_range_t insert_range(It from, It to, L transformer) { - return {{std::move(from), std::move(to)}, std::move(transformer)}; + 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. @@ -10562,6 +11810,8 @@ namespace sqlite_orm { // #include "ast/excluded.h" +#include // std::move + namespace sqlite_orm { namespace internal { @@ -10581,68 +11831,46 @@ namespace sqlite_orm { // #include "ast/upsert_clause.h" -#include // std::tuple -#include // std::false_type, std::true_type +// #include "ast/where.h" -namespace sqlite_orm { - namespace internal { +// #include "ast/into.h" - template - struct upsert_clause; +// #include "ast/group_by.h" - template - struct conflict_target { - using args_tuple = std::tuple; +// #include "ast/exists.h" - args_tuple args; +#include // std::move - upsert_clause> do_nothing() { - return {std::move(this->args), {}}; - } +// #include "../tags.h" - template - upsert_clause> do_update(ActionsArgs... actions) { - return {std::move(this->args), {std::make_tuple(std::forward(actions)...)}}; - } - }; +namespace sqlite_orm { + namespace internal { - template - struct upsert_clause, std::tuple> { - using target_args_tuple = std::tuple; - using actions_tuple = std::tuple; + template + struct exists_t : condition_t, negatable_t { + using expression_type = T; + using self = exists_t; - target_args_tuple target_args; + expression_type expression; - actions_tuple actions; + exists_t(expression_type expression_) : expression(std::move(expression_)) {} }; - - template - struct is_upsert_clause : std::false_type {}; - - template - struct is_upsert_clause> : std::true_type {}; } /** - * 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)))); + * 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::conflict_target on_conflict(Args... args) { - return {std::tuple(std::forward(args)...)}; + template + internal::exists_t exists(T expression) { + return {std::move(expression)}; } } -// #include "ast/where.h" - namespace sqlite_orm { namespace internal { @@ -10664,8 +11892,8 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T& t, const L& l) const { - l(t); + void operator()(const T& t, L& lambda) const { + lambda(t); } }; @@ -10673,9 +11901,9 @@ namespace sqlite_orm { * Simplified API */ template - void iterate_ast(const T& t, const L& l) { + void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; - iterator(t, l); + iterator(t, lambda); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -10684,7 +11912,7 @@ namespace sqlite_orm { using node_type = as_optional_t; template - void operator()(const node_type& node, const L& lambda) const { + void operator()(const node_type& node, L& lambda) const { iterate_ast(node.value, lambda); } }; @@ -10695,8 +11923,18 @@ namespace sqlite_orm { using node_type = std::reference_wrapper; template - void operator()(const node_type& r, const L& lambda) const { - iterate_ast(r.get(), lambda); + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.args, lambda); } }; @@ -10705,7 +11943,7 @@ namespace sqlite_orm { using node_type = excluded_t; template - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.expression, lambda); } }; @@ -10715,7 +11953,7 @@ namespace sqlite_orm { using node_type = upsert_clause, std::tuple>; template - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.actions, lambda); } }; @@ -10725,19 +11963,19 @@ namespace sqlite_orm { using node_type = where_t; template - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.expression, lambda); } }; template - struct ast_iterator::value>::type> { + struct ast_iterator>> { using node_type = T; template - void operator()(const node_type& binaryCondition, const L& l) const { - iterate_ast(binaryCondition.l, l); - iterate_ast(binaryCondition.r, l); + void operator()(const node_type& binaryCondition, L& lambda) const { + iterate_ast(binaryCondition.l, lambda); + iterate_ast(binaryCondition.r, lambda); } }; @@ -10746,9 +11984,9 @@ namespace sqlite_orm { using node_type = binary_operator; template - void operator()(const node_type& binaryOperator, const C& l) const { - iterate_ast(binaryOperator.lhs, l); - iterate_ast(binaryOperator.rhs, l); + void operator()(const node_type& binaryOperator, C& lambda) const { + iterate_ast(binaryOperator.lhs, lambda); + iterate_ast(binaryOperator.rhs, lambda); } }; @@ -10757,8 +11995,8 @@ namespace sqlite_orm { using node_type = columns_t; template - void operator()(const node_type& cols, const L& l) const { - iterate_ast(cols.columns, l); + void operator()(const node_type& cols, L& lambda) const { + iterate_ast(cols.columns, lambda); } }; @@ -10767,9 +12005,9 @@ namespace sqlite_orm { using node_type = dynamic_in_t; template - void operator()(const node_type& in, const C& l) const { - iterate_ast(in.left, l); - iterate_ast(in.argument, l); + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); } }; @@ -10778,9 +12016,9 @@ namespace sqlite_orm { using node_type = in_t; template - void operator()(const node_type& in, const C& l) const { - iterate_ast(in.left, l); - iterate_ast(in.argument, l); + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); } }; @@ -10789,9 +12027,9 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type& vec, const L& l) const { + void operator()(const node_type& vec, L& lambda) const { for(auto& i: vec) { - iterate_ast(i, l); + iterate_ast(i, lambda); } } }; @@ -10801,19 +12039,19 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type& vec, const L& l) const { - l(vec); + void operator()(const node_type& vec, L& lambda) const { + lambda(vec); } }; template - struct ast_iterator::value>::type> { + struct ast_iterator>> { using node_type = T; template - void operator()(const node_type& c, const L& l) const { - iterate_ast(c.left, l); - iterate_ast(c.right, l); + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.left, lambda); + iterate_ast(c.right, lambda); } }; @@ -10822,7 +12060,7 @@ namespace sqlite_orm { using node_type = into_t; template - void operator()(const node_type& node, const L& l) const { + void operator()(const node_type& /*node*/, L& /*lambda*/) const { //.. } }; @@ -10832,8 +12070,8 @@ namespace sqlite_orm { using node_type = insert_raw_t; template - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.args, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; @@ -10842,8 +12080,8 @@ namespace sqlite_orm { using node_type = replace_raw_t; template - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.args, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; @@ -10852,9 +12090,9 @@ namespace sqlite_orm { using node_type = select_t; template - void operator()(const node_type& sel, const L& l) const { - iterate_ast(sel.col, l); - iterate_ast(sel.conditions, l); + void operator()(const node_type& sel, L& lambda) const { + iterate_ast(sel.col, lambda); + iterate_ast(sel.conditions, lambda); } }; @@ -10863,8 +12101,8 @@ namespace sqlite_orm { using node_type = get_all_t; template - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; @@ -10873,8 +12111,8 @@ namespace sqlite_orm { using node_type = get_all_pointer_t; template - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; @@ -10884,8 +12122,8 @@ namespace sqlite_orm { using node_type = get_all_optional_t; template - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -10895,9 +12133,9 @@ namespace sqlite_orm { using node_type = update_all_t, Wargs...>; template - void operator()(const node_type& u, const L& l) const { - iterate_ast(u.set, l); - iterate_ast(u.conditions, l); + void operator()(const node_type& u, L& lambda) const { + iterate_ast(u.set, lambda); + iterate_ast(u.conditions, lambda); } }; @@ -10906,8 +12144,8 @@ namespace sqlite_orm { using node_type = remove_all_t; template - void operator()(const node_type& r, const L& l) const { - iterate_ast(r.conditions, l); + void operator()(const node_type& r, L& lambda) const { + iterate_ast(r.conditions, lambda); } }; @@ -10916,8 +12154,8 @@ namespace sqlite_orm { using node_type = set_t; template - void operator()(const node_type& s, const L& l) const { - iterate_ast(s.assigns, l); + void operator()(const node_type& s, L& lambda) const { + iterate_ast(s.assigns, lambda); } }; @@ -10926,20 +12164,31 @@ namespace sqlite_orm { using node_type = std::tuple; template - void operator()(const node_type& tuple, const L& l) const { - iterate_tuple(tuple, [&l](auto& v) { - iterate_ast(v, l); + void operator()(const node_type& node, L& lambda) const { + iterate_tuple(node, [&lambda](auto& v) { + iterate_ast(v, lambda); }); } }; + template + struct ast_iterator, void> { + using node_type = group_by_with_having; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + iterate_ast(node.expression, lambda); + } + }; + template struct ast_iterator, void> { using node_type = having_t; template - void operator()(const node_type& hav, const L& l) const { - iterate_ast(hav.t, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); } }; @@ -10948,8 +12197,8 @@ namespace sqlite_orm { using node_type = cast_t; template - void operator()(const node_type& c, const L& l) const { - iterate_ast(c.expression, l); + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.expression, lambda); } }; @@ -10958,8 +12207,8 @@ namespace sqlite_orm { using node_type = exists_t; template - void operator()(const node_type& e, const L& l) const { - iterate_ast(e.t, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); } }; @@ -10968,11 +12217,11 @@ namespace sqlite_orm { using node_type = like_t; template - void operator()(const node_type& lk, const L& l) const { - iterate_ast(lk.arg, l); - iterate_ast(lk.pattern, l); - lk.arg3.apply([&l](auto& value) { - iterate_ast(value, l); + 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); }); } }; @@ -10982,9 +12231,9 @@ namespace sqlite_orm { using node_type = glob_t; template - void operator()(const node_type& lk, const L& l) const { - iterate_ast(lk.arg, l); - iterate_ast(lk.pattern, l); + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); } }; @@ -10993,10 +12242,10 @@ namespace sqlite_orm { using node_type = between_t; template - void operator()(const node_type& b, const L& l) const { - iterate_ast(b.expr, l); - iterate_ast(b.b1, l); - iterate_ast(b.b2, l); + void operator()(const node_type& b, L& lambda) const { + iterate_ast(b.expr, lambda); + iterate_ast(b.b1, lambda); + iterate_ast(b.b2, lambda); } }; @@ -11005,8 +12254,8 @@ namespace sqlite_orm { using node_type = named_collate; template - void operator()(const node_type& col, const L& l) const { - iterate_ast(col.expr, l); + void operator()(const node_type& col, L& lambda) const { + iterate_ast(col.expr, lambda); } }; @@ -11015,8 +12264,8 @@ namespace sqlite_orm { using node_type = negated_condition_t; template - void operator()(const node_type& neg, const L& l) const { - iterate_ast(neg.c, l); + void operator()(const node_type& neg, L& lambda) const { + iterate_ast(neg.c, lambda); } }; @@ -11025,8 +12274,8 @@ namespace sqlite_orm { using node_type = is_null_t; template - void operator()(const node_type& i, const L& l) const { - iterate_ast(i.t, l); + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); } }; @@ -11035,8 +12284,8 @@ namespace sqlite_orm { using node_type = is_not_null_t; template - void operator()(const node_type& i, const L& l) const { - iterate_ast(i.t, l); + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); } }; @@ -11045,8 +12294,8 @@ namespace sqlite_orm { using node_type = function_call; template - void operator()(const node_type& f, const L& l) const { - iterate_ast(f.args, l); + void operator()(const node_type& f, L& lambda) const { + iterate_ast(f.args, lambda); } }; @@ -11055,8 +12304,29 @@ namespace sqlite_orm { using node_type = built_in_function_t; template - void operator()(const node_type& f, const L& l) const { - iterate_ast(f.args, l); + 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); } }; @@ -11065,8 +12335,8 @@ namespace sqlite_orm { using node_type = left_join_t; template - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11075,8 +12345,20 @@ namespace sqlite_orm { using node_type = on_t; template - void operator()(const node_type& o, const L& l) const { - iterate_ast(o.arg, l); + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.arg, lambda); + } + }; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct ast_iterator>> { + using node_type = T; + + template + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.column, lambda); } }; @@ -11085,8 +12367,8 @@ namespace sqlite_orm { using node_type = join_t; template - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11095,8 +12377,8 @@ namespace sqlite_orm { using node_type = left_outer_join_t; template - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11105,8 +12387,8 @@ namespace sqlite_orm { using node_type = inner_join_t; template - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11115,16 +12397,16 @@ namespace sqlite_orm { using node_type = simple_case_t; template - void operator()(const node_type& c, const L& l) const { - c.case_expression.apply([&l](auto& c_) { - iterate_ast(c_, l); + void operator()(const node_type& c, L& lambda) const { + c.case_expression.apply([&lambda](auto& c_) { + iterate_ast(c_, lambda); }); - iterate_tuple(c.args, [&l](auto& pair) { - iterate_ast(pair.first, l); - iterate_ast(pair.second, l); + iterate_tuple(c.args, [&lambda](auto& pair) { + iterate_ast(pair.first, lambda); + iterate_ast(pair.second, lambda); }); - c.else_expression.apply([&l](auto& el) { - iterate_ast(el, l); + c.else_expression.apply([&lambda](auto& el) { + iterate_ast(el, lambda); }); } }; @@ -11134,8 +12416,8 @@ namespace sqlite_orm { using node_type = as_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.expression, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); } }; @@ -11144,8 +12426,8 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.lim, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); } }; @@ -11154,10 +12436,10 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.lim, l); - a.off.apply([&l](auto& value) { - iterate_ast(value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); }); } }; @@ -11167,11 +12449,11 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type& a, const L& l) const { - a.off.apply([&l](auto& value) { - iterate_ast(value, l); + void operator()(const node_type& a, L& lambda) const { + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); }); - iterate_ast(a.lim, l); + iterate_ast(a.lim, lambda); } }; @@ -11180,8 +12462,8 @@ namespace sqlite_orm { using node_type = distinct_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); } }; @@ -11190,8 +12472,8 @@ namespace sqlite_orm { using node_type = all_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); } }; @@ -11200,8 +12482,8 @@ namespace sqlite_orm { using node_type = bitwise_not_t; template - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.argument, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.argument, lambda); } }; @@ -11210,8 +12492,8 @@ namespace sqlite_orm { using node_type = values_t; template - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.tuple, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.tuple, lambda); } }; @@ -11220,8 +12502,32 @@ namespace sqlite_orm { using node_type = dynamic_values_t; template - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.vector, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.vector, lambda); + } + }; + + /** + * Column alias or literal + */ + template + struct ast_iterator< + T, + std::enable_if_t, + polyfill::is_specialization_of>>> { + 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); } }; @@ -11230,8 +12536,8 @@ namespace sqlite_orm { using node_type = collate_t; template - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.expr, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expr, lambda); } }; @@ -11242,6 +12548,8 @@ namespace sqlite_orm { // #include "connection_holder.h" +// #include "util.h" + namespace sqlite_orm { namespace internal { @@ -11277,29 +12585,14 @@ namespace sqlite_orm { } iterator_t begin() { - sqlite3_stmt* stmt = nullptr; - auto db = this->connection.get(); - using context_t = serializator_context; - context_t context{this->storage.impl}; + using context_t = serializer_context; + context_t context{obtain_db_objects(this->storage)}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto query = serialize(this->args, context); - auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); - if(ret == SQLITE_OK) { - auto index = 1; - iterate_ast(this->args.conditions, [&index, stmt, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - return {stmt, *this}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->args, context))}; + iterate_ast(this->args.conditions, conditional_binder{stmt.get()}); + return {move(stmt), *this}; } iterator_t end() { @@ -11313,35 +12606,468 @@ namespace sqlite_orm { // #include "storage_base.h" -#include // std::function, std::bind -#include -#include // std::string -#include // std::stringstream -#include // std::move -#include // std::system_error, std::error_code, std::make_error_code -#include // std::vector -#include // std::make_shared, std::shared_ptr -#include // std::map -#include // std::decay, std::is_same -#include // std::iter_swap +#include +#include // std::function, std::bind +#include // std::string +#include // std::stringstream +#include // std::move +#include // std::system_error +#include // std::vector +#include // std::make_unique, std::unique_ptr +#include // std::map +#include // std::decay, std::is_same +#include // std::find_if + +// #include "functional/cxx_universal.h" + +// #include "functional/static_magic.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "pragma.h" + +#include +#include // std::string +#include // std::function +#include // std::shared_ptr +#include // std::vector +#include + +// #include "error_code.h" + +// #include "row_extractor.h" + +// #include "journal_mode.h" + +// #include "connection_holder.h" + +// #include "util.h" + +// #include "serializing_util.h" + +#include // std::index_sequence +#include +#include +#include +#include +#include // std::exchange, std::tuple_size +#if __cplusplus >= 202002L && __cpp_lib_concepts +#include +#include // std::find +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + namespace internal { + template + struct order_by_t; + + template + std::string serialize(const T& t, const serializer_context& context); + + template + std::string serialize_order_by(const T& t, const Ctx& context); + +#if __cplusplus >= 202002L && \ + __cpp_lib_concepts // contiguous iterator ranges depend on contiguous_iterator, sized_sentinel_for in all major implementations + inline void stream_sql_escaped(std::ostream& os, const std::string& str, char char2Escape) { + for(std::string::const_iterator it = str.cbegin(), next; true; it = next + 1) { + next = std::find(it, str.cend(), char2Escape); + os << std::string_view{it, next}; + + if(next == str.cend()) [[likely]] { + break; + } + os << std::string(2, char2Escape); + } + } +#else + inline void stream_sql_escaped(std::ostream& os, const std::string& str, char char2Escape) { + if(str.find(char2Escape) == str.npos) { + os << str; + } else { + for(char c: str) { + if(c == char2Escape) { + os << char2Escape; + } + os << c; + } + } + } +#endif + + inline void stream_identifier(std::ostream& ss, + const std::string& qualifier, + const std::string& identifier, + const std::string& 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, std::string{}, identifier, alias); + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier) { + return stream_identifier(ss, std::string{}, identifier, std::string{}); + } + + template + void stream_identifier(std::ostream& ss, const Tpl& tpl, std::index_sequence) { + static_assert(sizeof...(Is) > 0 && sizeof...(Is) <= 3, ""); + return stream_identifier(ss, std::get(tpl)...); + } + + template>, bool> = true> + void stream_identifier(std::ostream& ss, const Tpl& tpl) { + return stream_identifier(ss, tpl, std::make_index_sequence::value>{}); + } + + enum class stream_as { + conditions_tuple, + actions_tuple, + expressions_tuple, + dynamic_expressions, + serialized, + identifier, + identifiers, + values_placeholders, + table_columns, + non_generated_columns, + field_values_excluding, + mapped_columns_expressions, + column_constraints, + }; + + template + struct streaming { + template + auto operator()(const Ts&... ts) const { + return std::forward_as_tuple(*this, ts...); + } + + template + constexpr std::index_sequence<1u + Idx...> offset_index(std::index_sequence) const { + return {}; + } + }; + constexpr streaming streaming_conditions_tuple{}; + constexpr streaming streaming_actions_tuple{}; + constexpr streaming streaming_expressions_tuple{}; + constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_serialized{}; + constexpr streaming streaming_identifier{}; + constexpr streaming streaming_identifiers{}; + constexpr streaming streaming_values_placeholders{}; + constexpr streaming streaming_table_column_names{}; + constexpr streaming streaming_non_generated_column_names{}; + constexpr streaming streaming_field_values_excluding{}; + constexpr streaming streaming_mapped_columns_expressions{}; + constexpr streaming streaming_column_constraints{}; + + // serialize and stream a tuple of condition expressions; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& conditions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(conditions, [&ss, &context](auto& c) { + ss << " " << serialize(c, context); + }); + return ss; + } + + // serialize and stream a tuple of action expressions; + // space-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, T, Ctx> tpl) { + const auto& actions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(actions, [&ss, &context, first = true](auto& action) mutable { + constexpr std::array sep = {" ", ""}; + ss << sep[std::exchange(first, false)] << serialize(action, context); + }); + return ss; + } + + // serialize and stream a tuple of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(arg, context); + }); + return ss; + } + + // serialize and stream multi_order_by arguments; + // comma-separated + template + std::ostream& operator<<( + std::ostream& ss, + std::tuple&, const std::tuple...>&, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize_order_by(arg, context); + }); + return ss; + } + + // serialize and stream a vector of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, C, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < args.size(); ++i) { + ss << sep[std::exchange(first, false)] << serialize(args[i], context); + } + return ss; + } + + // stream a vector of already serialized strings; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& strings = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < strings.size(); ++i) { + ss << sep[std::exchange(first, false)] << strings[i]; + } + return ss; + } + + // stream an identifier described by a variadic string pack, which is one of: + // 1. identifier + // 2. identifier, alias + // 3. qualifier, identifier, alias + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Strings...> tpl) { + stream_identifier(ss, tpl, streaming_identifier.offset_index(std::index_sequence_for{})); + return ss; + } + + // stream a container of identifiers described by a string or a tuple, which is one of: + // 1. identifier + // 1. tuple(identifier) + // 2. tuple(identifier, alias), pair(identifier, alias) + // 3. tuple(qualifier, identifier, alias) + // + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& identifiers = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + bool first = true; + for(auto& identifier: identifiers) { + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, identifier); + } + return ss; + } + + // stream placeholders as part of a values clause + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Ts...> tpl) { + const size_t& columnsCount = get<1>(tpl); + const ptrdiff_t& valuesCount = get<2>(tpl); + + if(!valuesCount || !columnsCount) { + return ss; + } + + std::string result; + result.reserve((1 + (columnsCount * 1) + (columnsCount * 2 - 2) + 1) * valuesCount + (valuesCount * 2 - 2)); + + constexpr std::array sep = {", ", ""}; + for(ptrdiff_t i = 0, first = true; i < valuesCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "("; + for(size_t i = 0, first = true; i < columnsCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "?"; + } + result += ")"; + } + ss << result; + return ss; + } + + // stream a table's column identifiers, possibly qualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table, const bool&> tpl) { + const auto& table = get<1>(tpl); + const bool& qualified = get<2>(tpl); + + table.for_each_column([&ss, &tableName = qualified ? table.name : std::string{}, first = true]( + const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, tableName, column.name, std::string{}); + }); + return ss; + } -// #include "pragma.h" + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table> tpl) { + const auto& table = get<1>(tpl); + + table.template for_each_column_excluding( + [&ss, first = true](const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, column.name); + }); + return ss; + } -#include // std::string -#include -#include // std::function -#include // std::shared_ptr -#include // std::vector + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, PredFnCls, L, Ctx, Obj> tpl) { + using check_if_excluded = polyfill::remove_cvref_t>; + auto& excluded = get<2>(tpl); + auto& context = get<3>(tpl); + auto& object = get<4>(tpl); + using object_type = polyfill::remove_cvref_t; + auto& table = pick_table(context.db_objects); + + table.template for_each_column_excluding(call_as_template_base( + [&ss, &excluded, &context, &object, first = true](auto& column) mutable { + if(excluded(column)) { + return; + } -// #include "error_code.h" + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(column.member_pointer, object), context); + })); + return ss; + } -// #include "util.h" + // stream a tuple of mapped columns (which are member pointers or column pointers); + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& columns = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(columns, [&ss, &context, first = true](auto& colRef) mutable { + const std::string* columnName = find_column_name(context.db_objects, colRef); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } -// #include "row_extractor.h" + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, *columnName); + }); + return ss; + } -// #include "journal_mode.h" + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, + const column_constraints&, + const bool&, + Ctx> tpl) { + const auto& column = get<1>(tpl); + const bool& isNotNull = get<2>(tpl); + auto& context = get<3>(tpl); + + using constraints_type = constraints_type_t>; + constexpr size_t constraintsCount = std::tuple_size::value; + if(constraintsCount) { + std::vector constraintsStrings; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple(column.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context]( + auto& constraint) { + using constraint_type = std::decay_t; + constraintsStrings.push_back(serialize(constraint, context)); + if(is_primary_key_v) { + primaryKeyIndex = tupleIndex; + } else if(is_autoincrement_v) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); + } + for(auto& str: constraintsStrings) { + ss << str << ' '; + } + } + if(isNotNull) { + ss << "NOT NULL "; + } -// #include "connection_holder.h" + return ss; + } + } +} namespace sqlite_orm { @@ -11349,12 +13075,8 @@ namespace sqlite_orm { struct storage_base; template - int getPragmaCallback(void* data, int argc, char** argv, char**) { - auto& res = *(T*)data; - if(argc) { - res = row_extractor().extract(argv[0]); - } - return 0; + int getPragmaCallback(void* data, int argc, char** argv, char** x) { + return extract_single_value(data, argc, argv, x); } template<> @@ -11391,6 +13113,20 @@ namespace sqlite_orm { 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"); } @@ -11423,15 +13159,77 @@ namespace sqlite_orm { template std::vector integrity_check(T table_name) { - std::ostringstream oss; - oss << "integrity_check(" << table_name << ")"; - return this->get_pragma>(oss.str()); + std::ostringstream ss; + ss << "integrity_check(" << table_name << ")" << std::flush; + return this->get_pragma>(ss.str()); } std::vector integrity_check(int n) { - std::ostringstream oss; - oss << "integrity_check(" << n << ")"; - return this->get_pragma>(oss.str()); + std::ostringstream ss; + ss << "integrity_check(" << n << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + // will include generated columns in response as opposed to table_info + std::vector table_xinfo(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::vector result; + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + auto hidden = std::atoi(argv[index++]); + res.emplace_back(cid, move(name), move(type), notnull, 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, move(name), move(type), notnull, move(dflt_value), pk); + } + return 0; + }, + &result); + return result; } private: @@ -11444,16 +13242,9 @@ namespace sqlite_orm { template T get_pragma(const std::string& name) { auto connection = this->get_connection(); - auto query = "PRAGMA " + name; T result; - auto db = connection.get(); - auto rc = sqlite3_exec(db, query.c_str(), getPragmaCallback, &result, nullptr); - if(rc == SQLITE_OK) { - return result; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + return result; } /** @@ -11467,8 +13258,8 @@ namespace sqlite_orm { db = con.get(); } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value; - internal::perform_void_exec(db, ss.str()); + 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) { @@ -11477,14 +13268,14 @@ namespace sqlite_orm { db = con.get(); } std::stringstream ss; - ss << "PRAGMA " << name << " = " << internal::to_string(value); - internal::perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; + perform_void_exec(db, ss.str()); } }; } } -// #include "limit_accesor.h" +// #include "limit_accessor.h" #include #include // std::map @@ -11497,10 +13288,10 @@ namespace sqlite_orm { namespace internal { - struct limit_accesor { + struct limit_accessor { using get_connection_t = std::function; - limit_accesor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + limit_accessor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} int length() { return this->get(SQLITE_LIMIT_LENGTH); @@ -11627,6 +13418,7 @@ namespace sqlite_orm { // #include "transaction_guard.h" #include // std::function +#include // std::move // #include "connection_holder.h" @@ -11639,6 +13431,9 @@ namespace sqlite_orm { * 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 { /** @@ -11651,26 +13446,35 @@ namespace sqlite_orm { std::function commit_func_, std::function rollback_func_) : connection(std::move(connection_)), - commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} + commit_func(move(commit_func_)), rollback_func(move(rollback_func_)) {} + + transaction_guard_t(transaction_guard_t&& other) : + commit_on_destroy(other.commit_on_destroy), connection(std::move(other.connection)), + commit_func(move(other.commit_func)), rollback_func(move(other.rollback_func)), + gotta_fire(other.gotta_fire) { + other.gotta_fire = false; + } - ~transaction_guard_t() { + ~transaction_guard_t() noexcept(false) { if(this->gotta_fire) { - if(!this->commit_on_destroy) { - this->rollback_func(); - } else { + 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->commit_func(); this->gotta_fire = false; + this->commit_func(); } /** @@ -11679,8 +13483,8 @@ namespace sqlite_orm { * in its destructor. */ void rollback() { - this->rollback_func(); this->gotta_fire = false; + this->rollback_func(); } protected: @@ -11692,23 +13496,17 @@ namespace sqlite_orm { } } -// #include "statement_finalizer.h" - -// #include "type_printer.h" - -// #include "tuple_helper/tuple_helper.h" - // #include "row_extractor.h" -// #include "util.h" - // #include "connection_holder.h" // #include "backup.h" #include +#include // std::system_error #include // std::string #include +#include // std::move, std::exchange // #include "error_code.h" @@ -11731,21 +13529,19 @@ namespace sqlite_orm { const std::string& zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), - to(to_), from(from_), holder(move(holder_)) { + holder(move(holder_)), to(to_), from(from_) { if(!this->handle) { - throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); + throw std::system_error{orm_error_code::failed_to_init_a_backup}; } } backup_t(backup_t&& other) : - handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { - other.handle = nullptr; - } + handle(std::exchange(other.handle, nullptr)), holder(move(other.holder)), to(other.to), + from(other.from) {} ~backup_t() { if(this->handle) { (void)sqlite3_backup_finish(this->handle); - this->handle = nullptr; } } @@ -11772,9 +13568,9 @@ namespace sqlite_orm { protected: sqlite3_backup* handle = nullptr; + std::unique_ptr holder; connection_ref to; connection_ref from; - std::unique_ptr holder; }; } } @@ -11784,7 +13580,10 @@ namespace sqlite_orm { // #include "values_to_tuple.h" #include -#include // std::get, std::tuple_element +#include // std::index_sequence, std::make_index_sequence +#include // std::tuple, std::tuple_size, std::get + +// #include "functional/cxx_universal.h" // #include "row_extractor.h" @@ -11873,7 +13672,7 @@ namespace sqlite_orm { if(this->index < int(this->container.size()) && this->index >= 0) { return this->currentValue; } else { - throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + throw std::system_error{orm_error_code::index_is_out_of_bounds}; } } @@ -11909,10 +13708,10 @@ namespace sqlite_orm { arg_value operator[](int index) const { if(index < this->argsCount && index >= 0) { - auto valuePointer = this->values[index]; - return {valuePointer}; + sqlite3_value* value = this->values[index]; + return {value}; } else { - throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + throw std::system_error{orm_error_code::index_is_out_of_bounds}; } } @@ -11938,32 +13737,34 @@ namespace sqlite_orm { namespace internal { - /** - * T is a std::tuple type - * I is index to extract value from values C array to tuple. I must me < std::tuple_size::value - */ - template struct values_to_tuple { - - void extract(sqlite3_value** values, T& tuple, int argsCount) const { - using element_type = typename std::tuple_element::type; - std::get(tuple) = row_extractor().extract(values[I]); - - values_to_tuple().extract(values, tuple, argsCount); + template + void operator()(sqlite3_value** values, Tpl& tuple, int /*argsCount*/) const { + (*this)(values, tuple, std::make_index_sequence::value>{}); } - }; - template - struct values_to_tuple { - void extract(sqlite3_value** values, T& tuple, int argsCount) const { - //.. + void operator()(sqlite3_value** values, std::tuple& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); } - }; - template<> - struct values_to_tuple, 0> { - void extract(sqlite3_value** values, std::tuple& tuple, int argsCount) const { - std::get<0>(tuple) = arg_values(argsCount, values); + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + (this->extract(values[Idx], std::get(tuple)), ...); + } +#else + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + this->extract(values[I], std::get(tuple)); + (*this)(values, tuple, std::index_sequence{}); + } + template + void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence) const {} +#endif + template + void extract(sqlite3_value* value, T& t) const { + t = row_extractor{}.extract(value); } }; } @@ -11971,6 +13772,10 @@ namespace sqlite_orm { // #include "arg_values.h" +// #include "util.h" + +// #include "serializing_util.h" + namespace sqlite_orm { namespace internal { @@ -11980,42 +13785,93 @@ namespace sqlite_orm { std::function on_open; pragma_t pragma; - limit_accesor limit; + limit_accessor limit; transaction_guard_t transaction_guard() { this->begin_transaction(); - auto commitFunc = std::bind(static_cast(&storage_base::commit), this); - auto rollbackFunc = std::bind(static_cast(&storage_base::rollback), this); - return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; + 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 '" << indexName + "'"; - perform_void_exec(get_connection().get(), ss.str()); + 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(get_connection().get(), "VACUUM"); + perform_void_exec(this->get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ void drop_table(const std::string& tableName) { - auto con = this->get_connection(); - this->drop_table_internal(tableName, con.get()); + 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 << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; - perform_void_exec(get_connection().get(), ss.str()); + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << streaming_identifier("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + perform_exec( + db, + ss.str(), + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; + if(argc) { + res = !!std::atoi(argv[0]); + } + return 0; + }, + &result); + return result; + } + + void add_generated_cols(std::vector& columnsToAdd, + const std::vector& storageTableInfo) { + // iterate through storage columns + for(const table_xinfo& storageColumnInfo: storageTableInfo) { + if(storageColumnInfo.hidden) { + columnsToAdd.push_back(&storageColumnInfo); + } + } } + public: /** * sqlite3_changes function. */ @@ -12043,21 +13899,15 @@ namespace sqlite_orm { } /** - * Returns libsqltie3 lib version, not sqlite_orm + * Returns libsqlite3 version, not sqlite_orm */ std::string libversion() { return sqlite3_libversion(); } bool transaction(const std::function& f) { - auto guard = transaction_guard(); - auto shouldCommit = f(); - if(shouldCommit) { - guard.commit(); - } else { - guard.rollback(); - } - return shouldCommit; + auto guard = this->transaction_guard(); + return guard.commit_on_destroy = f(); } std::string current_timestamp() { @@ -12086,28 +13936,20 @@ namespace sqlite_orm { std::vector table_names() { auto con = this->get_connection(); std::vector tableNames; - std::string sql = "SELECT name FROM sqlite_master WHERE type='table'"; using data_t = std::vector; - auto db = con.get(); - int res = sqlite3_exec( - db, - sql.c_str(), + 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++) { + for(int i = 0; i < argc; ++i) { if(argv[i]) { - tableNames_.push_back(argv[i]); + tableNames_.emplace_back(argv[i]); } } return 0; }, - &tableNames, - nullptr); - - if(res != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + &tableNames); return tableNames; } @@ -12134,21 +13976,22 @@ namespace sqlite_orm { * } * }; * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. */ template void create_scalar_function() { - static_assert(is_scalar_function::value, "F cannot be a scalar function"); + static_assert(is_scalar_function_v, "F can't be an aggregate function"); std::stringstream ss; - ss << F::name(); + ss << F::name() << std::flush; auto name = ss.str(); using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; - auto argsCount = int(std::tuple_size::value); - if(std::is_same>::value) { - argsCount = -1; - } - this->scalarFunctions.emplace_back(new scalar_function_t{ + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ move(name), argsCount, []() -> int* { @@ -12156,19 +13999,19 @@ namespace sqlite_orm { }, /* call = */ [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& functionPointer = *static_cast(functionVoidPointer); + auto& function = *static_cast(functionVoidPointer); args_tuple argsTuple; - using tuple_size = std::tuple_size; - values_to_tuple().extract(values, argsTuple, argsCount); - auto result = call(functionPointer, std::move(argsTuple)); + values_to_tuple{}(values, argsTuple, argsCount); + auto result = call(function, std::move(argsTuple)); statement_binder().result(context, result); }, delete_function_callback, }); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); - try_to_create_function(db, static_cast(*this->scalarFunctions.back())); + sqlite3* db = this->connection->get(); + try_to_create_function(db, + static_cast(*this->scalarFunctions.back())); } } @@ -12194,21 +14037,22 @@ namespace sqlite_orm { * } * }; * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. */ template void create_aggregate_function() { - static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + static_assert(is_aggregate_function_v, "F can't be a scalar function"); std::stringstream ss; - ss << F::name(); + ss << F::name() << std::flush; auto name = ss.str(); using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; - auto argsCount = int(std::tuple_size::value); - if(std::is_same>::value) { - argsCount = -1; - } - this->aggregateFunctions.emplace_back(new aggregate_function_t{ + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ move(name), argsCount, /* create = */ @@ -12216,25 +14060,26 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& functionPointer = *static_cast(functionVoidPointer); + [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast(functionVoidPointer); args_tuple argsTuple; - using tuple_size = std::tuple_size; - values_to_tuple().extract(values, argsTuple, argsCount); - call(functionPointer, &F::step, move(argsTuple)); + values_to_tuple{}(values, argsTuple, argsCount); + call(function, &F::step, move(argsTuple)); }, /* finalCall = */ [](sqlite3_context* context, void* functionVoidPointer) { - auto& functionPointer = *static_cast(functionVoidPointer); - auto result = functionPointer.fin(); + auto& function = *static_cast(functionVoidPointer); + auto result = function.fin(); statement_binder().result(context, result); }, delete_function_callback, }); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); - try_to_create_function(db, static_cast(*this->aggregateFunctions.back())); + sqlite3* db = this->connection->get(); + try_to_create_function( + db, + static_cast(*this->aggregateFunctions.back())); } } @@ -12243,11 +14088,10 @@ namespace sqlite_orm { */ template void delete_scalar_function() { - static_assert(is_scalar_function::value, "F cannot be a scalar function"); + static_assert(is_scalar_function_v, "F cannot be an aggregate function"); std::stringstream ss; - ss << F::name(); - auto name = ss.str(); - this->delete_function_impl(name, this->scalarFunctions); + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), this->scalarFunctions); } /** @@ -12255,11 +14099,10 @@ namespace sqlite_orm { */ template void delete_aggregate_function() { - static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + static_assert(is_aggregate_function_v, "F cannot be a scalar function"); std::stringstream ss; - ss << F::name(); - auto name = ss.str(); - this->delete_function_impl(name, this->aggregateFunctions); + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), this->aggregateFunctions); } template @@ -12269,32 +14112,29 @@ namespace sqlite_orm { return collatingObject(leftLength, lhs, rightLength, rhs); }; std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - this->create_collation(name, move(func)); + ss << C::name() << std::flush; + this->create_collation(ss.str(), move(func)); } void create_collation(const std::string& name, collating_function f) { - collating_function* functionPointer = nullptr; + collating_function* function = nullptr; const auto functionExists = bool(f); if(functionExists) { - functionPointer = &(collatingFunctions[name] = std::move(f)); + function = &(collatingFunctions[name] = std::move(f)); } else { collatingFunctions.erase(name); } // create collations if db is open if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); auto resultCode = sqlite3_create_collation(db, name.c_str(), SQLITE_UTF8, - functionPointer, + function, functionExists ? collate_callback : nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } } @@ -12302,36 +14142,41 @@ namespace sqlite_orm { template void delete_collation() { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - this->create_collation(name, {}); + ss << C::name() << std::flush; + this->create_collation(ss.str(), {}); } void begin_transaction() { - this->connection->retain(); - if(1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } - auto db = this->connection->get(); - perform_void_exec(db, "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() { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); perform_void_exec(db, "COMMIT"); this->connection->release(); if(this->connection->retain_count() < 0) { - throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + throw std::system_error{orm_error_code::no_active_transaction}; } } void rollback() { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); perform_void_exec(db, "ROLLBACK"); this->connection->release(); if(this->connection->retain_count() < 0) { - throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + throw std::system_error{orm_error_code::no_active_transaction}; } } @@ -12387,6 +14232,15 @@ namespace sqlite_orm { 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 = move(handler); if(this->is_opened()) { @@ -12401,11 +14255,12 @@ namespace sqlite_orm { } protected: - storage_base(const std::string& filename_, int foreignKeysCount) : + 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(filename_)), cachedForeignKeysCount(foreignKeysCount) { + inMemory(filename.empty() || filename == ":memory:"), + connection(std::make_unique(move(filename))), + cachedForeignKeysCount(foreignKeysCount) { if(this->inMemory) { this->connection->retain(); this->on_open_internal(this->connection->get()); @@ -12432,7 +14287,16 @@ namespace sqlite_orm { } } - public: + 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); + } + + public: connection_ref get_connection() { connection_ref res{*this->connection}; if(1 == this->connection->retain_count()) { @@ -12445,29 +14309,13 @@ namespace sqlite_orm { void foreign_keys(sqlite3* db, bool value) { std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value; + ss << "PRAGMA foreign_keys = " << value << std::flush; perform_void_exec(db, ss.str()); } bool foreign_keys(sqlite3* db) { - std::string query = "PRAGMA foreign_keys"; - auto result = false; - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char**) -> int { - auto& res = *(bool*)data; - if(argc) { - res = row_extractor().extract(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + bool result = false; + perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } @@ -12491,8 +14339,7 @@ namespace sqlite_orm { auto resultCode = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } @@ -12505,11 +14352,11 @@ namespace sqlite_orm { } for(auto& functionPointer: this->scalarFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + try_to_create_function(db, static_cast(*functionPointer)); } for(auto& functionPointer: this->aggregateFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + try_to_create_function(db, static_cast(*functionPointer)); } if(this->on_open) { @@ -12518,7 +14365,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector>& functionsVector) const { + std::vector>& functionsVector) const { auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { return functionPointer->name == name; }); @@ -12527,7 +14374,7 @@ namespace sqlite_orm { it = functionsVector.end(); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); auto resultCode = sqlite3_create_function_v2(db, name.c_str(), 0, @@ -12538,16 +14385,15 @@ namespace sqlite_orm { nullptr, nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } } else { - throw std::system_error(std::make_error_code(orm_error_code::function_not_found)); + throw std::system_error{orm_error_code::function_not_found}; } } - void try_to_create_function(sqlite3* db, scalar_function_t& function) { + void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { auto resultCode = sqlite3_create_function_v2(db, function.name.c_str(), function.argumentsCount, @@ -12558,12 +14404,11 @@ namespace sqlite_orm { nullptr, nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } - void try_to_create_function(sqlite3* db, aggregate_function_t& function) { + void try_to_create_function(sqlite3* db, user_defined_aggregate_function_t& function) { auto resultCode = sqlite3_create_function(db, function.name.c_str(), function.argumentsCount, @@ -12573,15 +14418,14 @@ namespace sqlite_orm { aggregate_function_step_callback, aggregate_function_final_callback); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(resultCode, get_sqlite_error_category()), - sqlite3_errstr(resultCode)); + throw_translated_sqlite_error(resultCode); } } static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto functionPointer = static_cast(functionVoidPointer); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); if(*aggregateContextIntPointer == nullptr) { @@ -12592,7 +14436,7 @@ namespace sqlite_orm { static void aggregate_function_final_callback(sqlite3_context* context) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto functionPointer = static_cast(functionVoidPointer); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); functionPointer->finalCall(context, *aggregateContextIntPointer); @@ -12601,11 +14445,11 @@ namespace sqlite_orm { static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto functionPointer = static_cast(functionVoidPointer); std::unique_ptr callablePointer(functionPointer->create(), functionPointer->destroy); if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { - throw std::system_error(std::make_error_code(orm_error_code::arguments_count_does_not_match)); + throw std::system_error{orm_error_code::arguments_count_does_not_match}; } functionPointer->run(context, functionPointer, argsCount, values); } @@ -12619,33 +14463,13 @@ namespace sqlite_orm { std::string current_timestamp(sqlite3* db) { std::string result; - std::stringstream ss; - ss << "SELECT CURRENT_TIMESTAMP"; - auto query = ss.str(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char**) -> int { - auto& res = *(std::string*)data; - if(argc) { - if(argv[0]) { - res = row_extractor().extract(argv[0]); - } - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); return result; } - void drop_table_internal(const std::string& tableName, sqlite3* db) { + void drop_table_internal(sqlite3* db, const std::string& tableName) { std::stringstream ss; - ss << "DROP TABLE '" << tableName + "'"; + ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } @@ -12663,14 +14487,44 @@ namespace sqlite_orm { } } - // returns foreign keys count in storage definition - template - static int foreign_keys_count(T& storageImpl) { - auto res = 0; - storageImpl.for_each([&res](auto& impl) { - res += impl.foreign_keys_count(); - }); - return res; + bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) const { + bool notEqual = false; + + // iterate through storage columns + for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto& columnName = storageColumnInfo.name; + + // search for a column in db eith the same name + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { + return ti.name == columnName; + }); + if(dbColumnInfoIt != dbTableInfo.end()) { + auto& dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && + dbColumnInfo.pk == storageColumnInfo.pk && + (dbColumnInfo.hidden == 0) == (storageColumnInfo.hidden == 0); + if(!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; } const bool inMemory; @@ -12679,8 +14533,8 @@ namespace sqlite_orm { std::map collatingFunctions; const int cachedForeignKeysCount; std::function _busy_handler; - std::vector> scalarFunctions; - std::vector> aggregateFunctions; + std::vector> scalarFunctions; + std::vector> aggregateFunctions; }; } } @@ -12702,24 +14556,16 @@ namespace sqlite_orm { struct expression_object_type; template - struct expression_object_type> { - using type = typename std::decay::type; - }; + struct expression_object_type> : std::decay {}; template - struct expression_object_type>> { - using type = typename std::decay::type; - }; + struct expression_object_type>> : std::decay {}; template - struct expression_object_type> { - using type = typename std::decay::type; - }; + struct expression_object_type> : std::decay {}; template - struct expression_object_type>> { - using type = typename std::decay::type; - }; + struct expression_object_type>> : std::decay {}; template struct expression_object_type> { @@ -12731,19 +14577,24 @@ namespace sqlite_orm { using type = typename replace_range_t, L, O>::object_type; }; - template - struct expression_object_type> { - using type = typename std::decay::type; + template + struct expression_object_type> { + using type = T; }; - template - struct expression_object_type>> { - using type = typename std::decay::type; + template + struct expression_object_type, Ids...>> { + using type = T; }; + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + template struct expression_object_type> { - using transformer_type = L; using type = typename insert_range_t::object_type; }; @@ -12753,14 +14604,10 @@ namespace sqlite_orm { }; template - struct expression_object_type> { - using type = typename std::decay::type; - }; + struct expression_object_type> : std::decay {}; template - struct expression_object_type, Cols...>> { - using type = typename std::decay::type; - }; + struct expression_object_type, Cols...>> : std::decay {}; template struct get_ref_t { @@ -12782,7 +14629,7 @@ namespace sqlite_orm { template auto& get_ref(T& t) { - using arg_type = typename std::decay::type; + using arg_type = std::decay_t; get_ref_t g; return g(t); } @@ -12795,7 +14642,7 @@ namespace sqlite_orm { template auto& get_object(T& t) { - using expression_type = typename std::decay::type; + using expression_type = std::decay_t; get_object_t obj; return obj(t); } @@ -12806,7 +14653,7 @@ namespace sqlite_orm { template auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; @@ -12816,7 +14663,7 @@ namespace sqlite_orm { template auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; @@ -12826,22 +14673,41 @@ namespace sqlite_orm { template auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; } } -// #include "statement_serializator.h" +// #include "statement_serializer.h" #include // std::stringstream #include // std::string #include // std::enable_if, std::remove_pointer #include // std::vector #include // std::iter_swap -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include +#include +// #include "functional/cxx_string_view.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + +// #include "ast/group_by.h" + +// #include "ast/into.h" // #include "core_functions.h" @@ -12851,10 +14717,22 @@ namespace sqlite_orm { // #include "column.h" +// #include "indexed_column.h" + +// #include "function.h" + +// #include "prepared_statement.h" + // #include "rowid.h" +// #include "pointer_value.h" + // #include "type_printer.h" +// #include "field_printer.h" + +// #include "literal.h" + // #include "table_name_collector.h" #include // std::set @@ -12862,6 +14740,10 @@ namespace sqlite_orm { #include // std::function #include // std::type_index +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + // #include "select_constraints.h" // #include "alias.h" @@ -12874,14 +14756,14 @@ namespace sqlite_orm { struct table_name_collector { using table_name_set = std::set>; - using find_table_name_t = std::function; + using find_table_name_t = std::function; find_table_name_t find_table_name; mutable table_name_set table_names; table_name_collector() = default; - table_name_collector(find_table_name_t _find_table_name) : find_table_name(std::move(_find_table_name)) {} + table_name_collector(find_table_name_t find_table_name) : find_table_name{move(find_table_name)} {} template table_name_set operator()(const T&) const { @@ -12890,16 +14772,12 @@ namespace sqlite_orm { template void operator()(F O::*, std::string alias = {}) const { - if(this->find_table_name) { - table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); - } + table_names.emplace(this->find_table_name(typeid(O)), move(alias)); } template void operator()(const column_pointer&) const { - if(this->find_table_name) { - table_names.insert({this->find_table_name(typeid(T)), ""}); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template @@ -12909,52 +14787,44 @@ namespace sqlite_orm { template void operator()(const count_asterisk_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - if(!tableName.empty()) { - table_names.insert(std::make_pair(move(tableName), "")); - } + auto tableName = this->find_table_name(typeid(T)); + if(!tableName.empty()) { + table_names.emplace(move(tableName), ""); } } - template + template = true> void operator()(const asterisk_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); + } + + template = true> + void operator()(const asterisk_t&) const { + // note: not all alias classes have a nested A::type + static_assert(polyfill::is_detected_v, + "alias must have a nested alias::type typename"); + auto tableName = this->find_table_name(typeid(type_t)); + table_names.emplace(move(tableName), alias_extractor::get()); } template void operator()(const object_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template void operator()(const table_rowid_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template void operator()(const table_oid_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template void operator()(const table__rowid_t&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } }; @@ -12964,63 +14834,90 @@ namespace sqlite_orm { // #include "column_names_getter.h" +#include // std::system_error #include // std::string #include // std::vector #include // std::reference_wrapper // #include "error_code.h" +// #include "serializer_context.h" + // #include "select_constraints.h" +// #include "serializing_util.h" + +// #include "util.h" + namespace sqlite_orm { namespace internal { - template - std::string serialize(const T& t, const C& context); + template + std::string serialize(const T& t, const serializer_context& context); template struct column_names_getter { using expression_type = T; - template - std::vector operator()(const expression_type& t, const C& context) { + template + std::vector operator()(const expression_type& t, const Ctx& context) const { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); - if(columnName.length()) { + if(!columnName.empty()) { return {move(columnName)}; } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } } }; - template - std::vector get_column_names(const T& t, const C& context) { - column_names_getter serializator; - return serializator(t, context); + template + std::vector get_column_names(const T& t, const Ctx& context) { + column_names_getter serializer; + return serializer(t, context); + } + + template + std::vector collect_table_column_names(bool definedOrder, const Ctx& context) { + if(definedOrder) { + std::vector quotedNames; + auto& table = pick_table>(context.db_objects); + quotedNames.reserve(table.count_columns_amount()); + table.for_each_column(["edNames](const column_identifier& column) { + if(std::is_base_of::value) { + quotedNames.push_back(quote_identifier(alias_extractor::get()) + "." + + quote_identifier(column.name)); + } else { + quotedNames.push_back(quote_identifier(column.name)); + } + }); + return quotedNames; + } else if(std::is_base_of::value) { + return {quote_identifier(alias_extractor::get()) + ".*"}; + } else { + return {"*"}; + } } template struct column_names_getter, void> { using expression_type = std::reference_wrapper; - template - std::vector operator()(const expression_type& expression, const C& context) { + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { return get_column_names(expression.get(), context); } }; template - struct column_names_getter, void> { + struct column_names_getter> { using expression_type = asterisk_t; - template - std::vector operator()(const expression_type&, const C&) { - std::vector res; - res.push_back("*"); - return res; + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return collect_table_column_names(expression.defined_order, context); } }; @@ -13028,11 +14925,9 @@ namespace sqlite_orm { struct column_names_getter, void> { using expression_type = object_t; - template - std::vector operator()(const expression_type&, const C&) { - std::vector res; - res.push_back("*"); - return res; + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return collect_table_column_names(expression.defined_order, context); } }; @@ -13040,19 +14935,14 @@ namespace sqlite_orm { struct column_names_getter, void> { using expression_type = columns_t; - template - std::vector operator()(const expression_type& cols, const C& context) { + template + std::vector operator()(const expression_type& cols, const Ctx& context) const { std::vector columnNames; columnNames.reserve(static_cast(cols.count)); auto newContext = context; newContext.skip_table_name = false; iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { - auto columnName = serialize(m, newContext); - if(columnName.length()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } + columnNames.push_back(serialize(m, newContext)); }); return columnNames; } @@ -13061,10 +14951,9 @@ namespace sqlite_orm { } } -// #include "order_by_serializator.h" +// #include "order_by_serializer.h" #include // std::string -#include // std::vector #include // std::stringstream namespace sqlite_orm { @@ -13072,76 +14961,68 @@ namespace sqlite_orm { namespace internal { template - struct order_by_serializator; + struct order_by_serializer; - template - std::string serialize_order_by(const T& t, const C& context) { - order_by_serializator serializator; - return serializator(t, context); + template + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer serializer; + return serializer(t, context); } template - struct order_by_serializator, void> { + struct order_by_serializer, void> { using statement_type = order_by_t; - template - std::string operator()(const statement_type& orderBy, const C& context) const { + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - auto columnName = serialize(orderBy.expression, newContext); - ss << columnName << " "; - if(orderBy._collate_argument.length()) { - ss << "COLLATE " << orderBy._collate_argument << " "; + + ss << serialize(orderBy.expression, newContext); + if(!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; } switch(orderBy.asc_desc) { case 1: - ss << "ASC"; + ss << " ASC"; break; case -1: - ss << "DESC"; + ss << " DESC"; break; } return ss.str(); } }; - template - struct order_by_serializator, void> { - using statement_type = dynamic_order_by_t; + template + struct order_by_serializer, void> { + using statement_type = dynamic_order_by_t; - template - std::string operator()(const statement_type& orderBy, const C&) const { - std::vector expressions; - for(auto& entry: orderBy) { - std::string entryString; - { - std::stringstream ss; - ss << entry.name << " "; - if(!entry._collate_argument.empty()) { - ss << "COLLATE " << entry._collate_argument << " "; - } - switch(entry.asc_desc) { - case 1: - ss << "ASC"; - break; - case -1: - ss << "DESC"; - break; - } - entryString = ss.str(); - } - expressions.push_back(move(entryString)); - }; + template + std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { + int index = 0; + for(const dynamic_order_by_entry_t& entry: orderBy) { + if(index > 0) { ss << ", "; } - } - 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(); } }; @@ -13149,122 +15030,198 @@ namespace sqlite_orm { } } -// #include "values.h" +// #include "serializing_util.h" -// #include "table_type.h" +// #include "statement_binder.h" -// #include "indexed_column.h" +// #include "values.h" -// #include "function.h" +// #include "triggers.h" -// #include "ast/upsert_clause.h" +// #include "table_type_of.h" -// #include "ast/excluded.h" +// #include "index.h" + +// #include "util.h" namespace sqlite_orm { namespace internal { template - struct statement_serializator; + struct statement_serializer; - template - std::string serialize(const T& t, const C& context) { - statement_serializator serializator; - return serializator(t, context); + template + std::string serialize(const T& t, const serializer_context& context) { + statement_serializer serializer; + return serializer(t, context); } + /** + * Serializer for bindable types. + */ template - struct statement_serializator::value>::type> { + struct statement_serializer> { using statement_type = T; - template - std::string operator()(const statement_type& statement, const C& context) { + template + std::string operator()(const T& statement, const Ctx& context) const { if(context.replace_bindable_with_question) { return "?"; } else { - return field_printer{}(statement); + return this->do_serialize(statement); } } + + private: + template && !std::is_base_of::value +#ifndef SQLITE_ORM_OMITS_CODECVT + && !std::is_base_of::value +#endif + , + bool> = true> + std::string do_serialize(const X& c) const { + static_assert(std::is_same::value, ""); + + // implementation detail: utilizing field_printer + return field_printer{}(c); + } + + std::string do_serialize(const std::string& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const char* c) const { + return quote_string_literal(c); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const wchar_t* c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c)); + } +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string do_serialize(const std::string_view& c) const { + return quote_string_literal(std::string(c)); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring_view& c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c.data(), c.data() + c.size())); + } +#endif +#endif + /** + * Specialization for binary data (std::vector). + */ + std::string do_serialize(const std::vector& t) const { + return quote_blob_literal(field_printer>{}(t)); + } + + template + std::string do_serialize(const pointer_binding&) const { + // always serialize null (security reasons) + return field_printer{}(nullptr); + } + }; + + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + + template + struct statement_serializer, void> { + using statement_type = filtered_aggregate_function; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + ss << serialize(statement.function, context); + ss << " FILTER (WHERE " << serialize(statement.where, context) << ")"; + return ss.str(); + } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = excluded_t; - template - std::string operator()(const statement_type& statement, const C& context) { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "excluded."; - if(auto columnNamePointer = context.impl.column_name(statement.expression)) { - ss << "\"" << *columnNamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, statement.expression)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = as_optional_t; - template - std::string operator()(const statement_type& statement, const C& context) { + 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_serializator, void> { + struct statement_serializer, void> { using statement_type = std::reference_wrapper; - template - std::string operator()(const statement_type& s, const C& context) { + template + std::string operator()(const statement_type& s, const Ctx& context) const { return serialize(s.get(), context); } }; - template<> - struct statement_serializator { - using statement_type = std::nullptr_t; - - template - std::string operator()(const statement_type&, const C&) { - return "?"; - } - }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<> - struct statement_serializator { - using statement_type = std::nullopt_t; - - template - std::string operator()(const statement_type&, const C&) { - return "?"; - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = alias_holder; - template - std::string operator()(const statement_type&, const C&) { - return T::get(); + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(T::get()); + return ss.str(); } }; template - struct statement_serializator, std::tuple>, void> { + struct statement_serializer, std::tuple>, void> { using statement_type = upsert_clause, std::tuple>; - template - std::string operator()(const statement_type& statement, const C& context) const { + 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 = typename std::decay::type; + using value_type = std::decay_t; auto needParenthesis = std::is_member_pointer::value; ss << ' '; if(needParenthesis) { @@ -13279,86 +15236,69 @@ namespace sqlite_orm { if(std::tuple_size::value == 0) { ss << " NOTHING"; } else { - ss << " UPDATE"; auto updateContext = context; updateContext.use_parentheses = false; - iterate_tuple(statement.actions, [&ss, &updateContext](auto& value) { - ss << ' ' << serialize(value, updateContext); - }); + ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); } return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = built_in_function_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << statement.serialize() << "("; - std::vector args; - using args_type = typename std::decay::type::args_type; - args.reserve(std::tuple_size::value); - iterate_tuple(statement.args, [&args, &context](auto& v) { - args.push_back(serialize(v, context)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } + if(context.use_parentheses) { + ss << '('; + } + ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + if(context.use_parentheses) { + ss << ')'; } - ss << ")"; return ss.str(); } }; + template + struct statement_serializer, void> + : statement_serializer, void> {}; + template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = function_call; - template - std::string operator()(const statement_type& statement, const C& context) const { - using args_tuple = std::tuple; - + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << F::name() << "("; - auto index = 0; - iterate_tuple(statement.args, [&context, &ss, &index](auto& v) { - auto value = serialize(v, context); - ss << value; - if(index < std::tuple_size::value - 1) { - ss << ", "; - } - ++index; - }); - ss << ")"; + ss << F::name() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = as_t; - template - std::string operator()(const statement_type& c, const C& context) const { - auto tableAliasString = alias_extractor::get(); - return serialize(c.expression, context) + " AS " + tableAliasString; + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.expression, context) + " AS " << streaming_identifier(alias_extractor::get()); + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = alias_column_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << T::get() << "'."; + ss << streaming_identifier(alias_extractor::get()) << "."; } auto newContext = context; newContext.skip_table_name = true; @@ -13367,92 +15307,64 @@ namespace sqlite_orm { } }; - template<> - struct statement_serializator { - using statement_type = std::string; - - template - std::string operator()(const statement_type& c, const C& context) const { - if(context.replace_bindable_with_question) { - return "?"; - } else { - return "\'" + c + "\'"; - } - } - }; - - template<> - struct statement_serializator { - using statement_type = const char*; - - template - std::string operator()(const char* c, const C& context) const { - if(context.replace_bindable_with_question) { - return "?"; - } else { - return std::string("'") + c + "'"; - } - } - }; - template - struct statement_serializator { + struct statement_serializer { using statement_type = F O::*; - template - std::string operator()(const statement_type& m, const C& context) const { + template + std::string operator()(const statement_type& m, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } - if(auto columnnamePointer = context.column_name(m)) { - ss << "\"" << *columnnamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, m)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = rowid_t; - template - std::string operator()(const statement_type& s, const C&) { + template + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast(s); } }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = oid_t; - template - std::string operator()(const statement_type& s, const C&) { + template + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast(s); } }; template<> - struct statement_serializator<_rowid_t, void> { + struct statement_serializer<_rowid_t, void> { using statement_type = _rowid_t; - template - std::string operator()(const statement_type& s, const C&) { + template + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast(s); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = table_rowid_t; - template - std::string operator()(const statement_type& s, const C& context) { + template + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(s); return ss.str(); @@ -13460,14 +15372,14 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = table_oid_t; - template - std::string operator()(const statement_type& s, const C& context) { + template + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(s); return ss.str(); @@ -13475,14 +15387,14 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = table__rowid_t; - template - std::string operator()(const statement_type& s, const C& context) { + template + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(s); return ss.str(); @@ -13490,18 +15402,18 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = binary_operator; - template - std::string operator()(const statement_type& c, const C& context) const { - auto lhs = serialize(c.lhs, context); - auto rhs = serialize(c.rhs, context); + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto lhs = serialize(statement.lhs, context); + auto rhs = serialize(statement.rhs, context); std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << lhs << " " << static_cast(c) << " " << rhs; + ss << lhs << " " << statement.serialize() << " " << rhs; if(context.use_parentheses) { ss << ')'; } @@ -13510,21 +15422,21 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = count_asterisk_t; - template - std::string operator()(const statement_type&, const C& context) const { + template + std::string operator()(const statement_type&, const Ctx& context) const { return serialize(count_asterisk_without_type{}, context); } }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = count_asterisk_without_type; - template - std::string operator()(const statement_type& c, const C&) const { + template + std::string operator()(const statement_type& c, const Ctx&) const { std::stringstream ss; auto functionName = c.serialize(); ss << functionName << "(*)"; @@ -13533,11 +15445,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = distinct_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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 << ")"; @@ -13546,11 +15458,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = all_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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 << ")"; @@ -13559,30 +15471,30 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = column_pointer; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } - if(auto columnNamePointer = context.impl.column_name_simple(c.field)) { - ss << "\"" << *columnNamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, c)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = cast_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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() << ")"; @@ -13591,12 +15503,11 @@ namespace sqlite_orm { }; template - struct statement_serializator::value>::type> { + struct statement_serializer>> { using statement_type = T; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast(c) << " "; @@ -13606,11 +15517,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = simple_case_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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_) { @@ -13629,11 +15540,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = is_null_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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(); @@ -13641,11 +15552,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = is_not_null_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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(); @@ -13653,25 +15564,25 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = bitwise_not_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = serialize(c.argument, context); + ss << statement.serialize() << " "; + auto cString = serialize(statement.argument, context); ss << " (" << cString << " )"; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = negated_condition_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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); @@ -13681,12 +15592,11 @@ namespace sqlite_orm { }; template - struct statement_serializator::value>::type> { + struct statement_serializer>> { using statement_type = T; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; @@ -13702,11 +15612,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = named_collate; - template - std::string operator()(const statement_type& c, const C& context) const { + 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); @@ -13715,11 +15625,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = collate_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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); @@ -13728,74 +15638,88 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = dynamic_in_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast(c) << " "; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + constexpr bool isCompoundOperator = is_base_of_template_v; + if(isCompoundOperator) { + ss << '('; + } auto newContext = context; newContext.use_parentheses = true; - ss << serialize(c.argument, newContext); + ss << serialize(statement.argument, newContext); + if(isCompoundOperator) { + ss << ')'; + } return ss.str(); } }; template - struct statement_serializator>, void> { + struct statement_serializer>, void> { using statement_type = dynamic_in_t>; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast(c) << " ("; - for(size_t index = 0; index < c.argument.size(); ++index) { - auto& value = c.argument[index]; - ss << serialize(value, context); - if(index < c.argument.size() - 1) { - ss << ", "; - } + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; } - ss << ")"; + ss << " (" << streaming_dynamic_expressions(statement.argument, context) << ")"; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = in_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast(c) << " ("; - std::vector args; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; using args_type = std::tuple; - args.reserve(std::tuple_size::value); - iterate_tuple(c.argument, [&args, &context](auto& v) { - args.push_back(serialize(v, context)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } + constexpr bool theOnlySelect = + std::tuple_size::value == 1 && is_select_v>; + if(!theOnlySelect) { + ss << "("; + } + ss << streaming_expressions_tuple(statement.argument, context); + if(!theOnlySelect) { + ss << ")"; } - ss << ")"; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = like_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; @@ -13808,11 +15732,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = glob_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; @@ -13822,11 +15746,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = between_t; - template - std::string operator()(const statement_type& c, const C& context) const { + 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) << " "; @@ -13838,157 +15762,143 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = exists_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast(c) << " "; - ss << serialize(c.t, context); + ss << "EXISTS "; + ss << serialize(statement.expression, context); return ss.str(); } }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = autoincrement_t; - template - std::string operator()(const statement_type& c, const C&) const { - return static_cast(c); + template + std::string operator()(const statement_type&, const Ctx&) const { + return "AUTOINCREMENT"; + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_clause_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_clause_t::rollback: + return "ROLLBACK"; + case conflict_clause_t::abort: + return "ABORT"; + case conflict_clause_t::fail: + return "FAIL"; + case conflict_clause_t::ignore: + return "IGNORE"; + case conflict_clause_t::replace: + return "REPLACE"; + } + return {}; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.primary_key, context) + " AUTOINCREMENT"; } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = primary_key_t; - template - std::string operator()(const statement_type& c, const C& context) const { - auto res = static_cast(c); + 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; - auto columnsCount = std::tuple_size::value; + const size_t columnsCount = std::tuple_size::value; if(columnsCount) { - res += "("; - decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { - if(auto columnNamePointer = context.column_name(column)) { - res += *columnNamePointer; - if(columnIndex < columnsCount - 1) { - res += ", "; - } - ++columnIndex; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - res += ")"; + ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } - return res; + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = unique_t; - template - std::string operator()(const statement_type& c, const C& context) const { - auto res = static_cast(c); + 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; - auto columnsCount = std::tuple_size::value; + const size_t columnsCount = std::tuple_size::value; if(columnsCount) { - res += "("; - decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { - if(auto columnNamePointer = context.column_name(column)) { - res += *columnNamePointer; - if(columnIndex < columnsCount - 1) { - res += ", "; - } - ++columnIndex; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - res += ")"; + ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } - return res; + return ss.str(); } }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = collate_constraint_t; - template - std::string operator()(const statement_type& c, const C&) const { + template + std::string operator()(const statement_type& c, const Ctx&) const { return static_cast(c); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = default_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { return static_cast(c) + " (" + serialize(c.value, context) + ")"; } }; template - struct statement_serializator, std::tuple>, void> { + struct statement_serializer, std::tuple>, void> { using statement_type = foreign_key_t, std::tuple>; - template - std::string operator()(const statement_type& fk, const C& context) const { + template + std::string operator()(const statement_type& fk, const Ctx& context) const { std::stringstream ss; - std::vector columnNames; - using columns_type_t = typename std::decay::type::columns_type; - constexpr const size_t columnsCount = std::tuple_size::value; - columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, &context](auto& v) { - if(auto columnNamePointer = context.impl.column_name(v)) { - columnNames.push_back(*columnNamePointer); - } else { - columnNames.push_back({}); - } - }); - ss << "FOREIGN KEY("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; - if(i < columnNames.size() - 1) { - ss << ", "; - } - } - ss << ") REFERENCES "; - std::vector referencesNames; - using references_type_t = typename std::decay::type::references_type; - constexpr const size_t referencesCount = std::tuple_size::value; - referencesNames.reserve(referencesCount); + ss << "FOREIGN KEY(" << streaming_mapped_columns_expressions(fk.columns, context) << ") REFERENCES "; { - using first_reference_t = typename std::tuple_element<0, references_type_t>::type; - using first_reference_mapped_type = typename internal::table_type::type; - auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); - ss << '\'' << refTableName << '\''; - } - iterate_tuple(fk.references, [&referencesNames, &context](auto& v) { - if(auto columnNamePointer = context.impl.column_name(v)) { - referencesNames.push_back(*columnNamePointer); - } else { - referencesNames.push_back({}); - } - }); - ss << "("; - for(size_t i = 0; i < referencesNames.size(); ++i) { - ss << "'" << referencesNames[i] << "'"; - if(i < referencesNames.size() - 1) { - ss << ", "; - } + 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 << ")"; + ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; if(fk.on_update) { ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; } @@ -14000,354 +15910,294 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = check_t; - template - std::string operator()(const statement_type& c, const C& context) const { - return static_cast(c) + " " + serialize(c.expression, context); + 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 - struct statement_serializator, void> { - using statement_type = column_t; - - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << "'" << c.name << "' "; - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - using constraints_type = typename column_type::constraints_type; - ss << type_printer().print() << " "; - { - std::vector constraintsStrings; - constexpr const size_t constraintsCount = std::tuple_size::value; - constraintsStrings.reserve(constraintsCount); - int primaryKeyIndex = -1; - int autoincrementIndex = -1; - int tupleIndex = 0; - iterate_tuple( - c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto& v) { - using constraint_type = typename std::decay::type; - constraintsStrings.push_back(serialize(v, context)); - if(is_primary_key::value) { - primaryKeyIndex = tupleIndex; - } else if(std::is_same::value) { - autoincrementIndex = tupleIndex; - } - ++tupleIndex; - }); - if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { - iter_swap(constraintsStrings.begin() + primaryKeyIndex, - constraintsStrings.begin() + autoincrementIndex); - } - for(auto& str: constraintsStrings) { - ss << str << ' '; - } + if(statement.full) { + ss << "GENERATED ALWAYS "; } - if(c.not_null()) { - ss << "NOT NULL "; + ss << "AS ("; + ss << serialize(statement.expression, context) << ")"; + switch(statement.storage) { + case basic_generated_always::storage_type::not_specified: + //.. + break; + case basic_generated_always::storage_type::virtual_: + ss << " VIRTUAL"; + break; + case basic_generated_always::storage_type::stored: + ss << " STORED"; + break; } return ss.str(); } }; +#endif + template + struct statement_serializer, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type& column, const Ctx& context) const { + using column_type = statement_type; + + std::stringstream ss; + ss << streaming_identifier(column.name) << " " << type_printer>().print() + << " " + << streaming_column_constraints( + call_as_template_base(polyfill::identity{})(column), + column.is_not_null(), + context); + return ss.str(); + } + }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = remove_all_t; - template - std::string operator()(const statement_type& rem, const C& context) const { - auto& tImpl = context.impl.template get_impl(); + 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 '" << tImpl.table.name << "' "; - iterate_tuple(rem.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); + ss << "DELETE FROM " << streaming_identifier(table.name) + << streaming_conditions_tuple(rem.conditions, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = replace_t; - template - std::string operator()(const statement_type& rep, const C& context) const { - return serialize_replace_range_impl(rep, context, 1); + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")" + << " VALUES (" + << streaming_field_values_excluding(check_if{}, + empty_callable(), // don't exclude + context, + get_ref(statement.object)) + << ")"; + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = insert_explicit; - template - std::string operator()(const statement_type& ins, const C& context) const { - constexpr const size_t colsCount = std::tuple_size>::value; + template + std::string operator()(const statement_type& ins, const Ctx& context) const { + constexpr size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = typename std::decay::type; + using expression_type = std::decay_t; using object_type = typename expression_object_type::type; - auto& tImpl = context.impl.template get_impl(); + auto& table = pick_table(context.db_objects); std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector columnNames; - columnNames.reserve(colsCount); - { - auto columnsContext = context; - columnsContext.skip_table_name = true; - iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto& m) { - auto columnName = serialize(m, columnsContext); - if(!columnName.empty()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - } - ss << "("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES ("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "?"; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } + 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_serializator, void> { + struct statement_serializer, void> { using statement_type = update_t; - template - std::string operator()(const statement_type& upd, const C& context) const { - using expression_type = typename std::decay::type; + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; using object_type = typename expression_object_type::type; - auto& tImpl = context.impl.template get_impl(); + auto& table = pick_table(context.db_objects); std::stringstream ss; - ss << "UPDATE '" << tImpl.table.name << "' SET"; - std::vector setColumnNames; - tImpl.table.for_each_column([&setColumnNames, &tImpl](auto& column) { - if(!column.template has>() && - !tImpl.table.exists_in_composite_primary_key(column)) { - setColumnNames.emplace_back(column.name); - } - }); - for(size_t i = 0; i < setColumnNames.size(); ++i) { - ss << " \"" << setColumnNames[i] << "\"" - << " = ?"; - if(i < setColumnNames.size() - 1) { - ss << ","; - } - } - ss << " WHERE"; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << " \"" << primaryKeyColumnNames[i] << "\"" - << " = ?"; - if(i < primaryKeyColumnNames.size() - 1) { - ss << " AND"; - } - } + ss << "UPDATE " << streaming_identifier(table.name) << " SET "; + table.template for_each_column_excluding>( + [&table, &ss, &context, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + ss << " WHERE "; + table.for_each_column( + [&table, &context, &ss, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(!column.template is() && !table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {" AND ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = set_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast(statement); - auto assignsCount = std::tuple_size::value; - decltype(assignsCount) assignIndex = 0; + ss << "SET "; auto leftContext = context; leftContext.skip_table_name = true; - iterate_tuple(statement.assigns, - [&ss, &context, &leftContext, &assignIndex, assignsCount](auto& value) { - ss << ' ' << serialize(value.lhs, leftContext); - ss << ' ' << static_cast(value) << ' '; - ss << serialize(value.rhs, context); - if(assignIndex < assignsCount - 1) { - ss << ","; - } - ++assignIndex; - }); + 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 - struct statement_serializator, Wargs...>, void> { + struct statement_serializer, Wargs...>, void> { using statement_type = update_all_t, Wargs...>; - template - std::string operator()(const statement_type& upd, const C& context) const { - std::stringstream ss; - ss << "UPDATE "; - table_name_collector collector([&context](std::type_index ti) { - return context.impl.find_table_name(ti); + template + std::string operator()(const statement_type& upd, const Ctx& context) const { + table_name_collector collector([&context](const std::type_index& ti) { + return find_table_name(context.db_objects, ti); }); iterate_ast(upd.set.assigns, collector); - if(!collector.table_names.empty()) { - if(collector.table_names.size() == 1) { - ss << " '" << collector.table_names.begin()->first << "' "; - ss << static_cast(upd.set) << " "; - std::vector setPairs; - auto leftContext = context; - leftContext.skip_table_name = true; - iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { - std::stringstream sss; - sss << serialize(asgn.lhs, leftContext); - sss << " " << static_cast(asgn) << " "; - sss << serialize(asgn.rhs, context) << " "; - setPairs.push_back(sss.str()); - }); - auto setPairsCount = setPairs.size(); - for(size_t i = 0; i < setPairsCount; ++i) { - ss << setPairs[i] << " "; - if(i < setPairsCount - 1) { - ss << ", "; - } - } - iterate_tuple(upd.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); - } - } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); - } - } - }; - template - std::string serialize_insert_range_impl(const T& /*statement*/, const C& context, const int valuesCount) { - using object_type = typename expression_object_type::type; - auto& tImpl = context.impl.template get_impl(); - - std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector columnNames; - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); - - tImpl.table.for_each_column([&columnNames, &compositeKeyColumnNames](auto& c) { - using table_type = typename std::decay::type; - if(table_type::is_without_rowid || !c.template has>()) { - auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } + if(collector.table_names.empty()) { + throw std::system_error{orm_error_code::no_tables_specified}; } - }); - const auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - } else { - ss << "DEFAULT "; - } - ss << "VALUES "; - if(columnNamesCount) { - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(collector.table_names.begin()->first) << " SET "; + { + std::vector setPairs; + setPairs.reserve(std::tuple_size::assigns_type>::value); + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { + std::stringstream sss; + sss << serialize(asgn.lhs, leftContext); + sss << ' ' << asgn.serialize() << ' '; + sss << serialize(asgn.rhs, context); + setPairs.push_back(sss.str()); + }); + ss << streaming_serialized(setPairs) << streaming_conditions_tuple(upd.conditions, context); + return ss.str(); } - } else if(valuesCount != 1) { - throw std::system_error(std::make_error_code(orm_error_code::cannot_use_default_value)); } - - return ss.str(); - } + }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = insert_t; - template - std::string operator()(const statement_type& statement, const C& context) const { - return serialize_insert_range_impl(statement, context, 1); + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES"; + if(columnNamesCount) { + ss << " (" + << streaming_field_values_excluding( + mpl::conjunction>, + mpl::disjunction_fn>{}, + [&table](auto& column) { + return table.exists_in_composite_primary_key(column); + }, + context, + get_ref(statement.object)) + << ")"; + } + + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = into_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + std::stringstream ss; - auto& tImpl = context.impl.template get_impl(); - ss << "INTO " << tImpl.table.name; + ss << "INTO " << streaming_identifier(table.name); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = columns_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto index = 0; if(context.use_parentheses) { ss << '('; } - iterate_tuple(statement.columns, [&context, &ss, &index](auto& value) { - ss << serialize(value, context); - if(index < int(std::tuple_size>::value) - 1) { - ss << ", "; - } - ++index; - }); + ss << streaming_expressions_tuple(statement.columns, context); if(context.use_parentheses) { ss << ')'; } @@ -14356,28 +16206,26 @@ namespace sqlite_orm { }; template - struct statement_serializator< - T, - typename std::enable_if::value || is_replace_raw::value>::type> { + struct statement_serializer, is_replace_raw>>> { using statement_type = T; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if(is_insert_raw::value) { + if(is_insert_raw_v) { ss << "INSERT"; } else { ss << "REPLACE"; } iterate_tuple(statement.args, [&context, &ss](auto& value) { - using value_type = typename std::decay::type; + using value_type = std::decay_t; ss << ' '; - if(is_columns::value) { + if(is_columns_v) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if(is_values::value || is_select::value) { + } else if(is_values_v || is_select_v) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -14390,256 +16238,238 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = remove_t; - template - std::string operator()(const statement_type&, const C& context) const { - auto& tImpl = context.impl.template get_impl(); + 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 '" << tImpl.table.name << "' "; - ss << "WHERE "; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; + 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 - std::string serialize_replace_range_impl(const T& rep, const C& context, const int valuesCount) { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - auto& tImpl = context.impl.template get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { - ss << " \"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ++columnIndex; - }); - ss << " VALUES "; - auto valuesString = [columnsCount] { - std::stringstream ss_; - ss_ << "("; - for(auto i = 0; i < columnsCount; ++i) { - ss_ << "?"; - if(i < columnsCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); - } - template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = replace_range_t; - template - std::string operator()(const statement_type& rep, const C& context) const { - auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); - return serialize_replace_range_impl(rep, context, valuesCount); + template + std::string operator()(const statement_type& rep, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")"; + const auto valuesCount = std::distance(rep.range.first, rep.range.second); + const auto columnsCount = table.non_generated_columns_count(); + ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = insert_range_t; - template - std::string operator()(const statement_type& statement, const C& context) const { - const auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); - return serialize_insert_range_impl(statement, context, valuesCount); + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t valuesCount = std::distance(statement.range.first, statement.range.second); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES "; + if(columnNamesCount) { + ss << streaming_values_placeholders(columnNamesCount, valuesCount); + } else if(valuesCount != 1) { + throw std::system_error{orm_error_code::cannot_use_default_value}; + } + return ss.str(); } }; - template - std::string serialize_get_all_impl(const T& get, const C& context) { - using primary_type = typename T::type; + template + std::string serialize_get_all_impl(const T& get, const Ctx& context) { + using primary_type = type_t; table_name_collector collector; - collector.table_names.insert( - std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); - iterate_ast(get.conditions, collector); + collector.table_names.emplace(lookup_table_name(context.db_objects), ""); + // note: not collecting table names from get.conditions; + + auto& table = pick_table(context.db_objects); std::stringstream ss; - ss << "SELECT"; - auto& tImpl = context.impl.template get_impl(); - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount, &tImpl](auto& column) { - ss << " \"" << tImpl.table.name << "\"." - << "\"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ","; - } - ++columnIndex; - }); - ss << " FROM "; - std::vector> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto& tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; - } - ss << " "; + ss << "SELECT " << streaming_table_column_names(table, true); + if(!collector.table_names.empty()) { + ss << " FROM " << streaming_identifiers(collector.table_names); } - iterate_tuple(get.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); + ss << streaming_conditions_tuple(get.conditions, context); return ss.str(); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = get_all_optional_t; - template - std::string operator()(const statement_type& get, const C& context) const { + 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_serializator, void> { + struct statement_serializer, void> { using statement_type = get_all_pointer_t; - template - std::string operator()(const statement_type& get, const C& context) const { + template + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_all_impl(get, context); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = get_all_t; - template - std::string operator()(const statement_type& get, const C& context) const { + 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 C& context) { - using primary_type = typename T::type; - auto& tImpl = context.impl.template get_impl(); + 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"; - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { - ss << " \"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ", "; - } - ++columnIndex; - }); - ss << " FROM '" << tImpl.table.name << "' WHERE "; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - if(!primaryKeyColumnNames.empty()) { - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << " \"" << primaryKeyColumnNames[i] << "\"" - << " = ?"; - if(i < primaryKeyColumnNames.size() - 1) { - ss << " AND"; - } + ss << "SELECT " << streaming_table_column_names(table, false) << " FROM " + << streaming_identifier(table.name) << " WHERE "; + + auto primaryKeyColumnNames = table.primary_key_column_names(); + if(primaryKeyColumnNames.empty()) { + throw std::system_error{orm_error_code::table_has_no_primary_key_column}; + } + + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + if(i > 0) { + ss << " AND "; } - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); + ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } + return ss.str(); } template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = get_t; - template - std::string operator()(const statement_type& get, const C& context) const { + template + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_impl(get, context); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = get_pointer_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { return serialize_get_impl(statement, context); } }; template<> - struct statement_serializator { - using statement_type = insert_constraint; + struct statement_serializer { + using statement_type = conflict_action; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx&) const { switch(statement) { - case insert_constraint::abort: - return "OR ABORT"; - case insert_constraint::fail: - return "OR FAIL"; - case insert_constraint::ignore: - return "OR IGNORE"; - case insert_constraint::replace: - return "OR REPLACE"; - case insert_constraint::rollback: - return "OR ROLLBACK"; + case conflict_action::replace: + return "REPLACE"; + case conflict_action::abort: + return "ABORT"; + case conflict_action::fail: + return "FAIL"; + case conflict_action::ignore: + return "IGNORE"; + case conflict_action::rollback: + return "ROLLBACK"; } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return "OR " + serialize(statement.action, context); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = get_optional_t; - template - std::string operator()(const statement_type& get, const C& context) const { + 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_serializator, void> { + struct statement_serializer, void> { using statement_type = select_t; - template - std::string operator()(const statement_type& sel, const C& context) const { + template + std::string operator()(const statement_type& sel, const Ctx& context) const { std::stringstream ss; - const auto isCompoundOperator = is_base_of_template::value; + constexpr bool isCompoundOperator = is_base_of_template_v; if(!isCompoundOperator) { if(!sel.highest_level && context.use_parentheses) { ss << "("; @@ -14649,49 +16479,29 @@ namespace sqlite_orm { if(get_distinct(sel.col)) { ss << static_cast(distinct(0)) << " "; } - auto columnNames = get_column_names(sel.col, context); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; - } - } - table_name_collector collector([&context](std::type_index ti) { - return context.impl.find_table_name(ti); + ss << streaming_serialized(get_column_names(sel.col, context)); + table_name_collector collector([&context](const std::type_index& ti) { + return find_table_name(context.db_objects, ti); }); - const auto explicitFromItemsCount = count_tuple, is_from>::value; + constexpr bool explicitFromItemsCount = count_tuple, is_from>::value; if(!explicitFromItemsCount) { iterate_ast(sel.col, collector); iterate_ast(sel.conditions, collector); join_iterator()([&collector, &context](const auto& c) { - using original_join_type = typename std::decay::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy::type; - auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + using original_join_type = typename std::decay_t::join_type::type; + using cross_join_type = mapped_type_proxy_t; + auto crossJoinedTableName = lookup_table_name(context.db_objects); auto tableAliasString = alias_extractor::get(); - std::pair tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); + std::pair tableNameWithAlias{std::move(crossJoinedTableName), + std::move(tableAliasString)}; collector.table_names.erase(tableNameWithAlias); }); if(!collector.table_names.empty() && !isCompoundOperator) { - ss << " FROM "; - std::vector> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto& tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "'"; - if(!tableNamePair.second.empty()) { - ss << ' ' << tableNamePair.second; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ", "; - } - } + ss << " FROM " << streaming_identifiers(collector.table_names); } } - iterate_tuple(sel.conditions, [&context, &ss](auto& v) { - ss << ' ' << serialize(v, context); - }); - if(!is_base_of_template::value) { + ss << streaming_conditions_tuple(sel.conditions, context); + if(!is_base_of_template_v) { if(!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -14701,11 +16511,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = indexed_column_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + 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()) { @@ -14720,7 +16530,7 @@ namespace sqlite_orm { ss << " ASC"; break; default: - throw std::system_error(std::make_error_code(orm_error_code::incorrect_order)); + throw std::system_error{orm_error_code::incorrect_order}; } } return ss.str(); @@ -14728,165 +16538,313 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = index_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { ss << "UNIQUE "; } - using elements_type = typename std::decay::type::elements_type; - using head_t = typename std::tuple_element<0, elements_type>::type::column_type; - using indexed_type = typename table_type::type; - ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" - << context.impl.find_table_name(typeid(indexed_type)) << "' ("; + using elements_type = typename std::decay_t::elements_type; + using head_t = typename std::tuple_element_t<0, elements_type>::column_type; + using indexed_type = table_type_of_t; + ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " + << streaming_identifier(lookup_table_name(context.db_objects)); std::vector columnNames; - iterate_tuple(statement.elements, [&columnNames, &context](auto& v) { - auto columnName = serialize(v, context); - columnNames.push_back(move(columnName)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; + std::string whereString; + iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { + using value_type = std::decay_t; + if(!is_where_v) { + auto newContext = context; + newContext.use_parentheses = false; + auto whereString = serialize(value, newContext); + columnNames.push_back(move(whereString)); + } else { + auto columnName = serialize(value, context); + whereString = move(columnName); } + }); + ss << " (" << streaming_serialized(columnNames) << ")"; + if(!whereString.empty()) { + ss << ' ' << whereString; } - ss << ")"; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = from_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type&, const Ctx& context) const { using tuple = std::tuple; std::stringstream ss; ss << "FROM "; - size_t index = 0; - iterate_tuple([&context, &ss, &index](auto* itemPointer) { - using mapped_type = typename std::remove_pointer::type; - - auto aliasString = alias_extractor::get(); - ss << "'" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) - << "'"; - if(aliasString.length()) { - ss << " '" << aliasString << "'"; - } - if(index < std::tuple_size::value - 1) { - ss << ", "; + iterate_tuple([&context, &ss, first = true](auto* item) mutable { + using from_type = std::remove_pointer_t; + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::get()); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = old_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "OLD."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = new_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "NEW."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = raise_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + switch(statement.type) { + case raise_t::type_t::ignore: + return "RAISE(IGNORE)"; + + case raise_t::type_t::rollback: + return "RAISE(ROLLBACK, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::abort: + return "RAISE(ABORT, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::fail: + return "RAISE(FAIL, " + serialize(statement.message, context) + ")"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_timing; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_timing::trigger_before: + return "BEFORE"; + case trigger_timing::trigger_after: + return "AFTER"; + case trigger_timing::trigger_instead_of: + return "INSTEAD OF"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_type::trigger_delete: + return "DELETE"; + case trigger_type::trigger_insert: + return "INSERT"; + case trigger_type::trigger_update: + return "UPDATE"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " " << serialize(statement.type, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_update_type_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " UPDATE OF " + << streaming_mapped_columns_expressions(statement.columns, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.type_base, context); + ss << " ON " << streaming_identifier(lookup_table_name(context.db_objects)); + if(statement.do_for_each_row) { + ss << " FOR EACH ROW"; + } + statement.container_when.apply([&ss, &context](auto& value) { + ss << " WHEN " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + + ss << "TRIGGER IF NOT EXISTS " << streaming_identifier(statement.name) << " " + << serialize(statement.base, context); + ss << " BEGIN "; + iterate_tuple(statement.elements, [&ss, &context](auto& element) { + using element_type = std::decay_t; + if(is_select_v) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(element, newContext); + } else { + ss << serialize(element, context); } - ++index; + ss << ";"; }); + ss << " END"; + return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = where_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + 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 << ") "; + ss << '(' << whereString << ')'; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = order_by_t; - template - std::string operator()(const statement_type& orderBy, const C& context) const { + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - auto orderByString = serialize_order_by(orderBy, context); - ss << orderByString << " "; + ss << serialize_order_by(orderBy, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = dynamic_order_by_t; - template - std::string operator()(const statement_type& orderBy, const CC& context) const { + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { return serialize_order_by(orderBy, context); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = multi_order_by_t; - template - std::string operator()(const statement_type& orderBy, const C& context) const { + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; - std::vector expressions; - iterate_tuple(orderBy.args, [&expressions, &context](auto& v) { - auto expression = serialize_order_by(v, context); - expressions.push_back(move(expression)); - }); - ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + ss << static_cast(orderBy) << " " << streaming_expressions_tuple(orderBy.args, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = cross_join_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << static_cast(c) << " "; - ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + ss << static_cast(c) << " " + << streaming_identifier(lookup_table_name(context.db_objects)); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = inner_join_t; - template - std::string operator()(const statement_type& l, const C& context) const { + template + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast(l) << " "; - auto aliasString = alias_extractor::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast(l) << " " + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::get()) + << serialize(l.constraint, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = on_t; - template - std::string operator()(const statement_type& t, const C& context) const { + template + std::string operator()(const statement_type& t, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -14896,109 +16854,102 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = join_t; - template - std::string operator()(const statement_type& l, const C& context) const { + template + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast(l) << " "; - auto aliasString = alias_extractor::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast(l) << " " + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = left_join_t; - template - std::string operator()(const statement_type& l, const C& context) const { + template + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast(l) << " "; - auto aliasString = alias_extractor::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast(l) << " " + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = left_outer_join_t; - template - std::string operator()(const statement_type& l, const C& context) const { + template + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast(l) << " "; - auto aliasString = alias_extractor::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast(l) << " " + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = natural_join_t; - template - std::string operator()(const statement_type& c, const C& context) const { + template + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << static_cast(c) << " "; - ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + ss << static_cast(c) << " " + << streaming_identifier(lookup_table_name(context.db_objects)); + 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_serializator, void> { + struct statement_serializer, void> { using statement_type = group_by_t; - template - std::string operator()(const statement_type& groupBy, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - std::vector expressions; auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(groupBy.args, [&expressions, &newContext](auto& v) { - auto expression = serialize(v, newContext); - expressions.push_back(expression); - }); - ss << static_cast(groupBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = having_t; - template - std::string operator()(const statement_type& hav, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - ss << static_cast(hav) << " "; - ss << serialize(hav.t, newContext) << " "; + ss << "HAVING " << serialize(statement.expression, newContext); return ss.str(); } }; @@ -15008,11 +16959,11 @@ namespace sqlite_orm { * OI - offset is implicit */ template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = limit_t; - template - std::string operator()(const statement_type& limt, const C& context) const { + template + std::string operator()(const statement_type& limt, const Ctx& context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; @@ -15038,73 +16989,50 @@ namespace sqlite_orm { }; template<> - struct statement_serializator { + struct statement_serializer { using statement_type = default_values_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type&, const Ctx&) const { return "DEFAULT VALUES"; } }; - template - struct statement_serializator, void> { - using statement_type = using_t; + template + struct statement_serializer, void> { + using statement_type = using_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + 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) + " )"; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = std::tuple; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << '('; - auto index = 0; - using TupleSize = std::tuple_size; - iterate_tuple(statement, [&context, &index, &ss](auto& value) { - ss << serialize(value, context); - if(index < TupleSize::value - 1) { - ss << ", "; - } - ++index; - }); - ss << ')'; + ss << '(' << streaming_expressions_tuple(statement, context) << ')'; return ss.str(); } }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = values_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << "VALUES "; - { - auto index = 0; - auto& tuple = statement.tuple; - using tuple_type = typename std::decay::type; - using TupleSize = std::tuple_size; - iterate_tuple(tuple, [&context, &index, &ss](auto& value) { - ss << serialize(value, context); - if(index < TupleSize::value - 1) { - ss << ", "; - } - ++index; - }); - } + ss << "VALUES " << streaming_expressions_tuple(statement.tuple, context); if(context.use_parentheses) { ss << ')'; } @@ -15113,37 +17041,26 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { + struct statement_serializer, void> { using statement_type = dynamic_values_t; - template - std::string operator()(const statement_type& statement, const C& context) const { + template + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << "VALUES "; - { - auto vectorSize = statement.vector.size(); - for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { - auto& value = statement.vector[index]; - ss << serialize(value, context); - if(index < vectorSize - 1) { - ss << ", "; - } - } - } + ss << "VALUES " << streaming_dynamic_expressions(statement.vector, context); if(context.use_parentheses) { ss << ')'; } return ss.str(); } }; - } } -// #include "table_name_collector.h" +// #include "triggers.h" // #include "object_from_column_builder.h" @@ -15151,134 +17068,168 @@ namespace sqlite_orm { // #include "column.h" +// #include "index.h" + +// #include "util.h" + +// #include "serializing_util.h" + namespace sqlite_orm { namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v = false; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_preparable_v().prepare(std::declval()))>> = true; + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. */ - template + template struct storage_t : storage_base { - using self = storage_t; - using impl_type = storage_impl; + using self = storage_t; + using db_objects_type = db_objects_tuple; /** * @param filename database filename. - * @param impl_ storage_impl head + * @param dbObjects db_objects_tuple */ - storage_t(const std::string& filename, impl_type impl_) : - storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} - - storage_t(const storage_t& other) : storage_base(other), impl(other.impl) {} - - protected: - impl_type impl; - - template - friend struct view_t; + storage_t(std::string filename, db_objects_type dbObjects) : + storage_base{move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} - template - friend struct dynamic_order_by_t; + private: + db_objects_type db_objects; - template - friend struct iterator_t; + /** + * 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 - friend struct serializator_context_builder; + template + void create_table(sqlite3* db, const std::string& tableName, const Table& table) { + using table_type = std::decay_t; + using context_t = serializer_context; - template - void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) { - using table_type = typename std::decay::type; std::stringstream ss; - ss << "CREATE TABLE '" << tableName << "' ( "; - auto elementsCount = tableImpl.table.elements_count; - auto index = 0; - using context_t = serializator_context; - context_t context{this->impl}; - iterate_tuple(tableImpl.table.elements, [elementsCount, &index, &ss, &context](auto& element) { - ss << serialize(element, context); - if(index < elementsCount - 1) { - ss << ", "; - } - index++; - }); - ss << ")"; - if(table_type::is_without_rowid) { + context_t context{this->db_objects}; + ss << "CREATE TABLE " << streaming_identifier(tableName) << " ( " + << streaming_expressions_tuple(table.elements, context) << ")"; + if(table_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } + ss.flush(); + perform_void_exec(db, ss.str()); + } + + /** + * Copies sourceTableName to another table with name: destinationTableName + * Performs INSERT INTO %destinationTableName% () SELECT %table.column_names% FROM %sourceTableName% + */ + template + void copy_table(sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const; + +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; perform_void_exec(db, ss.str()); } +#endif + + template + void drop_create_with_loss(sqlite3* db, const Table& table) { + // eliminated all transaction handling + this->drop_table_internal(db, table.name); + this->create_table(db, table.name, table); + } - template - void backup_table(sqlite3* db, const I& tableImpl, const std::vector& columnsToIgnore) { + 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 = tableImpl.table.name + "_backup"; - if(tableImpl.table_exists(backupTableName, db)) { + auto backupTableName = table.name + "_backup"; + if(this->table_exists(db, backupTableName)) { int suffix = 1; do { - std::stringstream stream; - stream << suffix; - auto anotherBackupTableName = backupTableName + stream.str(); - if(!tableImpl.table_exists(anotherBackupTableName, db)) { - backupTableName = anotherBackupTableName; + std::stringstream ss; + ss << suffix << std::flush; + auto anotherBackupTableName = backupTableName + ss.str(); + if(!this->table_exists(db, anotherBackupTableName)) { + backupTableName = move(anotherBackupTableName); break; } ++suffix; } while(true); } + this->create_table(db, backupTableName, table); - this->create_table(db, backupTableName, tableImpl); + this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); - tableImpl.copy_table(db, backupTableName, columnsToIgnore); + this->drop_table_internal(db, table.name); - this->drop_table_internal(tableImpl.table.name, db); - - tableImpl.rename_table(db, backupTableName, tableImpl.table.name); + this->rename_table(db, backupTableName, table.name); } template void assert_mapped_type() const { - using mapped_types_tuples = std::tuple; - static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); + using mapped_types_tuple = std::tuple; + static_assert(mpl::invoke_t, mapped_types_tuple>::value, + "type is not mapped to a storage"); } - template + template, + std::enable_if_t = true> + void assert_insertable_type() const {} + + template, + std::enable_if_t = true> void assert_insertable_type() const { - auto& tImpl = this->get_impl(); - using table_type = typename std::decay::type; - using elements_type = typename std::decay::type; - - using is_without_rowid = std::integral_constant; - - static_if( - [](auto&) {}, // all right. it's a "without_rowid" table - [](auto& tImpl) { // unfortunately, this static_assert's can't see an composite keys(( - std::ignore = tImpl; - static_assert( - count_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_tuple::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."); - })(tImpl); + 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_impl() const { - return this->impl.template get_impl(); + auto& get_table() const { + return pick_table(this->db_objects); } template - auto& get_impl() { - return this->impl.template get_impl(); + auto& get_table() { + return pick_table(this->db_objects); } public: @@ -15415,11 +17366,11 @@ namespace sqlite_orm { /** * Select * by id routine. - * throws std::system_error(orm_error_code::not_found, orm_error_category) if object not found with given + * throws std::system_error{orm_error_code::not_found} if object not found with given * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be * extracted. Must be specified explicitly. * @return Object of type O where id is equal parameter passed or throws - * `std::system_error(orm_error_code::not_found, orm_error_category)` if there is no object with such id. + * `std::system_error{orm_error_code::not_found}` if there is no object with such id. */ template O get(Ids... ids) { @@ -15454,7 +17405,7 @@ namespace sqlite_orm { */ template std::shared_ptr get_no_throw(Ids... ids) { - return std::shared_ptr(get_pointer(std::forward(ids)...)); + return std::shared_ptr(this->get_pointer(std::forward(ids)...)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -15474,7 +17425,7 @@ namespace sqlite_orm { * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count * @return Number of O object in table. */ - template::type> + template> int count(Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(), std::forward(args)...); @@ -15531,7 +17482,7 @@ namespace sqlite_orm { class O, class... Args, class Tuple = std::tuple, - typename sfinae = typename std::enable_if::value >= 1>::type> + std::enable_if_t::value >= 1, bool> = true> std::string group_concat(F O::*m, Args&&... args) { return this->group_concat_internal(m, {}, std::forward(args)...); } @@ -15564,7 +17515,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with max value or null if sqlite engine returned null. */ - template::type> + template> std::unique_ptr max(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::max(m), std::forward(args)...); @@ -15580,7 +17531,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with min value or null if sqlite engine returned null. */ - template::type> + template> std::unique_ptr min(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::min(m), std::forward(args)...); @@ -15596,7 +17547,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with sum value or null if sqlite engine returned null. */ - template::type> + template> std::unique_ptr sum(F O::*m, Args&&... args) { this->assert_mapped_type(); std::vector> rows = @@ -15634,57 +17585,60 @@ namespace sqlite_orm { * For a single column use `auto rows = storage.select(&User::id, where(...)); * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); */ - template::type> + template> std::vector select(T m, Args... args) { - static_assert(!is_base_of_template::value || + static_assert(!is_base_of_template_v || std::tuple_size>::value == 0, "Cannot use args with a compound operator"); auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); return this->execute(statement); } - template - typename std::enable_if::value, std::string>::type - dump(const T& preparedStatement) const { - using context_t = serializator_context; - context_t context{this->impl}; - return serialize(preparedStatement.t, context); + template = true> + std::string dump(const T& preparedStatement, bool parametrized = true) const { + return this->dump(preparedStatement.expression, parametrized); + } + + template, + std::enable_if_t && !is_mapped_v, bool> = true> + std::string dump(E&& expression, bool parametrized = false) const { + static_assert(is_preparable_v, "Expression must be a high-level statement"); + + decltype(auto) e2 = static_if>( + [](auto expression) -> auto{ + expression.highest_level = true; + return expression; + }, + [](const auto& expression) -> decltype(auto) { + return (expression); + })(std::forward(expression)); + using context_t = serializer_context; + context_t context{this->db_objects}; + context.replace_bindable_with_question = parametrized; + // just like prepare_impl() + context.skip_table_name = false; + return serialize(e2, context); } /** * Returns a string representation of object of a class mapped to the storage. * Type of string has json-like style. */ - template - typename std::enable_if::value, std::string>::type - dump(const O& o) { - auto& tImpl = this->get_impl(); + template = true> + std::string dump(const O& object) const { + auto& table = this->get_table(); std::stringstream ss; ss << "{ "; - using pair = std::pair; - std::vector pairs; - tImpl.table.for_each_column([&pairs, &o](auto& c) { - using column_type = typename std::decay::type; + table.for_each_column([&ss, &object, first = true](auto& column) mutable { + using column_type = std::decay_t; using field_type = typename column_type::field_type; - pair p{c.name, std::string()}; - if(c.member_pointer) { - p.second = field_printer()(o.*c.member_pointer); - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - p.second = field_printer()(valueHolder.value); - } - pairs.push_back(move(p)); + constexpr std::array sep = {", ", ""}; + + ss << sep[std::exchange(first, false)] << column.name << " : '" + << field_printer{}(polyfill::invoke(column.member_pointer, object)) << "'"; }); - for(size_t i = 0; i < pairs.size(); ++i) { - auto& p = pairs[i]; - ss << p.first << " : '" << p.second << "'"; - if(i < pairs.size() - 1) { - ss << ", "; - } else { - ss << " }"; - } - } + ss << " }"; return ss.str(); } @@ -15701,33 +17655,34 @@ namespace sqlite_orm { this->execute(statement); } - template - void replace_range(It from, It to) { - using O = typename std::iterator_traits::value_type; + template + void replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; this->assert_mapped_type(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::replace_range(from, to)); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); this->execute(statement); } - template - void replace_range(It from, It to, L transformer) { - this->assert_mapped_type(); + template + void replace_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::replace_range(from, to, std::move(transformer))); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); this->execute(statement); } template int insert(const O& o, columns_t cols) { - constexpr const size_t colsCount = std::tuple_size>::value; - static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); return int(this->execute(statement)); @@ -15742,11 +17697,8 @@ namespace sqlite_orm { int insert(const O& o) { this->assert_mapped_type(); this->assert_insertable_type(); - - return call_insert_impl_and_catch_constraint_failed([this, &o]() { - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); - }); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); } /** @@ -15823,32 +17775,29 @@ namespace sqlite_orm { this->execute(statement); } - template - void insert_range(It from, It to) { - using O = typename std::iterator_traits::value_type; + template + void insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; this->assert_mapped_type(); this->assert_insertable_type(); if(from == to) { return; } - - call_insert_impl_and_catch_constraint_failed([this, from, to]() { - auto statement = this->prepare(sqlite_orm::insert_range(from, to)); - this->execute(statement); - }); + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } - template - void insert_range(It from, It to, L transformer) { - this->assert_mapped_type(); - this->assert_insertable_type(); + template + void insert_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + this->assert_insertable_type(); if(from == to) { return; } - call_insert_impl_and_catch_constraint_failed([this, from, to, transformer = std::move(transformer)]() { - auto statement = this->prepare(sqlite_orm::insert_range(from, to, std::move(transformer))); - this->execute(statement); - }); + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } /** @@ -15858,8 +17807,8 @@ namespace sqlite_orm { template void rename_table(std::string name) { this->assert_mapped_type(); - auto& tImpl = this->get_impl(); - tImpl.table.name = move(name); + auto& table = this->get_table(); + table.name = move(name); } using storage_base::rename_table; @@ -15871,111 +17820,157 @@ namespace sqlite_orm { template const std::string& tablename() const { this->assert_mapped_type(); - auto& tImpl = this->get_impl(); - return tImpl.table.name; + auto& table = this->get_table(); + return table.name; } template - const std::string* column_name(F O::*memberPointer) const { - return this->impl.column_name(memberPointer); - } - - protected: - template - sync_schema_result schema_status(const storage_impl, Tss...>&, sqlite3*, bool) { - return sync_schema_result::already_in_sync; + [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* + column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); } - template - sync_schema_result schema_status(const storage_impl, Tss...>& tImpl, - sqlite3* db, - bool preserve) { - return tImpl.schema_status(db, preserve); + template + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); } - template - sync_schema_result sync_table(const storage_impl, Tss...>& tableImpl, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; - using context_t = serializator_context; - context_t context{this->impl}; - auto query = serialize(tableImpl.table, context); - perform_void_exec(db, query); - return res; + protected: + template + sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; } - template - sync_schema_result sync_table(const storage_impl, Tss...>& tImpl, - sqlite3* db, - bool preserve) { - auto res = sync_schema_result::already_in_sync; - - auto schema_stat = tImpl.schema_status(db, preserve); - if(schema_stat != decltype(schema_stat)::already_in_sync) { - if(schema_stat == decltype(schema_stat)::new_table_created) { - this->create_table(db, tImpl.table.name, tImpl); - res = decltype(res)::new_table_created; - } else { - if(schema_stat == sync_schema_result::old_columns_removed || - schema_stat == sync_schema_result::new_columns_added || - schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + template + sync_schema_result schema_status(const table_t& table, + sqlite3* db, + bool preserve, + bool* attempt_to_preserve) { + if(attempt_to_preserve) { + *attempt_to_preserve = true; + } - // get table info provided in `make_table` call.. - auto storageTableInfo = tImpl.table.get_table_info(); + auto dbTableInfo = this->pragma.table_xinfo(table.name); + auto res = sync_schema_result::already_in_sync; - // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(db, table.name); + if(!gottaCreateTable) { - // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); - tImpl.calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; - if(schema_stat == sync_schema_result::old_columns_removed) { + if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } - // extra table columns than storage columns - this->backup_table(db, tImpl, {}); - res = decltype(res)::old_columns_removed; + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(!dbTableInfo.empty()) { + // extra table columns than storage columns + if(!preserve) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + res = sync_schema_result::old_columns_removed; +#else + gottaCreateTable = true; +#endif + } else { + res = sync_schema_result::old_columns_removed; } - - if(schema_stat == sync_schema_result::new_columns_added) { - for(auto columnPointer: columnsToAdd) { - tImpl.add_column(*columnPointer, db); + } + } + if(gottaCreateTable) { + res = sync_schema_result::dropped_and_recreated; + } else { + if(!columnsToAdd.empty()) { + // extra storage columns than table columns + for(const table_xinfo* colInfo: columnsToAdd) { + const basic_generated_always::storage_type* generatedStorageType = + table.find_column_generated_storage_type(colInfo->name); + if(generatedStorageType) { + if(*generatedStorageType == basic_generated_always::storage_type::stored) { + gottaCreateTable = true; + break; + } + // fallback cause VIRTUAL can be added + } else { + if(colInfo->notnull && colInfo->dflt_value.empty()) { + gottaCreateTable = true; + // no matter if preserve is true or false, there is no way to preserve data, so we wont try! + if(attempt_to_preserve) { + *attempt_to_preserve = false; + }; + break; + } } - res = decltype(res)::new_columns_added; } - - if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { - - // remove extra columns - this->backup_table(db, tImpl, columnsToAdd); - res = decltype(res)::new_columns_added_and_old_columns_removed; + if(!gottaCreateTable) { + if(res == sync_schema_result::old_columns_removed) { + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } else { + res = sync_schema_result::new_columns_added; + } + } else { + res = sync_schema_result::dropped_and_recreated; + } + } else { + if(res != sync_schema_result::old_columns_removed) { + res = sync_schema_result::already_in_sync; } - } else if(schema_stat == sync_schema_result::dropped_and_recreated) { - this->drop_table_internal(tImpl.table.name, db); - this->create_table(db, tImpl.table.name, tImpl); - res = decltype(res)::dropped_and_recreated; } } + } else { + res = sync_schema_result::new_table_created; } return res; } + template + sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; + using context_t = serializer_context; + context_t context{this->db_objects}; + auto query = serialize(index, context); + perform_void_exec(db, query); + return res; + } + + template + sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; // TODO Change accordingly + using context_t = serializer_context; + context_t context{this->db_objects}; + perform_void_exec(db, serialize(trigger, context)); + return res; + } + + template = true> + sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + + template + void add_column(sqlite3* db, const std::string& tableName, const C& column) const { + using context_t = serializer_context; + + context_t context{this->db_objects}; + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) + << std::flush; + perform_void_exec(db, ss.str()); + } + template prepared_statement_t prepare_impl(S statement) { - auto con = this->get_connection(); - sqlite3_stmt* stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; + using context_t = serializer_context; + context_t context{this->db_objects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto query = serialize(statement, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return prepared_statement_t{std::forward(statement), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + auto con = this->get_connection(); + sqlite3_stmt* stmt = prepare_stmt(con.get(), serialize(statement, context)); + return prepared_statement_t{std::forward(statement), stmt, con}; } public: @@ -15991,16 +17986,16 @@ namespace sqlite_orm { * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE * ... ADD COLUMN ...' command * * if there is any column existing in both db and storage but differs by any of - * properties/constraints (type, pk, notnull, dflt_value) table will be dropped and recreated Be aware that + * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db * schema the same as you specified in `make_storage` function call. A good point is that if you have no db * file at all it will be created and all tables also will be created with exact tables and columns you - * specified in `make_storage`, `make_table` and `make_column` call. The best practice is to call this + * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this * function right after storage creation. - * @param preserve affects on function behaviour in case it is needed to remove a column. If it is `false` - * so table will be dropped if there is column to remove, if `true` - table is being copied into another - * table, dropped and copied table is renamed with source table name. Warning: sync_schema doesn't check - * foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and rmeove column if SQLite version >= 3.35.0, + * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. + * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please * submit an issue https://github.com/fnc12/sqlite_orm/issues * @return std::map with std::string key equal table name and `sync_schema_result` as value. * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` @@ -16009,10 +18004,9 @@ namespace sqlite_orm { std::map sync_schema(bool preserve = false) { auto con = this->get_connection(); std::map result; - auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto& storageImpl) { - auto res = this->sync_table(storageImpl, db, preserve); - result.insert({storageImpl.table.name, res}); + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->sync_table(schemaObject, db, preserve); + result.emplace(schemaObject.name, status); }); return result; } @@ -16025,23 +18019,14 @@ namespace sqlite_orm { std::map sync_schema_simulate(bool preserve = false) { auto con = this->get_connection(); std::map result; - auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { - auto schemaStatus = this->schema_status(tableImpl, db, preserve); - result.insert({tableImpl.table.name, schemaStatus}); + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); + result.emplace(schemaObject.name, status); }); return result; } - /** - * 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->impl.table_exists(tableName, con.get()); - } + using storage_base::table_exists; // now that it is in storage_base make it into overload set template prepared_statement_t> prepare(select_t sel) { @@ -16110,23 +18095,25 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(remove_t rem) { - return prepare_impl>(std::move(rem)); + prepared_statement_t> prepare(remove_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); } template - prepared_statement_t> prepare(insert_t ins) { - using object_type = typename expression_object_type::type; + prepared_statement_t> prepare(insert_t statement) { + using object_type = typename expression_object_type::type; this->assert_mapped_type(); this->assert_insertable_type(); - return prepare_impl>(std::move(ins)); + return this->prepare_impl>(std::move(statement)); } template prepared_statement_t> prepare(replace_t rep) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); - return prepare_impl>(std::move(rep)); + return this->prepare_impl>(std::move(rep)); } template @@ -16134,672 +18121,333 @@ namespace sqlite_orm { using object_type = typename expression_object_type::type; this->assert_mapped_type(); this->assert_insertable_type(); - return prepare_impl>(std::move(statement)); + return this->prepare_impl>(std::move(statement)); } template - prepared_statement_t> prepare(replace_range_t rep) { - return prepare_impl>(std::move(rep)); + prepared_statement_t> prepare(replace_range_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); } template prepared_statement_t> prepare(insert_explicit ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); - return prepare_impl>(std::move(ins)); + return this->prepare_impl>(std::move(ins)); } template void execute(const prepared_statement_t>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - auto index = 1; - iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{statement.stmt}); + perform_step(stmt); } template void execute(const prepared_statement_t>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - auto index = 1; - iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{stmt}); + perform_step(stmt); } template int64 execute(const prepared_statement_t>& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto& tImpl = this->get_impl(); - auto& o = statement.t.obj; - sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto& m) { - using column_type = typename std::decay::type; - using field_type = typename column_result_t::type; - const field_type* value = tImpl.table.template get_object_field_pointer(o, m); - if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); - return sqlite3_last_insert_rowid(db); + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + tuple_value_binder{stmt}( + statement.expression.columns.columns, + [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { + return table.object_field_value(object, memberPointer); + }); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } template::value || is_replace::value)>::type* = nullptr> + std::enable_if_t, is_replace_range>, bool> = true> void execute(const prepared_statement_t& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto& tImpl = this->get_impl(); - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - - auto processObject = [&index, &stmt, &tImpl, db](auto& o) { - tImpl.table.for_each_column([&](auto& c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bind_value = field_value_binder{stmt}](auto& object) mutable { + table.template for_each_column_excluding( + call_as_template_base([&bind_value, &object](auto& column) { + bind_value(polyfill::invoke(column.member_pointer, object)); + })); }; - static_if{}>( - [&processObject](auto& statement) { - auto& transformer = statement.t.transformer; - std::for_each( /// - statement.t.range.first, - statement.t.range.second, - [&processObject, &transformer](auto& object) { - auto& realObject = transformer(object); - processObject(realObject); - }); + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif }, - [&processObject](auto& statement) { - auto& o = get_object(statement.t); + [&processObject](auto& expression) { + const object_type& o = get_object(expression); processObject(o); - })(statement); - perform_step(db, stmt); + })(statement.expression); + + perform_step(stmt); } - template::value || is_insert::value)>::type* = nullptr> + template, is_insert_range>, bool> = true> int64 execute(const prepared_statement_t& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto& tImpl = this->get_impl(); - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); - sqlite3_reset(stmt); - auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) { - tImpl.table.for_each_column([&](auto& c) { - using table_type = typename std::decay::type; - if(table_type::is_without_rowid || !c.template has>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != - statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bind_value = field_value_binder{stmt}](auto& object) mutable { + using is_without_rowid = typename std::decay_t::is_without_rowid; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + call_as_template_base([&table, &bind_value, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } - } - }); + })); }; - static_if{}>( - [&processObject](auto& statement) { - auto& transformer = statement.t.transformer; - std::for_each( /// - statement.t.range.first, - statement.t.range.second, - [&processObject, &transformer](auto& object) { - auto& realObject = transformer(object); - processObject(realObject); - }); + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif }, - [&processObject](auto& statement) { - auto& o = get_object(statement.t); + [&processObject](auto& expression) { + const object_type& o = get_object(expression); processObject(o); - })(statement); + })(statement.expression); - perform_step(db, stmt); - return sqlite3_last_insert_rowid(db); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } template void execute(const prepared_statement_t>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay::type; - if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + perform_step(stmt); } template void execute(const prepared_statement_t>& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto con = this->get_connection(); - auto db = con.get(); - auto& tImpl = this->get_impl(); - auto stmt = statement.stmt; - auto index = 1; - auto& o = get_object(statement.t); - sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { - if(!column.template has>() && - !tImpl.table.exists_in_composite_primary_key(column)) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(column.member_pointer) { - auto bind_res = - statement_binder().bind(stmt, index++, o.*column.member_pointer); - if(SQLITE_OK != bind_res) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(column.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - }); - tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { - if(column.template has>() || tImpl.table.exists_in_composite_primary_key(column)) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(column.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*column.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(column.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto& table = this->get_table(); + + field_value_binder bind_value{stmt}; + auto& object = get_object(statement.expression); + table.template for_each_column_excluding>( + call_as_template_base([&table, &bind_value, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } + })); + table.for_each_column([&table, &bind_value, &object](auto& column) { + if(column.template is() || table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } }); - perform_step(db, stmt); + perform_step(stmt); } template std::unique_ptr execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay::type; - if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::unique_ptr res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + res = std::make_unique(); + object_from_column_builder builder{*res, stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto res = std::make_unique(); - object_from_column_builder builder{*res, stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - return {}; - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + return res; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template std::optional execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay::type; - if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto res = std::make_optional(); - object_from_column_builder builder{res.value(), stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - return {}; - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template T execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay::type; - if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - T res; - object_from_column_builder builder{res, stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - throw std::system_error(std::make_error_code(sqlite_orm::orm_error_code::not_found)); - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + if(!res.has_value()) { + throw std::system_error{orm_error_code::not_found}; } + return move(res).value(); +#else + auto& table = this->get_table(); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error{orm_error_code::not_found}; + } break; + default: { + throw_translated_sqlite_error(stmt); + } + } +#endif } template void execute(const prepared_statement_t>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + perform_step(stmt); } template void execute(const prepared_statement_t, Wargs...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto& setArg) { - iterate_ast(setArg, [&index, stmt, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - }); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + conditional_binder bind_node{stmt}; + iterate_tuple(statement.expression.set.assigns, [&bind_node](auto& setArg) { + iterate_ast(setArg, bind_node); }); - perform_step(db, stmt); + iterate_ast(statement.expression.conditions, bind_node); + perform_step(stmt); } - template::type> + template> std::vector execute(const prepared_statement_t>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + std::vector res; - auto tableInfoPointer = this->impl.template find_table(); - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - using table_info_pointer_t = typename std::remove_pointer::type; - using table_info_t = typename std::decay::type; - row_extractor_builder::value, table_info_t> - builder; - auto rowExtractor = builder(tableInfoPointer); - res.push_back(rowExtractor.extract(stmt, 0)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, + [rowExtractor = make_row_extractor(lookup_table(this->db_objects)), + &res](sqlite3_stmt* stmt) { + res.push_back(rowExtractor.extract(stmt, 0)); + }); return res; } template R execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - T obj; - object_from_column_builder builder{obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(std::move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + T obj; + object_from_column_builder builder{obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); return res; } template R execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto obj = std::make_unique(); - object_from_column_builder builder{*obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_unique(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(move(obj)); + }); return res; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template R execute(const prepared_statement_t>& statement) { - auto& tImpl = this->get_impl(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay::type; - conditional_binder> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto obj = std::make_optional(); - object_from_column_builder builder{*obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); - return res; - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + sqlite3_stmt* stmt = reset_stmt(statement.stmt); - template - bool has_dependent_rows(const O& object) { - auto res = false; - this->impl.for_each([this, &object, &res](auto& storageImpl) { - if(res) { - return; - } - storageImpl.table.for_each_foreign_key([&storageImpl, this, &object, &res](auto& foreignKey) { - using ForeignKey = typename std::decay::type; - using TargetType = typename ForeignKey::target_type; - if(std::is_same::value) { - std::stringstream ss; - ss << "SELECT COUNT(*)"; - ss << " FROM " << storageImpl.table.name; - ss << " WHERE"; - auto columnIndex = 0; - iterate_tuple(foreignKey.columns, [&ss, &columnIndex, &storageImpl](auto& column) { - if(columnIndex > 0) { - ss << " AND"; - } - if(auto columnName = storageImpl.table.find_column_name(column)) { - ss << ' ' << *columnName << " = ?"; - } else { - throw std::system_error( - std::make_error_code(sqlite_orm::orm_error_code::column_not_found)); - } - ++columnIndex; - }); - auto query = ss.str(); - ss.flush(); - auto con = this->get_connection(); - sqlite3_stmt* stmt; - auto db = con.get(); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer(stmt); - columnIndex = 1; - iterate_tuple( - foreignKey.references, - [&columnIndex, stmt, &object, db](auto& memberPointer) { - using MemberPointer = typename std::decay::type; - using field_type = typename member_traits::field_type; - // if(column.member_pointer) { - if(SQLITE_OK != statement_binder().bind(stmt, - columnIndex++, - object.*memberPointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - /*} else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((object).*(column.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, columnIndex++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }*/ - }); - if(SQLITE_ROW != sqlite3_step(stmt)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - auto countResult = sqlite3_column_int(stmt, 0); - res = countResult > 0; - if(SQLITE_DONE != sqlite3_step(stmt)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_optional(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(move(obj)); }); return res; } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t - - template - struct is_storage : std::false_type {}; - - template - struct is_storage> : std::true_type {}; } - template - internal::storage_t make_storage(const std::string& filename, Ts... tables) { - return {filename, internal::storage_impl(std::forward(tables)...)}; + /* + * Factory function for a storage, from a database file and a bunch of database object definitions. + */ + template + internal::storage_t make_storage(std::string filename, DBO... dbObjects) { + return {move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; } /** @@ -16811,46 +18459,38 @@ namespace sqlite_orm { } #pragma once -#if defined(_MSC_VER) -#if defined(__RESTORE_MIN__) -__pragma(pop_macro("min")) -#undef __RESTORE_MIN__ -#endif -#if defined(__RESTORE_MAX__) - __pragma(pop_macro("max")) -#undef __RESTORE_MAX__ -#endif -#endif // defined(_MSC_VER) -#pragma once - #include // std::tuple #include // std::pair #include // std::reference_wrapper -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "conditions.h" + +// #include "operators.h" - // #include "conditions.h" +// #include "select_constraints.h" - // #include "operators.h" +// #include "prepared_statement.h" - // #include "select_constraints.h" +// #include "optional_container.h" - // #include "prepared_statement.h" +// #include "core_functions.h" - // #include "optional_container.h" +// #include "function.h" - // #include "core_functions.h" +// #include "ast/excluded.h" - // #include "function.h" +// #include "ast/upsert_clause.h" - // #include "ast/excluded.h" +// #include "ast/where.h" - // #include "ast/upsert_clause.h" +// #include "ast/into.h" - // #include "ast/where.h" +// #include "ast/group_by.h" - namespace sqlite_orm { +namespace sqlite_orm { namespace internal { @@ -16859,50 +18499,68 @@ __pragma(pop_macro("min")) using type = std::tuple; }; + template + using node_tuple_t = typename node_tuple::type; + template<> struct node_tuple { using type = std::tuple<>; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = typename node_tuple::type; + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple> {}; + + template + struct node_tuple, void> { + using args_tuple = node_tuple_t>; + using expression_tuple = node_tuple_t; + using type = tuple_cat_t; }; template - struct node_tuple, std::tuple>, void> { - using type = typename node_tuple>::type; - }; + struct node_tuple, std::tuple>, void> + : node_tuple> {}; template struct node_tuple, void> { - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template - struct node_tuple, void> { - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = where_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Literal + */ + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; template - struct node_tuple::value>::type> { + struct node_tuple>> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; }; template @@ -16910,276 +18568,217 @@ __pragma(pop_macro("min")) using node_type = binary_operator; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple::type; - using right_node_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = columns_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template struct node_tuple, void> { - using node_type = dynamic_in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = in_t; - using left_tuple = typename node_tuple::type; - using right_tuple = typename conc_tuple::type...>::type; - using type = typename conc_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = tuple_cat_t...>; + using type = tuple_cat_t; }; template - struct node_tuple::value>::type> { + struct node_tuple>> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = select_t; - using columns_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; - using type = typename conc_tuple::type; + using columns_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = insert_raw_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template struct node_tuple, void> { - using node_type = replace_raw_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template - struct node_tuple, void> { - using node_type = into_t; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = values_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template struct node_tuple, void> { - using node_type = std::tuple; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template struct node_tuple, void> { - using node_type = get_all_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template struct node_tuple, void> { - using node_type = get_all_pointer_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { - using node_type = get_all_optional_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, Wargs...>, void> { - using node_type = update_all_t, Wargs...>; - using set_tuple = typename conc_tuple::type...>::type; - using conditions_tuple = typename conc_tuple::type...>::type; - using type = typename conc_tuple::type; + using set_tuple = tuple_cat_t...>; + using conditions_tuple = tuple_cat_t...>; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = remove_all_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template - struct node_tuple, void> { - using node_type = having_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = cast_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = exists_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = optional_container; - using type = typename node_tuple::type; - }; - - template<> - struct node_tuple, void> { - using node_type = optional_container; - using type = std::tuple<>; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = like_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; - using escape_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using escape_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = glob_t; - using arg_tuple = typename node_tuple::type; - using pattern_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = between_t; - using expression_tuple = typename node_tuple::type; - using lower_tuple = typename node_tuple::type; - using upper_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using expression_tuple = node_tuple_t; + using lower_tuple = node_tuple_t; + using upper_tuple = node_tuple_t; + using type = tuple_cat_t; }; template - struct node_tuple, void> { - using node_type = named_collate; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = is_not_null_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = negated_condition_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = built_in_function_t; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = function_call; - using type = typename conc_tuple::type...>::type; + using type = tuple_cat_t...>; }; template - struct node_tuple, void> { - using node_type = left_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = on_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct node_tuple, void> : node_tuple> {}; template - struct node_tuple, void> { - using node_type = join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = left_outer_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = inner_join_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = simple_case_t; - using case_tuple = typename node_tuple::type; - using args_tuple = typename conc_tuple::type...>::type; - using else_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using case_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using else_tuple = node_tuple_t; + using type = tuple_cat_t; }; template struct node_tuple, void> { - using node_type = std::pair; - using left_tuple = typename node_tuple::type; - using right_tuple = typename node_tuple::type; - using type = typename conc_tuple::type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; }; template - struct node_tuple, void> { - using node_type = as_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using node_type = limit_t; - using type = typename node_tuple::type; - }; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = tuple_cat_t, node_tuple_t>; }; template struct node_tuple, void> { - using node_type = limit_t; - using type = typename conc_tuple::type, typename node_tuple::type>::type; + using type = tuple_cat_t, node_tuple_t>; }; } } @@ -17187,144 +18786,146 @@ __pragma(pop_macro("min")) #include // std::is_same, std::decay, std::remove_reference +// #include "functional/static_magic.h" + // #include "prepared_statement.h" // #include "ast_iterator.h" -// #include "static_magic.h" - // #include "expression_object_type.h" namespace sqlite_orm { template auto& get(internal::prepared_statement_t>& statement) { - return std::get(statement.t.range); + return std::get(statement.expression.range); } template const auto& get(const internal::prepared_statement_t>& statement) { - return std::get(statement.t.range); + return std::get(statement.expression.range); } template auto& get(internal::prepared_statement_t>& statement) { - return std::get(statement.t.range); + return std::get(statement.expression.range); } template const auto& get(const internal::prepared_statement_t>& statement) { - return std::get(statement.t.range); + return std::get(statement.expression.range); } template auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.t.ids)); + return internal::get_ref(std::get(statement.expression.ids)); } template auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.obj); } template const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.obj); } template auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template const auto& get(const internal::prepared_statement_t& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; - using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; - const result_tupe* result = nullptr; - auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto& node) { - using node_type = typename std::decay::type; - if(internal::is_bindable::value) { + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + const result_type* result = nullptr; + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { ++index; } if(index == N) { - internal::static_if{}>([](auto& r, auto& n) { - r = const_cast::type>(&n); - })(result, node); + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = &n; + }, + result, + node); } }); return internal::get_ref(*result); @@ -17332,24 +18933,407 @@ namespace sqlite_orm { template auto& get(internal::prepared_statement_t& statement) { - using statement_type = typename std::decay::type; + using statement_type = std::decay_t; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple::type; - using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element(N), bind_tuple>::type; - result_tupe* result = nullptr; - auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto& node) { - using node_type = typename std::decay::type; - if(internal::is_bindable::value) { + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + result_type* result = nullptr; + + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { ++index; } if(index == N) { - internal::static_if{}>([](auto& r, auto& n) { - r = const_cast::type>(&n); - })(result, node); + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = const_cast>(&n); + }, + result, + node); } }); return internal::get_ref(*result); } } +#pragma once + +/* + * Note: This feature needs constexpr variables with external linkage. + * which can be achieved before C++17's inline variables, but differs from compiler to compiler. + * Hence we make it only available for compilers supporting inline variables. + */ + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#include // std::integral_constant +#include // std::move + +// #include "functional/cxx_universal.h" + +// #include "pointer_value.h" + +namespace sqlite_orm { + + inline constexpr const char carray_pvt_name[] = "carray"; + using carray_pvt = std::integral_constant; + + template + using carray_pointer_arg = pointer_arg; + template + using carray_pointer_binding = pointer_binding; + template + using static_carray_pointer_binding = static_pointer_binding; + + /** + * Wrap a pointer of type 'carray' and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_carray_pointer(P* p, D d) noexcept -> pointer_binding { + return bindable_pointer(p, std::move(d)); + } + + /** + * Wrap a pointer of type 'carray' for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_carray_pointer(P* p) noexcept -> static_pointer_binding { + return statically_bindable_pointer(p); + } + + /** + * Generalized form of the 'remember' SQL function that is a pass-through for values + * (it returns its argument unchanged using move semantics) but also saves the + * value that is passed through into a bound variable. + */ + template + struct note_value_fn { + P operator()(P&& value, carray_pointer_arg

pv) const { + if(P* observer = pv) { + *observer = value; + } + return std::move(value); + } + + static constexpr const char* name() { + return "note_value"; + } + }; + + /** + * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c + */ + struct remember_fn : note_value_fn { + static constexpr const char* name() { + return "remember"; + } + }; +} +#endif +#pragma once + +#include // std::string + +// #include "column.h" + +// #include "table.h" + +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB +} +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once + +// #include "implementations/column_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include // std::make_unique + +// #include "../functional/cxx_core_features.h" + +// #include "../functional/static_magic.h" + +// #include "../functional/index_sequence_util.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../default_value_extractor.h" + +// #include "../column.h" + +namespace sqlite_orm { + namespace internal { + + template + std::unique_ptr column_constraints::default_value() const { + using default_op_index_sequence = + filter_tuple_sequence_t::template fn>; + + std::unique_ptr value; + call_if_constexpr( + [&value](auto& constraints, auto op_index_sequence) { + using default_op_index_sequence = decltype(op_index_sequence); + constexpr size_t opIndex = first_index_sequence_value(default_op_index_sequence{}); + value = std::make_unique(serialize_default_value(get(constraints))); + }, + this->constraints, + default_op_index_sequence{}); + return value; + } + + } +} + +// #include "implementations/table_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include // std::decay_t +#include // std::move +#include // std::find_if, std::ranges::find + +// #include "../type_printer.h" + +// #include "../column.h" + +// #include "../table.h" + +namespace sqlite_orm { + namespace internal { + + template + std::vector table_t::get_table_info() const { + std::vector res; + res.reserve(size_t(filter_tuple_sequence_t::size())); + this->for_each_column([&res](auto& column) { + using field_type = field_type_t>; + std::string dft; + if(auto d = column.default_value()) { + dft = move(*d); + } + res.emplace_back(-1, + column.name, + type_printer().print(), + column.is_not_null(), + dft, + column.template is(), + column.is_generated()); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { + auto& columnName = compositeKeyColumnNames[i]; +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(res, columnName, &table_xinfo::name); +#else + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { + return ti.name == columnName; + }); +#endif + if(it != res.end()) { + it->pk = static_cast(i + 1); + } + } + return res; + } + + } +} + +// #include "implementations/storage_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * this file is also used to separate implementation details from the main header file, + * e.g. usage of the dbstat table. + */ + +#include // std::is_same +#include +#include // std::reference_wrapper, std::cref +#include // std::find_if, std::ranges::find + +// #include "../dbstat.h" + +// #include "../util.h" + +// #include "../serializing_util.h" + +// #include "../storage.h" + +namespace sqlite_orm { + namespace internal { + + template + template> + sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if(std::is_same::value) { + return sync_schema_result::already_in_sync; + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB + auto res = sync_schema_result::already_in_sync; + bool attempt_to_preserve = true; + + auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); + if(schema_stat != sync_schema_result::already_in_sync) { + if(schema_stat == sync_schema_result::new_table_created) { + this->create_table(db, table.name, table); + res = sync_schema_result::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + if(schema_stat == sync_schema_result::old_columns_removed) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + for(auto& tableInfo: dbTableInfo) { + this->drop_column(db, table.name, tableInfo.name); + } + res = sync_schema_result::old_columns_removed; +#else + // extra table columns than storage columns + this->backup_table(db, table, {}); + res = sync_schema_result::old_columns_removed; +#endif + } + + if(schema_stat == sync_schema_result::new_columns_added) { + for(const table_xinfo* colInfo: columnsToAdd) { + table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { + if(column.name != colInfo->name) { + return; + } + this->add_column(db, tableName, column); + }); + } + res = sync_schema_result::new_columns_added; + } + + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + auto storageTableInfo = table.get_table_info(); + this->add_generated_cols(columnsToAdd, storageTableInfo); + + // remove extra columns and generated columns + this->backup_table(db, table, columnsToAdd); + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + this->add_generated_cols(columnsToAdd, storageTableInfo); + + if(preserve && attempt_to_preserve) { + this->backup_table(db, table, columnsToAdd); + } else { + this->drop_create_with_loss(db, table); + } + res = schema_stat; + } + } + } + return res; + } + + template + template + void storage_t::copy_table( + sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const { // must ignore generated columns + std::vector> columnNames; + columnNames.reserve(table.count_columns_amount()); + table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { + auto& columnName = column.name; +#if __cpp_lib_ranges >= 201911L + auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); +#else + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](const table_xinfo* tableInfo) { + return columnName == tableInfo->name; + }); +#endif + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.push_back(cref(columnName)); + } + }); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) + << std::flush; + perform_void_exec(db, ss.str()); + } + } +} + +#pragma once + +#if defined(_MSC_VER) +__pragma(pop_macro("max")) +__pragma(pop_macro("min")) +#endif // defined(_MSC_VER) diff --git a/src/SSVOpenHexagon/Online/Database.cpp b/src/SSVOpenHexagon/Online/Database.cpp index e319f3aab..c65ada0dd 100644 --- a/src/SSVOpenHexagon/Online/Database.cpp +++ b/src/SSVOpenHexagon/Online/Database.cpp @@ -41,31 +41,31 @@ inline auto makeStorage() { using namespace sqlite_orm; - auto storage = make_storage("ohdb.sqlite", // - // - make_table("users", // - make_column("id", &User::id, autoincrement(), primary_key()), // - make_column("steamId", &User::steamId, unique()), // - make_column("name", &User::name), // - make_column("passwordHash", &User::passwordHash) // - ), // - // - make_table("loginTokens", // - make_column( // - "id", &LoginToken::id, autoincrement(), primary_key()), // - make_column("userId", &LoginToken::userId, unique()), // - make_column("timestamp", &LoginToken::timestamp), // - make_column("token", &LoginToken::token) // - ), // - // - make_table("scores", // - make_column( // - "id", &Score::id, autoincrement(), primary_key()), // - make_column("levelValidator", &Score::levelValidator), // - make_column("timestamp", &Score::timestamp), // - make_column("userSteamId", &Score::userSteamId), // - make_column("value", &Score::value) // - ) // + auto storage = make_storage("ohdb.sqlite", // + // + make_table("users", // + make_column("id", &User::id, primary_key().autoincrement()), // + make_column("steamId", &User::steamId, unique()), // + make_column("name", &User::name), // + make_column("passwordHash", &User::passwordHash) // + ), // + // + make_table("loginTokens", // + make_column( // + "id", &LoginToken::id, primary_key().autoincrement()), // + make_column("userId", &LoginToken::userId, unique()), // + make_column("timestamp", &LoginToken::timestamp), // + make_column("token", &LoginToken::token) // + ), // + // + make_table("scores", // + make_column( // + "id", &Score::id, primary_key().autoincrement()), // + make_column("levelValidator", &Score::levelValidator), // + make_column("timestamp", &Score::timestamp), // + make_column("userSteamId", &Score::userSteamId), // + make_column("value", &Score::value) // + ) // // );