1293 lines
38 KiB
C++
1293 lines
38 KiB
C++
#pragma once
|
|
|
|
#include <string> // std::string
|
|
#include <type_traits> // std::enable_if, std::is_same, std::remove_const
|
|
#include <vector> // std::vector
|
|
#include <tuple> // std::tuple
|
|
#include <sstream> // 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<class T, bool has_offset, bool offset_is_implicit, class O>
|
|
struct limit_t : limit_string {
|
|
T lim;
|
|
optional_container<O> 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<class T>
|
|
struct is_limit : std::false_type {};
|
|
|
|
template<class T, bool has_offset, bool offset_is_implicit, class O>
|
|
struct is_limit<limit_t<T, has_offset, offset_is_implicit, O>> : std::true_type {};
|
|
|
|
/**
|
|
* Stores OFFSET only info
|
|
*/
|
|
template<class T>
|
|
struct offset_t {
|
|
T off;
|
|
};
|
|
|
|
template<class T>
|
|
using is_offset = polyfill::is_specialization_of<T, offset_t>;
|
|
|
|
/**
|
|
* Collated something
|
|
*/
|
|
template<class T>
|
|
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<class T>
|
|
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<class C>
|
|
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<class L, class R, class S, class Res>
|
|
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<class T>
|
|
SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v<T, binary_condition>;
|
|
|
|
template<class T>
|
|
using is_binary_condition = polyfill::bool_constant<is_binary_condition_v<T>>;
|
|
|
|
struct and_condition_string {
|
|
operator std::string() const {
|
|
return "AND";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Result of and operator
|
|
*/
|
|
template<class L, class R>
|
|
struct and_condition_t : binary_condition<L, R, and_condition_string, bool> {
|
|
using super = binary_condition<L, R, and_condition_string, bool>;
|
|
|
|
using super::super;
|
|
};
|
|
|
|
template<class L, class R>
|
|
and_condition_t<L, R> 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<class L, class R>
|
|
struct or_condition_t : binary_condition<L, R, or_condition_string, bool> {
|
|
using super = binary_condition<L, R, or_condition_string, bool>;
|
|
|
|
using super::super;
|
|
};
|
|
|
|
template<class L, class R>
|
|
or_condition_t<L, R> 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<class L, class R>
|
|
struct is_equal_t : binary_condition<L, R, is_equal_string, bool>, negatable_t {
|
|
using self = is_equal_t<L, R>;
|
|
|
|
using binary_condition<L, R, is_equal_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> collate_rtrim() const {
|
|
return {*this, collate_argument::rtrim};
|
|
}
|
|
|
|
named_collate<self> collate(std::string name) const {
|
|
return {*this, std::move(name)};
|
|
}
|
|
|
|
template<class C>
|
|
named_collate<self> 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<class L, class R>
|
|
struct is_not_equal_t : binary_condition<L, R, is_not_equal_string, bool>, negatable_t {
|
|
using self = is_not_equal_t<L, R>;
|
|
|
|
using binary_condition<L, R, is_not_equal_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> collate_rtrim() const {
|
|
return {*this, collate_argument::rtrim};
|
|
}
|
|
};
|
|
|
|
struct greater_than_string {
|
|
operator std::string() const {
|
|
return ">";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* > operator object.
|
|
*/
|
|
template<class L, class R>
|
|
struct greater_than_t : binary_condition<L, R, greater_than_string, bool>, negatable_t {
|
|
using self = greater_than_t<L, R>;
|
|
|
|
using binary_condition<L, R, greater_than_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> collate_rtrim() const {
|
|
return {*this, collate_argument::rtrim};
|
|
}
|
|
};
|
|
|
|
struct greater_or_equal_string {
|
|
operator std::string() const {
|
|
return ">=";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* >= operator object.
|
|
*/
|
|
template<class L, class R>
|
|
struct greater_or_equal_t : binary_condition<L, R, greater_or_equal_string, bool>, negatable_t {
|
|
using self = greater_or_equal_t<L, R>;
|
|
|
|
using binary_condition<L, R, greater_or_equal_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> collate_rtrim() const {
|
|
return {*this, collate_argument::rtrim};
|
|
}
|
|
};
|
|
|
|
struct lesser_than_string {
|
|
operator std::string() const {
|
|
return "<";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* < operator object.
|
|
*/
|
|
template<class L, class R>
|
|
struct lesser_than_t : binary_condition<L, R, lesser_than_string, bool>, negatable_t {
|
|
using self = lesser_than_t<L, R>;
|
|
|
|
using binary_condition<L, R, lesser_than_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> collate_rtrim() const {
|
|
return {*this, collate_argument::rtrim};
|
|
}
|
|
};
|
|
|
|
struct lesser_or_equal_string {
|
|
operator std::string() const {
|
|
return "<=";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* <= operator object.
|
|
*/
|
|
template<class L, class R>
|
|
struct lesser_or_equal_t : binary_condition<L, R, lesser_or_equal_string, bool>, negatable_t {
|
|
using self = lesser_or_equal_t<L, R>;
|
|
|
|
using binary_condition<L, R, lesser_or_equal_string, bool>::binary_condition;
|
|
|
|
collate_t<self> collate_binary() const {
|
|
return {*this, collate_argument::binary};
|
|
}
|
|
|
|
collate_t<self> collate_nocase() const {
|
|
return {*this, collate_argument::nocase};
|
|
}
|
|
|
|
collate_t<self> 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<class L, class A>
|
|
struct dynamic_in_t : condition_t, in_base, negatable_t {
|
|
using self = dynamic_in_t<L, A>;
|
|
|
|
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<class L, class... Args>
|
|
struct in_t : condition_t, in_base, negatable_t {
|
|
L left;
|
|
std::tuple<Args...> 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<class T>
|
|
struct is_null_t : is_null_string, negatable_t {
|
|
using self = is_null_t<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<class T>
|
|
struct is_not_null_t : is_not_null_string, negatable_t {
|
|
using self = is_not_null_t<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<class O>
|
|
struct order_by_t : order_by_base, order_by_string {
|
|
using expression_type = O;
|
|
using self = order_by_t<expression_type>;
|
|
|
|
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<class C>
|
|
self collate() const {
|
|
std::stringstream ss;
|
|
ss << C::name() << std::flush;
|
|
return this->collate(ss.str());
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ORDER BY pack holder.
|
|
*/
|
|
template<class... Args>
|
|
struct multi_order_by_t : order_by_string {
|
|
using args_type = std::tuple<Args...>;
|
|
|
|
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<class C>
|
|
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<entry_t>::const_iterator;
|
|
|
|
dynamic_order_by_t(const context_t& context_) : context(context_) {}
|
|
|
|
template<class O>
|
|
void push_back(order_by_t<O> 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<entry_t> entries;
|
|
context_t context;
|
|
};
|
|
|
|
template<class T>
|
|
SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v =
|
|
polyfill::disjunction_v<polyfill::is_specialization_of<T, order_by_t>,
|
|
polyfill::is_specialization_of<T, multi_order_by_t>,
|
|
polyfill::is_specialization_of<T, dynamic_order_by_t>>;
|
|
|
|
template<class T>
|
|
using is_order_by = polyfill::bool_constant<is_order_by_v<T>>;
|
|
|
|
struct between_string {
|
|
operator std::string() const {
|
|
return "BETWEEN";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* BETWEEN operator object.
|
|
*/
|
|
template<class A, class T>
|
|
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<class A, class T, class E>
|
|
struct like_t : condition_t, like_string, negatable_t {
|
|
using self = like_t<A, T, E>;
|
|
using arg_t = A;
|
|
using pattern_t = T;
|
|
using escape_t = E;
|
|
|
|
arg_t arg;
|
|
pattern_t pattern;
|
|
optional_container<escape_t> arg3; // not escape cause escape exists as a function here
|
|
|
|
like_t(arg_t arg_, pattern_t pattern_, optional_container<escape_t> escape_) :
|
|
arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {}
|
|
|
|
template<class C>
|
|
like_t<A, T, C> escape(C c) const {
|
|
optional_container<C> 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<class A, class T>
|
|
struct glob_t : condition_t, glob_string, internal::negatable_t {
|
|
using self = glob_t<A, 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<class T>
|
|
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<class T>
|
|
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<class T, class O>
|
|
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<class T, class O>
|
|
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<class T, class O>
|
|
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<class T>
|
|
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<class T, class M>
|
|
struct using_t {
|
|
column_pointer<T, M> 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<class T, class O>
|
|
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<std::string>(&User::id)
|
|
*/
|
|
template<class T, class E>
|
|
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<class... Args>
|
|
struct from_t {
|
|
using tuple_type = std::tuple<Args...>;
|
|
};
|
|
|
|
template<class T>
|
|
using is_from = polyfill::is_specialization_of<T, from_t>;
|
|
|
|
template<class T>
|
|
using is_constrained_join = polyfill::is_detected<on_type_t, T>;
|
|
}
|
|
|
|
/**
|
|
* Explicit FROM function. Usage:
|
|
* `storage.select(&User::id, from<User>());`
|
|
*/
|
|
template<class... Tables>
|
|
internal::from_t<Tables...> from() {
|
|
static_assert(sizeof...(Tables) > 0, "");
|
|
return {};
|
|
}
|
|
|
|
template<class T, internal::satisfies<std::is_base_of, internal::negatable_t, T> = true>
|
|
internal::negated_condition_t<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<class T, class R>
|
|
lesser_than_t<T, R> operator<(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
lesser_than_t<L, T> operator<(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
lesser_or_equal_t<T, R> operator<=(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
lesser_or_equal_t<L, T> operator<=(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
greater_than_t<T, R> operator>(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
greater_than_t<L, T> operator>(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
greater_or_equal_t<T, R> operator>=(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
greater_or_equal_t<L, T> operator>=(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
is_equal_t<T, R> operator==(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
is_equal_t<L, T> operator==(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
is_not_equal_t<T, R> operator!=(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
is_not_equal_t<L, T> operator!=(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
conc_t<T, R> operator||(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
conc_t<L, T> operator||(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
conc_t<L, R> operator||(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
|
|
template<class R, class E, satisfies<std::is_base_of, conc_string, E> = true>
|
|
conc_t<E, R> operator||(E expr, R r) {
|
|
return {std::move(expr), std::move(r)};
|
|
}
|
|
|
|
template<class L, class E, satisfies<std::is_base_of, conc_string, E> = true>
|
|
conc_t<L, E> operator||(L l, E expr) {
|
|
return {std::move(l), std::move(expr)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
add_t<T, R> operator+(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
add_t<L, T> operator+(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
add_t<L, R> operator+(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
sub_t<T, R> operator-(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
sub_t<L, T> operator-(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
sub_t<L, R> operator-(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
mul_t<T, R> operator*(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
mul_t<L, T> operator*(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
mul_t<L, R> operator*(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
div_t<T, R> operator/(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
div_t<L, T> operator/(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
div_t<L, R> operator/(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
|
|
template<class T, class R>
|
|
mod_t<T, R> operator%(expression_t<T> expr, R r) {
|
|
return {std::move(expr.value), std::move(r)};
|
|
}
|
|
|
|
template<class L, class T>
|
|
mod_t<L, T> operator%(L l, expression_t<T> expr) {
|
|
return {std::move(l), std::move(expr.value)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
mod_t<L, R> operator%(expression_t<L> l, expression_t<R> r) {
|
|
return {std::move(l.value), std::move(r.value)};
|
|
}
|
|
}
|
|
|
|
template<class F, class O>
|
|
internal::using_t<O, F O::*> using_(F O::*p) {
|
|
return {p};
|
|
}
|
|
template<class T, class M>
|
|
internal::using_t<T, M> using_(internal::column_pointer<T, M> cp) {
|
|
return {std::move(cp)};
|
|
}
|
|
|
|
template<class T>
|
|
internal::on_t<T> on(T t) {
|
|
return {std::move(t)};
|
|
}
|
|
|
|
template<class T>
|
|
internal::cross_join_t<T> cross_join() {
|
|
return {};
|
|
}
|
|
|
|
template<class T>
|
|
internal::natural_join_t<T> natural_join() {
|
|
return {};
|
|
}
|
|
|
|
template<class T, class O>
|
|
internal::left_join_t<T, O> left_join(O o) {
|
|
return {std::move(o)};
|
|
}
|
|
|
|
template<class T, class O>
|
|
internal::join_t<T, O> join(O o) {
|
|
return {std::move(o)};
|
|
}
|
|
|
|
template<class T, class O>
|
|
internal::left_outer_join_t<T, O> left_outer_join(O o) {
|
|
return {std::move(o)};
|
|
}
|
|
|
|
template<class T, class O>
|
|
internal::inner_join_t<T, O> inner_join(O o) {
|
|
return {std::move(o)};
|
|
}
|
|
|
|
template<class T>
|
|
internal::offset_t<T> offset(T off) {
|
|
return {std::move(off)};
|
|
}
|
|
|
|
template<class T>
|
|
internal::limit_t<T, false, false, void> limit(T lim) {
|
|
return {std::move(lim)};
|
|
}
|
|
|
|
template<class T, class O, internal::satisfies_not<internal::is_offset, T> = true>
|
|
internal::limit_t<T, true, true, O> limit(O off, T lim) {
|
|
return {std::move(lim), {std::move(off)}};
|
|
}
|
|
|
|
template<class T, class O>
|
|
internal::limit_t<T, true, false, O> limit(T lim, internal::offset_t<O> offt) {
|
|
return {std::move(lim), {std::move(offt.off)}};
|
|
}
|
|
|
|
template<class L,
|
|
class R,
|
|
std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::condition_t, L>,
|
|
std::is_base_of<internal::condition_t, R>>,
|
|
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<class L, class R>
|
|
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<class L,
|
|
class R,
|
|
std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::condition_t, L>,
|
|
std::is_base_of<internal::condition_t, R>>,
|
|
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<class L, class R>
|
|
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<class T>
|
|
internal::is_not_null_t<T> is_not_null(T t) {
|
|
return {std::move(t)};
|
|
}
|
|
|
|
template<class T>
|
|
internal::is_null_t<T> is_null(T t) {
|
|
return {std::move(t)};
|
|
}
|
|
|
|
template<class L, class E>
|
|
internal::dynamic_in_t<L, std::vector<E>> in(L l, std::vector<E> values) {
|
|
return {std::move(l), std::move(values), false};
|
|
}
|
|
|
|
template<class L, class E>
|
|
internal::dynamic_in_t<L, std::vector<E>> in(L l, std::initializer_list<E> values) {
|
|
return {std::move(l), std::move(values), false};
|
|
}
|
|
|
|
template<class L, class A>
|
|
internal::dynamic_in_t<L, A> in(L l, A arg) {
|
|
return {std::move(l), std::move(arg), false};
|
|
}
|
|
|
|
template<class L, class E>
|
|
internal::dynamic_in_t<L, std::vector<E>> not_in(L l, std::vector<E> values) {
|
|
return {std::move(l), std::move(values), true};
|
|
}
|
|
|
|
template<class L, class E>
|
|
internal::dynamic_in_t<L, std::vector<E>> not_in(L l, std::initializer_list<E> values) {
|
|
return {std::move(l), std::move(values), true};
|
|
}
|
|
|
|
template<class L, class A>
|
|
internal::dynamic_in_t<L, A> not_in(L l, A arg) {
|
|
return {std::move(l), std::move(arg), true};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::is_equal_t<L, R> is_equal(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::is_equal_t<L, R> eq(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::is_not_equal_t<L, R> is_not_equal(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::is_not_equal_t<L, R> ne(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::greater_than_t<L, R> greater_than(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::greater_than_t<L, R> gt(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::greater_or_equal_t<L, R> greater_or_equal(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::greater_or_equal_t<L, R> ge(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::lesser_than_t<L, R> lesser_than(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::lesser_than_t<L, R> lt(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::lesser_or_equal_t<L, R> lesser_or_equal(L l, R r) {
|
|
return {std::move(l), std::move(r)};
|
|
}
|
|
|
|
template<class L, class R>
|
|
internal::lesser_or_equal_t<L, R> 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<colalias_a>(&User::name), order_by(get<colalias_a>()))
|
|
*/
|
|
template<class O, internal::satisfies_not<std::is_base_of, integer_printer, type_printer<O>> = true>
|
|
internal::order_by_t<O> order_by(O o) {
|
|
return {std::move(o)};
|
|
}
|
|
|
|
/**
|
|
* ORDER BY positional ordinal
|
|
*
|
|
* Examples:
|
|
* storage.select(&User::name, order_by(1))
|
|
*/
|
|
template<class O, internal::satisfies<std::is_base_of, integer_printer, type_printer<O>> = true>
|
|
internal::order_by_t<internal::literal_holder<O>> order_by(O o) {
|
|
return {{std::move(o)}};
|
|
}
|
|
|
|
/**
|
|
* ORDER BY column1, column2
|
|
* Example: storage.get_all<Singer>(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc())
|
|
*/
|
|
template<class... Args>
|
|
internal::multi_order_by_t<Args...> multi_order_by(Args&&... args) {
|
|
return {std::make_tuple(std::forward<Args>(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<class S>
|
|
internal::dynamic_order_by_t<internal::serializer_context<typename S::db_objects_type>>
|
|
dynamic_order_by(const S& storage) {
|
|
internal::serializer_context_builder<S> builder(storage);
|
|
return builder();
|
|
}
|
|
|
|
/**
|
|
* X BETWEEN Y AND Z
|
|
* Example: storage.select(between(&User::id, 10, 20))
|
|
*/
|
|
template<class A, class T>
|
|
internal::between_t<A, 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<class A, class T>
|
|
internal::like_t<A, T, void> like(A a, T t) {
|
|
return {std::move(a), std::move(t), {}};
|
|
}
|
|
|
|
/**
|
|
* X GLOB Y
|
|
* Example: storage.select(glob(&User::name, "*S"))
|
|
*/
|
|
template<class A, class T>
|
|
internal::glob_t<A, 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<class A, class T, class E>
|
|
internal::like_t<A, T, E> like(A a, T t, E e) {
|
|
return {std::move(a), std::move(t), {std::move(e)}};
|
|
}
|
|
|
|
/**
|
|
* CAST(X AS type).
|
|
* Example: cast<std::string>(&User::id)
|
|
*/
|
|
template<class T, class E>
|
|
internal::cast_t<T, E> cast(E e) {
|
|
return {std::move(e)};
|
|
}
|
|
}
|