diff --git a/native/CommonCpp/sqlite_orm/sqlite_orm.h b/native/CommonCpp/sqlite_orm/sqlite_orm.h index 22c493422..fbbde1ca7 100644 --- a/native/CommonCpp/sqlite_orm/sqlite_orm.h +++ b/native/CommonCpp/sqlite_orm/sqlite_orm.h @@ -1,13405 +1,13405 @@ #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")) #undef max #define __RESTORE_MAX__ #endif #endif // defined(_MSC_VER) #include // due to #166 #if __cplusplus >= 201703L // use of C++17 or higher // Enables use of std::optional in SQLITE_ORM. #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif #pragma once #include // std::error_code, std::system_error #include // std::string #include #include #include // std::ostringstream namespace sqlite_orm { enum class orm_error_code { not_found = 1, type_is_not_mapped_to_storage, trying_to_dereference_null_iterator, too_many_tables_specified, incorrect_set_fields_specified, column_not_found, table_has_no_primary_key_column, cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, incorrect_order, }; } namespace sqlite_orm { class orm_error_category : public std::error_category { public: const char *name() const noexcept override final { return "ORM error"; } std::string message(int c) const override final { switch(static_cast(c)) { case orm_error_code::not_found: return "Not found"; case orm_error_code::type_is_not_mapped_to_storage: return "Type is not mapped to storage"; case orm_error_code::trying_to_dereference_null_iterator: return "Trying to dereference null iterator"; case orm_error_code::too_many_tables_specified: return "Too many tables specified"; case orm_error_code::incorrect_set_fields_specified: return "Incorrect set fields specified"; case orm_error_code::column_not_found: return "Column not found"; case orm_error_code::table_has_no_primary_key_column: return "Table has no primary key column"; case orm_error_code::cannot_start_a_transaction_within_a_transaction: return "Cannot start a transaction within a transaction"; case orm_error_code::no_active_transaction: return "No active transaction"; case orm_error_code::invalid_collate_argument_enum: return "Invalid collate_argument enum"; case orm_error_code::failed_to_init_a_backup: return "Failed to init a backup"; case orm_error_code::unknown_member_value: return "Unknown member value"; case orm_error_code::incorrect_order: return "Incorrect order"; default: return "unknown error"; } } }; class sqlite_error_category : public std::error_category { public: const char *name() const noexcept override final { return "SQLite error"; } std::string message(int c) const override final { return sqlite3_errstr(c); } }; inline const orm_error_category &get_orm_error_category() { static orm_error_category res; return res; } inline const sqlite_error_category &get_sqlite_error_category() { static sqlite_error_category res; return res; } template std::string get_error_message(sqlite3 *db, T &&... args) { std::ostringstream stream; using unpack = int[]; static_cast(unpack{0, (static_cast(static_cast(stream << args)), 0)...}); stream << sqlite3_errmsg(db); return stream.str(); } 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)...)); } } namespace std { template<> struct is_error_code_enum : std::true_type {}; 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()); } } #pragma once #include // std::tuple, std::get #include // std::false_type, std::true_type // #include "static_magic.h" #include // std::false_type, std::true_type, std::integral_constant namespace sqlite_orm { // got from here // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { static inline decltype(auto) empty_callable() { static auto res = [](auto &&...) {}; return (res); } template decltype(auto) static_if(std::true_type, const T &t, const F &) { return (t); } template decltype(auto) static_if(std::false_type, const T &, const F &f) { return (f); } template decltype(auto) static_if(const T &t, const F &f) { return static_if(std::integral_constant{}, t, f); } template decltype(auto) static_if(const T &t) { return static_if(std::integral_constant{}, t, empty_callable()); } template using static_not = std::integral_constant; } } 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 { template struct has_type; template struct has_type> : std::false_type {}; template struct has_type> : has_type> {}; template struct has_type> : std::true_type {}; template using tuple_contains_type = typename has_type::type; template struct iterator { template void operator()(const std::tuple &t, const L &l, bool reverse = true) { if(reverse) { l(std::get(t)); iterator()(t, l, reverse); } else { iterator()(t, l, reverse); l(std::get(t)); } } }; template struct iterator<0, Args...> { template void operator()(const std::tuple &t, const L &l, bool /*reverse*/ = true) { l(std::get<0>(t)); } }; template struct iterator { template void operator()(const std::tuple<> &, const L &, bool /*reverse*/ = true) { //.. } }; 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); } } namespace internal { template void move_tuple(L &lhs, R &rhs) { using bool_type = std::integral_constant; static_if([](auto &l, auto &r) { tuple_helper::move_tuple_impl(l, r); })(lhs, rhs); } template void iterate_tuple(const std::tuple &t, const L &l) { using tuple_type = std::tuple; tuple_helper::iterator::value - 1, Args...>()(t, l, false); } template using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); template struct conc_tuple { using type = tuple_cat_t; }; template class C> struct count_tuple; template class C> struct count_tuple, C> { static constexpr const int value = 0; }; template class C> struct count_tuple, C> { static constexpr const int value = C::value + count_tuple, C>::value; }; } } #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 namespace sqlite_orm { /** * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) */ template struct type_printer; struct integer_printer { inline const std::string &print() { static const std::string res = "INTEGER"; return res; } }; struct text_printer { inline const std::string &print() { static const std::string res = "TEXT"; return res; } }; struct real_printer { inline const std::string &print() { static const std::string res = "REAL"; return res; } }; struct blob_printer { inline const std::string &print() { static const std::string res = "BLOB"; return res; } }; // Note unsigned/signed char and simple char used for storing integer values, not char values. template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public integer_printer {}; template<> struct type_printer : public text_printer {}; template<> struct type_printer : public text_printer {}; template<> struct type_printer : public text_printer {}; template<> struct type_printer : public real_printer {}; template<> struct type_printer : public real_printer {}; template struct type_printer, void> : public type_printer {}; template struct type_printer, void> : public type_printer {}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct type_printer, void> : public type_printer {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<> struct type_printer, void> : public blob_printer {}; } #pragma once namespace sqlite_orm { namespace internal { 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 namespace sqlite_orm { namespace constraints { /** * AUTOINCREMENT constraint class. */ struct autoincrement_t { operator std::string() const { return "AUTOINCREMENT"; } }; struct primary_key_base { enum class order_by { unspecified, ascending, descending, }; order_by asc_option = order_by::unspecified; 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 constraint class. * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when * used withen `make_column` function. */ template struct primary_key_t : primary_key_base { 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 asc() const { auto res = *this; res.asc_option = order_by::ascending; return res; } primary_key_t desc() const { auto res = *this; res.asc_option = order_by::descending; return res; } }; struct unique_base { operator std::string() const { return "UNIQUE"; } }; /** * UNIQUE constraint class. */ template struct unique_t : unique_base { using columns_tuple = std::tuple; columns_tuple columns; unique_t(columns_tuple columns_) : columns(move(columns_)) {} }; /** * DEFAULT constraint class. * T is a value type. */ template struct default_t { using value_type = T; value_type value; operator std::string() const { return "DEFAULT"; } }; #if SQLITE_VERSION_NUMBER >= 3006019 /** * FOREIGN KEY constraint class. * Cs are columns which has foreign key * Rs are column which C references to * Available in SQLite 3.6.19 or higher */ template struct foreign_key_t; enum class foreign_key_action { none, // not specified no_action, restrict_, set_null, set_default, cascade, }; inline std::ostream &operator<<(std::ostream &os, foreign_key_action action) { switch(action) { case decltype(action)::no_action: os << "NO ACTION"; break; case decltype(action)::restrict_: os << "RESTRICT"; break; case decltype(action)::set_null: os << "SET NULL"; break; case decltype(action)::set_default: os << "SET DEFAULT"; break; case decltype(action)::cascade: os << "CASCADE"; break; case decltype(action)::none: break; } return os; } struct on_update_delete_base { const bool update; // true if update and false if delete operator std::string() const { if(this->update) { return "ON UPDATE"; } else { return "ON DELETE"; } } }; /** * F - foreign key class */ template struct on_update_delete_t : on_update_delete_base { using foreign_key_type = F; const foreign_key_type &fk; on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : on_update_delete_base{update_}, fk(fk_), _action(action_) {} foreign_key_action _action = foreign_key_action::none; foreign_key_type no_action() const { auto res = this->fk; if(update) { res.on_update._action = foreign_key_action::no_action; } else { res.on_delete._action = foreign_key_action::no_action; } return res; } foreign_key_type restrict_() const { auto res = this->fk; if(update) { res.on_update._action = foreign_key_action::restrict_; } else { res.on_delete._action = foreign_key_action::restrict_; } return res; } foreign_key_type set_null() const { auto res = this->fk; if(update) { res.on_update._action = foreign_key_action::set_null; } else { res.on_delete._action = foreign_key_action::set_null; } return res; } foreign_key_type set_default() const { auto res = this->fk; if(update) { res.on_update._action = foreign_key_action::set_default; } else { res.on_delete._action = foreign_key_action::set_default; } return res; } foreign_key_type cascade() const { auto res = this->fk; if(update) { res.on_update._action = foreign_key_action::cascade; } else { res.on_delete._action = foreign_key_action::cascade; } return res; } operator bool() const { return this->_action != decltype(this->_action)::none; } }; template struct foreign_key_t, std::tuple> { using columns_type = std::tuple; using references_type = std::tuple; using self = foreign_key_t; columns_type columns; references_type references; on_update_delete_t on_update; on_update_delete_t on_delete; static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} foreign_key_t(const self &other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} self &operator=(const self &other) { this->columns = other.columns; this->references = other.references; this->on_update = {*this, true, other.on_update._action}; this->on_delete = {*this, false, other.on_delete._action}; return *this; } template void for_each_column(const L &) {} template constexpr bool has_every() const { return false; } }; /** * Cs can be a class member pointer, a getter function member pointer or setter * func member pointer * Available in SQLite 3.6.19 or higher */ template struct foreign_key_intermediate_t { using tuple_type = std::tuple; tuple_type columns; 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)...)}; } }; #endif struct collate_t { internal::collate_argument argument = internal::collate_argument::binary; collate_t(internal::collate_argument argument_) : argument(argument_) {} operator std::string() const { std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); return res; } static std::string string_from_collate_argument(internal::collate_argument argument) { switch(argument) { case decltype(argument)::binary: return "BINARY"; case decltype(argument)::nocase: return "NOCASE"; case decltype(argument)::rtrim: return "RTRIM"; } throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); } }; struct check_string { operator std::string() const { return "CHECK"; } }; template struct check_t : check_string { using expression_type = T; expression_type expression; check_t(expression_type expression_) : expression(std::move(expression_)) {} }; template struct is_constraint : std::false_type {}; template<> struct is_constraint : std::true_type {}; template struct is_constraint> : std::true_type {}; template struct is_constraint> : std::true_type {}; template struct is_constraint> : std::true_type {}; template struct is_constraint> : std::true_type {}; template<> struct is_constraint : std::true_type {}; template struct is_constraint> : std::true_type {}; template struct constraints_size; template<> struct constraints_size<> { static constexpr const int value = 0; }; template struct constraints_size { static constexpr const int value = is_constraint::value + constraints_size::value; }; } #if SQLITE_VERSION_NUMBER >= 3006019 /** * FOREIGN KEY constraint construction function that takes member pointer as argument * Available in SQLite 3.6.19 or higher */ template constraints::foreign_key_intermediate_t foreign_key(Cs... columns) { return {std::make_tuple(std::forward(columns)...)}; } #endif /** * UNIQUE constraint builder function. */ template constraints::unique_t unique(Args... args) { return {std::make_tuple(std::forward(args)...)}; } inline constraints::unique_t<> unique() { return {{}}; } inline constraints::autoincrement_t autoincrement() { return {}; } template constraints::primary_key_t primary_key(Cs... cs) { return {std::make_tuple(std::forward(cs)...)}; } inline constraints::primary_key_t<> primary_key() { return {{}}; } template constraints::default_t default_value(T t) { return {std::move(t)}; } inline constraints::collate_t collate_nocase() { return {internal::collate_argument::nocase}; } inline constraints::collate_t collate_binary() { return {internal::collate_argument::binary}; } inline constraints::collate_t collate_rtrim() { return {internal::collate_argument::rtrim}; } template constraints::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 {}; } } #pragma once #include // std::false_type, std::true_type #include // std::shared_ptr, std::unique_ptr #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED #include // std::optional #endif // SQLITE_ORM_OPTIONAL_SUPPORTED namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. * Default nullability status for all types is `NOT NULL`. So if you want to map * custom type as `NULL` (for example: boost::optional) you have to create a specialiation * of type_is_nullable for your type and derive from `std::true_type`. */ template struct type_is_nullable : public 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. */ template struct type_is_nullable> : public std::true_type { bool operator()(const std::unique_ptr &t) const { return static_cast(t); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED /** * This is a specialization for std::optional. std::optional is nullable. */ template struct type_is_nullable> : public std::true_type { bool operator()(const std::optional &t) const { return t.has_value(); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED } #pragma once #include // std::unique_ptr #include // std::string #include // std::stringstream // #include "constraints.h" // #include "serializator_context.h" namespace sqlite_orm { namespace internal { struct serializator_context_base { bool replace_bindable_with_question = false; bool skip_table_name = true; bool use_parentheses = true; template std::string column_name(F O::*) const { return {}; } }; template struct serializator_context : serializator_context_base { using impl_type = I; const impl_type &impl; serializator_context(const impl_type &impl_) : impl(impl_) {} template std::string column_name(F O::*m) const { return this->impl.column_name(m); } }; template struct serializator_context_builder { using storage_type = S; using impl_type = typename storage_type::impl_type; serializator_context_builder(const storage_type &storage_) : storage(storage_) {} serializator_context operator()() const { return {this->storage.impl}; } const storage_type &storage; }; } } namespace sqlite_orm { namespace internal { template std::string serialize(const T &t); /** * 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 { template std::unique_ptr operator()(const A &) { return {}; } template std::unique_ptr operator()(const constraints::default_t &t) { serializator_context_base context; return std::make_unique(serialize(t.value, context)); } }; } } #pragma once #include // std::false_type, std::true_type // #include "negatable.h" namespace sqlite_orm { namespace internal { struct negatable_t {}; } } namespace sqlite_orm { namespace internal { /** * Inherit this class to support arithmetic types overloading */ struct arithmetic_t {}; template struct binary_operator : Ds... { using left_type = L; using right_type = R; left_type lhs; right_type rhs; binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; struct conc_string { operator std::string() const { return "||"; } }; /** * Result of concatenation || operator */ template using conc_t = binary_operator; struct add_string { operator std::string() const { return "+"; } }; /** * Result of addition + operator */ template using add_t = binary_operator; struct sub_string { operator std::string() const { return "-"; } }; /** * Result of substitute - operator */ template using sub_t = binary_operator; struct mul_string { operator std::string() const { return "*"; } }; /** * Result of multiply * operator */ template using mul_t = binary_operator; struct div_string { operator std::string() const { return "/"; } }; /** * Result of divide / operator */ template using div_t = binary_operator; struct mod_string { operator std::string() const { return "%"; } }; /** * Result of mod % operator */ template using mod_t = binary_operator; struct bitwise_shift_left_string { operator std::string() const { return "<<"; } }; /** * Result of bitwise shift left << operator */ template using bitwise_shift_left_t = binary_operator; struct bitwise_shift_right_string { operator std::string() const { return ">>"; } }; /** * Result of bitwise shift right >> operator */ template using bitwise_shift_right_t = binary_operator; struct bitwise_and_string { operator std::string() const { return "&"; } }; /** * Result of bitwise and & operator */ template using bitwise_and_t = binary_operator; struct bitwise_or_string { operator std::string() const { return "|"; } }; /** * Result of bitwise or | operator */ template using bitwise_or_t = binary_operator; struct bitwise_not_string { operator std::string() const { return "~"; } }; /** * Result of bitwise not ~ operator */ template struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { using argument_type = T; argument_type argument; bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} }; struct assign_string { operator std::string() const { return "="; } }; /** * Result of assign = operator */ template using assign_t = binary_operator; /** * Assign operator traits. Common case */ template struct is_assign_t : public std::false_type {}; /** * Assign operator traits. Specialized case */ template struct is_assign_t> : public std::true_type {}; /** * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t */ template struct expression_t { T t; expression_t(T t_) : t(std::move(t_)) {} template assign_t operator=(R r) const { return {this->t, std::move(r)}; } assign_t operator=(std::nullptr_t) const { return {this->t, nullptr}; } }; } /** * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or * `storage.update(set(c(&User::name) = "Dua Lipa")); */ template internal::expression_t c(T t) { return {std::move(t)}; } /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users */ template internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } /** * Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users */ template internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } template internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } template internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } template internal::mod_t mod(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_and_t bitwise_and(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_or_t bitwise_or(L l, R r) { return {std::move(l), std::move(r)}; } template internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } template internal::assign_t assign(L l, R r) { return {std::move(l), std::move(r)}; } } #pragma once #include // std::tuple #include // std::string #include // std::unique_ptr #include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer // #include "type_is_nullable.h" // #include "tuple_helper.h" // #include "default_value_extractor.h" // #include "constraints.h" // #include "getter_traits.h" namespace sqlite_orm { namespace internal { template struct is_field_member_pointer : std::false_type {}; template struct is_field_member_pointer::value && !std::is_member_function_pointer::value>::type> : std::true_type {}; template struct field_member_traits; template struct field_member_traits::value>::type> { using object_type = O; using field_type = F; }; /** * Getters aliases */ template using getter_by_value_const = T (O::*)() const; template using getter_by_value = T (O::*)(); template using getter_by_ref_const = T &(O::*)() const; template using getter_by_ref = T &(O::*)(); template using getter_by_const_ref_const = const T &(O::*)() const; template using getter_by_const_ref = const T &(O::*)(); /** * Setters aliases */ template using setter_by_value = void (O::*)(T); template using setter_by_ref = void (O::*)(T &); template using setter_by_const_ref = void (O::*)(const T &); template struct is_getter : std::false_type {}; template struct is_getter> : std::true_type {}; template struct is_getter> : std::true_type {}; template struct is_getter> : std::true_type {}; template struct is_getter> : std::true_type {}; template struct is_getter> : std::true_type {}; template struct is_getter> : std::true_type {}; template struct is_setter : std::false_type {}; template struct is_setter> : std::true_type {}; template struct is_setter> : std::true_type {}; template struct is_setter> : std::true_type {}; template struct getter_traits; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = false; }; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = false; }; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = true; }; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = true; }; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = true; }; template struct getter_traits> { using object_type = O; using field_type = T; static constexpr const bool returns_lvalue = true; }; template struct setter_traits; template struct setter_traits> { using object_type = O; using field_type = T; }; template struct setter_traits> { using object_type = O; using field_type = T; }; template struct setter_traits> { using object_type = O; using field_type = T; }; template struct member_traits; template struct member_traits::value>::type> { using object_type = typename field_member_traits::object_type; using field_type = typename field_member_traits::field_type; }; template struct member_traits::value>::type> { using object_type = typename getter_traits::object_type; using field_type = typename getter_traits::field_type; }; template struct member_traits::value>::type> { using object_type = typename setter_traits::object_type; using field_type = typename setter_traits::field_type; }; } } namespace sqlite_orm { namespace internal { struct column_base { /** * Column name. Specified during construction in `make_column`. */ const std::string name; }; /** * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage * O is a mapped class, e.g. User * T is a mapped class'es field type, e.g. &User::name * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc */ template struct column_t : column_base { using object_type = O; using field_type = T; using constraints_type = std::tuple; using member_pointer_t = field_type object_type::*; using getter_type = G; using setter_type = S; /** * Member pointer used to read/write member */ member_pointer_t member_pointer /* = nullptr*/; /** * Getter member function pointer to get a value. If member_pointer is null than * `getter` and `setter` must be not null */ getter_type getter /* = nullptr*/; /** * Setter member function */ setter_type setter /* = nullptr*/; /** * Constraints tuple */ constraints_type constraints; column_t(std::string name_, member_pointer_t member_pointer_, getter_type getter_, setter_type setter_, constraints_type constraints_) : column_base{std::move(name_)}, member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} /** * Simplified interface for `NOT NULL` constraint */ bool not_null() const { return !type_is_nullable::value; } template constexpr bool has() const { return tuple_helper::tuple_contains_type::value; } template constexpr bool has_every() const { if(has() && has()) { return true; } else { return has_every(); } } template constexpr bool has_every() const { return has(); } /** * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr */ std::unique_ptr default_value() const { std::unique_ptr res; iterate_tuple(this->constraints, [&res](auto &v) { auto dft = internal::default_value_extractor()(v); if(dft) { res = std::move(dft); } }); return res; } }; /** * Column traits. Common case. */ template struct is_column : public std::false_type {}; /** * Column traits. Specialized case case. */ template struct is_column> : public std::true_type {}; template struct column_field_type { using type = void; }; template struct column_field_type> { using type = typename column_t::field_type; }; template struct column_constraints_type { using type = std::tuple<>; }; template struct column_constraints_type> { using type = typename column_t::constraints_type; }; } /** * 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(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); static_assert(internal::is_field_member_pointer::value, "second argument expected as a member field pointer, not member function pointer"); return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; } /** * Column builder function with setter and getter. You should use it to create columns instead of constructor */ 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, "Getter and setter must get and set same data type"); static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } /** * Column builder function with getter and setter (reverse order). You should use it to create columns instead of * constructor */ 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, "Getter and setter must get and set same data type"); static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } } #pragma once #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 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 { std::string operator()(const T &t) const { std::stringstream stream; stream << t; return stream.str(); } }; /** * Upgrade to integer is required when using unsigned char(uint8_t) */ template<> struct field_printer { std::string operator()(const unsigned char &t) const { std::stringstream stream; stream << +t; return stream.str(); } }; /** * Upgrade to integer is required when using signed char(int8_t) */ template<> struct field_printer { std::string operator()(const signed char &t) const { std::stringstream stream; stream << +t; return stream.str(); } }; /** * char is neigher signer 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(); } }; template<> struct field_printer { std::string operator()(const std::string &t) const { return t; } }; template<> struct field_printer> { std::string operator()(const std::vector &t) const { std::stringstream ss; ss << std::hex; for(auto c: t) { ss << c; } return ss.str(); } }; template<> struct field_printer { std::string operator()(const std::nullptr_t &) const { return "null"; } }; template struct field_printer> { std::string operator()(const std::shared_ptr &t) const { if(t) { return field_printer()(*t); } else { return field_printer()(nullptr); } } }; template struct field_printer> { std::string operator()(const std::unique_ptr &t) const { if(t) { return field_printer()(*t); } else { return field_printer()(nullptr); } } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct field_printer> { std::string operator()(const std::optional &t) const { if(t.has_value()) { return field_printer()(*t); } else { return field_printer()(nullptr); } } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED } #pragma once #include // std::string #include // std::enable_if, std::is_same #include // std::vector #include // std::tuple // #include "collate_argument.h" // #include "constraints.h" // #include "optional_container.h" namespace sqlite_orm { namespace internal { /** * This is a cute class which allows storing something or nothing * depending on template argument. Useful for optional class members */ template struct optional_container { using type = T; type field; template void apply(const L &l) const { l(this->field); } }; template<> struct optional_container { using type = void; template void apply(const L &) const { //.. } }; } } // #include "negatable.h" namespace sqlite_orm { namespace internal { struct arithmetic_t; } namespace internal { struct limit_string { operator std::string() const { return "LIMIT"; } }; /** * Stores LIMIT/OFFSET info */ template struct limit_t : limit_string { T lim; internal::optional_container off; limit_t() = default; limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} }; template struct is_limit : std::false_type {}; template struct is_limit> : std::true_type {}; /** * Stores OFFSET only info */ template struct offset_t { T off; }; template struct is_offset : std::false_type {}; template struct is_offset> : std::true_type {}; /** * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators */ struct condition_t {}; /** * Collated something */ template struct collate_t : public condition_t { T expr; internal::collate_argument argument; collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { return constraints::collate_t{this->argument}; } }; struct named_collate_base { std::string name; operator std::string() const { return "COLLATE " + this->name; } }; /** * Collated something with custom collate function */ template struct named_collate : named_collate_base { T expr; named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} }; struct negated_condition_string { operator std::string() const { return "NOT"; } }; /** * Result of not operator */ template struct negated_condition_t : condition_t, negated_condition_string { C c; negated_condition_t(C c_) : c(std::move(c_)) {} }; /** * Base class for binary conditions */ template struct binary_condition : public condition_t { using left_type = L; using right_type = R; left_type l; right_type r; binary_condition() = default; binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} }; struct and_condition_string { operator std::string() const { return "AND"; } }; /** * Result of and operator */ template struct and_condition_t : binary_condition, and_condition_string { using super = binary_condition; using super::super; }; struct or_condition_string { operator std::string() const { return "OR"; } }; /** * Result of or operator */ template struct or_condition_t : binary_condition, or_condition_string { using super = binary_condition; using super::super; }; struct is_equal_string { operator std::string() const { return "="; } }; /** * = and == operators object */ template struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { using self = is_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } named_collate collate(std::string name) const { return {*this, std::move(name)}; } }; struct is_not_equal_string { operator std::string() const { return "!="; } }; /** * != operator object */ template struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { using self = is_not_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } }; struct greater_than_string { operator std::string() const { return ">"; } }; /** * > operator object. */ template struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { using self = greater_than_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } }; struct greater_or_equal_string { operator std::string() const { return ">="; } }; /** * >= operator object. */ template struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { using self = greater_or_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } }; struct lesser_than_string { operator std::string() const { return "<"; } }; /** * < operator object. */ template struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { using self = lesser_than_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } }; struct lesser_or_equal_string { operator std::string() const { return "<="; } }; /** * <= operator object. */ template struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { using self = lesser_or_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } collate_t collate_nocase() const { return {*this, internal::collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, internal::collate_argument::rtrim}; } }; struct in_base { bool negative = false; // used in not_in operator std::string() const { if(!this->negative) { return "IN"; } else { return "NOT IN"; } } }; /** * IN operator object. */ template struct in_t : condition_t, in_base, internal::negatable_t { using self = in_t; L l; // left expression A arg; // in arg in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} }; struct is_null_string { operator std::string() const { return "IS NULL"; } }; /** * IS NULL operator object. */ template struct is_null_t : is_null_string, internal::negatable_t { using self = is_null_t; T t; is_null_t(T t_) : t(std::move(t_)) {} }; struct is_not_null_string { operator std::string() const { return "IS NOT NULL"; } }; /** * IS NOT NULL operator object. */ template struct is_not_null_t : is_not_null_string, internal::negatable_t { using self = is_not_null_t; T t; is_not_null_t(T t_) : t(std::move(t_)) {} }; struct where_string { operator std::string() const { return "WHERE"; } }; /** * WHERE argument holder. * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc */ template struct where_t : where_string { C c; where_t(C c_) : c(std::move(c_)) {} }; template struct is_where : std::false_type {}; template struct is_where> : std::true_type {}; struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; }; struct order_by_string { operator std::string() const { return "ORDER BY"; } }; /** * ORDER BY argument holder. */ template struct order_by_t : order_by_base, order_by_string { using self = order_by_t; O o; order_by_t(O o_) : o(std::move(o_)) {} self asc() { auto res = *this; res.asc_desc = 1; return res; } self desc() { auto res = *this; res.asc_desc = -1; return res; } self collate_binary() const { auto res = *this; res._collate_argument = constraints::collate_t::string_from_collate_argument( sqlite_orm::internal::collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; res._collate_argument = constraints::collate_t::string_from_collate_argument( sqlite_orm::internal::collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; res._collate_argument = constraints::collate_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; res._collate_argument = std::move(name); return res; } }; /** * ORDER BY pack holder. */ template struct multi_order_by_t : order_by_string { using args_type = std::tuple; args_type args; multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} }; struct dynamic_order_by_entry_t : order_by_base { std::string name; dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : order_by_base{asc_desc_, move(collate_argument_)}, name(move(name_)) {} }; /** * C - serializator context class */ template struct dynamic_order_by_t : order_by_string { using context_t = C; using entry_t = dynamic_order_by_entry_t; using const_iterator = typename std::vector::const_iterator; dynamic_order_by_t(const context_t &context_) : context(context_) {} template void push_back(order_by_t order_by) { auto newContext = this->context; newContext.skip_table_name = true; auto columnName = serialize(order_by.o, newContext); entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); } const_iterator begin() const { return this->entries.begin(); } const_iterator end() const { return this->entries.end(); } void clear() { this->entries.clear(); } protected: std::vector entries; context_t context; }; template 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_)) {} }; template struct is_group_by : std::false_type {}; template struct is_group_by> : std::true_type {}; struct between_string { operator std::string() const { return "BETWEEN"; } }; /** * BETWEEN operator object. */ template struct between_t : condition_t, between_string { using expression_type = A; using lower_type = T; using upper_type = T; expression_type expr; lower_type b1; upper_type b2; between_t(expression_type expr_, lower_type b1_, upper_type b2_) : expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} }; struct like_string { operator std::string() const { return "LIKE"; } }; /** * LIKE operator object. */ template struct like_t : condition_t, like_string, internal::negatable_t { using self = like_t; using arg_t = A; using pattern_t = T; using escape_t = E; arg_t arg; pattern_t pattern; sqlite_orm::internal::optional_container arg3; // not escape cause escape exists as a function here like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape_) : arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template like_t escape(C c) const { sqlite_orm::internal::optional_container newArg3{std::move(c)}; return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; struct glob_string { operator std::string() const { return "GLOB"; } }; template struct glob_t : condition_t, glob_string, internal::negatable_t { using self = glob_t; using arg_t = A; using pattern_t = T; arg_t arg; pattern_t pattern; glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} }; struct cross_join_string { operator std::string() const { return "CROSS JOIN"; } }; /** * CROSS JOIN holder. * T is joined type which represents any mapped table. */ template struct cross_join_t : cross_join_string { using type = T; }; struct natural_join_string { operator std::string() const { return "NATURAL JOIN"; } }; /** * NATURAL JOIN holder. * T is joined type which represents any mapped table. */ template struct natural_join_t : natural_join_string { using type = T; }; struct left_join_string { operator std::string() const { return "LEFT JOIN"; } }; /** * LEFT JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template struct left_join_t : left_join_string { using type = T; using on_type = O; on_type constraint; left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; struct join_string { operator std::string() const { return "JOIN"; } }; /** * Simple JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template struct join_t : join_string { using type = T; using on_type = O; on_type constraint; join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; struct left_outer_join_string { operator std::string() const { return "LEFT OUTER JOIN"; } }; /** * LEFT OUTER JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template struct left_outer_join_t : left_outer_join_string { using type = T; using on_type = O; on_type constraint; left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; struct on_string { operator std::string() const { return "ON"; } }; /** * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN * T is on type argument. */ template struct on_t : on_string { using arg_type = T; arg_type arg; on_t(arg_type arg_) : arg(std::move(arg_)) {} }; /** * USING argument holder. */ template struct using_t { F O::*column = nullptr; operator std::string() const { return "USING"; } }; struct inner_join_string { operator std::string() const { return "INNER JOIN"; } }; /** * INNER JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template struct inner_join_t : inner_join_string { using type = T; using on_type = O; on_type constraint; inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; struct 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"; } }; /** * CAST holder. * T is a type to cast to * E is an expression type * Example: cast(&User::id) */ template struct cast_t : cast_string { using to_type = T; using expression_type = E; expression_type expression; cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; } template::value>::type> internal::negated_condition_t operator!(T arg) { return {std::move(arg)}; } /** * Cute operators for columns */ template internal::lesser_than_t operator<(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::lesser_than_t operator<(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::greater_than_t operator>(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::greater_than_t operator>(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::is_equal_t operator==(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::is_equal_t operator==(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::conc_t operator||(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::conc_t operator||(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::conc_t operator||(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::add_t operator+(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::add_t operator+(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::add_t operator+(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::sub_t operator-(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::sub_t operator-(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::sub_t operator-(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::mul_t operator*(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::mul_t operator*(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::mul_t operator*(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::div_t operator/(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::div_t operator/(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::div_t operator/(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::mod_t operator%(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template internal::mod_t operator%(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template internal::mod_t operator%(internal::expression_t l, internal::expression_t r) { return {std::move(l.t), std::move(r.t)}; } template internal::using_t using_(F O::*p) { return {std::move(p)}; } template internal::on_t on(T t) { return {std::move(t)}; } template internal::cross_join_t cross_join() { return {}; } template internal::natural_join_t natural_join() { return {}; } template internal::left_join_t left_join(O o) { return {std::move(o)}; } template internal::join_t join(O o) { return {std::move(o)}; } template internal::left_outer_join_t left_outer_join(O o) { return {std::move(o)}; } template internal::inner_join_t inner_join(O o) { return {std::move(o)}; } template internal::offset_t offset(T off) { return {std::move(off)}; } template internal::limit_t limit(T lim) { return {std::move(lim)}; } template typename std::enable_if::value, internal::limit_t>::type limit(O off, T lim) { return {std::move(lim), {std::move(off)}}; } template internal::limit_t limit(T lim, internal::offset_t offt) { return {std::move(lim), {std::move(offt.off)}}; } template::value || std::is_base_of::value>::type> internal::and_condition_t operator&&(L l, R r) { return {std::move(l), std::move(r)}; } template::value || std::is_base_of::value>::type> internal::or_condition_t operator||(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_not_null_t is_not_null(T t) { return {std::move(t)}; } template internal::is_null_t is_null(T t) { return {std::move(t)}; } template internal::in_t> in(L l, std::vector values) { return {std::move(l), std::move(values), false}; } template internal::in_t> in(L l, std::initializer_list values) { return {std::move(l), std::move(values), false}; } template internal::in_t in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template internal::in_t> not_in(L l, std::vector values) { return {std::move(l), std::move(values), true}; } template internal::in_t> not_in(L l, std::initializer_list values) { return {std::move(l), std::move(values), true}; } template internal::in_t not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } template internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template internal::lesser_than_t lesser_than(L l, R r) { return {std::move(l), std::move(r)}; } template internal::lesser_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template internal::lesser_or_equal_t lesser_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::lesser_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } template internal::where_t where(C c) { return {std::move(c)}; } /** * ORDER BY column * Example: storage.select(&User::name, order_by(&User::id)) */ template internal::order_by_t order_by(O o) { return {std::move(o)}; } /** * ORDER BY column1, column2 * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template internal::multi_order_by_t multi_order_by(Args &&... args) { return {std::make_tuple(std::forward(args)...)}; } /** * ORDER BY column1, column2 * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member * function Example: * auto orderBy = dynamic_order_by(storage); * if(someCondition) { * orderBy.push_back(&User::id); * } else { * orderBy.push_back(&User::name); * orderBy.push_back(&User::birthDate); * } */ template internal::dynamic_order_by_t> dynamic_order_by(const S &storage) { internal::serializator_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)) */ template internal::between_t between(A expr, T b1, T b2) { return {std::move(expr), std::move(b1), std::move(b2)}; } /** * X LIKE Y * Example: storage.select(like(&User::name, "T%")) */ template internal::like_t like(A a, T t) { return {std::move(a), std::move(t), {}}; } /** * X GLOB Y * Example: storage.select(glob(&User::name, "*S")) */ template internal::glob_t glob(A a, T t) { return {std::move(a), std::move(t)}; } /** * X LIKE Y ESCAPE Z * Example: storage.select(like(&User::name, "T%", "%")) */ template internal::like_t like(A a, T t, E e) { return {std::move(a), std::move(t), {std::move(e)}}; } /** * 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) */ template internal::cast_t cast(E e) { return {std::move(e)}; } } #pragma once #include // std::enable_if, std::is_base_of, std::is_member_pointer #include // std::stringstream #include // std::string namespace sqlite_orm { /** * This is base class for every class which is used as a custom table alias. * For more information please look through self_join.cpp example */ struct alias_tag {}; namespace internal { /** * 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 */ template struct table_alias : alias_tag { using type = T; static char get() { return A; } }; /** * Column expression with table alias attached like 'C.ID'. This is not a column alias */ template struct alias_column_t { using alias_type = T; using column_type = C; column_type column; alias_column_t(){}; alias_column_t(column_type column_) : column(column_) {} }; template struct alias_extractor; template struct alias_extractor::value>::type> { static std::string get() { std::stringstream ss; ss << T::get(); return ss.str(); } }; template struct alias_extractor::value>::type> { static std::string get() { return {}; } }; template struct as_t { using alias_type = T; using expression_type = E; expression_type expression; }; template struct alias_holder { using type = T; }; } /** * @return column with table alias attached. Place it instead of a column statement in case you need to specify a * column with table alias prefix like 'a.column'. For more information please look through self_join.cpp example */ template internal::alias_column_t alias_column(C c) { static_assert(std::is_member_pointer::value, "alias_column argument must be a member pointer mapped to a storage"); return {c}; } template internal::as_t as(E expression) { return {std::move(expression)}; } template internal::alias_holder get() { return {}; } template using alias_a = internal::table_alias; template using alias_b = internal::table_alias; template using alias_c = internal::table_alias; template using alias_d = internal::table_alias; template using alias_e = internal::table_alias; template using alias_f = internal::table_alias; template using alias_g = internal::table_alias; template using alias_h = internal::table_alias; template using alias_i = internal::table_alias; template using alias_j = internal::table_alias; template using alias_k = internal::table_alias; template using alias_l = internal::table_alias; template using alias_m = internal::table_alias; template using alias_n = internal::table_alias; template using alias_o = internal::table_alias; template using alias_p = internal::table_alias; template using alias_q = internal::table_alias; template using alias_r = internal::table_alias; template using alias_s = internal::table_alias; template using alias_t = internal::table_alias; template using alias_u = internal::table_alias; template using alias_v = internal::table_alias; template using alias_w = internal::table_alias; template using alias_x = internal::table_alias; template using alias_y = internal::table_alias; template using alias_z = internal::table_alias; } #pragma once // #include "conditions.h" namespace sqlite_orm { namespace internal { template struct join_iterator { template void operator()(const L &) { //.. } }; template<> struct join_iterator<> { template void operator()(const L &) { //.. } }; template struct join_iterator : public join_iterator { using super = join_iterator; template void operator()(const L &l) { this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = cross_join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = natural_join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = left_join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = left_outer_join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; template struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; using join_type = inner_join_t; template void operator()(const L &l) { l(*this); this->super::operator()(l); } }; } } #pragma once #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if #include // std::unique_ptr #include // std::vector // #include "conditions.h" // #include "operators.h" // #include "is_base_of_template.h" #include // std::true_type, std::false_type, std::declval namespace sqlite_orm { namespace internal { /* * This is because of bug in MSVC, for more information, please visit * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 */ #if defined(_MSC_VER) template class Base, typename Derived> struct is_base_of_template_impl { template 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; #else template class C, typename... Ts> std::true_type is_base_of_template_impl(const C *); template class C> std::false_type is_base_of_template_impl(...); template class C> using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); #endif } } namespace sqlite_orm { namespace internal { template struct unique_ptr_result_of {}; /** * Base class for operator overloading * R - return type * S - class with operator std::string * Args - function arguments types */ template struct core_function_t : S, internal::arithmetic_t { using return_type = R; using string_type = S; using args_type = std::tuple; static constexpr const size_t args_size = std::tuple_size::value; args_type args; core_function_t(args_type &&args_) : args(std::move(args_)) {} }; struct length_string { operator std::string() const { return "LENGTH"; } }; struct abs_string { operator std::string() const { return "ABS"; } }; struct lower_string { operator std::string() const { return "LOWER"; } }; struct upper_string { operator std::string() const { return "UPPER"; } }; struct changes_string { operator std::string() const { return "CHANGES"; } }; struct trim_string { operator std::string() const { return "TRIM"; } }; struct ltrim_string { operator std::string() const { return "LTRIM"; } }; struct rtrim_string { operator std::string() const { return "RTRIM"; } }; struct hex_string { operator std::string() const { return "HEX"; } }; struct quote_string { operator std::string() const { return "QUOTE"; } }; struct randomblob_string { operator std::string() const { return "RANDOMBLOB"; } }; struct instr_string { operator std::string() const { return "INSTR"; } }; struct replace_string { operator std::string() const { return "REPLACE"; } }; struct round_string { operator std::string() const { return "ROUND"; } }; #if SQLITE_VERSION_NUMBER >= 3007016 struct char_string { operator std::string() const { return "CHAR"; } }; struct random_string { operator std::string() const { return "RANDOM"; } }; #endif struct coalesce_string { operator std::string() const { return "COALESCE"; } }; struct date_string { operator std::string() const { return "DATE"; } }; struct time_string { operator std::string() const { return "TIME"; } }; struct datetime_string { operator std::string() const { return "DATETIME"; } }; struct julianday_string { operator std::string() const { return "JULIANDAY"; } }; struct strftime_string { operator std::string() const { return "STRFTIME"; } }; struct zeroblob_string { operator std::string() const { return "ZEROBLOB"; } }; struct substr_string { operator std::string() const { return "SUBSTR"; } }; #ifdef SQLITE_SOUNDEX struct soundex_string { operator std::string() const { return "SOUNDEX"; } }; #endif struct total_string { operator std::string() const { return "TOTAL"; } }; struct sum_string { operator std::string() const { return "SUM"; } }; struct count_string { operator std::string() const { return "COUNT"; } }; /** * T is use to specify type explicitly for queries like * SELECT COUNT(*) FROM table_name; * T can be omitted with void. */ template struct count_asterisk_t : count_string { using type = T; }; /** * The same thing as count() but without T arg. * Is used in cases like this: * SELECT cust_code, cust_name, cust_city, grade * FROM customer * WHERE grade=2 AND EXISTS * (SELECT COUNT(*) * FROM customer * WHERE grade=2 * GROUP BY grade * HAVING COUNT(*)>2); * `c++` * auto rows = * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), * where(is_equal(&Customer::grade, 2) * and exists(select(count(), * where(is_equal(&Customer::grade, 2)), * group_by(&Customer::grade), * having(greater_than(count(), 2)))))); */ struct count_asterisk_without_type : count_string {}; struct avg_string { operator std::string() const { return "AVG"; } }; struct max_string { operator std::string() const { return "MAX"; } }; struct min_string { operator std::string() const { return "MIN"; } }; struct group_concat_string { operator std::string() const { return "GROUP_CONCAT"; } }; } /** * Cute operators for core functions */ template< class F, class R, typename = typename std::enable_if::value>::type> internal::lesser_than_t operator<(F f, R r) { return {std::move(f), std::move(r)}; } template< class F, class R, typename = typename std::enable_if::value>::type> internal::lesser_or_equal_t operator<=(F f, R r) { return {std::move(f), std::move(r)}; } template< class F, class R, typename = typename std::enable_if::value>::type> internal::greater_than_t operator>(F f, R r) { return {std::move(f), std::move(r)}; } template< class F, class R, typename = typename std::enable_if::value>::type> internal::greater_or_equal_t operator>=(F f, R r) { return {std::move(f), std::move(r)}; } template< class F, class R, typename = typename std::enable_if::value>::type> internal::is_equal_t operator==(F f, R r) { return {std::move(f), std::move(r)}; } template< class F, class R, typename = typename std::enable_if::value>::type> internal::is_not_equal_t operator!=(F f, R r) { return {std::move(f), std::move(r)}; } /** * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template internal::core_function_t length(T t) { std::tuple args{std::forward(t)}; return {move(args)}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template internal::core_function_t, internal::abs_string, T> abs(T t) { std::tuple args{std::forward(t)}; return {move(args)}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template internal::core_function_t lower(T t) { std::tuple args{std::forward(t)}; return {move(args)}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template internal::core_function_t upper(T t) { std::tuple args{std::forward(t)}; return {move(args)}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ inline internal::core_function_t changes() { return {{}}; } /** * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template internal::core_function_t trim(T t) { std::tuple args{std::forward(t)}; return {move(args)}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template internal::core_function_t trim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template internal::core_function_t ltrim(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template internal::core_function_t ltrim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template internal::core_function_t rtrim(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template internal::core_function_t rtrim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } /** * HEX(X) function https://sqlite.org/lang_corefunc.html#hex */ template internal::core_function_t hex(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote */ template internal::core_function_t quote(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob */ template internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr */ template internal::core_function_t instr(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ template internal::core_function_t replace(X x, Y y, Z z) { std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; return {move(args)}; } /** * ROUND(X) function https://sqlite.org/lang_corefunc.html#round */ template internal::core_function_t round(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round */ template internal::core_function_t round(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } #if SQLITE_VERSION_NUMBER >= 3007016 /** * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template internal::core_function_t char_(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ inline internal::core_function_t random() { return {{}}; } #endif /** * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template internal::core_function_t coalesce(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template internal::core_function_t date(Args... args) { std::tuple t{std::forward(args)...}; return {move(t)}; } /** * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template internal::core_function_t time(Args... args) { std::tuple t{std::forward(args)...}; return {move(t)}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template internal::core_function_t datetime(Args... args) { std::tuple t{std::forward(args)...}; return {move(t)}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template internal::core_function_t julianday(Args... args) { std::tuple t{std::forward(args)...}; return {move(t)}; } /** * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template internal::core_function_t strftime(Args... args) { std::tuple t{std::forward(args)...}; return {move(t)}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { std::tuple args{std::forward(n)}; return {move(args)}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template internal::core_function_t substr(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template internal::core_function_t substr(X x, Y y, Z z) { std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; return {move(args)}; } #ifdef SQLITE_SOUNDEX /** * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex */ template internal::core_function_t soundex(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } #endif /** * TOTAL(X) aggregate function. */ template internal::core_function_t total(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * SUM(X) aggregate function. */ template internal::core_function_t, internal::sum_string, X> sum(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * COUNT(X) aggregate function. */ template internal::core_function_t count(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * COUNT(*) without FROM function. */ inline internal::count_asterisk_without_type count() { return {}; } /** * COUNT(*) with FROM function. Specified type T will be serializeed as * a from argument. */ template internal::count_asterisk_t count() { return {}; } /** * AVG(X) aggregate function. */ template internal::core_function_t avg(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * MAX(X) aggregate function. */ template internal::core_function_t, internal::max_string, X> max(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * MIN(X) aggregate function. */ template internal::core_function_t, internal::min_string, X> min(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * GROUP_CONCAT(X) aggregate function. */ template internal::core_function_t group_concat(X x) { std::tuple args{std::forward(x)}; return {move(args)}; } /** * GROUP_CONCAT(X, Y) aggregate function. */ template internal::core_function_t group_concat(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; return {move(args)}; } template::value + std::is_base_of::value > 0)>::type> internal::add_t operator+(L l, R r) { return {std::move(l), std::move(r)}; } template::value + std::is_base_of::value > 0)>::type> internal::sub_t operator-(L l, R r) { return {std::move(l), std::move(r)}; } template::value + std::is_base_of::value > 0)>::type> internal::mul_t operator*(L l, R r) { return {std::move(l), std::move(r)}; } template::value + std::is_base_of::value > 0)>::type> internal::div_t operator/(L l, R r) { return {std::move(l), std::move(r)}; } template::value + std::is_base_of::value > 0)>::type> internal::mod_t operator%(L l, R r) { return {std::move(l), std::move(r)}; } } #pragma once 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; } }; 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); } } } #pragma once #include // std::string #include // std::declval #include // std::tuple, std::get, std::tuple_size // #include "is_base_of_template.h" // #include "tuple_helper.h" // #include "optional_container.h" namespace sqlite_orm { namespace internal { /** * DISCTINCT generic container. */ template struct distinct_t { T t; operator std::string() const { return "DISTINCT"; } }; /** * ALL generic container. */ template struct all_t { T t; operator std::string() const { return "ALL"; } }; template struct columns_t { using columns_type = std::tuple; columns_type columns; bool distinct = false; static constexpr const int count = std::tuple_size::value; }; struct set_string { operator std::string() const { return "SET"; } }; template struct set_t : set_string { using assigns_type = std::tuple; assigns_type assigns; set_t(assigns_type assigns_) : assigns(move(assigns_)) {} }; /** * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). * Is useful when mapped type is derived from other type and base class has members mapped to a storage. */ template struct column_pointer { using type = T; using field_type = F; field_type field; }; /** * Subselect object type. */ template struct select_t { using return_type = T; using conditions_type = std::tuple; return_type col; conditions_type conditions; bool highest_level = false; }; /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ template struct compound_operator { using left_type = L; using right_type = R; left_type left; right_type right; compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { this->left.highest_level = true; this->right.highest_level = true; } }; struct union_base { bool all = false; operator std::string() const { if(!this->all) { return "UNION"; } else { return "UNION ALL"; } } }; /** * UNION object type. */ template struct union_t : public compound_operator, union_base { using 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) : union_t(std::move(l), std::move(r), false) {} }; /** * EXCEPT object type. */ template struct except_t : public compound_operator { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; operator std::string() const { return "EXCEPT"; } }; /** * INTERSECT object type. */ template struct intersect_t : public compound_operator { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; operator std::string() const { return "INTERSECT"; } }; /** * Generic way to get DISTINCT value from any type. */ template bool get_distinct(const T &) { return false; } template bool get_distinct(const columns_t &cols) { return cols.distinct; } template struct asterisk_t { using type = T; }; template struct object_t { using type = T; }; template struct then_t { using expression_type = T; expression_type expression; }; template struct simple_case_t { using return_type = R; using case_expression_type = T; using args_type = std::tuple; using else_expression_type = E; optional_container case_expression; args_type args; optional_container else_expression; }; /** * T is a case expression type * E is else type (void is ELSE is omitted) * Args... is a pack of WHEN expressions */ template struct simple_case_builder { using return_type = R; using case_expression_type = T; using args_type = std::tuple; using else_expression_type = E; optional_container case_expression; args_type args; optional_container else_expression; template simple_case_builder> when(W w, then_t t) { using result_args_type = std::tuple>; std::pair newPair{std::move(w), std::move(t.expression)}; result_args_type result_args = std::tuple_cat(std::move(this->args), std::move(std::make_tuple(newPair))); std::get::value - 1>(result_args) = std::move(newPair); return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; } simple_case_t end() { return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; } template simple_case_builder else_(El el) { return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; } }; template void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); } } template internal::then_t then(T t) { return {std::move(t)}; } template internal::simple_case_builder case_(T t) { return {{std::move(t)}}; } template internal::simple_case_builder case_() { return {}; } template internal::distinct_t distinct(T t) { return {std::move(t)}; } template internal::all_t all(T t) { return {std::move(t)}; } template internal::columns_t distinct(internal::columns_t cols) { cols.distinct = true; return cols; } /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) */ template internal::set_t set(Args... args) { using arg_tuple = std::tuple; static_assert(std::tuple_size::value == internal::count_tuple::value, "set function accepts assign operators only"); return {std::make_tuple(std::forward(args)...)}; } template internal::columns_t columns(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * Use it like this: * struct MyType : BaseType { ... }; * storage.select(column(&BaseType::id)); */ template internal::column_pointer column(F f) { return {std::move(f)}; } /** * Public function for subselect query. Is useful in UNION queries. */ template internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), std::make_tuple(std::forward(args)...)}; } /** * Public function for UNION operator. * lhs and rhs are subselect objects. * Look through example in examples/union.cpp */ template internal::union_t union_(L lhs, R rhs) { return {std::move(lhs), std::move(rhs)}; } /** * Public function for EXCEPT operator. * lhs and rhs are subselect objects. * Look through example in examples/except.cpp */ template internal::except_t except(L lhs, R rhs) { return {std::move(lhs), std::move(rhs)}; } template internal::intersect_t intersect(L lhs, R rhs) { return {std::move(lhs), std::move(rhs)}; } /** * Public function for UNION ALL operator. * lhs and rhs are subselect objects. * Look through example in examples/union.cpp */ template internal::union_t union_all(L lhs, R rhs) { return {std::move(lhs), std::move(rhs), true}; } /** * SELECT * FROM T function. * T is typed mapped to a storage. * Example: auto rows = storage.select(asterisk()); * // decltype(rows) is std::vector> * If you need to fetch result as objects not tuple please use `object` instead. */ template internal::asterisk_t asterisk() { return {}; } /** * SELECT * FROM T function. * T is typed mapped to a storage. * Example: auto rows = storage.select(object()); * // decltype(rows) is std::vector * If you need to fetch result as tuples not objects please use `asterisk` instead. */ template internal::object_t object() { return {}; } } #pragma once #include // std::enable_if, std::is_member_pointer // #include "select_constraints.h" // #include "column.h" namespace sqlite_orm { namespace internal { /** * Trait class used to define table mapped type by setter/getter/member * T - member pointer */ template struct table_type; template struct table_type::value && !std::is_member_function_pointer::value>::type> { 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> { using type = T; }; } } #pragma once #include // std::string namespace sqlite_orm { struct table_info { int cid = 0; std::string name; std::string type; bool notnull = false; std::string dflt_value; int pk = 0; }; } #pragma once #include namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor */ struct statement_finalizer { sqlite3_stmt *stmt = nullptr; statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {} inline ~statement_finalizer() { sqlite3_finalize(this->stmt); } }; } #pragma once namespace sqlite_orm { /** * 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>; }; template using arithmetic_tag_t = typename arithmetic_tag::type; } #pragma once #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 "is_std_ptr.h" namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). */ template struct is_std_ptr : std::false_type {}; template struct is_std_ptr> : std::true_type { using element_type = T; static std::shared_ptr make(const T &v) { return std::make_shared(v); } }; template struct is_std_ptr> : std::true_type { using element_type = T; static std::unique_ptr make(const T &v) { return std::make_unique(v); } }; } namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. */ template struct statement_binder : std::false_type {}; /** * Specialization for arithmetic types. */ template struct statement_binder::value>> { int bind(sqlite3_stmt *stmt, int index, const V &value) { return bind(stmt, index, value, tag()); } private: using tag = arithmetic_tag_t; int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag &) { return sqlite3_bind_int(stmt, index, static_cast(value)); } int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag &) { return sqlite3_bind_int64(stmt, index, static_cast(value)); } int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag &) { return sqlite3_bind_double(stmt, index, static_cast(value)); } }; /** * Specialization for std::string and C-string. */ template struct statement_binder< V, std::enable_if_t::value || std::is_same::value>> { int bind(sqlite3_stmt *stmt, int index, const V &value) { return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT); } private: const char *string_data(const std::string &s) const { return s.c_str(); } const char *string_data(const char *s) const { return s; } }; #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>> { int bind(sqlite3_stmt *stmt, int index, const V &value) { std::wstring_convert> converter; std::string utf8Str = converter.to_bytes(value); return statement_binder().bind(stmt, index, utf8Str); } }; #endif // SQLITE_ORM_OMITS_CODECVT /** * Specialization for std::nullptr_t. */ template<> struct statement_binder { int bind(sqlite3_stmt *stmt, int index, const std::nullptr_t &) { return sqlite3_bind_null(stmt, index); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<> struct statement_binder { int bind(sqlite3_stmt *stmt, int index, const std::nullopt_t &) { return sqlite3_bind_null(stmt, index); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_binder::value>> { using value_type = typename is_std_ptr::element_type; int bind(sqlite3_stmt *stmt, int index, const V &value) { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, nullptr); } } }; /** * Specialization for optional type (std::vector). */ template<> struct statement_binder, void> { int bind(sqlite3_stmt *stmt, int index, const std::vector &value) { if(value.size()) { return sqlite3_bind_blob(stmt, index, (const void *)&value.front(), int(value.size()), SQLITE_TRANSIENT); } else { return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); } } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_binder, void> { using value_type = T; int bind(sqlite3_stmt *stmt, int index, const std::optional &value) { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, std::nullopt); } } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED namespace internal { template using is_bindable = std::integral_constant>::value>; struct conditional_binder_base { 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 { using conditional_binder_base::conditional_binder_base; int operator()(const T &t) const { return statement_binder().bind(this->stmt, this->index++, t); } }; template struct conditional_binder : conditional_binder_base { using conditional_binder_base::conditional_binder_base; int operator()(const T &) const { return SQLITE_OK; } }; template struct bindable_filter_single; template struct bindable_filter_single::value>::type> { using type = std::tuple; }; template struct bindable_filter_single::value>::type> { using type = std::tuple<>; }; template struct bindable_filter; template struct bindable_filter> { using type = typename conc_tuple::type...>::type; }; } } #pragma once #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll +#include // atof, atoi, atoll #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 // std::copy #include // std::back_inserter #include // std::tuple, std::tuple_size, std::tuple_element // #include "arithmetic_tag.h" // #include "journal_mode.h" #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 #undef DELETE #endif enum class journal_mode : signed char { DELETE = 0, TRUNCATE = 1, PERSIST = 2, MEMORY = 3, WAL = 4, OFF = 5, }; namespace internal { inline const std::string &to_string(journal_mode j) { static std::string res[] = { "DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF", }; return res[static_cast(j)]; } inline std::unique_ptr journal_mode_from_string(const std::string &str) { std::string upper_str; std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { return static_cast(std::toupper(static_cast(c))); }); static std::array all = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, journal_mode::MEMORY, journal_mode::WAL, journal_mode::OFF, }}; for(auto j: all) { if(to_string(j) == upper_str) { return std::make_unique(j); } } return {}; } } } // #include "error_code.h" namespace sqlite_orm { /** * Helper class used to cast values from argv to V class * which depends from column type. * */ template struct row_extractor { // used in sqlite3_exec (select) V extract(const char *row_value); // used in sqlite_column (iteration, get_all) V extract(sqlite3_stmt *stmt, int columnIndex); }; /** * Specialization for arithmetic types. */ template struct row_extractor::value>> { V extract(const char *row_value) { return extract(row_value, tag()); } V extract(sqlite3_stmt *stmt, int columnIndex) { return extract(stmt, columnIndex, tag()); } private: using tag = arithmetic_tag_t; V extract(const char *row_value, const int_or_smaller_tag &) { return static_cast(atoi(row_value)); } V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag &) { return static_cast(sqlite3_column_int(stmt, columnIndex)); } V extract(const char *row_value, const bigint_tag &) { return static_cast(atoll(row_value)); } V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag &) { return static_cast(sqlite3_column_int64(stmt, columnIndex)); } V extract(const char *row_value, const real_tag &) { return static_cast(atof(row_value)); } V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag &) { return static_cast(sqlite3_column_double(stmt, columnIndex)); } }; /** * Specialization for std::string. */ template<> struct row_extractor { std::string extract(const char *row_value) { if(row_value) { return row_value; } else { return {}; } } std::string extract(sqlite3_stmt *stmt, int columnIndex) { auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); if(cStr) { return cStr; } else { return {}; } } }; #ifndef SQLITE_ORM_OMITS_CODECVT /** * Specialization for std::wstring. */ template<> struct row_extractor { std::wstring extract(const char *row_value) { if(row_value) { std::wstring_convert> converter; return converter.from_bytes(row_value); } else { return {}; } } std::wstring extract(sqlite3_stmt *stmt, int columnIndex) { auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); if(cStr) { std::wstring_convert> converter; return converter.from_bytes(cStr); } else { return {}; } } }; #endif // SQLITE_ORM_OMITS_CODECVT template struct row_extractor::value>> { using value_type = typename is_std_ptr::element_type; V extract(const char *row_value) { if(row_value) { return is_std_ptr::make(row_extractor().extract(row_value)); } else { return {}; } } V extract(sqlite3_stmt *stmt, int columnIndex) { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); } else { return {}; } } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct row_extractor, void> { using value_type = T; std::optional extract(const char *row_value) { if(row_value) { return std::make_optional(row_extractor().extract(row_value)); } else { return std::nullopt; } } std::optional extract(sqlite3_stmt *stmt, int columnIndex) { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return std::make_optional(row_extractor().extract(stmt, columnIndex)); } else { return std::nullopt; } } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** * Specialization for std::vector. */ template<> struct row_extractor> { std::vector extract(const char *row_value) { if(row_value) { auto len = ::strlen(row_value); return this->go(row_value, len); } else { return {}; } } std::vector extract(sqlite3_stmt *stmt, int columnIndex) { auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); return this->go(bytes, len); } protected: std::vector go(const char *bytes, size_t len) { if(len) { std::vector res; res.reserve(len); std::copy(bytes, bytes + len, std::back_inserter(res)); return res; } else { return {}; } } }; template struct row_extractor> { std::tuple extract(char **argv) { std::tuple res; this->extract::value>(res, argv); return res; } std::tuple extract(sqlite3_stmt *stmt, int /*columnIndex*/) { std::tuple res; this->extract::value>(res, stmt); return res; } protected: template::type * = nullptr> void extract(std::tuple &t, sqlite3_stmt *stmt) { 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 *) { //.. } template::type * = nullptr> void extract(std::tuple &t, char **argv) { using tuple_type = typename std::tuple_element>::type; std::get(t) = row_extractor().extract(argv[I - 1]); this->extract(t, argv); } template::type * = nullptr> void extract(std::tuple &, char **) { //.. } }; /** * Specialization for journal_mode. */ template<> struct row_extractor { journal_mode extract(const char *row_value) { if(row_value) { 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)); } } else { throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); } } journal_mode extract(sqlite3_stmt *stmt, int columnIndex) { auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); return this->extract(cStr); } }; } #pragma once #include namespace sqlite_orm { enum class sync_schema_result { /** * created new table, table with the same tablename did not exist */ new_table_created, /** * table schema is the same as storage, nothing to be done */ already_in_sync, /** * removed excess columns in table (than storage) without dropping a table */ old_columns_removed, /** * lacking columns in table (than storage) added without dropping a table */ new_columns_added, /** * both old_columns_removed and new_columns_added */ new_columns_added_and_old_columns_removed, /** * old table is dropped and new is recreated. Reasons : * 1. delete excess columns in the table than storage if preseve = false * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint * 3. Reasons 1 and 2 both together * 4. data_type mismatch between table and storage. */ dropped_and_recreated, }; inline std::ostream &operator<<(std::ostream &os, sync_schema_result value) { switch(value) { case sync_schema_result::new_table_created: return os << "new table created"; case sync_schema_result::already_in_sync: return os << "table and storage is already in sync."; case sync_schema_result::old_columns_removed: return os << "old excess columns removed"; case sync_schema_result::new_columns_added: return os << "new columns added"; case sync_schema_result::new_columns_added_and_old_columns_removed: return os << "old excess columns removed and new columns added"; case sync_schema_result::dropped_and_recreated: return os << "old table dropped and recreated"; } return os; } } #pragma once #include // std::tuple, std::make_tuple #include // std::string #include // std::forward // #include "indexed_column.h" #include // std::string namespace sqlite_orm { namespace internal { template struct indexed_column_t { using column_type = C; column_type column_or_expression; std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); res._collation_name = move(name); return res; } indexed_column_t asc() { auto res = std::move(*this); res._order = 1; return res; } indexed_column_t desc() { auto res = std::move(*this); res._order = -1; return res; } }; template struct indexed_column_maker { using type = indexed_column_t; indexed_column_t operator()(C col) const { return {std::move(col)}; } }; template struct indexed_column_maker> { using type = indexed_column_t; indexed_column_t operator()(indexed_column_t col) const { return std::move(col); } }; template auto make_indexed_column(C col) { indexed_column_maker maker; return maker(std::move(col)); } } /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) */ template internal::indexed_column_t indexed_column(C column_or_expression) { return {std::move(column_or_expression)}; } } namespace sqlite_orm { namespace internal { struct index_base { std::string name; bool unique = false; }; template struct index_t : index_base { using columns_type = std::tuple; using object_type = void; index_t(std::string name_, bool unique_, columns_type columns_) : index_base{move(name_), unique_}, columns(move(columns_)) {} columns_type columns; }; } template internal::index_t::type...> make_index(const std::string &name, Cols... cols) { return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template internal::index_t::type...> make_unique_index(const std::string &name, Cols... cols) { return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } } #pragma once // #include "alias.h" namespace sqlite_orm { namespace internal { /** * If T is alias than mapped_type_proxy::type is alias::type * otherwise T is T. */ template struct mapped_type_proxy { using type = T; }; template struct mapped_type_proxy::value>::type> { using type = typename T::type; }; } } #pragma once #include // std::string namespace sqlite_orm { namespace internal { struct rowid_t { operator std::string() const { return "rowid"; } }; struct oid_t { operator std::string() const { return "oid"; } }; struct _rowid_t { operator std::string() const { return "_rowid_"; } }; template struct table_rowid_t : public rowid_t { using type = T; }; template struct table_oid_t : public oid_t { using type = T; }; template struct table__rowid_t : public _rowid_t { using type = T; }; } inline internal::rowid_t rowid() { return {}; } inline internal::oid_t oid() { return {}; } inline internal::_rowid_t _rowid_() { return {}; } template internal::table_rowid_t rowid() { return {}; } template internal::table_oid_t oid() { return {}; } template internal::table__rowid_t _rowid_() { return {}; } } #pragma once #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper // #include "core_functions.h" // #include "select_constraints.h" // #include "operators.h" // #include "rowid.h" // #include "alias.h" // #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; 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 type = std::tuple; }; /** * 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 {}; /** * Final specialisation */ template struct storage_mapped_columns_impl, T, void> { using type = std::tuple<>; }; 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; }; template struct storage_mapped_columns_impl< S, T, typename std::enable_if::value>::type> : storage_mapped_columns_impl {}; } } } namespace sqlite_orm { using int64 = sqlite_int64; using uint64 = sqlite_uint64; namespace internal { /** * This is a proxy class used to define what type must have result type depending on select * arguments (member pointer, aggregate functions, etc). Below you can see specializations * for different types. E.g. specialization for internal::length_t has `type` int cause * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals * c++ SELECT return type for T * T - C++ type * SFINAE - sfinae argument */ template struct column_result_t; template struct column_result_t::value && !std::is_member_function_pointer::value>::type> { using type = F; }; /** * 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; }; /** * 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, S, X>, void> { using type = std::unique_ptr::type>; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t { using type = int; }; template struct column_result_t, void> { using type = typename column_result_t::type; }; template struct column_result_t, void> { using type = typename column_result_t::type; }; template struct column_result_t, void> { using type = std::string; }; template struct column_result_t, void> { using type = double; }; template struct column_result_t, void> { using type = double; }; template struct column_result_t, void> { using type = double; }; template struct column_result_t, void> { using type = double; }; template struct column_result_t, void> { using type = double; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t, void> { using type = int; }; template struct column_result_t { using type = int64; }; template struct column_result_t { using type = int64; }; template struct column_result_t { using type = int64; }; template struct column_result_t, void> { using type = int64; }; template struct column_result_t, void> { using type = int64; }; template struct column_result_t, void> { using type = int64; }; template struct column_result_t, void> { using type = typename column_result_t::type; }; template struct column_result_t> : column_result_t {}; template struct column_result_t, void> { using type = std::tuple::type>::type...>; }; 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; static_assert(std::is_same::value, "Compound subselect queries must return same types"); using type = left_result; }; /** * Result for the most simple queries like `SELECT 1` */ template struct column_result_t::value>::type> { using type = T; }; /** * Result for the most simple queries like `SELECT 'ototo'` */ template struct column_result_t { using type = std::string; }; template struct column_result_t { using type = std::string; }; template struct column_result_t, void> : column_result_t::type, void> {}; template struct column_result_t, void> { using type = typename storage_traits::storage_mapped_columns::type; }; template struct column_result_t, void> { using type = T; }; template struct column_result_t, void> { using type = T; }; template struct column_result_t, void> { using type = R; }; template struct column_result_t, void> { using type = bool; }; template struct column_result_t, void> { using type = bool; }; template struct column_result_t, void> { using type = bool; }; template struct column_result_t, void> : column_result_t {}; } } #pragma once #include // std::string #include // std::remove_reference, std::is_same, std::is_base_of #include // std::vector #include // std::tuple_size, std::tuple_element #include // std::reverse, std::find_if // #include "column_result.h" // #include "static_magic.h" // #include "typed_comparator.h" // #include "constraints.h" // #include "tuple_helper.h" // #include "table_info.h" // #include "type_printer.h" // #include "column.h" namespace sqlite_orm { namespace internal { struct table_base { /** * Table name. */ std::string name; bool _without_rowid = false; }; /** * Table interface class. Implementation is hidden in `table_impl` class. */ template struct table_t : table_base { using object_type = T; using columns_type = std::tuple; static constexpr const int columns_count = static_cast(std::tuple_size::value); columns_type columns; table_t(decltype(name) name_, columns_type columns_) : table_base{std::move(name_)}, columns(std::move(columns_)) {} table_t without_rowid() const { auto res = *this; res._without_rowid = true; return res; } /** * 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; } /** * @return vector of column names of table. */ std::vector column_names() const { std::vector res; this->for_each_column([&res](auto &c) { res.push_back(c.name); }); return res; } /** * Calls **l** with every primary key dedicated constraint */ template void for_each_primary_key(const L &l) const { iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); } 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; } 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; } template std::vector composite_key_columns_names(const constraints::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) { res.push_back(this->find_column_name(v)); }); return res; } /** * 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> std::string find_column_name(F O::*m) const { std::string res; this->template for_each_column_with_field_type([&res, m](auto &c) { if(c.member_pointer == m) { res = c.name; } }); return res; } /** * Searches column name by class getter function member pointer passed as first argument. * @return column name or empty string if nothing found. */ template std::string find_column_name(G getter, typename std::enable_if::value>::type * = nullptr) const { std::string res; 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; } /** * Searches column name by class setter function member pointer passed as first argument. * @return column name or empty string if nothing found. */ template std::string find_column_name(S setter, typename std::enable_if::value>::type * = nullptr) const { std::string res; 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; } /** * 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 l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template void for_each_column(const L &l) const { iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); } template void for_each_column_with_field_type(const L &l) const { iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; using field_type = typename column_field_type::type; static_if{}>(l)(column); }); } /** * 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 l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template void for_each_column_with(const L &l) const { using tuple_helper::tuple_contains_type; iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; using constraints_type = typename column_constraints_type::type; static_if{}>(l)(column); }); } std::vector get_table_info() const { std::vector res; res.reserve(size_t(this->columns_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; } }; } /** * 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)...)}; } } #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::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 "row_extractor.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; template struct field_value_holder::returns_lvalue>::type> { using type = typename getter_traits::field_type; const type &value; }; template struct field_value_holder::returns_lvalue>::type> { using type = typename getter_traits::field_type; type value; }; } } namespace sqlite_orm { namespace internal { struct storage_impl_base { 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; } void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } static bool get_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; // 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; } 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}); } 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; } }; /** * This is a generic implementation. Used as a tail in storage_impl inheritance chain */ template struct storage_impl; template struct storage_impl : public storage_impl { using table_type = H; using super = storage_impl; storage_impl(H h, Ts... ts) : super(std::forward(ts)...), table(std::move(h)) {} table_type table; template void for_each(const L &l) { this->super::for_each(l); l(*this); } #if SQLITE_VERSION_NUMBER >= 3006019 /** * Returns foreign keys count in table definition */ int foreign_keys_count() { auto res = 0; iterate_tuple(this->table.columns, [&res](auto &c) { if(internal::is_foreign_key::type>::value) { ++res; } }); 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. */ template std::string column_name_simple(F O::*m) const { return this->table.find_column_name(m); } /** * Cute function used to find column name by its type and member pointer. Uses SFINAE to * skip inequal type O. */ template std::string column_name(F O::*m, typename std::enable_if::value>::type * = nullptr) const { return this->table.find_column_name(m); } /** * Opposite version of function defined above. Just calls same function in superclass. */ template std::string column_name(F O::*m, typename std::enable_if::value>::type * = nullptr) const { return this->super::column_name(m); } template std::string column_name(const column_pointer &c, typename std::enable_if::value>::type * = nullptr) const { return this->column_name_simple(c.field); } template std::string column_name(const column_pointer &c, typename std::enable_if::value>::type * = nullptr) const { return this->super::column_name(c); } template const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return *this; } template const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return this->super::template get_impl(); } template auto &get_impl(typename std::enable_if::value>::type * = nullptr) { return *this; } template auto &get_impl(typename std::enable_if::value>::type * = nullptr) { return this->super::template get_impl(); } template const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { return &this->table; } template const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { return this->super::template find_table(); } std::string find_table_name(std::type_index ti) const { std::type_index thisTypeIndex{typeid(typename H::object_type)}; if(thisTypeIndex == ti) { return this->table.name; } else { return this->super::find_table_name(ti); } } 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 << " "; } auto query = ss.str(); sqlite3_stmt *stmt; auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(prepareResult == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { //.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } /** * 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::ignore = columnsToIgnore; 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 << " "; } ss << "FROM '" << this->table.name << "' "; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { //.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } sync_schema_result schema_status(sqlite3 *db, bool preserve) const { auto res = sync_schema_result::already_in_sync; // first let's see if table with such name exists.. auto gottaCreateTable = !this->table_exists(this->table.name, db); if(!gottaCreateTable) { // get table info provided in `make_table` call.. auto storageTableInfo = this->table.get_table_info(); // now get current table info from db using `PRAGMA table_info` query.. auto dbTableInfo = this->get_table_info(this->table.name, db); // this vector will contain pointers to columns that gotta be added.. std::vector columnsToAdd; if(this->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { gottaCreateTable = true; } 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; } private: using self = storage_impl; }; template<> struct storage_impl<> : storage_impl_base { std::string find_table_name(std::type_index) const { return {}; } template void for_each(const L &) {} int foreign_keys_count() { return 0; } template const void *find_table() const { return nullptr; } }; template struct is_storage_impl : std::false_type {}; 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::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::stringstream #include // std::map #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple #include // std::forward, std::pair #include // std::find #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED #include // std::optional #endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "alias.h" // #include "row_extractor_builder.h" // #include "row_extractor.h" // #include "mapped_row_extractor.h" #include // #include "object_from_column_builder.h" #include // #include "row_extractor.h" namespace sqlite_orm { namespace internal { struct object_from_column_builder_base { sqlite3_stmt *stmt = nullptr; mutable int index = 0; }; /** * This is a cute lambda replacement which is used in several places. */ template struct object_from_column_builder : object_from_column_builder_base { using object_type = O; object_type &object; object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} template void operator()(const C &c) const { using field_type = typename C::field_type; auto value = row_extractor().extract(this->stmt, this->index++); if(c.member_pointer) { this->object.*c.member_pointer = std::move(value); } else { ((this->object).*(c.setter))(std::move(value)); } } }; } } namespace sqlite_orm { namespace internal { /** * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. * Main difference from regular `row_extractor` is that this class takes table info which is required * for constructing objects by member pointers. To construct please use `row_extractor_builder` class * Type arguments: * V is value type just like regular `row_extractor` has * T is table info class `table_t` */ template struct mapped_row_extractor { using table_info_t = T; mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { V res; object_from_column_builder builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } const table_info_t &tableInfo; }; } } namespace sqlite_orm { namespace internal { /** * This builder is used to construct different row extractors depending on type. * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. */ template struct row_extractor_builder; template struct row_extractor_builder { row_extractor operator()(const I * /*tableInfo*/) const { return {}; } }; template struct row_extractor_builder { mapped_row_extractor operator()(const I *tableInfo) const { return {*tableInfo}; } }; template auto make_row_extractor(const I *tableInfo) { using builder_t = row_extractor_builder; return builder_t{}(tableInfo); } } } // #include "error_code.h" // #include "type_printer.h" // #include "tuple_helper.h" // #include "constraints.h" // #include "type_is_nullable.h" // #include "field_printer.h" // #include "rowid.h" // #include "operators.h" // #include "select_constraints.h" // #include "core_functions.h" // #include "conditions.h" // #include "statement_binder.h" // #include "column_result.h" // #include "mapped_type_proxy.h" // #include "sync_schema_result.h" // #include "table_info.h" // #include "storage_impl.h" // #include "journal_mode.h" // #include "field_value_holder.h" // #include "view.h" #include // std::shared_ptr #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::decay #include // std::move #include // std::ptrdiff_t #include // std::input_iterator_tag #include // std::system_error #include // std::make_error_code // #include "row_extractor.h" // #include "statement_finalizer.h" // #include "error_code.h" // #include "object_from_column_builder.h" namespace sqlite_orm { namespace internal { template struct iterator_t { using view_type = V; using value_type = typename view_type::mapped_type; 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. */ std::shared_ptr stmt; view_type &view; /** * shared_ptr is used over unique_ptr here * so that the iterator can be copyable. */ std::shared_ptr current; void extract_value(std::unique_ptr &temp) { temp = std::make_unique(); auto &storage = this->view.storage; auto &impl = storage.template get_impl(); object_from_column_builder builder{*temp, *this->stmt}; impl.table.for_each_column(builder); } public: using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; using iterator_category = std::input_iterator_tag; iterator_t(sqlite3_stmt *stmt_, view_type &view_) : stmt(std::make_shared(stmt_)), view(view_) { this->operator++(); } iterator_t(const iterator_t &) = default; iterator_t(iterator_t &&) = default; iterator_t &operator=(iterator_t &&) = default; iterator_t &operator=(const iterator_t &) = default; ~iterator_t() { if(this->stmt) { statement_finalizer f{*this->stmt}; } } value_type &operator*() { if(!this->stmt) { throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); } if(!this->current) { std::unique_ptr value; this->extract_value(value); this->current = move(value); } return *this->current; } value_type *operator->() { return &(this->operator*()); } void operator++() { if(this->stmt && *this->stmt) { auto ret = sqlite3_step(*this->stmt); switch(ret) { case SQLITE_ROW: this->current = nullptr; break; case SQLITE_DONE: { statement_finalizer f{*this->stmt}; *this->stmt = nullptr; } 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)); } } } } void operator++(int) { this->operator++(); } bool operator==(const iterator_t &other) const { if(this->stmt && other.stmt) { return *this->stmt == *other.stmt; } else { if(!this->stmt && !other.stmt) { return true; } else { return false; } } } bool operator!=(const iterator_t &other) const { return !(*this == other); } }; } } // #include "ast_iterator.h" #include // std::vector #include // std::reference_wrapper // #include "conditions.h" // #include "select_constraints.h" // #include "operators.h" // #include "tuple_helper.h" // #include "core_functions.h" // #include "prepared_statement.h" #include #include // std::iterator_traits #include // std::string #include // std::true_type, std::false_type #include // std::pair // #include "connection_holder.h" #include #include // std::string #include // std::system_error // #include "error_code.h" namespace sqlite_orm { namespace internal { struct connection_holder { connection_holder(std::string filename_) : filename(move(filename_)) {} void retain() { ++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)); } } } void release() { --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)); } } } sqlite3 *get() const { return this->db; } int retain_count() const { return this->_retain_count; } const std::string filename; protected: sqlite3 *db = nullptr; int _retain_count = 0; }; struct connection_ref { connection_ref(connection_holder &holder_) : holder(holder_) { this->holder.retain(); } connection_ref(const connection_ref &other) : holder(other.holder) { this->holder.retain(); } connection_ref(connection_ref &&other) : holder(other.holder) { this->holder.retain(); } ~connection_ref() { this->holder.release(); } sqlite3 *get() const { return this->holder.get(); } protected: connection_holder &holder; }; } } // #include "select_constraints.h" namespace sqlite_orm { namespace internal { struct prepared_statement_base { sqlite3_stmt *stmt = nullptr; connection_ref con; ~prepared_statement_base() { if(this->stmt) { sqlite3_finalize(this->stmt); this->stmt = nullptr; } } std::string sql() const { if(this->stmt) { if(auto res = sqlite3_sql(this->stmt)) { return res; } else { return {}; } } else { return {}; } } #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 {}; } } else { return {}; } } #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 {}; } } else { return {}; } } #endif }; template struct prepared_statement_t : prepared_statement_base { using expression_type = T; expression_type t; prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; template struct is_prepared_statement : std::false_type {}; template struct is_prepared_statement> : std::true_type {}; /** * T - type of object to obtain from a database */ template struct get_all_t { using type = T; using return_type = R; using conditions_type = std::tuple; conditions_type conditions; }; template struct get_all_pointer_t { using type = T; using return_type = R; using conditions_type = std::tuple; conditions_type conditions; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct get_all_optional_t { using type = T; using return_type = R; using conditions_type = std::tuple; conditions_type conditions; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct update_all_t; template struct update_all_t, Wargs...> { using set_type = set_t; using conditions_type = std::tuple; set_type set; conditions_type conditions; }; template struct remove_all_t { using type = T; using conditions_type = std::tuple; conditions_type conditions; }; template struct get_t { using type = T; using ids_type = std::tuple; ids_type ids; }; template struct get_pointer_t { using type = T; using ids_type = std::tuple; ids_type ids; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct get_optional_t { using type = T; using ids_type = std::tuple; ids_type ids; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct update_t { using type = T; type obj; }; template struct remove_t { using type = T; using ids_type = std::tuple; ids_type ids; }; template struct insert_t { using type = T; type obj; }; template struct insert_explicit { using type = T; using columns_type = columns_t; type obj; columns_type columns; }; template struct replace_t { using type = T; type obj; }; template struct insert_range_t { using iterator_type = It; using object_type = typename std::iterator_traits::value_type; std::pair range; }; template struct replace_range_t { using iterator_type = It; using object_type = typename std::iterator_traits::value_type; std::pair range; }; } /** * Create a replace range statement */ template internal::replace_range_t replace_range(It from, It to) { return {{std::move(from), std::move(to)}}; } /** * Create an insert range statement */ template internal::insert_range_t insert_range(It from, It to) { return {{std::move(from), std::move(to)}}; } /** * Create a replace statement. * T is an object type mapped to a storage. * Usage: storage.replace(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.replace(std::ref(myUserInstance)); */ template internal::replace_t replace(T obj) { return {std::move(obj)}; } /** * Create an insert statement. * T is an object type mapped to a storage. * Usage: storage.insert(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.insert(std::ref(myUserInstance)); */ template internal::insert_t insert(T obj) { return {std::move(obj)}; } /** * Create an explicit insert statement. * T is an object type mapped to a storage. * Cols is columns types aparameter pack. Must contain member pointers * Usage: storage.insert(myUserInstance, columns(&User::id, &User::name)); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); */ template internal::insert_explicit insert(T obj, internal::columns_t cols) { return {std::move(obj), std::move(cols)}; } /** * Create a remove statement * T is an object type mapped to a storage. * Usage: remove(5); */ template internal::remove_t remove(Ids... ids) { std::tuple idsTuple{std::forward(ids)...}; return {move(idsTuple)}; } /** * Create an update statement. * T is an object type mapped to a storage. * Usage: storage.update(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.update(std::ref(myUserInstance)); */ template internal::update_t update(T obj) { return {std::move(obj)}; } /** * Create a get statement. * T is an object type mapped to a storage. * Usage: get(5); */ template internal::get_t get(Ids... ids) { std::tuple idsTuple{std::forward(ids)...}; return {move(idsTuple)}; } /** * Create a get pointer statement. * T is an object type mapped to a storage. * Usage: get_pointer(5); */ template internal::get_pointer_t get_pointer(Ids... ids) { std::tuple idsTuple{std::forward(ids)...}; return {move(idsTuple)}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED /** * Create a get optional statement. * T is an object type mapped to a storage. * Usage: get_optional(5); */ template internal::get_optional_t get_optional(Ids... ids) { std::tuple idsTuple{std::forward(ids)...}; return {move(idsTuple)}; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** * Create a remove all statement. * T is an object type mapped to a storage. * Usage: storage.remove_all(...); */ template internal::remove_all_t remove_all(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } /** * Create a get all statement. * T is an object type mapped to a storage. * Usage: storage.get_all(...); */ template internal::get_all_t, Args...> get_all(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } /** * Create a get all statement. * T is an object type mapped to a storage. * R is a container type. std::vector is default * Usage: storage.get_all(...); */ template internal::get_all_t get_all(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } /** * Create an update all statement. * Usage: storage.update_all(set(...), ...); */ template internal::update_all_t, Wargs...> update_all(internal::set_t set, Wargs... wh) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(wh)...}; return {std::move(set), move(conditions)}; } /** * Create a get all pointer statement. * T is an object type mapped to a storage. * Usage: storage.get_all_pointer(...); */ template internal::get_all_pointer_t>, Args...> get_all_pointer(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } /** * Create a get all pointer statement. * T is an object type mapped to a storage. * R is a container return type. std::vector> is default * Usage: storage.get_all_pointer(...); */ template internal::get_all_pointer_t get_all_pointer(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED /** * Create a get all optional statement. * T is an object type mapped to a storage. * Usage: storage.get_all_optional(...); */ template internal::get_all_optional_t>, Args...> get_all_optional(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } /** * Create a get all optional statement. * T is an object type mapped to a storage. * R is a container return type. std::vector> is default * Usage: storage.get_all_optional(...); */ template internal::get_all_optional_t get_all_optional(Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); args_tuple conditions{std::forward(args)...}; return {move(conditions)}; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED } // #include "values.h" #include // std::vector #include #include // std::tuple namespace sqlite_orm { namespace internal { template struct values_t { std::tuple tuple; }; template struct dynamic_values_t { std::vector vector; }; } template internal::values_t values(Args... args) { return {{std::forward(args)...}}; } template internal::dynamic_values_t values(std::vector vector) { return {{move(vector)}}; } } namespace sqlite_orm { namespace internal { /** * ast_iterator accepts any expression and a callable object * which will be called for any node of provided expression. * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then * callable object will be called with 5, &User::id and 10. * ast_iterator is used mostly in finding literals to be bound to * a statement. To use it just call `iterate_ast(object, callable);` * T is an ast element. E.g. where_t */ template struct ast_iterator { using node_type = T; /** * L is a callable type. Mostly is a templated lambda */ template void operator()(const T &t, const L &l) const { l(t); } }; /** * Simplified API */ template void iterate_ast(const T &t, const L &l) { ast_iterator iterator; iterator(t, l); } template struct ast_iterator, void> { using node_type = std::reference_wrapper; template void operator()(const node_type &r, const L &l) const { iterate_ast(r.get(), l); } }; template struct ast_iterator, void> { using node_type = where_t; template void operator()(const node_type &where, const L &l) const { iterate_ast(where.c, l); } }; template struct ast_iterator::value>::type> { 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); } }; template struct ast_iterator, void> { 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); } }; template struct ast_iterator, void> { using node_type = columns_t; template void operator()(const node_type &cols, const L &l) const { iterate_ast(cols.columns, l); } }; template struct ast_iterator, void> { using node_type = in_t; template void operator()(const node_type &in, const C &l) const { iterate_ast(in.l, l); iterate_ast(in.arg, l); } }; template struct ast_iterator, void> { using node_type = std::vector; template void operator()(const node_type &vec, const L &l) const { for(auto &i: vec) { iterate_ast(i, l); } } }; template<> struct ast_iterator, void> { using node_type = std::vector; template void operator()(const node_type &vec, const L &l) const { l(vec); } }; template struct ast_iterator::value>::type> { 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); } }; template struct ast_iterator, void> { 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); } }; template struct ast_iterator, void> { using node_type = get_all_t; template void operator()(const node_type &get, const L &l) const { iterate_ast(get.conditions, l); } }; template struct ast_iterator, void> { using node_type = get_all_pointer_t; template void operator()(const node_type &get, const L &l) const { iterate_ast(get.conditions, l); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct ast_iterator, void> { using node_type = get_all_optional_t; template void operator()(const node_type &get, const L &l) const { iterate_ast(get.conditions, l); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct ast_iterator, Wargs...>, void> { 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); } }; template struct ast_iterator, void> { using node_type = remove_all_t; template void operator()(const node_type &r, const L &l) const { iterate_ast(r.conditions, l); } }; template struct ast_iterator, void> { using node_type = set_t; template void operator()(const node_type &s, const L &l) const { iterate_ast(s.assigns, l); } }; template struct ast_iterator, void> { 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); }); } }; 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); } }; template struct ast_iterator, void> { using node_type = cast_t; template void operator()(const node_type &c, const L &l) const { iterate_ast(c.expression, l); } }; template struct ast_iterator, void> { using node_type = exists_t; template void operator()(const node_type &e, const L &l) const { iterate_ast(e.t, l); } }; template struct ast_iterator, void> { 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); }); } }; template struct ast_iterator, void> { 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); } }; template struct ast_iterator, void> { 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); } }; template struct ast_iterator, void> { using node_type = named_collate; template void operator()(const node_type &col, const L &l) const { iterate_ast(col.expr, l); } }; template struct ast_iterator, void> { using node_type = negated_condition_t; template void operator()(const node_type &neg, const L &l) const { iterate_ast(neg.c, l); } }; template struct ast_iterator, void> { using node_type = is_null_t; template void operator()(const node_type &i, const L &l) const { iterate_ast(i.t, l); } }; template struct ast_iterator, void> { using node_type = is_not_null_t; template void operator()(const node_type &i, const L &l) const { iterate_ast(i.t, l); } }; template struct ast_iterator, void> { using node_type = core_function_t; template void operator()(const node_type &f, const L &l) const { iterate_ast(f.args, l); } }; template struct ast_iterator, void> { using node_type = left_join_t; template void operator()(const node_type &j, const L &l) const { iterate_ast(j.constraint, l); } }; template struct ast_iterator, void> { using node_type = on_t; template void operator()(const node_type &o, const L &l) const { iterate_ast(o.arg, l); } }; template struct ast_iterator, void> { using node_type = join_t; template void operator()(const node_type &j, const L &l) const { iterate_ast(j.constraint, l); } }; template struct ast_iterator, void> { using node_type = left_outer_join_t; template void operator()(const node_type &j, const L &l) const { iterate_ast(j.constraint, l); } }; template struct ast_iterator, void> { using node_type = inner_join_t; template void operator()(const node_type &j, const L &l) const { iterate_ast(j.constraint, l); } }; template struct ast_iterator, void> { 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); }); iterate_tuple(c.args, [&l](auto &pair) { iterate_ast(pair.first, l); iterate_ast(pair.second, l); }); c.else_expression.apply([&l](auto &el) { iterate_ast(el, l); }); } }; template struct ast_iterator, void> { using node_type = as_t; template void operator()(const node_type &a, const L &l) const { iterate_ast(a.expression, l); } }; template struct ast_iterator, void> { using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { iterate_ast(a.lim, l); } }; template struct ast_iterator, void> { 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); }); } }; template struct ast_iterator, void> { 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); }); iterate_ast(a.lim, l); } }; template struct ast_iterator, void> { using node_type = distinct_t; template void operator()(const node_type &a, const L &l) const { iterate_ast(a.t, l); } }; template struct ast_iterator, void> { using node_type = all_t; template void operator()(const node_type &a, const L &l) const { iterate_ast(a.t, l); } }; template struct ast_iterator, void> { using node_type = bitwise_not_t; template void operator()(const node_type &a, const L &l) const { iterate_ast(a.argument, l); } }; template struct ast_iterator, void> { using node_type = values_t; template void operator()(const node_type &node, const L &l) const { iterate_ast(node.tuple, l); } }; template struct ast_iterator, void> { using node_type = dynamic_values_t; template void operator()(const node_type &node, const L &l) const { iterate_ast(node.vector, l); } }; template struct ast_iterator, void> { using node_type = collate_t; template void operator()(const node_type &node, const L &l) const { iterate_ast(node.expr, l); } }; } } // #include "prepared_statement.h" // #include "connection_holder.h" namespace sqlite_orm { namespace internal { template struct view_t { using mapped_type = T; using storage_type = S; using self = view_t; storage_type &storage; connection_ref connection; get_all_t, Args...> args; view_t(storage_type &stor, decltype(connection) conn, Args &&... args_) : storage(stor), connection(std::move(conn)), args{std::make_tuple(std::forward(args_)...)} {} size_t size() { return this->storage.template count(); } bool empty() { return !this->size(); } iterator_t end() { return {nullptr, *this}; } iterator_t begin() { sqlite3_stmt *stmt = nullptr; auto db = this->connection.get(); using context_t = serializator_context; context_t context{this->storage.impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(this->args, context); auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; 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)); } } }; } } // #include "ast_iterator.h" // #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 "pragma.h" #include // std::string #include #include // std::function #include // std::shared_ptr // #include "error_code.h" // #include "row_extractor.h" // #include "journal_mode.h" // #include "connection_holder.h" namespace sqlite_orm { namespace internal { struct storage_base; } struct pragma_t { using get_connection_t = std::function; pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} void busy_timeout(int value) { this->set_pragma("busy_timeout", value); } int busy_timeout() { return this->get_pragma("busy_timeout"); } sqlite_orm::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } void journal_mode(sqlite_orm::journal_mode value) { this->_journal_mode = -1; this->set_pragma("journal_mode", value); this->_journal_mode = static_cast_journal_mode)>(value); } int synchronous() { return this->get_pragma("synchronous"); } void synchronous(int value) { this->_synchronous = -1; this->set_pragma("synchronous", value); this->_synchronous = value; } int user_version() { return this->get_pragma("user_version"); } void user_version(int value) { this->set_pragma("user_version", value); } int auto_vacuum() { return this->get_pragma("auto_vacuum"); } void auto_vacuum(int value) { this->set_pragma("auto_vacuum", value); } protected: friend struct storage_base; public: int _synchronous = -1; signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; template T get_pragma(const std::string &name) { auto connection = this->get_connection(); auto query = "PRAGMA " + name; T result; auto db = connection.get(); auto rc = sqlite3_exec( db, query.c_str(), [](void *data, int argc, char **argv, char **) -> int { auto &res = *(T *)data; if(argc) { res = row_extractor().extract(argv[0]); } return 0; }, &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)); } } /** * Yevgeniy Zakharov: I wanted to refactore this function with statements and value bindings * but it turns out that bindings in pragma statements are not supported. */ template void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) { auto con = this->get_connection(); if(!db) { db = con.get(); } std::stringstream ss; ss << "PRAGMA " << name << " = " << value; auto query = ss.str(); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) { auto con = this->get_connection(); if(!db) { db = con.get(); } std::stringstream ss; ss << "PRAGMA " << name << " = " << internal::to_string(value); auto query = ss.str(); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } }; } // #include "limit_accesor.h" #include #include // std::map #include // std::function #include // std::shared_ptr // #include "connection_holder.h" namespace sqlite_orm { namespace internal { struct limit_accesor { using get_connection_t = std::function; limit_accesor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} int length() { return this->get(SQLITE_LIMIT_LENGTH); } void length(int newValue) { this->set(SQLITE_LIMIT_LENGTH, newValue); } int sql_length() { return this->get(SQLITE_LIMIT_SQL_LENGTH); } void sql_length(int newValue) { this->set(SQLITE_LIMIT_SQL_LENGTH, newValue); } int column() { return this->get(SQLITE_LIMIT_COLUMN); } void column(int newValue) { this->set(SQLITE_LIMIT_COLUMN, newValue); } int expr_depth() { return this->get(SQLITE_LIMIT_EXPR_DEPTH); } void expr_depth(int newValue) { this->set(SQLITE_LIMIT_EXPR_DEPTH, newValue); } int compound_select() { return this->get(SQLITE_LIMIT_COMPOUND_SELECT); } void compound_select(int newValue) { this->set(SQLITE_LIMIT_COMPOUND_SELECT, newValue); } int vdbe_op() { return this->get(SQLITE_LIMIT_VDBE_OP); } void vdbe_op(int newValue) { this->set(SQLITE_LIMIT_VDBE_OP, newValue); } int function_arg() { return this->get(SQLITE_LIMIT_FUNCTION_ARG); } void function_arg(int newValue) { this->set(SQLITE_LIMIT_FUNCTION_ARG, newValue); } int attached() { return this->get(SQLITE_LIMIT_ATTACHED); } void attached(int newValue) { this->set(SQLITE_LIMIT_ATTACHED, newValue); } int like_pattern_length() { return this->get(SQLITE_LIMIT_LIKE_PATTERN_LENGTH); } void like_pattern_length(int newValue) { this->set(SQLITE_LIMIT_LIKE_PATTERN_LENGTH, newValue); } int variable_number() { return this->get(SQLITE_LIMIT_VARIABLE_NUMBER); } void variable_number(int newValue) { this->set(SQLITE_LIMIT_VARIABLE_NUMBER, newValue); } int trigger_depth() { return this->get(SQLITE_LIMIT_TRIGGER_DEPTH); } void trigger_depth(int newValue) { this->set(SQLITE_LIMIT_TRIGGER_DEPTH, newValue); } #if SQLITE_VERSION_NUMBER >= 3008007 int worker_threads() { return this->get(SQLITE_LIMIT_WORKER_THREADS); } void worker_threads(int newValue) { this->set(SQLITE_LIMIT_WORKER_THREADS, newValue); } #endif protected: get_connection_t get_connection; friend struct storage_base; /** * Stores limit set between connections. */ std::map limits; int get(int id) { auto connection = this->get_connection(); return sqlite3_limit(connection.get(), id, -1); } void set(int id, int newValue) { this->limits[id] = newValue; auto connection = this->get_connection(); sqlite3_limit(connection.get(), id, newValue); } }; } } // #include "transaction_guard.h" #include // std::function // #include "connection_holder.h" namespace sqlite_orm { namespace internal { /** * Class used as a guard for a transaction. Calls `ROLLBACK` in destructor. * Has explicit `commit()` and `rollback()` functions. After explicit function is fired * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to * make it call `COMMIT` on destroy. */ struct transaction_guard_t { /** * This is a public lever to tell a guard what it must do in its destructor * if `gotta_fire` is true */ bool commit_on_destroy = false; transaction_guard_t(connection_ref connection_, std::function commit_func_, std::function rollback_func_) : connection(std::move(connection_)), commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} ~transaction_guard_t() { if(this->gotta_fire) { if(!this->commit_on_destroy) { this->rollback_func(); } else { this->commit_func(); } } } /** * 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; } /** * Call `ROLLBACK` explicitly. After this call * guard will not call `COMMIT` or `ROLLBACK` * in its destructor. */ void rollback() { this->rollback_func(); this->gotta_fire = false; } protected: connection_ref connection; std::function commit_func; std::function rollback_func; bool gotta_fire = true; }; } } // #include "statement_finalizer.h" // #include "type_printer.h" // #include "tuple_helper.h" // #include "row_extractor.h" // #include "connection_holder.h" // #include "backup.h" #include #include // std::string #include // #include "error_code.h" // #include "connection_holder.h" namespace sqlite_orm { namespace internal { /** * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class * to have maximum control on a backup operation. In case you need a single backup in one line you * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. */ struct backup_t { backup_t(connection_ref to_, const std::string &zDestName, connection_ref from_, const std::string &zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), to(to_), from(from_), holder(move(holder_)) { if(!this->handle) { throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); } } backup_t(backup_t &&other) : handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } ~backup_t() { if(this->handle) { (void)sqlite3_backup_finish(this->handle); this->handle = nullptr; } } /** * Calls sqlite3_backup_step with pages argument */ int step(int pages) { return sqlite3_backup_step(this->handle, pages); } /** * Returns sqlite3_backup_remaining result */ int remaining() const { return sqlite3_backup_remaining(this->handle); } /** * Returns sqlite3_backup_pagecount result */ int pagecount() const { return sqlite3_backup_pagecount(this->handle); } protected: sqlite3_backup *handle = nullptr; connection_ref to; connection_ref from; std::unique_ptr holder; }; } } namespace sqlite_orm { namespace internal { struct storage_base { using collating_function = std::function; std::function on_open; pragma_t pragma; limit_accesor 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)}; } void drop_index(const std::string &indexName) { auto con = this->get_connection(); auto db = con.get(); std::stringstream ss; ss << "DROP INDEX '" << indexName + "'"; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } void vacuum() { auto con = this->get_connection(); auto db = con.get(); std::string query = "VACUUM"; sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } /** * Drops table with given name. */ void drop_table(const std::string &tableName) { auto con = this->get_connection(); this->drop_table_internal(tableName, con.get()); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string &from, const std::string &to) { auto con = this->get_connection(); std::stringstream ss; ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; this->perform_query_without_result(ss.str(), con.get()); } /** * sqlite3_changes function. */ int changes() { auto con = this->get_connection(); return sqlite3_changes(con.get()); } /** * sqlite3_total_changes function. */ int total_changes() { auto con = this->get_connection(); return sqlite3_total_changes(con.get()); } int64 last_insert_rowid() { auto con = this->get_connection(); return sqlite3_last_insert_rowid(con.get()); } int busy_timeout(int ms) { auto con = this->get_connection(); return sqlite3_busy_timeout(con.get(), ms); } /** * Returns libsqltie3 lib version, not sqlite_orm */ std::string libversion() { return sqlite3_libversion(); } bool transaction(const std::function &f) { this->begin_transaction(); auto shouldCommit = f(); if(shouldCommit) { this->commit(); } else { this->rollback(); } return shouldCommit; } std::string current_timestamp() { auto con = this->get_connection(); return this->current_timestamp(con.get()); } #if SQLITE_VERSION_NUMBER >= 3007010 /** * \fn db_release_memory * \brief Releases freeable memory of database. It is function can/should be called periodically by * application, if application has less memory usage constraint. \note sqlite3_db_release_memory added * in 3.7.10 https://sqlite.org/changes.html */ int db_release_memory() { auto con = this->get_connection(); return sqlite3_db_release_memory(con.get()); } #endif /** * Returns existing permanent table names in database. Doesn't check storage itself - works only with * actual database. * @return Returns list of tables in database. */ std::vector table_names() { auto con = this->get_connection(); std::vector tableNames; 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(), [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { auto &tableNames_ = *(data_t *)data; for(int i = 0; i < argc; i++) { if(argv[i]) { tableNames_.push_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)); } return tableNames; } void open_forever() { this->isOpenedForever = true; this->connection->retain(); if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } } void create_collation(const std::string &name, collating_function f) { collating_function *functionPointer = nullptr; if(f) { functionPointer = &(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(); if(sqlite3_create_collation(db, name.c_str(), SQLITE_UTF8, functionPointer, f ? collate_callback : nullptr) != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } } void begin_transaction() { this->connection->retain(); if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } auto db = this->connection->get(); this->begin_transaction(db); } void commit() { auto db = this->connection->get(); this->commit(db); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); } } void rollback() { auto db = this->connection->get(); this->rollback(db); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); } } void backup_to(const std::string &filename) { auto backup = this->make_backup_to(filename); backup.step(-1); } void backup_to(storage_base &other) { auto backup = this->make_backup_to(other); backup.step(-1); } void backup_from(const std::string &filename) { auto backup = this->make_backup_from(filename); backup.step(-1); } void backup_from(storage_base &other) { auto backup = this->make_backup_from(other); backup.step(-1); } backup_t make_backup_to(const std::string &filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", move(holder)}; } backup_t make_backup_to(storage_base &other) { return {other.get_connection(), "main", this->get_connection(), "main", {}}; } backup_t make_backup_from(const std::string &filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", move(holder)}; } backup_t make_backup_from(storage_base &other) { return {this->get_connection(), "main", other.get_connection(), "main", {}}; } const std::string &filename() const { return this->connection->filename; } /** * Checks whether connection to database is opened right now. * Returns always `true` for in memory databases. */ bool is_opened() const { return this->connection->retain_count() > 0; } int busy_handler(std::function handler) { _busy_handler = move(handler); if(this->is_opened()) { if(_busy_handler) { return sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } else { return sqlite3_busy_handler(this->connection->get(), nullptr, nullptr); } } else { return SQLITE_OK; } } protected: storage_base(const std::string &filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename_.empty() || filename_ == ":memory:"), connection(std::make_unique(filename_)), cachedForeignKeysCount(foreignKeysCount) { if(this->inMemory) { this->connection->retain(); this->on_open_internal(this->connection->get()); } } storage_base(const storage_base &other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), connection(std::make_unique(other.connection->filename)), cachedForeignKeysCount(other.cachedForeignKeysCount) { if(this->inMemory) { this->connection->retain(); this->on_open_internal(this->connection->get()); } } ~storage_base() { if(this->isOpenedForever) { this->connection->release(); } if(this->inMemory) { this->connection->release(); } } const bool inMemory; bool isOpenedForever = false; std::unique_ptr connection; std::map collatingFunctions; const int cachedForeignKeysCount; std::function _busy_handler; connection_ref get_connection() { connection_ref res{*this->connection}; if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } return res; } #if SQLITE_VERSION_NUMBER >= 3006019 void foreign_keys(sqlite3 *db, bool value) { std::stringstream ss; ss << "PRAGMA foreign_keys = " << value; auto query = ss.str(); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } 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)); } return result; } #endif void on_open_internal(sqlite3 *db) { #if SQLITE_VERSION_NUMBER >= 3006019 if(this->cachedForeignKeysCount) { this->foreign_keys(db, true); } #endif if(this->pragma._synchronous != -1) { this->pragma.synchronous(this->pragma._synchronous); } if(this->pragma._journal_mode != -1) { this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); } for(auto &p: this->collatingFunctions) { if(sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback) != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } for(auto &p: this->limit.limits) { sqlite3_limit(db, p.first, p.second); } if(_busy_handler) { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } if(this->on_open) { this->on_open(db); } } void begin_transaction(sqlite3 *db) { std::stringstream ss; ss << "BEGIN TRANSACTION"; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } void commit(sqlite3 *db) { std::stringstream ss; ss << "COMMIT"; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } void rollback(sqlite3 *db) { std::stringstream ss; ss << "ROLLBACK"; auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } 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)); } return result; } void drop_table_internal(const std::string &tableName, sqlite3 *db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; this->perform_query_without_result(ss.str(), db); } void perform_query_without_result(const std::string &query, sqlite3 *db) { sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) { auto &f = *(collating_function *)arg; return f(leftLen, lhs, rightLen, rhs); } static int busy_handler_callback(void *selfPointer, int triesCount) { auto &storage = *static_cast(selfPointer); if(storage._busy_handler) { return storage._busy_handler(triesCount); } else { return 0; } } // returns foreign keys count in storage definition template static int foreign_keys_count(T &storageImpl) { auto res = 0; storageImpl.for_each([&res](auto &impl) { res += impl.foreign_keys_count(); }); return res; } }; } } // #include "prepared_statement.h" // #include "expression_object_type.h" #include // std::decay #include // std::reference_wrapper // #include "prepared_statement.h" namespace sqlite_orm { namespace internal { template struct expression_object_type; template struct expression_object_type> { using type = typename std::decay::type; }; template struct expression_object_type>> { using type = typename std::decay::type; }; template struct expression_object_type> { using type = typename std::decay::type; }; template struct expression_object_type>> { using type = typename std::decay::type; }; template struct expression_object_type> { using type = typename std::decay::type; }; template struct expression_object_type>> { using type = typename std::decay::type; }; template struct expression_object_type> { using type = typename std::decay::type; }; template struct expression_object_type, Cols...>> { using type = typename std::decay::type; }; template struct get_ref_t { template auto &operator()(O &t) const { return t; } }; template struct get_ref_t> { template auto &operator()(O &t) const { return t.get(); } }; template auto &get_ref(T &t) { using arg_type = typename std::decay::type; get_ref_t g; return g(t); } template struct get_object_t; template struct get_object_t : get_object_t {}; template auto &get_object(T &t) { using expression_type = typename std::decay::type; get_object_t obj; return obj(t); } template struct get_object_t> { using expression_type = replace_t; template auto &operator()(O &e) const { return get_ref(e.obj); } }; template struct get_object_t> { using expression_type = insert_t; template auto &operator()(O &e) const { return get_ref(e.obj); } }; template struct get_object_t> { using expression_type = update_t; template auto &operator()(O &e) const { return get_ref(e.obj); } }; } } // #include "statement_serializator.h" #include // std::stringstream #include // std::string #include // std::enable_if #include // std::vector #include // std::iter_swap // #include "core_functions.h" // #include "constraints.h" // #include "conditions.h" // #include "column.h" // #include "rowid.h" // #include "type_printer.h" // #include "table_name_collector.h" #include // std::set #include // std::string #include // std::function #include // std::type_index // #include "select_constraints.h" // #include "alias.h" // #include "core_functions.h" namespace sqlite_orm { namespace internal { struct table_name_collector { using table_name_set = std::set>; using find_table_name_t = std::function; find_table_name_t find_table_name; mutable table_name_set table_names; template table_name_set operator()(const T &) const { return {}; } template void operator()(F O::*, std::string alias = {}) const { if(this->find_table_name) { table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); } } template void operator()(const column_pointer &) const { if(this->find_table_name) { table_names.insert({this->find_table_name(typeid(T)), ""}); } } template void operator()(const alias_column_t &a) const { (*this)(a.column, alias_extractor::get()); } template void operator()(const count_asterisk_t &) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); if(!tableName.empty()) { table_names.insert(std::make_pair(move(tableName), "")); } } } template void operator()(const asterisk_t &) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); } } template void operator()(const object_t &) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); } } }; } } // #include "column_names_getter.h" #include // std::string #include // std::vector #include // std::reference_wrapper // #include "error_code.h" // #include "select_constraints.h" namespace sqlite_orm { namespace internal { template std::string serialize(const T &t, const C &context); template struct column_names_getter { using expression_type = T; template std::vector operator()(const expression_type &t, const C &context) { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); if(columnName.length()) { return {move(columnName)}; } else { throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } } }; template std::vector get_column_names(const T &t, const C &context) { column_names_getter serializator; return serializator(t, context); } template struct column_names_getter, void> { using expression_type = std::reference_wrapper; template std::vector operator()(const expression_type &expression, const C &context) { return get_column_names(expression.get(), context); } }; template struct column_names_getter, void> { using expression_type = asterisk_t; template std::vector operator()(const expression_type &, const C &) { std::vector res; res.push_back("*"); return res; } }; template struct column_names_getter, void> { using expression_type = object_t; template std::vector operator()(const expression_type &, const C &) { std::vector res; res.push_back("*"); return res; } }; template struct column_names_getter, void> { using expression_type = columns_t; template std::vector operator()(const expression_type &cols, const C &context) { std::vector columnNames; columnNames.reserve(static_cast(cols.count)); auto newContext = context; newContext.skip_table_name = false; iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { auto columnName = serialize(m, newContext); if(columnName.length()) { columnNames.push_back(columnName); } else { throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } }); return columnNames; } }; } } // #include "order_by_serializator.h" #include // std::string #include // std::vector #include // std::stringstream namespace sqlite_orm { namespace internal { template struct order_by_serializator; template std::string serialize_order_by(const T &t, const C &context) { order_by_serializator serializator; return serializator(t, context); } template struct order_by_serializator, void> { using statement_type = order_by_t; template std::string operator()(const statement_type &orderBy, const C &context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(orderBy.o, newContext); ss << columnName << " "; if(orderBy._collate_argument.length()) { ss << "COLLATE " << orderBy._collate_argument << " "; } switch(orderBy.asc_desc) { case 1: ss << "ASC"; break; case -1: ss << "DESC"; break; } return ss.str(); } }; template struct order_by_serializator, void> { using statement_type = dynamic_order_by_t; template std::string operator()(const statement_type &orderBy, const C &) const { std::vector expressions; for(auto &entry: orderBy) { std::string entryString; { std::stringstream ss; ss << entry.name << " "; if(!entry._collate_argument.empty()) { ss << "COLLATE " << entry._collate_argument << " "; } switch(entry.asc_desc) { case 1: ss << "ASC"; break; case -1: ss << "DESC"; break; } entryString = ss.str(); } expressions.push_back(move(entryString)); }; std::stringstream ss; ss << static_cast(orderBy) << " "; for(size_t i = 0; i < expressions.size(); ++i) { ss << expressions[i]; if(i < expressions.size() - 1) { ss << ", "; } } ss << " "; return ss.str(); } }; } } // #include "values.h" // #include "table_type.h" // #include "indexed_column.h" namespace sqlite_orm { namespace internal { template struct statement_serializator; template std::string serialize(const T &t, const C &context) { statement_serializator serializator; return serializator(t, context); } template struct statement_serializator::value>::type> { using statement_type = T; template std::string operator()(const statement_type &statement, const C &context) { if(context.replace_bindable_with_question) { return "?"; } else { return field_printer{}(statement); } } }; template struct statement_serializator, void> { using statement_type = std::reference_wrapper; template std::string operator()(const statement_type &s, const C &context) { return serialize(s.get(), context); } }; template<> struct statement_serializator { using statement_type = std::nullptr_t; template std::string operator()(const statement_type &, const C &) { return "?"; } }; template struct statement_serializator, void> { using statement_type = alias_holder; template std::string operator()(const statement_type &, const C &) { return T::get(); } }; template struct statement_serializator, void> { using statement_type = core_function_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << "("; std::vector args; using args_type = typename std::decay::type::args_type; args.reserve(std::tuple_size::value); iterate_tuple(c.args, [&args, &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 << ", "; } } ss << ")"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = as_t; template std::string operator()(const statement_type &c, const C &context) const { auto tableAliasString = alias_extractor::get(); return serialize(c.expression, context) + " AS " + tableAliasString; } }; template struct statement_serializator, void> { using statement_type = alias_column_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << T::get() << "'."; } auto newContext = context; newContext.skip_table_name = true; ss << serialize(c.column, newContext); return ss.str(); } }; template<> struct statement_serializator { using statement_type = std::string; template std::string operator()(const statement_type &c, const C &context) const { if(context.replace_bindable_with_question) { return "?"; } else { return "\"" + c + "\""; } } }; template<> struct statement_serializator { using statement_type = const char *; template std::string operator()(const char *c, const C &context) const { if(context.replace_bindable_with_question) { return "?"; } else { return std::string("'") + c + "'"; } } }; template struct statement_serializator { using statement_type = F O::*; template std::string operator()(const statement_type &m, const C &context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; } ss << "\"" << context.column_name(m) << "\""; return ss.str(); } }; template<> struct statement_serializator { using statement_type = rowid_t; template std::string operator()(const statement_type &s, const C &) { return static_cast(s); } }; template<> struct statement_serializator { using statement_type = oid_t; template std::string operator()(const statement_type &s, const C &) { return static_cast(s); } }; template<> struct statement_serializator<_rowid_t, void> { using statement_type = _rowid_t; template std::string operator()(const statement_type &s, const C &) { return static_cast(s); } }; template struct statement_serializator, void> { using statement_type = table_rowid_t; template std::string operator()(const statement_type &s, const C &context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; } ss << static_cast(s); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = table_oid_t; template std::string operator()(const statement_type &s, const C &context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; } ss << static_cast(s); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = table__rowid_t; template std::string operator()(const statement_type &s, const C &context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; } ss << static_cast(s); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = binary_operator; template std::string operator()(const statement_type &c, const C &context) const { auto lhs = serialize(c.lhs, context); auto rhs = serialize(c.rhs, context); std::stringstream ss; ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = count_asterisk_t; template std::string operator()(const statement_type &, const C &context) const { return serialize(count_asterisk_without_type{}, context); } }; template<> struct statement_serializator { using statement_type = count_asterisk_without_type; template std::string operator()(const statement_type &c, const C &) const { std::stringstream ss; ss << static_cast(c) << "(*)"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = distinct_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; auto expr = serialize(c.t, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = all_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; auto expr = serialize(c.t, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = column_pointer; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; } ss << "\"" << context.impl.column_name_simple(c.field) << "\""; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = cast_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; return ss.str(); } }; template struct statement_serializator::value>::type> { using statement_type = T; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast(c) << " "; ss << serialize(c.right, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = simple_case_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << "CASE "; c.case_expression.apply([&ss, context](auto &c_) { ss << serialize(c_, context) << " "; }); iterate_tuple(c.args, [&ss, context](auto &pair) { ss << "WHEN " << serialize(pair.first, context) << " "; ss << "THEN " << serialize(pair.second, context) << " "; }); c.else_expression.apply([&ss, context](auto &el) { ss << "ELSE " << serialize(el, context) << " "; }); ss << "END"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = is_null_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = is_not_null_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = bitwise_not_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.argument, context); ss << " (" << cString << " )"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = negated_condition_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.c, context); ss << " (" << cString << " )"; return ss.str(); } }; template struct statement_serializator::value>::type> { using statement_type = T; template std::string operator()(const statement_type &c, const C &context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; if(context.use_parentheses) { ss << "("; } ss << leftString << " " << static_cast(c) << " " << rightString; if(context.use_parentheses) { ss << ")"; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = named_collate; template std::string operator()(const statement_type &c, const C &context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); return res + " " + static_cast(c); } }; template struct statement_serializator, void> { using statement_type = collate_t; template std::string operator()(const statement_type &c, const C &context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); return res + " " + static_cast(c); } }; template struct statement_serializator, void> { using statement_type = in_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; auto leftString = serialize(c.l, context); ss << leftString << " " << static_cast(c) << " "; auto newContext = context; newContext.use_parentheses = true; ss << serialize(c.arg, newContext); return ss.str(); } }; template struct statement_serializator>, void> { using statement_type = in_t>; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; auto leftString = serialize(c.l, context); if(context.use_parentheses) { ss << '('; } ss << leftString << " " << static_cast(c) << " ( "; for(size_t index = 0; index < c.arg.size(); ++index) { auto &value = c.arg[index]; ss << " " << serialize(value, context); if(index < c.arg.size() - 1) { ss << ", "; } } ss << " )"; if(context.use_parentheses) { ss << ')'; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = like_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; ss << serialize(c.pattern, context); c.arg3.apply([&ss, &context](auto &value) { ss << " ESCAPE " << serialize(value, context); }); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = glob_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; ss << serialize(c.pattern, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = between_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; auto expr = serialize(c.expr, context); ss << expr << " " << static_cast(c) << " "; ss << serialize(c.b1, context); ss << " AND "; ss << serialize(c.b2, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = exists_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << serialize(c.t, context); return ss.str(); } }; template<> struct statement_serializator { using statement_type = constraints::autoincrement_t; template std::string operator()(const statement_type &c, const C &) const { return static_cast(c); } }; template struct statement_serializator, void> { using statement_type = constraints::primary_key_t; template std::string operator()(const statement_type &c, const C &context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { res += context.column_name(column); if(columnIndex < columnsCount - 1) { res += ", "; } ++columnIndex; }); res += ")"; } return res; } }; template struct statement_serializator, void> { using statement_type = constraints::unique_t; template std::string operator()(const statement_type &c, const C &context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { res += context.column_name(column); if(columnIndex < columnsCount - 1) { res += ", "; } ++columnIndex; }); res += ")"; } return res; } }; template<> struct statement_serializator { using statement_type = constraints::collate_t; template std::string operator()(const statement_type &c, const C &) const { return static_cast(c); } }; template struct statement_serializator, void> { using statement_type = constraints::default_t; template std::string operator()(const statement_type &c, const C &context) const { return static_cast(c) + " (" + serialize(c.value, context) + ")"; } }; template struct statement_serializator, std::tuple>, void> { using statement_type = constraints::foreign_key_t, std::tuple>; template std::string operator()(const statement_type &fk, const C &context) const { std::stringstream ss; std::vector columnNames; using columns_type_t = typename std::decay::type::columns_type; constexpr const size_t columnsCount = std::tuple_size::value; columnNames.reserve(columnsCount); iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { columnNames.push_back(context.impl.column_name(v)); }); ss << "FOREIGN KEY("; for(size_t i = 0; i < columnNames.size(); ++i) { ss << "'" << columnNames[i] << "'"; if(i < columnNames.size() - 1) { ss << ", "; } } ss << ") REFERENCES "; std::vector referencesNames; using references_type_t = typename std::decay::type::references_type; constexpr const size_t referencesCount = std::tuple_size::value; referencesNames.reserve(referencesCount); { using first_reference_t = typename std::tuple_element<0, references_type_t>::type; using first_reference_mapped_type = typename internal::table_type::type; auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); ss << '\'' << refTableName << '\''; } iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { referencesNames.push_back(context.impl.column_name(v)); }); ss << "("; for(size_t i = 0; i < referencesNames.size(); ++i) { ss << "'" << referencesNames[i] << "'"; if(i < referencesNames.size() - 1) { ss << ", "; } } ss << ")"; if(fk.on_update) { ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; } if(fk.on_delete) { ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = constraints::check_t; template std::string operator()(const statement_type &c, const C &context) const { return static_cast(c) + " " + serialize(c.expression, context); } }; template struct statement_serializator, void> { using statement_type = column_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << "'" << c.name << "' "; using column_type = typename std::decay::type; using field_type = typename column_type::field_type; using constraints_type = typename column_type::constraints_type; ss << type_printer().print() << " "; { std::vector constraintsStrings; constexpr const size_t constraintsCount = std::tuple_size::value; constraintsStrings.reserve(constraintsCount); int primaryKeyIndex = -1; int autoincrementIndex = -1; int tupleIndex = 0; iterate_tuple( c.constraints, [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { using constraint_type = typename std::decay::type; constraintsStrings.push_back(serialize(v, context)); if(is_primary_key::value) { primaryKeyIndex = tupleIndex; } else if(std::is_same::value) { autoincrementIndex = tupleIndex; } ++tupleIndex; }); if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { iter_swap(constraintsStrings.begin() + primaryKeyIndex, constraintsStrings.begin() + autoincrementIndex); } for(auto &str: constraintsStrings) { ss << str << ' '; } } if(c.not_null()) { ss << "NOT NULL "; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = remove_all_t; template std::string operator()(const statement_type &rem, const C &context) const { auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; iterate_tuple(rem.conditions, [&context, &ss](auto &v) { ss << serialize(v, context); }); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = replace_t; template std::string operator()(const statement_type &rep, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "REPLACE INTO '" << tImpl.table.name << "' ("; auto columnNames = tImpl.table.column_names(); auto columnNamesCount = columnNames.size(); for(size_t i = 0; i < columnNamesCount; ++i) { ss << "\"" << columnNames[i] << "\""; if(i < columnNamesCount - 1) { ss << ","; } else { ss << ")"; } ss << " "; } ss << "VALUES("; for(size_t i = 0; i < columnNamesCount; ++i) { ss << "?"; if(i < columnNamesCount - 1) { ss << ", "; } else { ss << ")"; } } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = insert_explicit; template std::string operator()(const statement_type &ins, const C &context) const { constexpr const size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector columnNames; columnNames.reserve(colsCount); { auto columnsContext = context; columnsContext.skip_table_name = true; iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { auto columnName = serialize(m, columnsContext); if(!columnName.empty()) { columnNames.push_back(columnName); } else { throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } }); } ss << "("; for(size_t i = 0; i < columnNames.size(); ++i) { ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ","; } else { ss << ")"; } ss << " "; } ss << "VALUES ("; for(size_t i = 0; i < columnNames.size(); ++i) { ss << "?"; if(i < columnNames.size() - 1) { ss << ","; } else { ss << ")"; } ss << " "; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = update_t; template std::string operator()(const statement_type &upd, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "UPDATE '" << tImpl.table.name << "' SET "; std::vector setColumnNames; tImpl.table.for_each_column([&setColumnNames](auto &c) { if(!c.template has>()) { setColumnNames.emplace_back(c.name); } }); for(size_t i = 0; i < setColumnNames.size(); ++i) { ss << "\"" << setColumnNames[i] << "\"" << " = ?"; if(i < setColumnNames.size() - 1) { ss << ","; } ss << " "; } ss << "WHERE "; auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ?"; if(i < primaryKeyColumnNames.size() - 1) { ss << " AND"; } ss << " "; } return ss.str(); } }; template struct statement_serializator, Wargs...>, void> { using statement_type = update_all_t, Wargs...>; template std::string operator()(const statement_type &upd, const C &context) const { std::stringstream ss; ss << "UPDATE "; table_name_collector collector{[&context](std::type_index ti) { return context.impl.find_table_name(ti); }}; iterate_ast(upd.set.assigns, collector); if(!collector.table_names.empty()) { if(collector.table_names.size() == 1) { ss << " '" << collector.table_names.begin()->first << "' "; ss << static_cast(upd.set) << " "; std::vector setPairs; auto leftContext = context; leftContext.skip_table_name = true; iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { std::stringstream sss; sss << serialize(asgn.lhs, leftContext); sss << " " << static_cast(asgn) << " "; sss << serialize(asgn.rhs, context) << " "; setPairs.push_back(sss.str()); }); auto setPairsCount = setPairs.size(); for(size_t i = 0; i < setPairsCount; ++i) { ss << setPairs[i] << " "; if(i < setPairsCount - 1) { ss << ", "; } } iterate_tuple(upd.conditions, [&context, &ss](auto &v) { ss << serialize(v, context); }); return ss.str(); } else { throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); } } else { throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); } } }; template struct statement_serializator, void> { using statement_type = insert_t; template std::string operator()(const statement_type &, const C &context) const { using object_type = typename expression_object_type::type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector columnNames; auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { if(tImpl.table._without_rowid || !c.template has>()) { auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); if(it == compositeKeyColumnNames.end()) { columnNames.emplace_back(c.name); } } }); auto columnNamesCount = columnNames.size(); if(columnNamesCount) { ss << "("; for(size_t i = 0; i < columnNamesCount; ++i) { ss << "\"" << columnNames[i] << "\""; if(i < columnNamesCount - 1) { ss << ","; } else { ss << ")"; } ss << " "; } } else { ss << "DEFAULT "; } ss << "VALUES "; if(columnNamesCount) { ss << "("; for(size_t i = 0; i < columnNamesCount; ++i) { ss << "?"; if(i < columnNamesCount - 1) { ss << ", "; } else { ss << ")"; } } } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = remove_t; template std::string operator()(const statement_type &, const C &context) const { auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; ss << "WHERE "; auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? "; if(i < primaryKeyColumnNames.size() - 1) { ss << "AND "; } } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = replace_range_t; template std::string operator()(const statement_type &rep, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_type::object_type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "REPLACE INTO '" << tImpl.table.name << "' ("; auto columnNames = tImpl.table.column_names(); auto columnNamesCount = columnNames.size(); for(size_t i = 0; i < columnNamesCount; ++i) { ss << "\"" << columnNames[i] << "\""; if(i < columnNamesCount - 1) { ss << ", "; } else { ss << ") "; } } ss << "VALUES "; auto valuesString = [columnNamesCount] { std::stringstream ss_; ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { ss_ << "?"; if(i < columnNamesCount - 1) { ss_ << ", "; } else { ss_ << ")"; } } return ss_.str(); }(); auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); for(auto i = 0; i < valuesCount; ++i) { ss << valuesString; if(i < valuesCount - 1) { ss << ","; } ss << " "; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = insert_range_t; template std::string operator()(const statement_type &statement, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_type::object_type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' ("; std::vector columnNames; tImpl.table.for_each_column([&columnNames](auto &c) { if(!c.template has>()) { columnNames.emplace_back(c.name); } }); auto columnNamesCount = columnNames.size(); for(size_t i = 0; i < columnNamesCount; ++i) { ss << "\"" << columnNames[i] << "\""; if(i < columnNamesCount - 1) { ss << ","; } else { ss << ")"; } ss << " "; } ss << "VALUES "; auto valuesString = [columnNamesCount] { std::stringstream ss_; ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { ss_ << "?"; if(i < columnNamesCount - 1) { ss_ << ", "; } else { ss_ << ")"; } } return ss_.str(); }(); auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); for(auto i = 0; i < valuesCount; ++i) { ss << valuesString; if(i < valuesCount - 1) { ss << ","; } ss << " "; } return ss.str(); } }; template std::string serialize_get_all_impl(const T &get, const C &context) { using primary_type = typename T::type; table_name_collector collector; collector.table_names.insert( std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); iterate_ast(get.conditions, collector); std::stringstream ss; ss << "SELECT "; auto &tImpl = context.impl.template get_impl(); auto columnNames = tImpl.table.column_names(); for(size_t i = 0; i < columnNames.size(); ++i) { ss << "\"" << tImpl.table.name << "\"." << "\"" << columnNames[i] << "\""; if(i < columnNames.size() - 1) { ss << ", "; } else { ss << " "; } } ss << "FROM "; std::vector> tableNames(collector.table_names.begin(), collector.table_names.end()); for(size_t i = 0; i < tableNames.size(); ++i) { auto &tableNamePair = tableNames[i]; ss << "'" << tableNamePair.first << "' "; if(!tableNamePair.second.empty()) { ss << tableNamePair.second << " "; } if(int(i) < int(tableNames.size()) - 1) { ss << ","; } ss << " "; } iterate_tuple(get.conditions, [&context, &ss](auto &v) { ss << serialize(v, context); }); return ss.str(); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = get_all_optional_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_all_impl(get, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = get_all_pointer_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_all_impl(get, context); } }; template struct statement_serializator, void> { using statement_type = get_all_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_all_impl(get, context); } }; template std::string serialize_get_impl(const T &, const C &context) { using primary_type = typename T::type; auto &tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "SELECT "; auto columnNames = tImpl.table.column_names(); for(size_t i = 0; i < columnNames.size(); ++i) { ss << "\"" << columnNames[i] << "\""; if(i < columnNames.size() - 1) { ss << ","; } ss << " "; } ss << "FROM '" << tImpl.table.name << "' WHERE "; auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); if(!primaryKeyColumnNames.empty()) { for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? "; if(i < primaryKeyColumnNames.size() - 1) { ss << "AND"; } ss << ' '; } return ss.str(); } else { throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); } } template struct statement_serializator, void> { using statement_type = get_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_impl(get, context); } }; template struct statement_serializator, void> { using statement_type = get_pointer_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_impl(get, context); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = get_optional_t; template std::string operator()(const statement_type &get, const C &context) const { return serialize_get_impl(get, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = select_t; template std::string operator()(const statement_type &sel, const C &context) const { std::stringstream ss; if(!is_base_of_template::value) { if(!sel.highest_level) { ss << "( "; } ss << "SELECT "; } if(get_distinct(sel.col)) { ss << static_cast(distinct(0)) << " "; } auto columnNames = get_column_names(sel.col, context); for(size_t i = 0; i < columnNames.size(); ++i) { ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ","; } ss << " "; } table_name_collector collector{[&context](std::type_index ti) { return context.impl.find_table_name(ti); }}; iterate_ast(sel.col, collector); iterate_ast(sel.conditions, collector); internal::join_iterator()([&collector, &context](const auto &c) { using original_join_type = typename std::decay::type::join_type::type; using cross_join_type = typename internal::mapped_type_proxy::type; auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); auto tableAliasString = alias_extractor::get(); std::pair tableNameWithAlias(std::move(crossJoinedTableName), std::move(tableAliasString)); collector.table_names.erase(tableNameWithAlias); }); if(!collector.table_names.empty()) { ss << "FROM "; std::vector> tableNames(collector.table_names.begin(), collector.table_names.end()); for(size_t i = 0; i < tableNames.size(); ++i) { auto &tableNamePair = tableNames[i]; ss << "'" << tableNamePair.first << "' "; if(!tableNamePair.second.empty()) { ss << tableNamePair.second << " "; } if(int(i) < int(tableNames.size()) - 1) { ss << ","; } ss << " "; } } iterate_tuple(sel.conditions, [&context, &ss](auto &v) { ss << serialize(v, context); }); if(!is_base_of_template::value) { if(!sel.highest_level) { ss << ") "; } } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = indexed_column_t; template std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); if(!statement._collation_name.empty()) { ss << " COLLATE " << statement._collation_name; } if(statement._order) { switch(statement._order) { case -1: ss << " DESC"; break; case 1: ss << " ASC"; break; default: throw std::system_error(std::make_error_code(orm_error_code::incorrect_order)); } } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = index_t; template std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { ss << "UNIQUE "; } using columns_type = typename std::decay::type::columns_type; using head_t = typename std::tuple_element<0, columns_type>::type::column_type; using indexed_type = typename table_type::type; ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" << context.impl.find_table_name(typeid(indexed_type)) << "' ("; std::vector columnNames; iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { columnNames.push_back(context.column_name(v.column_or_expression)); }); for(size_t i = 0; i < columnNames.size(); ++i) { ss << "'" << columnNames[i] << "'"; if(i < columnNames.size() - 1) { ss << ", "; } } ss << ")"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = where_t; template std::string operator()(const statement_type &w, const C &context) const { std::stringstream ss; ss << static_cast(w) << " "; auto whereString = serialize(w.c, context); ss << "( " << whereString << ") "; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = order_by_t; template std::string operator()(const statement_type &orderBy, const C &context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; auto orderByString = serialize_order_by(orderBy, context); ss << orderByString << " "; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = dynamic_order_by_t; template std::string operator()(const statement_type &orderBy, const CC &context) const { return serialize_order_by(orderBy, context); } }; template struct statement_serializator, void> { using statement_type = multi_order_by_t; template std::string operator()(const statement_type &orderBy, const C &context) const { std::stringstream ss; std::vector expressions; iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { auto expression = serialize_order_by(v, context); expressions.push_back(move(expression)); }); ss << static_cast(orderBy) << " "; for(size_t i = 0; i < expressions.size(); ++i) { ss << expressions[i]; if(i < expressions.size() - 1) { ss << ", "; } } ss << " "; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = cross_join_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = inner_join_t; template std::string operator()(const statement_type &l, const C &context) const { std::stringstream ss; ss << static_cast(l) << " "; auto aliasString = alias_extractor::get(); ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; if(aliasString.length()) { ss << "'" << aliasString << "' "; } ss << serialize(l.constraint, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = on_t; template std::string operator()(const statement_type &t, const C &context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; ss << static_cast(t) << " " << serialize(t.arg, newContext) << " "; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = join_t; template std::string operator()(const statement_type &l, const C &context) const { std::stringstream ss; ss << static_cast(l) << " "; ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; ss << serialize(l.constraint, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = left_join_t; template std::string operator()(const statement_type &l, const C &context) const { std::stringstream ss; ss << static_cast(l) << " "; ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; ss << serialize(l.constraint, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = left_outer_join_t; template std::string operator()(const statement_type &l, const C &context) const { std::stringstream ss; ss << static_cast(l) << " "; ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; ss << serialize(l.constraint, context); return ss.str(); } }; template struct statement_serializator, void> { using statement_type = natural_join_t; template std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = group_by_t; template std::string operator()(const statement_type &groupBy, const C &context) const { std::stringstream ss; std::vector expressions; auto newContext = context; newContext.skip_table_name = false; iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { auto expression = serialize(v, newContext); expressions.push_back(expression); }); ss << static_cast(groupBy) << " "; for(size_t i = 0; i < expressions.size(); ++i) { ss << expressions[i]; if(i < expressions.size() - 1) { ss << ", "; } } ss << " "; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = having_t; template std::string operator()(const statement_type &hav, const C &context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; ss << static_cast(hav) << " "; ss << serialize(hav.t, newContext) << " "; return ss.str(); } }; /** * HO - has offset * OI - offset is implicit */ template struct statement_serializator, void> { using statement_type = limit_t; template std::string operator()(const statement_type &limt, const C &context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; if(HO) { if(OI) { limt.off.apply([&newContext, &ss](auto &value) { ss << serialize(value, newContext); }); ss << ", "; ss << serialize(limt.lim, newContext); } else { ss << serialize(limt.lim, newContext) << " OFFSET "; limt.off.apply([&newContext, &ss](auto &value) { ss << serialize(value, newContext); }); } } else { ss << serialize(limt.lim, newContext); } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = using_t; template std::string operator()(const statement_type &statement, const C &context) const { auto newContext = context; newContext.skip_table_name = true; return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; } }; template struct statement_serializator, void> { using statement_type = std::tuple; template std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; ss << '('; auto index = 0; using TupleSize = std::tuple_size; iterate_tuple(statement, [&context, &index, &ss](auto &value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; } ++index; }); ss << ')'; return ss.str(); } }; template struct statement_serializator, void> { using statement_type = values_t; template std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } ss << "VALUES "; { auto index = 0; auto &tuple = statement.tuple; using tuple_type = typename std::decay::type; using TupleSize = std::tuple_size; iterate_tuple(tuple, [&context, &index, &ss](auto &value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; } ++index; }); } if(context.use_parentheses) { ss << ')'; } return ss.str(); } }; template struct statement_serializator, void> { using statement_type = dynamic_values_t; template std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } ss << "VALUES "; { auto vectorSize = statement.vector.size(); for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { auto &value = statement.vector[index]; ss << serialize(value, context); if(index < vectorSize - 1) { ss << ", "; } } } if(context.use_parentheses) { ss << ')'; } return ss.str(); } }; } } // #include "table_name_collector.h" // #include "object_from_column_builder.h" namespace sqlite_orm { namespace internal { /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. */ template struct storage_t : storage_base { using self = storage_t; using impl_type = storage_impl; /** * @param filename database filename. * @param impl_ storage_impl head */ storage_t(const std::string &filename, impl_type impl_) : storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} protected: impl_type impl; template friend struct view_t; template friend struct dynamic_order_by_t; template friend struct iterator_t; template friend struct serializator_context_builder; template void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { std::stringstream ss; ss << "CREATE TABLE '" << tableName << "' ( "; auto columnsCount = tableImpl.table.columns_count; auto index = 0; using context_t = serializator_context; context_t context{this->impl}; iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { ss << serialize(c, context); if(index < columnsCount - 1) { ss << ", "; } index++; }); ss << ") "; if(tableImpl.table._without_rowid) { ss << "WITHOUT ROWID "; } auto query = ss.str(); sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template void backup_table(sqlite3 *db, const I &tableImpl, 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)) { int suffix = 1; do { std::stringstream stream; stream << suffix; auto anotherBackupTableName = backupTableName + stream.str(); if(!tableImpl.table_exists(anotherBackupTableName, db)) { backupTableName = anotherBackupTableName; break; } ++suffix; } while(true); } this->create_table(db, backupTableName, tableImpl); tableImpl.copy_table(db, backupTableName, columnsToIgnore); this->drop_table_internal(tableImpl.table.name, db); tableImpl.rename_table(db, backupTableName, tableImpl.table.name); } template void assert_mapped_type() const { using mapped_types_tuples = std::tuple; static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); } template auto &get_impl() const { return this->impl.template get_impl(); } template auto &get_impl() { return this->impl.template get_impl(); } public: template view_t iterate(Args &&... args) { this->assert_mapped_type(); auto con = this->get_connection(); return {*this, std::move(con), std::forward(args)...}; } /** * Delete from routine. * O is an object's type. Must be specified explicitly. * @param args optional conditions: `where`, `join` etc * @example: storage.remove_all(); - DELETE FROM users * @example: storage.remove_all(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) */ template void remove_all(Args &&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); this->execute(statement); } /** * Delete routine. * O is an object's type. Must be specified explicitly. * @param ids ids of object to be removed. */ template void remove(Ids... ids) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::remove(std::forward(ids)...)); this->execute(statement); } /** * Update routine. Sets all non primary key fields where primary key is equal. * O is an object type. May be not specified explicitly cause it can be deduced by * compiler from first parameter. * @param o object to be updated. */ template void update(const O &o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::update(std::ref(o))); this->execute(statement); } template void update_all(internal::set_t set, Wargs... wh) { auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); this->execute(statement); } protected: template std::string group_concat_internal(F O::*m, std::unique_ptr y, Args &&... args) { this->assert_mapped_type(); std::vector rows; if(y) { rows = this->select(sqlite_orm::group_concat(m, move(*y)), std::forward(args)...); } else { rows = this->select(sqlite_orm::group_concat(m), std::forward(args)...); } if(!rows.empty()) { return move(rows.front()); } else { return {}; } } public: /** * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. * @return All objects of type O stored in database at the moment in `std::vector`. * @note If you need to return the result in a different container type then use a different `get_all` function overload `get_all>` * @example: storage.get_all() - SELECT * FROM users * @example: storage.get_all(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template auto get_all(Args &&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); } /** * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. * R is an explicit return type. This type must have `push_back(O &&)` function. * @return All objects of type O stored in database at the moment in `R`. * @example: storage.get_all>(); - SELECT * FROM users * @example: storage.get_all>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template auto get_all(Args &&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); } /** * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. * @return All objects of type O as `std::unique_ptr` inside a `std::vector` stored in database at the moment. * @note If you need to return the result in a different container type then use a different `get_all_pointer` function overload `get_all_pointer>` * @example: storage.get_all_pointer(); - SELECT * FROM users * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template auto get_all_pointer(Args &&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); } /** * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. * R is a container type. std::vector> is default * @return All objects of type O as std::unique_ptr stored in database at the moment. * @example: storage.get_all_pointer>(); - SELECT * FROM users * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template auto get_all_pointer(Args &&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); } /** * Select * by id routine. * throws std::system_error(orm_error_code::not_found, orm_error_category) if object not found with given * 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. */ template O get(Ids... ids) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get(std::forward(ids)...)); return this->execute(statement); } /** * The same as `get` function but doesn't throw an exception if noting found but returns std::unique_ptr * with null value. throws std::system_error in case of db error. */ template std::unique_ptr get_pointer(Ids... ids) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); return this->execute(statement); } /** * A previous version of get_pointer() that returns a shared_ptr * instead of a unique_ptr. New code should prefer get_pointer() * unless the data needs to be shared. * * @note * Most scenarios don't need shared ownership of data, so we should prefer * unique_ptr when possible. It's more efficient, doesn't require atomic * ops for a reference count (which can cause major slowdowns on * weakly-ordered platforms like ARM), and can be easily promoted to a * shared_ptr, exactly like we're doing here. * (Conversely, you _can't_ go from shared back to unique.) */ template std::shared_ptr get_no_throw(Ids... ids) { return std::shared_ptr(get_pointer(std::forward(ids)...)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED /** * The same as `get` function but doesn't throw an exception if noting found but * returns an empty std::optional. throws std::system_error in case of db error. */ template std::optional get_optional(Ids... ids) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); return this->execute(statement); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count * @return Number of O object in table. */ template::type> int count(Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(), std::forward(args)...); if(!rows.empty()) { return rows.front(); } else { return 0; } } /** * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count * @param m member pointer to class mapped to the storage. */ template int count(F O::*m, Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); if(!rows.empty()) { return rows.front(); } else { return 0; } } /** * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg * @param m is a class member pointer (the same you passed into make_column). * @return average value from db. */ template double avg(F O::*m, Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::avg(m), std::forward(args)...); if(!rows.empty()) { return rows.front(); } else { return 0; } } template std::string group_concat(F O::*m) { return this->group_concat_internal(m, {}); } /** * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat * @param m is a class member pointer (the same you passed into make_column). * @return group_concat query result. */ template, typename sfinae = typename std::enable_if::value >= 1>::type> std::string group_concat(F O::*m, Args &&... args) { return this->group_concat_internal(m, {}, std::forward(args)...); } /** * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat * @param m is a class member pointer (the same you passed into make_column). * @return group_concat query result. */ template std::string group_concat(F O::*m, std::string y, Args &&... args) { return this->group_concat_internal(m, std::make_unique(move(y)), std::forward(args)...); } template std::string group_concat(F O::*m, const char *y, Args &&... args) { std::unique_ptr str; if(y) { str = std::make_unique(y); } else { str = std::make_unique(); } return this->group_concat_internal(m, move(str), std::forward(args)...); } /** * MAX(x) query. * @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> std::unique_ptr max(F O::*m, Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::max(m), std::forward(args)...); if(!rows.empty()) { return std::move(rows.front()); } else { return {}; } } /** * MIN(x) query. * @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> std::unique_ptr min(F O::*m, Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::min(m), std::forward(args)...); if(!rows.empty()) { return std::move(rows.front()); } else { return {}; } } /** * SUM(x) query. * @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> std::unique_ptr sum(F O::*m, Args &&... args) { this->assert_mapped_type(); std::vector> rows = this->select(sqlite_orm::sum(m), std::forward(args)...); if(!rows.empty()) { if(rows.front()) { return std::make_unique(std::move(*rows.front())); } else { return {}; } } else { return {}; } } /** * TOTAL(x) query. * @param m is a class member pointer (the same you passed into make_column). * @return total value (the same as SUM but not nullable. More details here * https://www.sqlite.org/lang_aggfunc.html) */ template double total(F O::*m, Args &&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::total(m), std::forward(args)...); if(!rows.empty()) { return std::move(rows.front()); } else { return {}; } } /** * Select a single column into std::vector or multiple columns into std::vector>. * 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> std::vector select(T m, Args... args) { static_assert(!is_base_of_template::value || 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); } /** * 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(); std::stringstream ss; ss << "{ "; using pair = std::pair; std::vector pairs; tImpl.table.for_each_column([&pairs, &o](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; pair p{c.name, std::string()}; if(c.member_pointer) { p.second = field_printer()(o.*c.member_pointer); } else { using getter_type = typename column_type::getter_type; field_value_holder valueHolder{((o).*(c.getter))()}; p.second = field_printer()(valueHolder.value); } pairs.push_back(move(p)); }); for(size_t i = 0; i < pairs.size(); ++i) { auto &p = pairs[i]; ss << p.first << " : '" << p.second << "'"; if(i < pairs.size() - 1) { ss << ", "; } else { ss << " }"; } } return ss.str(); } /** * This is REPLACE (INSERT OR REPLACE) function. * Also if you need to insert value with knows id you should * also you this function instead of insert cause inserts ignores * id and creates own one. */ template void replace(const O &o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); this->execute(statement); } template void replace_range(It from, It to) { using O = typename std::iterator_traits::value_type; this->assert_mapped_type(); if(from == to) { return; } auto statement = this->prepare(sqlite_orm::replace_range(from, to)); 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"); this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); return int(this->execute(statement)); } /** * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed * object doesn't matter. * @return id of just created object. */ template int insert(const O &o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); return int(this->execute(statement)); } template void insert_range(It from, It to) { using O = typename std::iterator_traits::value_type; this->assert_mapped_type(); if(from == to) { return; } auto statement = this->prepare(sqlite_orm::insert_range(from, to)); this->execute(statement); } /** * Change table name inside storage's schema info. This function does not * affect database */ template void rename_table(std::string name) { this->assert_mapped_type(); auto &tImpl = this->get_impl(); tImpl.table.name = move(name); } using storage_base::rename_table; /** * Get table's name stored in storage's schema info. This function does not call * any SQLite queries */ template const std::string &tablename() const { this->assert_mapped_type(); auto &tImpl = this->get_impl(); return tImpl.table.name; } protected: template sync_schema_result sync_table(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); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } return res; } 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) { // get table info provided in `make_table` call.. auto storageTableInfo = tImpl.table.get_table_info(); // now get current table info from db using `PRAGMA table_info` query.. auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. std::vector columnsToAdd; tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { // extra table columns than storage columns this->backup_table(db, tImpl, {}); res = decltype(res)::old_columns_removed; } if(schema_stat == sync_schema_result::new_columns_added) { for(auto columnPointer: columnsToAdd) { tImpl.add_column(*columnPointer, db); } 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; } } 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; } } } return res; } public: /** * This is a cute function used to replace migration up/down functionality. * It performs check storage schema with actual db schema and: * * if there are excess tables exist in db they are ignored (not dropped) * * every table from storage is compared with it's db analog and * * if table doesn't exist it is being created * * if table exists its colums are being compared with table_info from db and * * if there are columns in db that do not exist in storage (excess) table will be dropped and * recreated * * 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 * `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 * 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 * 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` * can be printed out on std::ostream with `operator<<`. */ 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 &tableImpl) { auto res = this->sync_table(tableImpl, db, preserve); result.insert({tableImpl.table.name, res}); }); return result; } /** * This function returns the same map that `sync_schema` returns but it * doesn't perform `sync_schema` actually - just simulates it in case you want to know * what will happen if you sync your schema. */ 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](auto tableImpl) { result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); }); 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()); } template prepared_statement_t> prepare(select_t sel) { sel.highest_level = true; auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(sel, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(sel), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(get_all_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(get_all_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_all_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t, Wargs...>> prepare(update_all_t, Wargs...> upd) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(remove_all_t rem) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, std::move(con)}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(get_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(get_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(update_t upd) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(remove_t rem) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(insert_t ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(replace_t rep) { auto con = this->get_connection(); sqlite3_stmt *stmt; using object_type = typename expression_object_type::type; this->assert_mapped_type(); auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(insert_range_t statement) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(statement, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(statement), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(replace_range_t rep) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template prepared_statement_t> prepare(insert_explicit ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); using context_t = serializator_context; context_t context{this->impl}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template int64 execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; 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)); } }); if(sqlite3_step(stmt) == SQLITE_DONE) { return sqlite3_last_insert_rowid(db); } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template void execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_type::object_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); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { 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)); } } }); } if(sqlite3_step(stmt) == SQLITE_DONE) { //.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template void execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_type::object_type; auto index = 1; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto &tImpl = this->get_impl(); sqlite3_reset(stmt); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; 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)); } } } }); } if(sqlite3_step(stmt) == SQLITE_DONE) { //.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template void execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; 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 stmt = statement.stmt; auto index = 1; auto &o = get_object(statement.t); auto &tImpl = this->get_impl(); sqlite3_reset(stmt); tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { 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)); } } }); if(sqlite3_step(stmt) == SQLITE_DONE) { //.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template int64 execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; int64 res = 0; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; auto &tImpl = this->get_impl(); auto &o = get_object(statement.t); auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { if(tImpl.table._without_rowid || !c.template has>()) { auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); if(it == compositeKeyColumnNames.end()) { using column_type = typename std::decay::type; 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)); } } } } }); if(sqlite3_step(stmt) == SQLITE_DONE) { res = sqlite3_last_insert_rowid(db); } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } return res; } 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)); } }); if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template void execute(const prepared_statement_t> &statement) { using statement_type = typename std::decay::type; 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](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { auto bind_res = statement_binder().bind(stmt, index++, o.*c.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).*(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)); } } } }); tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { if(c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; 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)); } } } }); if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } 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)); } }); 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)); } } } #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)); } }); 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)); } } } #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)); } }); 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)); } } } 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)); } }); if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } 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)); } }); if(sqlite3_step(stmt) == SQLITE_DONE) { // done.. } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } template::type> 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)); } }); 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); 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)); } }); 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); 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)); } }); 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); 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 }; // 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(tables...)}; } /** * sqlite3_threadsafe() interface. */ inline int threadsafe() { return sqlite3_threadsafe(); } } #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 // #include "conditions.h" // #include "operators.h" // #include "select_constraints.h" // #include "prepared_statement.h" // #include "optional_container.h" // #include "core_functions.h" namespace sqlite_orm { namespace internal { template struct node_tuple { using type = std::tuple; }; template<> struct node_tuple { using type = std::tuple<>; }; template struct node_tuple, void> { using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = where_t; using type = typename node_tuple::type; }; template struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; using left_node_tuple = typename node_tuple::type; using right_node_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; template struct node_tuple, void> { 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; }; template struct node_tuple, void> { using node_type = columns_t; using type = typename conc_tuple::type...>::type; }; template struct node_tuple, void> { using node_type = in_t; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; template struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; 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; }; template struct node_tuple, void> { using node_type = get_all_t; using type = typename conc_tuple::type...>::type; }; template struct node_tuple, void> { using node_type = get_all_pointer_t; using type = typename conc_tuple::type...>::type; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { using node_type = get_all_optional_t; using type = typename conc_tuple::type...>::type; }; #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; }; template struct node_tuple, void> { using node_type = remove_all_t; using type = typename conc_tuple::type...>::type; }; template struct node_tuple, void> { using node_type = having_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = cast_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = exists_t; using type = typename node_tuple::type; }; 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<>; }; 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; }; 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; }; 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; }; template struct node_tuple, void> { using node_type = named_collate; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = is_null_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = is_not_null_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = negated_condition_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = core_function_t; using type = typename conc_tuple::type...>::type; }; template struct node_tuple, void> { using node_type = left_join_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = on_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = join_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = left_outer_join_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = inner_join_t; using type = typename node_tuple::type; }; 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; }; 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; }; template struct node_tuple, void> { using node_type = as_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = limit_t; using type = typename node_tuple::type; }; template struct node_tuple, void> { using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; template struct node_tuple, void> { using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; } } #pragma once #include // std::is_same, std::decay, std::remove_reference // #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); } template const auto &get(const internal::prepared_statement_t> &statement) { return std::get(statement.t.range); } template auto &get(internal::prepared_statement_t> &statement) { return std::get(statement.t.range); } template const auto &get(const internal::prepared_statement_t> &statement) { return std::get(statement.t.range); } template auto &get(internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } template const auto &get(const internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } template auto &get(internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } template const auto &get(const internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template auto &get(internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } template const auto &get(const internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template auto &get(internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.ids)); } template const auto &get(const internal::prepared_statement_t> &statement) { return internal::get_ref(std::get(statement.t.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); } 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); } 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); } 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); } 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); } 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); } 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); } 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); } template const auto &get(const internal::prepared_statement_t &statement) { using statement_type = typename std::decay::type; 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::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) { ++index; } if(index == N) { internal::static_if{}>([](auto &r, auto &n) { r = const_cast::type>(&n); })(result, node); } }); return internal::get_ref(*result); } template auto &get(internal::prepared_statement_t &statement) { using statement_type = typename std::decay::type; 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::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) { ++index; } if(index == N) { internal::static_if{}>([](auto &r, auto &n) { r = const_cast::type>(&n); })(result, node); } }); return internal::get_ref(*result); } }