#pragma once #include // std::string #include // std::enable_if, std::is_same, std::remove_const #include // std::vector #include // std::tuple #include // std::stringstream #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "is_base_of_template.h" #include "type_traits.h" #include "collate_argument.h" #include "constraints.h" #include "optional_container.h" #include "serializer_context.h" #include "tags.h" #include "alias_traits.h" #include "expression.h" #include "type_printer.h" #include "literal.h" namespace sqlite_orm { namespace internal { struct limit_string { operator std::string() const { return "LIMIT"; } }; /** * Stores LIMIT/OFFSET info */ template struct limit_t : limit_string { T lim; optional_container off; limit_t() = default; limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} }; template struct is_limit : std::false_type {}; template struct is_limit> : std::true_type {}; /** * Stores OFFSET only info */ template struct offset_t { T off; }; template using is_offset = polyfill::is_specialization_of; /** * Collated something */ template struct collate_t : public condition_t { T expr; collate_argument argument; collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { return collate_constraint_t{this->argument}; } }; struct named_collate_base { std::string name; operator std::string() const { return "COLLATE " + this->name; } }; /** * Collated something with custom collate function */ template struct named_collate : named_collate_base { T expr; named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} }; struct negated_condition_string { operator std::string() const { return "NOT"; } }; /** * Result of not operator */ template struct negated_condition_t : condition_t, negated_condition_string { C c; negated_condition_t(C c_) : c(std::move(c_)) {} }; /** * Base class for binary conditions * L is left argument type * R is right argument type * S is 'string' class (a class which has cast to `std::string` operator) * Res is result type */ template struct binary_condition : condition_t, S { using left_type = L; using right_type = R; using result_type = Res; left_type l; right_type r; binary_condition() = default; binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} }; template SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; template using is_binary_condition = polyfill::bool_constant>; struct and_condition_string { operator std::string() const { return "AND"; } }; /** * Result of and operator */ template struct and_condition_t : binary_condition { using super = binary_condition; using super::super; }; template and_condition_t make_and_condition(L left, R right) { return {std::move(left), std::move(right)}; } struct or_condition_string { operator std::string() const { return "OR"; } }; /** * Result of or operator */ template struct or_condition_t : binary_condition { using super = binary_condition; using super::super; }; template or_condition_t make_or_condition(L left, R right) { return {std::move(left), std::move(right)}; } struct is_equal_string { operator std::string() const { return "="; } }; /** * = and == operators object */ template struct is_equal_t : binary_condition, negatable_t { using self = is_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } named_collate collate(std::string name) const { return {*this, std::move(name)}; } template named_collate collate() const { std::stringstream ss; ss << C::name() << std::flush; return {*this, ss.str()}; } }; struct is_not_equal_string { operator std::string() const { return "!="; } }; /** * != operator object */ template struct is_not_equal_t : binary_condition, negatable_t { using self = is_not_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } }; struct greater_than_string { operator std::string() const { return ">"; } }; /** * > operator object. */ template struct greater_than_t : binary_condition, negatable_t { using self = greater_than_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } }; struct greater_or_equal_string { operator std::string() const { return ">="; } }; /** * >= operator object. */ template struct greater_or_equal_t : binary_condition, negatable_t { using self = greater_or_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } }; struct lesser_than_string { operator std::string() const { return "<"; } }; /** * < operator object. */ template struct lesser_than_t : binary_condition, negatable_t { using self = lesser_than_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } }; struct lesser_or_equal_string { operator std::string() const { return "<="; } }; /** * <= operator object. */ template struct lesser_or_equal_t : binary_condition, negatable_t { using self = lesser_or_equal_t; using binary_condition::binary_condition; collate_t collate_binary() const { return {*this, collate_argument::binary}; } collate_t collate_nocase() const { return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { return {*this, collate_argument::rtrim}; } }; struct in_base { bool negative = false; // used in not_in #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED in_base(bool negative) : negative{negative} {} #endif }; /** * IN operator object. */ template struct dynamic_in_t : condition_t, in_base, negatable_t { using self = dynamic_in_t; L left; // left expression A argument; // in arg dynamic_in_t(L left_, A argument_, bool negative_) : in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; template struct in_t : condition_t, in_base, negatable_t { L left; std::tuple argument; in_t(L left_, decltype(argument) argument_, bool negative_) : in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; struct is_null_string { operator std::string() const { return "IS NULL"; } }; /** * IS NULL operator object. */ template struct is_null_t : is_null_string, negatable_t { using self = is_null_t; T t; is_null_t(T t_) : t(std::move(t_)) {} }; struct is_not_null_string { operator std::string() const { return "IS NOT NULL"; } }; /** * IS NOT NULL operator object. */ template struct is_not_null_t : is_not_null_string, negatable_t { using self = is_not_null_t; T t; is_not_null_t(T t_) : t(std::move(t_)) {} }; struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED order_by_base() = default; order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} #endif }; struct order_by_string { operator std::string() const { return "ORDER BY"; } }; /** * ORDER BY argument holder. */ template struct order_by_t : order_by_base, order_by_string { using expression_type = O; using self = order_by_t; expression_type expression; order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} self asc() const { auto res = *this; res.asc_desc = 1; return res; } self desc() const { auto res = *this; res.asc_desc = -1; return res; } self collate_binary() const { auto res = *this; res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; res._collate_argument = std::move(name); return res; } template self collate() const { std::stringstream ss; ss << C::name() << std::flush; return this->collate(ss.str()); } }; /** * ORDER BY pack holder. */ template struct multi_order_by_t : order_by_string { using args_type = std::tuple; args_type args; multi_order_by_t(args_type args_) : args{std::move(args_)} {} }; struct dynamic_order_by_entry_t : order_by_base { std::string name; dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} }; /** * C - serializer context class */ template struct dynamic_order_by_t : order_by_string { using context_t = C; using entry_t = dynamic_order_by_entry_t; using const_iterator = typename std::vector::const_iterator; dynamic_order_by_t(const context_t& context_) : context(context_) {} template void push_back(order_by_t order_by) { auto newContext = this->context; newContext.skip_table_name = true; auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, std::move(order_by._collate_argument)); } const_iterator begin() const { return this->entries.begin(); } const_iterator end() const { return this->entries.end(); } void clear() { this->entries.clear(); } protected: std::vector entries; context_t context; }; template SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = polyfill::disjunction_v, polyfill::is_specialization_of, polyfill::is_specialization_of>; template using is_order_by = polyfill::bool_constant>; struct between_string { operator std::string() const { return "BETWEEN"; } }; /** * BETWEEN operator object. */ template struct between_t : condition_t, between_string { using expression_type = A; using lower_type = T; using upper_type = T; expression_type expr; lower_type b1; upper_type b2; between_t(expression_type expr_, lower_type b1_, upper_type b2_) : expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} }; struct like_string { operator std::string() const { return "LIKE"; } }; /** * LIKE operator object. */ template struct like_t : condition_t, like_string, negatable_t { using self = like_t; using arg_t = A; using pattern_t = T; using escape_t = E; arg_t arg; pattern_t pattern; optional_container arg3; // not escape cause escape exists as a function here like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template like_t escape(C c) const { optional_container newArg3{std::move(c)}; return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; struct glob_string { operator std::string() const { return "GLOB"; } }; template struct glob_t : condition_t, glob_string, 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 { column_pointer column; operator std::string() const { return "USING"; } }; struct inner_join_string { operator std::string() const { return "INNER JOIN"; } }; /** * INNER JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template struct inner_join_t : inner_join_string { using type = T; using on_type = O; on_type constraint; inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; struct cast_string { operator std::string() const { return "CAST"; } }; /** * CAST holder. * T is a type to cast to * E is an expression type * Example: cast(&User::id) */ template struct cast_t : cast_string { using to_type = T; using expression_type = E; expression_type expression; cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; template struct from_t { using tuple_type = std::tuple; }; template using is_from = polyfill::is_specialization_of; template using is_constrained_join = polyfill::is_detected; } /** * Explicit FROM function. Usage: * `storage.select(&User::id, from());` */ template internal::from_t from() { static_assert(sizeof...(Tables) > 0, ""); return {}; } template = true> internal::negated_condition_t operator!(T arg) { return {std::move(arg)}; } // Deliberately put operators for `expression_t` into the internal namespace // to facilitate ADL (Argument Dependent Lookup) namespace internal { /** * Cute operators for columns */ template lesser_than_t operator<(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template lesser_than_t operator<(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template lesser_or_equal_t operator<=(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template lesser_or_equal_t operator<=(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template greater_than_t operator>(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template greater_than_t operator>(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template greater_or_equal_t operator>=(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template greater_or_equal_t operator>=(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template is_equal_t operator==(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template is_equal_t operator==(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template is_not_equal_t operator!=(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template is_not_equal_t operator!=(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template conc_t operator||(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template conc_t operator||(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template conc_t operator||(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } template = true> conc_t operator||(E expr, R r) { return {std::move(expr), std::move(r)}; } template = true> conc_t operator||(L l, E expr) { return {std::move(l), std::move(expr)}; } template add_t operator+(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template add_t operator+(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template add_t operator+(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } template sub_t operator-(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template sub_t operator-(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template sub_t operator-(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } template mul_t operator*(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template mul_t operator*(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template mul_t operator*(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } template div_t operator/(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template div_t operator/(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template div_t operator/(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } template mod_t operator%(expression_t expr, R r) { return {std::move(expr.value), std::move(r)}; } template mod_t operator%(L l, expression_t expr) { return {std::move(l), std::move(expr.value)}; } template mod_t operator%(expression_t l, expression_t r) { return {std::move(l.value), std::move(r.value)}; } } template internal::using_t using_(F O::*p) { return {p}; } template internal::using_t using_(internal::column_pointer cp) { return {std::move(cp)}; } 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 = true> internal::limit_t limit(O off, T lim) { return {std::move(lim), {std::move(off)}}; } template internal::limit_t limit(T lim, internal::offset_t offt) { return {std::move(lim), {std::move(offt.off)}}; } template, std::is_base_of>, bool> = true> auto operator&&(L l, R r) { using internal::get_from_expression; return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template auto and_(L l, R r) { using internal::get_from_expression; return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template, std::is_base_of>, bool> = true> auto operator||(L l, R r) { using internal::get_from_expression; return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template auto or_(L l, R r) { using internal::get_from_expression; return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template internal::is_not_null_t is_not_null(T t) { return {std::move(t)}; } template internal::is_null_t is_null(T t) { return {std::move(t)}; } template internal::dynamic_in_t> in(L l, std::vector values) { return {std::move(l), std::move(values), false}; } template internal::dynamic_in_t> in(L l, std::initializer_list values) { return {std::move(l), std::move(values), false}; } template internal::dynamic_in_t in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template internal::dynamic_in_t> not_in(L l, std::vector values) { return {std::move(l), std::move(values), true}; } template internal::dynamic_in_t> not_in(L l, std::initializer_list values) { return {std::move(l), std::move(values), true}; } template internal::dynamic_in_t not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } template internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template internal::is_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)}; } /** * ORDER BY column, column alias or expression * * Examples: * storage.select(&User::name, order_by(&User::id)) * storage.select(as(&User::name), order_by(get())) */ template> = true> internal::order_by_t order_by(O o) { return {std::move(o)}; } /** * ORDER BY positional ordinal * * Examples: * storage.select(&User::name, order_by(1)) */ template> = true> internal::order_by_t> order_by(O o) { return {{std::move(o)}}; } /** * ORDER BY column1, column2 * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template internal::multi_order_by_t multi_order_by(Args&&... args) { return {std::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::serializer_context_builder builder(storage); return builder(); } /** * X BETWEEN Y AND Z * Example: storage.select(between(&User::id, 10, 20)) */ template internal::between_t between(A expr, T b1, T b2) { return {std::move(expr), std::move(b1), std::move(b2)}; } /** * X LIKE Y * Example: storage.select(like(&User::name, "T%")) */ template internal::like_t like(A a, T t) { return {std::move(a), std::move(t), {}}; } /** * X GLOB Y * Example: storage.select(glob(&User::name, "*S")) */ template internal::glob_t glob(A a, T t) { return {std::move(a), std::move(t)}; } /** * X LIKE Y ESCAPE Z * Example: storage.select(like(&User::name, "T%", "%")) */ template internal::like_t like(A a, T t, E e) { return {std::move(a), std::move(t), {std::move(e)}}; } /** * CAST(X AS type). * Example: cast(&User::id) */ template internal::cast_t cast(E e) { return {std::move(e)}; } }