Shoppe-Manne/headers/3rd-party/sqlite_orm.h

9712 lines
358 KiB
C
Raw Permalink Normal View History

2019-05-21 00:57:01 +00:00
#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 <ciso646> // due to #166
#pragma once
#include <system_error> // std::error_code, std::system_error
#include <string> // std::string
#include <sqlite3.h>
#include <stdexcept>
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,
};
}
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<orm_error_code>(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";
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;
}
}
namespace std
{
template <>
struct is_error_code_enum<sqlite_orm::orm_error_code> : std::true_type{};
inline std::error_code make_error_code(sqlite_orm::orm_error_code errorCode) {
return std::error_code(static_cast<int>(errorCode), sqlite_orm::get_orm_error_category());
}
}
#pragma once
#include <map> // std::map
#include <string> // std::string
#include <regex> // std::regex, std::regex_match
#include <memory> // std::make_unique, std::unique_ptr
#include <vector> // std::vector
#include <cctype> // std::toupper
namespace sqlite_orm {
using int64 = sqlite_int64;
using uint64 = sqlite_uint64;
// numeric and real are the same for c++
enum class sqlite_type {
INTEGER,
TEXT,
BLOB,
REAL,
};
/**
* @param str case doesn't matter - it is uppercased before comparing.
*/
inline std::unique_ptr<sqlite_type> to_sqlite_type(const std::string &str) {
auto asciiStringToUpper = [](std::string &s){
std::transform(s.begin(),
s.end(),
s.begin(),
[](char c){
return std::toupper(c);
});
};
auto upperStr = str;
asciiStringToUpper(upperStr);
static std::map<sqlite_type, std::vector<std::regex>> typeMap = {
{ sqlite_type::INTEGER, {
std::regex("INT"),
std::regex("INT.*"),
std::regex("TINYINT"),
std::regex("SMALLINT"),
std::regex("MEDIUMINT"),
std::regex("BIGINT"),
std::regex("UNSIGNED BIG INT"),
std::regex("INT2"),
std::regex("INT8"),
} }, { sqlite_type::TEXT, {
std::regex("CHARACTER\\([[:digit:]]+\\)"),
std::regex("VARCHAR\\([[:digit:]]+\\)"),
std::regex("VARYING CHARACTER\\([[:digit:]]+\\)"),
std::regex("NCHAR\\([[:digit:]]+\\)"),
std::regex("NATIVE CHARACTER\\([[:digit:]]+\\)"),
std::regex("NVARCHAR\\([[:digit:]]+\\)"),
std::regex("CLOB"),
std::regex("TEXT"),
} }, { sqlite_type::BLOB, {
std::regex("BLOB"),
} }, { sqlite_type::REAL, {
std::regex("REAL"),
std::regex("DOUBLE"),
std::regex("DOUBLE PRECISION"),
std::regex("FLOAT"),
std::regex("NUMERIC"),
std::regex("DECIMAL\\([[:digit:]]+,[[:digit:]]+\\)"),
std::regex("BOOLEAN"),
std::regex("DATE"),
std::regex("DATETIME"),
} },
};
for(auto &p : typeMap) {
for(auto &r : p.second) {
if(std::regex_match(upperStr, r)){
return std::make_unique<sqlite_type>(p.first);
}
}
}
return {};
}
}
#pragma once
#include <tuple> // std::tuple
#include <type_traits> // std::false_type, std::true_type
#include <utility> // std::index_sequence, std::index_sequence_for
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 <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
template<size_t N, class ...Args>
struct iterator {
template<class L>
void operator()(const std::tuple<Args...> &t, L l, bool reverse = true) {
if(reverse){
l(std::get<N>(t));
iterator<N - 1, Args...>()(t, l, reverse);
}else{
iterator<N - 1, Args...>()(t, l, reverse);
l(std::get<N>(t));
}
}
};
template<class ...Args>
struct iterator<0, Args...>{
template<class L>
void operator()(const std::tuple<Args...> &t, L l, bool /*reverse*/ = true) {
l(std::get<0>(t));
}
};
template<size_t N>
struct iterator<N> {
template<class L>
void operator()(const std::tuple<> &, L, bool /*reverse*/ = true) {
//..
}
};
template <class F, typename T, std::size_t... I>
void tuple_for_each_impl(F&& f, const T& t, std::index_sequence<I...>){
int _[] = { (f(std::get<I>(t)), int{}) ... };
(void)_;
}
template <typename F, typename ...Args>
void tuple_for_each(const std::tuple<Args...>& t, F&& f){
tuple_for_each_impl(std::forward<F>(f), t, std::index_sequence_for<Args...>{});
}
}
}
#pragma once
#include <type_traits> // 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 static_magic {
template <typename T, typename F>
auto static_if(std::true_type, T t, F f) { return t; }
template <typename T, typename F>
auto static_if(std::false_type, T t, F f) { return f; }
template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(std::integral_constant<bool, B>{}, t, f); }
template <bool B, typename T>
auto static_if(T t) { return static_if(std::integral_constant<bool, B>{}, t, [](auto&&...){}); }
}
}
#pragma once
#include <string> // std::string
#include <memory> // std::shared_ptr, std::unique_ptr
#include <vector> // std::vector
namespace sqlite_orm {
/**
* This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT)
*/
template<class T>
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<unsigned char> : public integer_printer {};
template<>
struct type_printer<signed char> : public integer_printer {};
template<>
struct type_printer<char> : public integer_printer {};
template<>
struct type_printer<unsigned short int> : public integer_printer {};
template<>
struct type_printer<short> : public integer_printer {};
template<>
struct type_printer<unsigned int> : public integer_printer {};
template<>
struct type_printer<int> : public integer_printer {};
template<>
struct type_printer<unsigned long> : public integer_printer {};
template<>
struct type_printer<long> : public integer_printer {};
template<>
struct type_printer<unsigned long long> : public integer_printer {};
template<>
struct type_printer<long long> : public integer_printer {};
template<>
struct type_printer<bool> : public integer_printer {};
template<>
struct type_printer<std::string> : public text_printer {};
template<>
struct type_printer<std::wstring> : public text_printer {};
template<>
struct type_printer<const char*> : public text_printer {};
template<>
struct type_printer<float> : public real_printer {};
template<>
struct type_printer<double> : public real_printer {};
template<class T>
struct type_printer<std::shared_ptr<T>> : public type_printer<T> {};
template<class T>
struct type_printer<std::unique_ptr<T>> : public type_printer<T> {};
template<>
struct type_printer<std::vector<char>> : public blob_printer {};
}
#pragma once
namespace sqlite_orm {
namespace internal {
enum class collate_argument {
binary,
nocase,
rtrim,
};
}
}
#pragma once
#include <string> // std::string
#include <tuple> // std::tuple, std::make_tuple
#include <sstream> // std::stringstream
#include <type_traits> // std::is_base_of, std::false_type, std::true_type
#include <ostream> // std::ostream
namespace sqlite_orm {
namespace constraints {
/**
* AUTOINCREMENT constraint class.
*/
struct autoincrement_t {
operator std::string() const {
return "AUTOINCREMENT";
}
};
/**
* PRIMARY KEY constraint class.
* Cs is parameter pack which contains columns (member pointer and/or function pointers). Can be empty when used withen `make_column` function.
*/
template<class ...Cs>
struct primary_key_t {
std::tuple<Cs...> columns;
enum class order_by {
unspecified,
ascending,
descending,
};
order_by asc_option = order_by::unspecified;
primary_key_t(decltype(columns) c):columns(std::move(c)){}
using field_type = void; // for column iteration. Better be deleted
using constraints_type = std::tuple<>;
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_t<Cs...> asc() const {
auto res = *this;
res.asc_option = order_by::ascending;
return res;
}
primary_key_t<Cs...> desc() const {
auto res = *this;
res.asc_option = order_by::descending;
return res;
}
};
/**
* UNIQUE constraint class.
*/
struct unique_t {
operator std::string() const {
return "UNIQUE";
}
};
/**
* DEFAULT constraint class.
* T is a value type.
*/
template<class T>
struct default_t {
using value_type = T;
value_type value;
operator std::string() const {
std::stringstream ss;
ss << "DEFAULT ";
auto needQuotes = std::is_base_of<text_printer, type_printer<T>>::value;
if(needQuotes){
ss << "'";
}
ss << this->value;
if(needQuotes){
ss << "'";
}
return ss.str();
}
};
#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<class A, class B>
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;
}
/**
* F - foreign key class
*/
template<class F>
struct on_update_delete_t {
using foreign_key_type = F;
const foreign_key_type &fk;
const bool update; // true if update and false if delete
on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : fk(fk_), update(update_), _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;
}
operator std::string() const {
if(this->update){
return "ON UPDATE";
}else{
return "ON DELETE";
}
}
};
template<class ...Cs, class ...Rs>
struct foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> {
using columns_type = std::tuple<Cs...>;
using references_type = std::tuple<Rs...>;
using self = foreign_key_t<columns_type, references_type>;
columns_type columns;
references_type references;
on_update_delete_t<self> on_update;
on_update_delete_t<self> on_delete;
static_assert(std::tuple_size<columns_type>::value == std::tuple_size<references_type>::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;
}
using field_type = void; // for column iteration. Better be deleted
using constraints_type = std::tuple<>;
template<class L>
void for_each_column(L) {}
template<class ...Opts>
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<class ...Cs>
struct foreign_key_intermediate_t {
using tuple_type = std::tuple<Cs...>;
tuple_type columns;
foreign_key_intermediate_t(tuple_type columns_): columns(std::move(columns_)) {}
template<class ...Rs>
foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> references(Rs ...references) {
using ret_type = foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>;
return ret_type(std::move(this->columns), std::make_tuple(std::forward<Rs>(references)...));
}
};
#endif
struct collate_t {
internal::collate_argument argument;
collate_t(internal::collate_argument argument_): argument(argument_) {}
operator std::string() const {
std::string res = "COLLATE " + 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";
}
}
};
template<class T>
struct is_constraint : std::false_type {};
template<>
struct is_constraint<autoincrement_t> : std::true_type {};
template<class ...Cs>
struct is_constraint<primary_key_t<Cs...>> : std::true_type {};
template<>
struct is_constraint<unique_t> : std::true_type {};
template<class T>
struct is_constraint<default_t<T>> : std::true_type {};
template<class C, class R>
struct is_constraint<foreign_key_t<C, R>> : std::true_type {};
template<>
struct is_constraint<collate_t> : std::true_type {};
template<class ...Args>
struct constraints_size;
template<>
struct constraints_size<> {
static constexpr const int value = 0;
};
template<class H, class ...Args>
struct constraints_size<H, Args...> {
static constexpr const int value = is_constraint<H>::value + constraints_size<Args...>::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<class ...Cs>
constraints::foreign_key_intermediate_t<Cs...> foreign_key(Cs ...columns) {
return {std::make_tuple(std::forward<Cs>(columns)...)};
}
#endif
/**
* UNIQUE constraint builder function.
*/
inline constraints::unique_t unique() {
return {};
}
inline constraints::autoincrement_t autoincrement() {
return {};
}
template<class ...Cs>
inline constraints::primary_key_t<Cs...> primary_key(Cs ...cs) {
using ret_type = constraints::primary_key_t<Cs...>;
return ret_type(std::make_tuple(cs...));
}
template<class T>
constraints::default_t<T> default_value(T t) {
return {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};
}
namespace internal {
/**
* FOREIGN KEY traits. Common case
*/
template<class T>
struct is_foreign_key : std::false_type {};
/**
* FOREIGN KEY traits. Specialized case
*/
template<class C, class R>
struct is_foreign_key<constraints::foreign_key_t<C, R>> : std::true_type {};
/**
* PRIMARY KEY traits. Common case
*/
template<class T>
struct is_primary_key : public std::false_type {};
/**
* PRIMARY KEY traits. Specialized case
*/
template<class ...Cs>
struct is_primary_key<constraints::primary_key_t<Cs...>> : public std::true_type {};
}
}
#pragma once
#include <type_traits> // std::false_type, std::true_type
#include <memory> // std::shared_ptr, std::unique_ptr
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<class T>
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<class T>
struct type_is_nullable<std::shared_ptr<T>> : public std::true_type {
bool operator()(const std::shared_ptr<T> &t) const {
return static_cast<bool>(t);
}
};
/**
* This is a specialization for std::unique_ptr. std::unique_ptr is nullable too.
*/
template<class T>
struct type_is_nullable<std::unique_ptr<T>> : public std::true_type {
bool operator()(const std::unique_ptr<T> &t) const {
return static_cast<bool>(t);
}
};
}
#pragma once
#include <memory> // std::unique_ptr
#include <string> // std::string
#include <sstream> // std::stringstream
// #include "constraints.h"
namespace sqlite_orm {
namespace internal {
/**
* This class is used in tuple interation to know whether tuple constains `default_value_t`
* constraint class and what it's value if it is
*/
struct default_value_extractor {
template<class A>
std::unique_ptr<std::string> operator() (const A &) {
return {};
}
template<class T>
std::unique_ptr<std::string> operator() (const constraints::default_t<T> &t) {
std::stringstream ss;
ss << t.value;
return std::make_unique<std::string>(ss.str());
}
};
}
}
#pragma once
#include <type_traits> // std::false_type, std::true_type
namespace sqlite_orm {
namespace internal {
/**
* Inherit this class to support arithmetic types overloading
*/
struct arithmetic_t {};
/**
* Result of concatenation || operator
*/
template<class L, class R>
struct conc_t {
L l;
R r;
};
/**
* Result of addition + operator
*/
template<class L, class R>
struct add_t : arithmetic_t {
using left_type = L;
using right_type = R;
left_type l;
right_type r;
add_t() = default;
add_t(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {}
};
/**
* Result of subscribe - operator
*/
template<class L, class R>
struct sub_t : arithmetic_t {
using left_type = L;
using right_type = R;
left_type l;
right_type r;
sub_t() = default;
sub_t(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {}
};
/**
* Result of multiply * operator
*/
template<class L, class R>
struct mul_t : arithmetic_t {
using left_type = L;
using right_type = R;
left_type l;
right_type r;
mul_t() = default;
mul_t(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {}
};
/**
* Result of divide / operator
*/
template<class L, class R>
struct div_t : arithmetic_t {
using left_type = L;
using right_type = R;
left_type l;
right_type r;
div_t() = default;
div_t(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {}
};
/**
* Result of mod % operator
*/
template<class L, class R>
struct mod_t : arithmetic_t {
using left_type = L;
using right_type = R;
left_type l;
right_type r;
mod_t() = default;
mod_t(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {}
};
/**
* Result of assign = operator
*/
template<class L, class R>
struct assign_t {
L l;
R r;
assign_t(){}
assign_t(L l_, R r_): l(l_), r(r_) {}
};
/**
* Assign operator traits. Common case
*/
template<class T>
struct is_assign_t : public std::false_type {};
/**
* Assign operator traits. Specialized case
*/
template<class L, class R>
struct is_assign_t<assign_t<L, R>> : public std::true_type {};
/**
* Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t
*/
template<class T>
struct expression_t {
T t;
expression_t(T t_): t(t_) {}
template<class R>
assign_t<T, R> operator=(R r) const {
return {this->t, r};
}
};
}
/**
* Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or `storage.update(set(c(&User::name) = "Dua Lipa"));
*/
template<class T>
internal::expression_t<T> c(T t) {
using result_type = internal::expression_t<T>;
return result_type(t);
}
/**
* Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT name + '@gmail.com' FROM users
*/
template<class L, class R>
internal::conc_t<L, R> conc(L l, R r) {
return {l, r};
}
/**
* Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users
*/
template<class L, class R>
internal::add_t<L, R> add(L l, R r) {
return {l, r};
}
/**
* Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users
*/
template<class L, class R>
internal::sub_t<L, R> sub(L l, R r) {
return {l, r};
}
template<class L, class R>
internal::mul_t<L, R> mul(L l, R r) {
return {l, r};
}
template<class L, class R>
internal::div_t<L, R> div(L l, R r) {
return {l, r};
}
template<class L, class R>
internal::mod_t<L, R> mod(L l, R r) {
return {l, r};
}
template<class L, class R>
internal::assign_t<L, R> assign(L l, R r) {
return {std::move(l), std::move(r)};
}
}
#pragma once
#include <tuple> // std::tuple
#include <string> // std::string
#include <memory> // std::unique_ptr
#include <type_traits> // std::true_type, std::false_type, std::is_same, std::enable_if
// #include "type_is_nullable.h"
// #include "tuple_helper.h"
// #include "default_value_extractor.h"
// #include "constraints.h"
namespace sqlite_orm {
namespace internal {
/**
* 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<class O, class T, class G/* = const T& (O::*)() const*/, class S/* = void (O::*)(T)*/, class ...Op>
struct column_t {
using object_type = O;
using field_type = T;
using constraints_type = std::tuple<Op...>;
using member_pointer_t = field_type object_type::*;
using getter_type = G;
using setter_type = S;
/**
* Column name. Specified during construction in `make_column`.
*/
const std::string name;
/**
* 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;
/**
* Simplified interface for `NOT NULL` constraint
*/
bool not_null() const {
return !type_is_nullable<field_type>::value;
}
template<class Opt>
constexpr bool has() const {
return tuple_helper::tuple_contains_type<Opt, constraints_type>::value;
}
template<class O1, class O2, class ...Opts>
constexpr bool has_every() const {
if(has<O1>() && has<O2>()) {
return true;
}else{
return has_every<Opts...>();
}
}
template<class O1>
constexpr bool has_every() const {
return has<O1>();
}
/**
* Simplified interface for `DEFAULT` constraint
* @return string representation of default value if it exists otherwise nullptr
*/
std::unique_ptr<std::string> default_value() {
std::unique_ptr<std::string> res;
tuple_helper::iterator<std::tuple_size<constraints_type>::value - 1, Op...>()(constraints, [&res](auto &v){
auto dft = internal::default_value_extractor()(v);
if(dft){
res = std::move(dft);
}
});
return res;
}
};
/**
* Column traits. Common case.
*/
template<class T>
struct is_column : public std::false_type {};
/**
* Column traits. Specialized case case.
*/
template<class O, class T, class ...Op>
struct is_column<column_t<O, T, Op...>> : public std::true_type {};
template<class O, class T>
using getter_by_value_const = T (O::*)() const;
template<class O, class T>
using getter_by_value = T (O::*)();
template<class O, class T>
using getter_by_ref_const = T& (O::*)() const;
template<class O, class T>
using getter_by_ref = T& (O::*)();
template<class O, class T>
using getter_by_const_ref_const = const T& (O::*)() const;
template<class O, class T>
using getter_by_const_ref = const T& (O::*)();
template<class O, class T>
using setter_by_value = void (O::*)(T);
template<class O, class T>
using setter_by_ref = void (O::*)(T&);
template<class O, class T>
using setter_by_const_ref = void (O::*)(const T&);
template<class T>
struct is_getter : std::false_type {};
template<class O, class T>
struct is_getter<getter_by_value_const<O, T>> : std::true_type {};
template<class O, class T>
struct is_getter<getter_by_value<O, T>> : std::true_type {};
template<class O, class T>
struct is_getter<getter_by_ref_const<O, T>> : std::true_type {};
template<class O, class T>
struct is_getter<getter_by_ref<O, T>> : std::true_type {};
template<class O, class T>
struct is_getter<getter_by_const_ref_const<O, T>> : std::true_type {};
template<class O, class T>
struct is_getter<getter_by_const_ref<O, T>> : std::true_type {};
template<class T>
struct is_setter : std::false_type {};
template<class O, class T>
struct is_setter<setter_by_value<O, T>> : std::true_type {};
template<class O, class T>
struct is_setter<setter_by_ref<O, T>> : std::true_type {};
template<class O, class T>
struct is_setter<setter_by_const_ref<O, T>> : std::true_type {};
template<class T>
struct getter_traits;
template<class O, class T>
struct getter_traits<getter_by_value_const<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = false;
};
template<class O, class T>
struct getter_traits<getter_by_value<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = false;
};
template<class O, class T>
struct getter_traits<getter_by_ref_const<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = true;
};
template<class O, class T>
struct getter_traits<getter_by_ref<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = true;
};
template<class O, class T>
struct getter_traits<getter_by_const_ref_const<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = true;
};
template<class O, class T>
struct getter_traits<getter_by_const_ref<O, T>> {
using object_type = O;
using field_type = T;
static constexpr const bool returns_lvalue = true;
};
template<class T>
struct setter_traits;
template<class O, class T>
struct setter_traits<setter_by_value<O, T>> {
using object_type = O;
using field_type = T;
};
template<class O, class T>
struct setter_traits<setter_by_ref<O, T>> {
using object_type = O;
using field_type = T;
};
template<class O, class T>
struct setter_traits<setter_by_const_ref<O, T>> {
using object_type = O;
using field_type = T;
};
}
/**
* Column builder function. You should use it to create columns instead of constructor
*/
template<class O, class T,
typename = typename std::enable_if<!std::is_member_function_pointer<T O::*>::value>::type,
class ...Op>
internal::column_t<O, T, const T& (O::*)() const, void (O::*)(T), Op...> make_column(const std::string &name, T O::*m, Op ...constraints){
static_assert(constraints::constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, "Incorrect constraints pack");
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<class G, class S,
typename = typename std::enable_if<internal::is_getter<G>::value>::type,
typename = typename std::enable_if<internal::is_setter<S>::value>::type,
class ...Op>
internal::column_t<
typename internal::setter_traits<S>::object_type,
typename internal::setter_traits<S>::field_type,
G, S, Op...> make_column(const std::string &name,
S setter,
G getter,
Op ...constraints)
{
static_assert(std::is_same<typename internal::setter_traits<S>::field_type, typename internal::getter_traits<G>::field_type>::value,
"Getter and setter must get and set same data type");
static_assert(constraints::constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::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<class G, class S,
typename = typename std::enable_if<internal::is_getter<G>::value>::type,
typename = typename std::enable_if<internal::is_setter<S>::value>::type,
class ...Op>
internal::column_t<
typename internal::setter_traits<S>::object_type,
typename internal::setter_traits<S>::field_type,
G, S, Op...> make_column(const std::string &name,
G getter,
S setter,
Op ...constraints)
{
static_assert(std::is_same<typename internal::setter_traits<S>::field_type, typename internal::getter_traits<G>::field_type>::value,
"Getter and setter must get and set same data type");
static_assert(constraints::constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, "Incorrect constraints pack");
return {name, nullptr, getter, setter, std::make_tuple(constraints...)};
}
}
#pragma once
#include <string> // std::string
#include <sstream> // std::stringstream
#include <vector> // std::vector
#include <cstddef> // std::nullptr_t
#include <memory> // std::shared_ptr, std::unique_ptr
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<class T>
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<unsigned char> {
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<signed char> {
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<char> {
std::string operator()(const char &t) const {
std::stringstream stream;
stream << +t;
return stream.str();
}
};
template<>
struct field_printer<std::string> {
std::string operator()(const std::string &t) const {
return t;
}
};
template<>
struct field_printer<std::vector<char>> {
std::string operator()(const std::vector<char> &t) const {
std::stringstream ss;
ss << std::hex;
for(auto c : t) {
ss << c;
}
return ss.str();
}
};
template<>
struct field_printer<std::nullptr_t> {
std::string operator()(const std::nullptr_t &) const {
return "null";
}
};
template<class T>
struct field_printer<std::shared_ptr<T>> {
std::string operator()(const std::shared_ptr<T> &t) const {
if(t){
return field_printer<T>()(*t);
}else{
return field_printer<std::nullptr_t>()(nullptr);
}
}
};
template<class T>
struct field_printer<std::unique_ptr<T>> {
std::string operator()(const std::unique_ptr<T> &t) const {
if(t){
return field_printer<T>()(*t);
}else{
return field_printer<std::nullptr_t>()(nullptr);
}
}
};
}
#pragma once
#include <string> // std::string
// #include "collate_argument.h"
// #include "constraints.h"
namespace sqlite_orm {
namespace conditions {
/**
* Stores LIMIT/OFFSET info
*/
struct limit_t {
int lim = 0;
bool has_offset = false;
bool offset_is_implicit = false;
int off = 0;
limit_t() = default;
limit_t(decltype(lim) lim_): lim(lim_) {}
limit_t(decltype(lim) lim_,
decltype(has_offset) has_offset_,
decltype(offset_is_implicit) offset_is_implicit_,
decltype(off) off_):
lim(lim_),
has_offset(has_offset_),
offset_is_implicit(offset_is_implicit_),
off(off_){}
operator std::string () const {
return "LIMIT";
}
};
/**
* Stores OFFSET only info
*/
struct offset_t {
int off;
};
/**
* Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators
*/
struct condition_t {};
/**
* Collated something
*/
template<class T>
struct collate_t : public condition_t {
T expr;
internal::collate_argument argument;
collate_t(T expr_, internal::collate_argument argument_): expr(expr_), argument(argument_) {}
operator std::string () const {
return constraints::collate_t{this->argument};
}
};
/**
* Collated something with custom collate function
*/
template<class T>
struct named_collate {
T expr;
std::string name;
named_collate() = default;
named_collate(T expr_, std::string name_): expr(expr_), name(std::move(name_)) {}
operator std::string () const {
return "COLLATE " + this->name;
}
};
/**
* Result of not operator
*/
template<class C>
struct negated_condition_t : public condition_t {
C c;
negated_condition_t() = default;
negated_condition_t(C c_): c(c_) {}
operator std::string () const {
return "NOT";
}
};
/**
* Result of and operator
*/
template<class L, class R>
struct and_condition_t : public condition_t {
L l;
R r;
and_condition_t() = default;
and_condition_t(L l_, R r_): l(l_), r(r_) {}
operator std::string () const {
return "AND";
}
};
/**
* Result of or operator
*/
template<class L, class R>
struct or_condition_t : public condition_t {
L l;
R r;
or_condition_t() = default;
or_condition_t(L l_, R r_): l(l_), r(r_) {}
operator std::string () const {
return "OR";
}
};
/**
* Base class for binary conditions
*/
template<class L, class R>
struct binary_condition : public condition_t {
L l;
R r;
binary_condition() = default;
binary_condition(L l_, R r_): l(l_), r(r_) {}
};
/**
* = and == operators object
*/
template<class L, class R>
struct is_equal_t : public binary_condition<L, R> {
using self = is_equal_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return "=";
}
negated_condition_t<self> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
named_collate<self> collate(std::string name) const {
return {*this, std::move(name)};
}
};
/**
* != operator object
*/
template<class L, class R>
struct is_not_equal_t : public binary_condition<L, R> {
using self = is_not_equal_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return "!=";
}
negated_condition_t<self> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
};
/**
* > operator object.
*/
template<class L, class R>
struct greater_than_t : public binary_condition<L, R> {
using self = greater_than_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return ">";
}
negated_condition_t<self> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
};
/**
* >= operator object.
*/
template<class L, class R>
struct greater_or_equal_t : public binary_condition<L, R> {
using self = greater_or_equal_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return ">=";
}
negated_condition_t<self> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
};
/**
* < operator object.
*/
template<class L, class R>
struct lesser_than_t : public binary_condition<L, R> {
using self = lesser_than_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return "<";
}
negated_condition_t<self> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
};
/**
* <= operator object.
*/
template<class L, class R>
struct lesser_or_equal_t : public binary_condition<L, R> {
using self = lesser_or_equal_t<L, R>;
using binary_condition<L, R>::binary_condition;
operator std::string () const {
return "<=";
}
negated_condition_t<lesser_or_equal_t<L, R>> operator!() const {
return {*this};
}
collate_t<self> collate_binary() const {
return {*this, internal::collate_argument::binary};
}
collate_t<self> collate_nocase() const {
return {*this, internal::collate_argument::nocase};
}
collate_t<self> collate_rtrim() const {
return {*this, internal::collate_argument::rtrim};
}
};
/**
* IN operator object.
*/
template<class L, class A>
struct in_t : public condition_t {
using self = in_t<L, A>;
L l; // left expression
A arg; // in arg
bool negative = false; // used in not_in
in_t() = default;
in_t(L l_, A arg_, bool negative_): l(l_), arg(std::move(arg_)), negative(negative_) {}
negated_condition_t<self> operator!() const {
return {*this};
}
operator std::string () const {
if(!this->negative){
return "IN";
}else{
return "NOT IN";
}
}
};
/**
* IS NULL operator object.
*/
template<class T>
struct is_null_t {
using self = is_null_t<T>;
T t;
negated_condition_t<self> operator!() const {
return {*this};
}
operator std::string () const {
return "IS NULL";
}
};
/**
* IS NOT NULL operator object.
*/
template<class T>
struct is_not_null_t {
using self = is_not_null_t<T>;
T t;
negated_condition_t<self> operator!() const {
return {*this};
}
operator std::string () const {
return "IS NOT NULL";
}
};
/**
* WHERE argument holder.
*/
template<class C>
struct where_t {
C c;
operator std::string () const {
return "WHERE";
}
};
/**
* ORDER BY argument holder.
*/
template<class O>
struct order_by_t {
using self = order_by_t<O>;
O o;
int asc_desc = 0; // 1: asc, -1: desc
std::string _collate_argument;
order_by_t(): o() {}
order_by_t(O o_): o(o_) {}
operator std::string() const {
return "ORDER BY";
}
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(internal::collate_argument::binary);
return res;
}
self collate_nocase() const {
auto res = *this;
res._collate_argument = constraints::collate_t::string_from_collate_argument(internal::collate_argument::nocase);
return res;
}
self collate_rtrim() const {
auto res = *this;
res._collate_argument = constraints::collate_t::string_from_collate_argument(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<class ...Args>
struct multi_order_by_t {
std::tuple<Args...> args;
operator std::string() const {
return static_cast<std::string>(order_by_t<void*>());
}
};
/**
* GROUP BY pack holder.
*/
template<class ...Args>
struct group_by_t {
std::tuple<Args...> args;
operator std::string() const {
return "GROUP BY";
}
};
/**
* BETWEEN operator object.
*/
template<class A, class T>
struct between_t : public condition_t {
A expr;
T b1;
T b2;
between_t() = default;
between_t(A expr_, T b1_, T b2_): expr(expr_), b1(b1_), b2(b2_) {}
operator std::string() const {
return "BETWEEN";
}
};
/**
* LIKE operator object.
*/
template<class A, class T>
struct like_t : public condition_t {
A a;
T t;
like_t() = default;
like_t(A a_, T t_): a(a_), t(t_) {}
operator std::string() const {
return "LIKE";
}
};
/**
* CROSS JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct cross_join_t {
using type = T;
operator std::string() const {
return "CROSS JOIN";
}
};
/**
* NATURAL JOIN holder.
* T is joined type which represents any mapped table.
*/
template<class T>
struct natural_join_t {
using type = T;
operator std::string() const {
return "NATURAL 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 {
using type = T;
using on_type = O;
on_type constraint;
operator std::string() const {
return "LEFT 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 {
using type = T;
using on_type = O;
on_type constraint;
operator std::string() const {
return "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 {
using type = T;
using on_type = O;
on_type constraint;
operator std::string() const {
return "LEFT OUTER JOIN";
}
};
/**
* 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 {
using type = T;
type t;
operator std::string() const {
return "ON";
}
};
/**
* USING argument holder.
*/
template<class F, class O>
struct using_t {
F O::*column;
operator std::string() const {
return "USING";
}
};
/**
* 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 {
using type = T;
using on_type = O;
on_type constraint;
operator std::string() const {
return "INNER JOIN";
}
};
template<class T>
struct exists_t : condition_t {
using type = T;
using self = exists_t<type>;
type t;
exists_t() = default;
exists_t(T t_) : t(std::move(t_)) {}
operator std::string() const {
return "EXISTS";
}
negated_condition_t<self> operator!() const {
return {*this};
}
};
/**
* HAVING holder.
* T is having argument type.
*/
template<class T>
struct having_t {
using type = T;
type t;
operator std::string() const {
return "HAVING";
}
};
template<class T, class E>
struct cast_t {
using to_type = T;
using expression_type = E;
expression_type expression;
operator std::string() const {
return "CAST";
}
};
}
/**
* Cute operators for columns
*/
template<class T, class R>
conditions::lesser_than_t<T, R> operator<(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::lesser_than_t<L, T> operator<(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
conditions::lesser_or_equal_t<T, R> operator<=(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::lesser_or_equal_t<L, T> operator<=(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
conditions::greater_than_t<T, R> operator>(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::greater_than_t<L, T> operator>(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
conditions::greater_or_equal_t<T, R> operator>=(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::greater_or_equal_t<L, T> operator>=(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
conditions::is_equal_t<T, R> operator==(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::is_equal_t<L, T> operator==(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
conditions::is_not_equal_t<T, R> operator!=(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
conditions::is_not_equal_t<L, T> operator!=(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class T, class R>
internal::conc_t<T, R> operator||(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::conc_t<L, T> operator||(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::conc_t<L, R> operator||(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class T, class R>
internal::add_t<T, R> operator+(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::add_t<L, T> operator+(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::add_t<L, R> operator+(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class T, class R>
internal::sub_t<T, R> operator-(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::sub_t<L, T> operator-(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::sub_t<L, R> operator-(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class T, class R>
internal::mul_t<T, R> operator*(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::mul_t<L, T> operator*(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::mul_t<L, R> operator*(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class T, class R>
internal::div_t<T, R> operator/(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::div_t<L, T> operator/(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::div_t<L, R> operator/(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class T, class R>
internal::mod_t<T, R> operator%(internal::expression_t<T> expr, R r) {
return {expr.t, r};
}
template<class L, class T>
internal::mod_t<L, T> operator%(L l, internal::expression_t<T> expr) {
return {l, expr.t};
}
template<class L, class R>
internal::mod_t<L, R> operator%(internal::expression_t<L> l, internal::expression_t<R> r) {
return {l.t, r.t};
}
template<class F, class O>
conditions::using_t<F, O> using_(F O::*p) {
return {p};
}
template<class T>
conditions::on_t<T> on(T t) {
return {t};
}
template<class T>
conditions::cross_join_t<T> cross_join() {
return {};
}
template<class T>
conditions::natural_join_t<T> natural_join() {
return {};
}
template<class T, class O>
conditions::left_join_t<T, O> left_join(O o) {
return {o};
}
template<class T, class O>
conditions::join_t<T, O> join(O o) {
return {o};
}
template<class T, class O>
conditions::left_outer_join_t<T, O> left_outer_join(O o) {
return {o};
}
template<class T, class O>
conditions::inner_join_t<T, O> inner_join(O o) {
return {o};
}
inline conditions::offset_t offset(int off) {
return {off};
}
inline conditions::limit_t limit(int lim) {
return {lim};
}
inline conditions::limit_t limit(int off, int lim) {
return {lim, true, true, off};
}
inline conditions::limit_t limit(int lim, conditions::offset_t offt) {
return {lim, true, false, offt.off };
}
template<
class L,
class R,
typename = typename std::enable_if<std::is_base_of<conditions::condition_t, L>::value || std::is_base_of<conditions::condition_t, R>::value>::type
>
conditions::and_condition_t<L, R> operator &&(const L &l, const R &r) {
return {l, r};
}
template<
class L,
class R,
typename = typename std::enable_if<std::is_base_of<conditions::condition_t, L>::value || std::is_base_of<conditions::condition_t, R>::value>::type
>
conditions::or_condition_t<L, R> operator ||(const L &l, const R &r) {
return {l, r};
}
template<class T>
conditions::is_not_null_t<T> is_not_null(T t) {
return {t};
}
template<class T>
conditions::is_null_t<T> is_null(T t) {
return {t};
}
template<class L, class E>
conditions::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>
conditions::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>
conditions::in_t<L, A> in(L l, A arg) {
return {std::move(l), std::move(arg), false};
}
template<class L, class E>
conditions::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>
conditions::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>
conditions::in_t<L, A> not_in(L l, A arg) {
return {std::move(l), std::move(arg), true};
}
template<class L, class R>
conditions::is_equal_t<L, R> is_equal(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::is_equal_t<L, R> eq(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::is_not_equal_t<L, R> is_not_equal(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::is_not_equal_t<L, R> ne(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::greater_than_t<L, R> greater_than(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::greater_than_t<L, R> gt(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::greater_or_equal_t<L, R> greater_or_equal(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::greater_or_equal_t<L, R> ge(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::lesser_than_t<L, R> lesser_than(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::lesser_than_t<L, R> lt(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::lesser_or_equal_t<L, R> lesser_or_equal(L l, R r) {
return {l, r};
}
template<class L, class R>
conditions::lesser_or_equal_t<L, R> le(L l, R r) {
return {l, r};
}
template<class C>
conditions::where_t<C> where(C c) {
return {c};
}
template<class O>
conditions::order_by_t<O> order_by(O o) {
return {o};
}
template<class ...Args>
conditions::multi_order_by_t<Args...> multi_order_by(Args&& ...args) {
return {std::make_tuple(std::forward<Args>(args)...)};
}
template<class ...Args>
conditions::group_by_t<Args...> group_by(Args&& ...args) {
return {std::make_tuple(std::forward<Args>(args)...)};
}
template<class A, class T>
conditions::between_t<A, T> between(A expr, T b1, T b2) {
return {expr, b1, b2};
}
template<class A, class T>
conditions::like_t<A, T> like(A a, T t) {
return {a, t};
}
template<class T>
conditions::exists_t<T> exists(T t) {
return {std::move(t)};
}
template<class T>
conditions::having_t<T> having(T t) {
return {t};
}
template<class T, class E>
conditions::cast_t<T, E> cast(E e) {
return {e};
}
}
#pragma once
#include <type_traits> // std::enable_if, std::is_base_of, std::is_member_pointer
#include <sstream> // std::stringstream
#include <string> // 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<class T, char A>
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<class T, class C>
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<class T, class SFINAE = void>
struct alias_extractor;
template<class T>
struct alias_extractor<T, typename std::enable_if<std::is_base_of<alias_tag, T>::value>::type> {
static std::string get() {
std::stringstream ss;
ss << T::get();
return ss.str();
}
};
template<class T>
struct alias_extractor<T, typename std::enable_if<!std::is_base_of<alias_tag, T>::value>::type> {
static std::string get() {
return {};
}
};
template<class T, class E>
struct as_t {
using alias_type = T;
using expression_type = E;
expression_type expression;
};
template<class T>
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<class T, class C>
internal::alias_column_t<T, C> alias_column(C c) {
static_assert(std::is_member_pointer<C>::value, "alias_column argument must be a member pointer mapped to a storage");
return {c};
}
template<class T, class E>
internal::as_t<T, E> as(E expression) {
return {std::move(expression)};
}
template<class T>
internal::alias_holder<T> get() {
return {};
}
template<class T> using alias_a = internal::table_alias<T, 'a'>;
template<class T> using alias_b = internal::table_alias<T, 'b'>;
template<class T> using alias_c = internal::table_alias<T, 'c'>;
template<class T> using alias_d = internal::table_alias<T, 'd'>;
template<class T> using alias_e = internal::table_alias<T, 'e'>;
template<class T> using alias_f = internal::table_alias<T, 'f'>;
template<class T> using alias_g = internal::table_alias<T, 'g'>;
template<class T> using alias_h = internal::table_alias<T, 'h'>;
template<class T> using alias_i = internal::table_alias<T, 'i'>;
template<class T> using alias_j = internal::table_alias<T, 'j'>;
template<class T> using alias_k = internal::table_alias<T, 'k'>;
template<class T> using alias_l = internal::table_alias<T, 'l'>;
template<class T> using alias_m = internal::table_alias<T, 'm'>;
template<class T> using alias_n = internal::table_alias<T, 'n'>;
template<class T> using alias_o = internal::table_alias<T, 'o'>;
template<class T> using alias_p = internal::table_alias<T, 'p'>;
template<class T> using alias_q = internal::table_alias<T, 'q'>;
template<class T> using alias_r = internal::table_alias<T, 'r'>;
template<class T> using alias_s = internal::table_alias<T, 's'>;
template<class T> using alias_t = internal::table_alias<T, 't'>;
template<class T> using alias_u = internal::table_alias<T, 'u'>;
template<class T> using alias_v = internal::table_alias<T, 'v'>;
template<class T> using alias_w = internal::table_alias<T, 'w'>;
template<class T> using alias_x = internal::table_alias<T, 'x'>;
template<class T> using alias_y = internal::table_alias<T, 'y'>;
template<class T> using alias_z = internal::table_alias<T, 'z'>;
}
#pragma once
// #include "conditions.h"
namespace sqlite_orm {
namespace internal {
template<class ...Args>
struct join_iterator {
template<class L>
void operator()(L) {
//..
}
};
template<>
struct join_iterator<> {
template<class L>
void operator()(L) {
//..
}
};
template<class H, class ...Tail>
struct join_iterator<H, Tail...> : public join_iterator<Tail...>{
using super = join_iterator<Tail...>;
H h;
template<class L>
void operator()(L l) {
this->super::operator()(l);
}
};
template<class T, class ...Tail>
struct join_iterator<conditions::cross_join_t<T>, Tail...> : public join_iterator<Tail...>{
using super = join_iterator<Tail...>;
conditions::cross_join_t<T> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
template<class T, class ...Tail>
struct join_iterator<conditions::natural_join_t<T>, Tail...> : public join_iterator<Tail...>{
using super = join_iterator<Tail...>;
conditions::natural_join_t<T> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
template<class T, class O, class ...Tail>
struct join_iterator<conditions::left_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
using super = join_iterator<Tail...>;
conditions::left_join_t<T, O> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
template<class T, class O, class ...Tail>
struct join_iterator<conditions::join_t<T, O>, Tail...> : public join_iterator<Tail...> {
using super = join_iterator<Tail...>;
conditions::join_t<T, O> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
template<class T, class O, class ...Tail>
struct join_iterator<conditions::left_outer_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
using super = join_iterator<Tail...>;
conditions::left_outer_join_t<T, O> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
template<class T, class O, class ...Tail>
struct join_iterator<conditions::inner_join_t<T, O>, Tail...> : public join_iterator<Tail...> {
using super = join_iterator<Tail...>;
conditions::inner_join_t<T, O> h;
template<class L>
void operator()(L l) {
l(h);
this->super::operator()(l);
}
};
}
}
#pragma once
#include <string> // std::string
#include <tuple> // std::make_tuple
#include <type_traits> // std::forward, std::is_base_of, std::enable_if
// #include "conditions.h"
// #include "operators.h"
namespace sqlite_orm {
namespace core_functions {
/**
* Base class for operator overloading
*/
struct core_function_t {};
/**
* LENGTH(x) function https://sqlite.org/lang_corefunc.html#length
*/
template<class T>
struct length_t : public core_function_t {
T t;
length_t() = default;
length_t(T t_): t(t_) {}
operator std::string() const {
return "LENGTH";
}
};
/**
* ABS(x) function https://sqlite.org/lang_corefunc.html#abs
*/
template<class T>
struct abs_t : public core_function_t {
T t;
abs_t() = default;
abs_t(T t_): t(t_) {}
operator std::string() const {
return "ABS";
}
};
/**
* LOWER(x) function https://sqlite.org/lang_corefunc.html#lower
*/
template<class T>
struct lower_t : public core_function_t {
T t;
lower_t() = default;
lower_t(T t_): t(t_) {}
operator std::string() const {
return "LOWER";
}
};
/**
* UPPER(x) function https://sqlite.org/lang_corefunc.html#upper
*/
template<class T>
struct upper_t : public core_function_t {
T t;
upper_t() = default;
upper_t(T t_): t(t_) {}
operator std::string() const {
return "UPPER";
}
};
/**
* CHANGES() function https://sqlite.org/lang_corefunc.html#changes
*/
struct changes_t : public core_function_t {
operator std::string() const {
return "CHANGES";
}
};
/**
* TRIM(X) function https://sqlite.org/lang_corefunc.html#trim
*/
template<class X>
struct trim_single_t : public core_function_t {
X x;
trim_single_t() = default;
trim_single_t(X x_): x(x_) {}
operator std::string() const {
return "TRIM";
}
};
/**
* TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim
*/
template<class X, class Y>
struct trim_double_t : public core_function_t {
X x;
Y y;
trim_double_t() = default;
trim_double_t(X x_, Y y_): x(x_), y(y_) {}
operator std::string() const {
return static_cast<std::string>(trim_single_t<X>(0));
}
};
/**
* LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim
*/
template<class X>
struct ltrim_single_t : public core_function_t {
X x;
ltrim_single_t() = default;
ltrim_single_t(X x_): x(x_) {}
operator std::string() const {
return "LTRIM";
}
};
/**
* LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim
*/
template<class X, class Y>
struct ltrim_double_t : public core_function_t {
X x;
Y y;
ltrim_double_t() = default;
ltrim_double_t(X x_, Y y_): x(x_), y(y_) {}
operator std::string() const {
return static_cast<std::string>(ltrim_single_t<X>(0));
}
};
/**
* RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim
*/
template<class X>
struct rtrim_single_t : public core_function_t {
X x;
rtrim_single_t() = default;
rtrim_single_t(X x_): x(x_) {}
operator std::string() const {
return "RTRIM";
}
};
/**
* RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim
*/
template<class X, class Y>
struct rtrim_double_t : public core_function_t {
X x;
Y y;
rtrim_double_t() = default;
rtrim_double_t(X x_, Y y_): x(x_), y(y_) {}
operator std::string() const {
return static_cast<std::string>(rtrim_single_t<X>(0));
}
};
#if SQLITE_VERSION_NUMBER >= 3007016
/**
* CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char
*/
template<class ...Args>
struct char_t_ : public core_function_t {
using args_type = std::tuple<Args...>;
args_type args;
char_t_() = default;
char_t_(args_type args_): args(args_) {}
operator std::string() const {
return "CHAR";
}
};
struct random_t : core_function_t, internal::arithmetic_t {
operator std::string() const {
return "RANDOM";
}
};
#endif
template<class T, class ...Args>
struct date_t : core_function_t {
using modifiers_type = std::tuple<Args...>;
T timestring;
modifiers_type modifiers;
date_t() = default;
date_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {}
operator std::string() const {
return "DATE";
}
};
template<class T, class ...Args>
struct datetime_t : core_function_t {
using modifiers_type = std::tuple<Args...>;
T timestring;
modifiers_type modifiers;
datetime_t() = default;
datetime_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {}
operator std::string() const {
return "DATETIME";
}
};
template<class T, class ...Args>
struct julianday_t : core_function_t, internal::arithmetic_t {
using modifiers_type = std::tuple<Args...>;
T timestring;
modifiers_type modifiers;
julianday_t() = default;
julianday_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {}
operator std::string() const {
return "JULIANDAY";
}
};
}
/**
* Cute operators for core functions
*/
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::lesser_than_t<F, R> operator<(F f, R r) {
return {f, r};
}
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::lesser_or_equal_t<F, R> operator<=(F f, R r) {
return {f, r};
}
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::greater_than_t<F, R> operator>(F f, R r) {
return {f, r};
}
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::greater_or_equal_t<F, R> operator>=(F f, R r) {
return {f, r};
}
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::is_equal_t<F, R> operator==(F f, R r) {
return {f, r};
}
template<
class F,
class R,
typename = typename std::enable_if<std::is_base_of<core_functions::core_function_t, F>::value>::type>
conditions::is_not_equal_t<F, R> operator!=(F f, R r) {
return {f, r};
}
inline core_functions::random_t random() {
return {};
}
template<class T, class ...Args, class Res = core_functions::date_t<T, Args...>>
Res date(T timestring, Args ...modifiers) {
return Res(timestring, std::make_tuple(std::forward<Args>(modifiers)...));
}
template<class T, class ...Args, class Res = core_functions::datetime_t<T, Args...>>
Res datetime(T timestring, Args ...modifiers) {
return Res(timestring, std::make_tuple(std::forward<Args>(modifiers)...));
}
template<class T, class ...Args, class Res = core_functions::julianday_t<T, Args...>>
Res julianday(T timestring, Args ...modifiers) {
return Res(timestring, std::make_tuple(std::forward<Args>(modifiers)...));
}
#if SQLITE_VERSION_NUMBER >= 3007016
template<class ...Args>
core_functions::char_t_<Args...> char_(Args&& ...args) {
using result_type = core_functions::char_t_<Args...>;
return result_type(std::make_tuple(std::forward<Args>(args)...));
}
#endif
template<class X, class Res = core_functions::trim_single_t<X>>
Res trim(X x) {
return Res(x);
}
template<class X, class Y, class Res = core_functions::trim_double_t<X, Y>>
Res trim(X x, Y y) {
return Res(x, y);
}
template<class X, class Res = core_functions::ltrim_single_t<X>>
Res ltrim(X x) {
return Res(x);
}
template<class X, class Y, class Res = core_functions::ltrim_double_t<X, Y>>
Res ltrim(X x, Y y) {
return Res(x, y);
}
template<class X, class Res = core_functions::rtrim_single_t<X>>
Res rtrim(X x) {
return Res(x);
}
template<class X, class Y, class Res = core_functions::rtrim_double_t<X, Y>>
Res rtrim(X x, Y y) {
return Res(x, y);
}
inline core_functions::changes_t changes() {
return {};
}
template<class T>
core_functions::length_t<T> length(T t) {
using result_type = core_functions::length_t<T>;
return result_type(t);
}
template<class T>
core_functions::abs_t<T> abs(T t) {
using result_type = core_functions::abs_t<T>;
return result_type(t);
}
template<class T, class Res = core_functions::lower_t<T>>
Res lower(T t) {
return Res(t);
}
template<class T, class Res = core_functions::upper_t<T>>
Res upper(T t) {
return Res(t);
}
template<
class L,
class R,
typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + std::is_base_of<internal::arithmetic_t, R>::value > 0)>::type>
internal::add_t<L, R> operator+(L l, R r) {
return {std::move(l), std::move(r)};
}
template<
class L,
class R,
typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + std::is_base_of<internal::arithmetic_t, R>::value > 0)>::type>
internal::sub_t<L, R> operator-(L l, R r) {
return {std::move(l), std::move(r)};
}
template<
class L,
class R,
typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + std::is_base_of<internal::arithmetic_t, R>::value > 0)>::type>
internal::mul_t<L, R> operator*(L l, R r) {
return {std::move(l), std::move(r)};
}
template<
class L,
class R,
typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + std::is_base_of<internal::arithmetic_t, R>::value > 0)>::type>
internal::div_t<L, R> operator/(L l, R r) {
return {std::move(l), std::move(r)};
}
template<
class L,
class R,
typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + std::is_base_of<internal::arithmetic_t, R>::value > 0)>::type>
internal::mod_t<L, R> operator%(L l, R r) {
return {std::move(l), std::move(r)};
}
}
#pragma once
namespace sqlite_orm {
namespace aggregate_functions {
template<class T>
struct avg_t {
T t;
operator std::string() const {
return "AVG";
}
};
template<class T>
struct count_t {
T t;
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<class T>
struct count_asterisk_t {
using type = T;
operator std::string() const {
return "COUNT";
}
};
struct count_asterisk_without_type {
operator std::string() const {
return "COUNT";
}
};
template<class T>
struct sum_t {
T t;
operator std::string() const {
return "SUM";
}
};
template<class T>
struct total_t {
T t;
operator std::string() const {
return "TOTAL";
}
};
template<class T>
struct max_t {
T t;
operator std::string() const {
return "MAX";
}
};
template<class T>
struct min_t {
T t;
operator std::string() const {
return "MIN";
}
};
template<class T>
struct group_concat_single_t {
T t;
operator std::string() const {
return "GROUP_CONCAT";
}
};
template<class T>
struct group_concat_double_t {
T t;
std::string y;
operator std::string() const {
return "GROUP_CONCAT";
}
};
}
template<class T>
aggregate_functions::avg_t<T> avg(T t) {
return {t};
}
template<class T>
aggregate_functions::count_t<T> count(T t) {
return {t};
}
inline aggregate_functions::count_asterisk_without_type count() {
return {};
}
template<class T>
aggregate_functions::count_asterisk_t<T> count() {
return {};
}
template<class T>
aggregate_functions::sum_t<T> sum(T t) {
return {t};
}
template<class T>
aggregate_functions::max_t<T> max(T t) {
return {t};
}
template<class T>
aggregate_functions::min_t<T> min(T t) {
return {t};
}
template<class T>
aggregate_functions::total_t<T> total(T t) {
return {t};
}
template<class T>
aggregate_functions::group_concat_single_t<T> group_concat(T t) {
return {t};
}
template<class T, class Y>
aggregate_functions::group_concat_double_t<T> group_concat(T t, Y y) {
return {t, y};
}
}
#pragma once
namespace sqlite_orm {
namespace internal {
/**
* Cute class used to compare setters/getters and member pointers with each other.
*/
template<class L, class R>
struct typed_comparator {
bool operator()(const L &, const R &) const {
return false;
}
};
template<class O>
struct typed_comparator<O, O> {
bool operator()(const O &lhs, const O &rhs) const {
return lhs == rhs;
}
};
template<class L, class R>
bool compare_any(const L &lhs, const R &rhs) {
return typed_comparator<L, R>()(lhs, rhs);
}
}
}
#pragma once
#include <string> // std::string
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 <template <typename...> class Base, typename Derived>
struct is_base_of_template_impl {
template<typename... Ts>
static constexpr std::true_type test(const Base<Ts...>*);
static constexpr std::false_type test(...);
using type = decltype(test(std::declval<Derived*>()));
};
template <typename Derived, template <typename...> class Base>
using is_base_of_template = typename is_base_of_template_impl<Base, Derived>::type;
#else
template <template <typename...> class C, typename...Ts>
std::true_type is_base_of_template_impl(const C<Ts...>*);
template <template <typename...> class C>
std::false_type is_base_of_template_impl(...);
template <typename T, template <typename...> class C>
using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));
#endif
/**
* DISCTINCT generic container.
*/
template<class T>
struct distinct_t {
T t;
operator std::string() const {
return "DISTINCT";
}
};
/**
* ALL generic container.
*/
template<class T>
struct all_t {
T t;
operator std::string() const {
return "ALL";
}
};
template<class ...Args>
struct columns_t {
bool distinct = false;
template<class L>
void for_each(L) const {
//..
}
int count() const {
return 0;
}
};
template<class T, class ...Args>
struct columns_t<T, Args...> : public columns_t<Args...> {
T m;
columns_t(decltype(m) m_, Args&& ...args): super(std::forward<Args>(args)...), m(m_) {}
template<class L>
void for_each(L l) const {
l(this->m);
this->super::for_each(l);
}
int count() const {
return 1 + this->super::count();
}
private:
using super = columns_t<Args...>;
};
template<class ...Args>
struct set_t {
operator std::string() const {
return "SET";
}
template<class F>
void for_each(F) {
//..
}
};
template<class L, class ...Args>
struct set_t<L, Args...> : public set_t<Args...> {
static_assert(is_assign_t<typename std::remove_reference<L>::type>::value, "set_t argument must be assign_t");
L l;
using super = set_t<Args...>;
using self = set_t<L, Args...>;
set_t(L l_, Args&& ...args) : super(std::forward<Args>(args)...), l(std::forward<L>(l_)) {}
template<class F>
void for_each(F f) {
f(l);
this->super::for_each(f);
}
};
/**
* 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<class T, class F>
struct column_pointer {
using type = T;
using field_type = F;
field_type field;
};
/**
* Subselect object type.
*/
template<class T, class ...Args>
struct select_t {
using return_type = T;
using conditions_type = std::tuple<Args...>;
return_type col;
conditions_type conditions;
bool highest_level = false;
};
/**
* Base for UNION, UNION ALL, EXCEPT and INTERSECT
*/
template<class L, class R>
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;
}
};
/**
* UNION object type.
*/
template<class L, class R>
struct union_t : public compound_operator<L, R> {
using super = compound_operator<L, R>;
using left_type = typename super::left_type;
using right_type = typename super::right_type;
bool all = false;
union_t(left_type l, right_type r, decltype(all) all_): super(std::move(l), std::move(r)), all(all_) {}
union_t(left_type l, right_type r): union_t(std::move(l), std::move(r), false) {}
operator std::string() const {
if(!this->all){
return "UNION";
}else{
return "UNION ALL";
}
}
};
/**
* EXCEPT object type.
*/
template<class L, class R>
struct except_t : public compound_operator<L, R> {
using super = compound_operator<L, R>;
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<class L, class R>
struct intersect_t : public compound_operator<L, R> {
using super = compound_operator<L, R>;
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<class T>
bool get_distinct(const T &t) {
return false;
}
template<class ...Args>
bool get_distinct(const columns_t<Args...> &cols) {
return cols.distinct;
}
template<class T>
struct asterisk_t {
using type = T;
};
}
template<class T>
internal::distinct_t<T> distinct(T t) {
return {t};
}
template<class T>
internal::all_t<T> all(T t) {
return {t};
}
template<class ...Args>
internal::columns_t<Args...> distinct(internal::columns_t<Args...> 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<class ...Args>
internal::set_t<Args...> set(Args&& ...args) {
return {std::forward<Args>(args)...};
}
template<class ...Args>
internal::columns_t<Args...> columns(Args&& ...args) {
return {std::forward<Args>(args)...};
}
/**
* Use it like this:
* struct MyType : BaseType { ... };
* storage.select(column<MyType>(&BaseType::id));
*/
template<class T, class F>
internal::column_pointer<T, F> column(F f) {
return {f};
}
/**
* Public function for subselect query. Is useful in UNION queries.
*/
template<class T, class ...Args>
internal::select_t<T, Args...> select(T t, Args ...args) {
return {std::move(t), std::make_tuple<Args...>(std::forward<Args>(args)...)};
}
/**
* Public function for UNION operator.
* lhs and rhs are subselect objects.
* Look through example in examples/union.cpp
*/
template<class L, class R>
internal::union_t<L, R> 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<class L, class R>
internal::except_t<L, R> except(L lhs, R rhs) {
return {std::move(lhs), std::move(rhs)};
}
template<class L, class R>
internal::intersect_t<L, R> 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<class L, class R>
internal::union_t<L, R> union_all(L lhs, R rhs) {
return {std::move(lhs), std::move(rhs), true};
}
template<class T>
internal::asterisk_t<T> asterisk() {
return {};
}
}
#pragma once
#include <string> // std::string
#include <sqlite3.h>
#include <system_error> // std::error_code, std::system_error
// #include "error_code.h"
namespace sqlite_orm {
namespace internal {
struct database_connection {
database_connection(const std::string &filename) {
auto rc = sqlite3_open(filename.c_str(), &this->db);
if(rc != SQLITE_OK){
throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()));
}
}
~database_connection() {
sqlite3_close(this->db);
}
sqlite3* get_db() {
return this->db;
}
protected:
sqlite3 *db = nullptr;
};
}
}
#pragma once
#include <type_traits> // 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<class T, class SFINAE = void>
struct table_type;
template<class O, class F>
struct table_type<F O::*, typename std::enable_if<std::is_member_pointer<F O::*>::value && !std::is_member_function_pointer<F O::*>::value>::type> {
using type = O;
};
template<class T>
struct table_type<T, typename std::enable_if<is_getter<T>::value>::type> {
using type = typename getter_traits<T>::object_type;
};
template<class T>
struct table_type<T, typename std::enable_if<is_setter<T>::value>::type> {
using type = typename setter_traits<T>::object_type;
};
template<class T, class F>
struct table_type<column_pointer<T, F>, void> {
using type = T;
};
}
}
#pragma once
#include <string> // std::string
namespace sqlite_orm {
struct table_info {
int cid;
std::string name;
std::string type;
bool notnull;
std::string dflt_value;
int pk;
};
}
#pragma once
#include <sqlite3.h>
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<class V>
struct arithmetic_tag
{
using type = std::conditional_t<
std::is_integral<V>::value,
// Integer class
std::conditional_t<
sizeof(V) <= sizeof(int),
int_or_smaller_tag,
bigint_tag
>,
// Floating-point class
real_tag
>;
};
template<class V>
using arithmetic_tag_t = typename arithmetic_tag<V>::type;
}
#pragma once
namespace sqlite_orm {
/**
* Specialization for optional type (std::shared_ptr / std::unique_ptr).
*/
template <typename T>
struct is_std_ptr : std::false_type {};
template <typename T>
struct is_std_ptr<std::shared_ptr<T>> : std::true_type {
static std::shared_ptr<T> make(const T& v) {
return std::make_shared<T>(v);
}
};
template <typename T>
struct is_std_ptr<std::unique_ptr<T>> : std::true_type {
static std::unique_ptr<T> make(const T& v) {
return std::make_unique<T>(v);
}
};
}
#pragma once
#include <sqlite3.h>
#include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same
#include <string> // std::string, std::wstring
#ifndef SQLITE_ORM_OMITS_CODECVT
#include <codecvt> // std::wstring_convert, std::codecvt_utf8_utf16
#endif // SQLITE_ORM_OMITS_CODECVT
#include <vector> // std::vector
#include <cstddef> // std::nullptr_t
// #include "is_std_ptr.h"
namespace sqlite_orm {
/**
* Helper class used for binding fields to sqlite3 statements.
*/
template<class V, typename Enable = void>
struct statement_binder {
int bind(sqlite3_stmt *stmt, int index, const V &value);
};
/**
* Specialization for arithmetic types.
*/
template<class V>
struct statement_binder<
V,
std::enable_if_t<std::is_arithmetic<V>::value>
>
{
int bind(sqlite3_stmt *stmt, int index, const V &value) {
return bind(stmt, index, value, tag());
}
private:
using tag = arithmetic_tag_t<V>;
int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag&) {
return sqlite3_bind_int(stmt, index, static_cast<int>(value));
}
int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag&) {
return sqlite3_bind_int64(stmt, index, static_cast<sqlite3_int64>(value));
}
int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag&) {
return sqlite3_bind_double(stmt, index, static_cast<double>(value));
}
};
/**
* Specialization for std::string and C-string.
*/
template<class V>
struct statement_binder<
V,
std::enable_if_t<
std::is_same<V, std::string>::value
||
std::is_same<V, const char*>::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<class V>
struct statement_binder<
V,
std::enable_if_t<
std::is_same<V, std::wstring>::value
||
std::is_same<V, const wchar_t*>::value
>
>
{
int bind(sqlite3_stmt *stmt, int index, const V &value) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string utf8Str = converter.to_bytes(value);
return statement_binder<decltype(utf8Str)>().bind(stmt, index, utf8Str);
}
};
#endif // SQLITE_ORM_OMITS_CODECVT
/**
* Specialization for std::nullptr_t.
*/
template<class V>
struct statement_binder<
V,
std::enable_if_t<std::is_same<V, std::nullptr_t>::value>
>
{
int bind(sqlite3_stmt *stmt, int index, const V &) {
return sqlite3_bind_null(stmt, index);
}
};
template<class V>
struct statement_binder<
V,
std::enable_if_t<is_std_ptr<V>::value>
>
{
using value_type = typename V::element_type;
int bind(sqlite3_stmt *stmt, int index, const V &value) {
if(value){
return statement_binder<value_type>().bind(stmt, index, *value);
}else{
return statement_binder<std::nullptr_t>().bind(stmt, index, nullptr);
}
}
};
/**
* Specialization for optional type (std::vector<char>).
*/
template<class V>
struct statement_binder<
V,
std::enable_if_t<std::is_same<V, std::vector<char>>::value>
>
{
int bind(sqlite3_stmt *stmt, int index, const V &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);
}
}
};
}
#pragma once
#include <sqlite3.h>
#include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if
#include <cstdlib> // atof, atoi, atoll
#include <string> // std::string, std::wstring
#ifndef SQLITE_ORM_OMITS_CODECVT
#include <codecvt> // std::wstring_convert, std::codecvt_utf8_utf16
#endif // SQLITE_ORM_OMITS_CODECVT
#include <vector> // std::vector
#include <cstring> // strlen
#include <algorithm> // std::copy
#include <iterator> // std::back_inserter
#include <tuple> // std::tuple, std::tuple_size, std::tuple_element
// #include "arithmetic_tag.h"
// #include "journal_mode.h"
#include <string> // std::string
#include <memory> // std::unique_ptr
#include <array> // std::array
#include <algorithm> // std::transform
#include <locale> // 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 : 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<int>(j)];
}
inline std::unique_ptr<journal_mode> journal_mode_from_string(const std::string &str) {
std::string upper_str;
std::transform(str.begin(), str.end(), std::back_inserter(upper_str), ::toupper);
static std::array<journal_mode, 6> 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<journal_mode>(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<class V, typename Enable = void>
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<class V>
struct row_extractor<
V,
std::enable_if_t<std::is_arithmetic<V>::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>;
V extract(const char *row_value, const int_or_smaller_tag&) {
return static_cast<V>(atoi(row_value));
}
V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag&) {
return static_cast<V>(sqlite3_column_int(stmt, columnIndex));
}
V extract(const char *row_value, const bigint_tag&) {
return static_cast<V>(atoll(row_value));
}
V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag&) {
return static_cast<V>(sqlite3_column_int64(stmt, columnIndex));
}
V extract(const char *row_value, const real_tag&) {
return static_cast<V>(atof(row_value));
}
V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag&) {
return static_cast<V>(sqlite3_column_double(stmt, columnIndex));
}
};
/**
* Specialization for std::string.
*/
template<class V>
struct row_extractor<
V,
std::enable_if_t<std::is_same<V, std::string>::value>
>
{
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<class V>
struct row_extractor<
V,
std::enable_if_t<std::is_same<V, std::wstring>::value>
>
{
std::wstring extract(const char *row_value) {
if(row_value){
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> 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<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(cStr);
}else{
return {};
}
}
};
#endif // SQLITE_ORM_OMITS_CODECVT
/**
* Specialization for std::vector<char>.
*/
template<class V>
struct row_extractor<
V,
std::enable_if_t<std::is_same<V, std::vector<char>>::value>
>
{
std::vector<char> extract(const char *row_value) {
if(row_value){
auto len = ::strlen(row_value);
return this->go(row_value, static_cast<int>(len));
}else{
return {};
}
}
std::vector<char> extract(sqlite3_stmt *stmt, int columnIndex) {
auto bytes = static_cast<const char *>(sqlite3_column_blob(stmt, columnIndex));
auto len = sqlite3_column_bytes(stmt, columnIndex);
return this->go(bytes, len);
}
protected:
std::vector<char> go(const char *bytes, int len) {
if(len){
std::vector<char> res;
res.reserve(len);
std::copy(bytes,
bytes + len,
std::back_inserter(res));
return res;
}else{
return {};
}
}
};
template<class V>
struct row_extractor<
V,
std::enable_if_t<is_std_ptr<V>::value>
>
{
using value_type = typename V::element_type;
V extract(const char *row_value) {
if(row_value){
return is_std_ptr<V>::make(row_extractor<value_type>().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<V>::make(row_extractor<value_type>().extract(stmt, columnIndex));
}else{
return {};
}
}
};
/**
* Specialization for std::vector<char>.
*/
template<>
struct row_extractor<std::vector<char>> {
std::vector<char> extract(const char *row_value) {
if(row_value){
auto len = ::strlen(row_value);
return this->go(row_value, static_cast<int>(len));
}else{
return {};
}
}
std::vector<char> extract(sqlite3_stmt *stmt, int columnIndex) {
auto bytes = static_cast<const char *>(sqlite3_column_blob(stmt, columnIndex));
auto len = sqlite3_column_bytes(stmt, columnIndex);
return this->go(bytes, len);
}
protected:
std::vector<char> go(const char *bytes, int len) {
if(len){
std::vector<char> res;
res.reserve(len);
std::copy(bytes,
bytes + len,
std::back_inserter(res));
return res;
}else{
return {};
}
}
};
template<class ...Args>
struct row_extractor<std::tuple<Args...>> {
std::tuple<Args...> extract(char **argv) {
std::tuple<Args...> res;
this->extract<std::tuple_size<decltype(res)>::value>(res, argv);
return res;
}
std::tuple<Args...> extract(sqlite3_stmt *stmt, int /*columnIndex*/) {
std::tuple<Args...> res;
this->extract<std::tuple_size<decltype(res)>::value>(res, stmt);
return res;
}
protected:
template<size_t I, typename std::enable_if<I != 0>::type * = nullptr>
void extract(std::tuple<Args...> &t, sqlite3_stmt *stmt) {
using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type;
std::get<I - 1>(t) = row_extractor<tuple_type>().extract(stmt, I - 1);
this->extract<I - 1>(t, stmt);
}
template<size_t I, typename std::enable_if<I == 0>::type * = nullptr>
void extract(std::tuple<Args...> &, sqlite3_stmt *) {
//..
}
template<size_t I, typename std::enable_if<I != 0>::type * = nullptr>
void extract(std::tuple<Args...> &t, char **argv) {
using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type;
std::get<I - 1>(t) = row_extractor<tuple_type>().extract(argv[I - 1]);
this->extract<I - 1>(t, argv);
}
template<size_t I, typename std::enable_if<I == 0>::type * = nullptr>
void extract(std::tuple<Args...> &, char **) {
//..
}
};
/**
* Specialization for journal_mode.
*/
template<class V>
struct row_extractor<
V,
std::enable_if_t<std::is_same<V, journal_mode>::value>
>
{
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 <ostream>
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";
}
}
}
#pragma once
#include <tuple> // std::tuple, std::make_tuple
#include <string> // std::string
namespace sqlite_orm {
namespace internal {
template<class ...Cols>
struct index_t {
using columns_type = std::tuple<Cols...>;
using object_type = void;
std::string name;
bool unique;
columns_type columns;
template<class L>
void for_each_column_with_constraints(L) {}
};
}
template<class ...Cols>
internal::index_t<Cols...> make_index(const std::string &name, Cols ...cols) {
return {name, false, std::make_tuple(cols...)};
}
template<class ...Cols>
internal::index_t<Cols...> make_unique_index(const std::string &name, Cols ...cols) {
return {name, true, std::make_tuple(cols...)};
}
}
#pragma once
// #include "alias.h"
namespace sqlite_orm {
namespace internal {
/**
* If T is alias than mapped_type_proxy<T>::type is alias::type
* otherwise T is T.
*/
template<class T, class sfinae = void>
struct mapped_type_proxy {
using type = T;
};
template<class T>
struct mapped_type_proxy<T, typename std::enable_if<std::is_base_of<alias_tag, T>::value>::type> {
using type = typename T::type;
};
}
}
#pragma once
#include <string> // 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<class T>
struct table_rowid_t : public rowid_t {
using type = T;
};
template<class T>
struct table_oid_t : public oid_t {
using type = T;
};
template<class T>
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<class T>
internal::table_rowid_t<T> rowid() {
return {};
}
template<class T>
internal::table_oid_t<T> oid() {
return {};
}
template<class T>
internal::table__rowid_t<T> _rowid_() {
return {};
}
}
#pragma once
#include <type_traits> // std::enable_if, std::is_same, std::decay
#include <tuple> // std::tuple
// #include "core_functions.h"
// #include "aggregate_functions.h"
// #include "select_constraints.h"
// #include "operators.h"
// #include "rowid.h"
// #include "alias.h"
// #include "column.h"
// #include "storage_traits.h"
#include <type_traits> // std::is_same, std::enable_if, std::true_type, std::false_type, std::integral_constant
#include <tuple> // std::tuple
namespace sqlite_orm {
namespace internal {
template<class ...Ts>
struct storage_impl;
template<typename... Args>
struct table_impl;
namespace storage_traits {
/**
* S - storage_impl type
* T - mapped or not mapped data type
*/
template<class S, class T, class SFINAE = void>
struct type_is_mapped_impl;
/**
* S - storage
* T - mapped or not mapped data type
*/
template<class S, class T>
struct type_is_mapped : type_is_mapped_impl<typename S::impl_type, T> {};
/**
* Final specialisation
*/
template<class T>
struct type_is_mapped_impl<storage_impl<>, T, void> : std::false_type {};
template<class S, class T>
struct type_is_mapped_impl<S, T, typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> : std::true_type {};
template<class S, class T>
struct type_is_mapped_impl<S, T, typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type>
: type_is_mapped_impl<typename S::super, T> {};
/**
* S - storage_impl type
* T - mapped or not mapped data type
*/
template<class S, class T, class SFINAE = void>
struct storage_columns_count_impl;
/**
* S - storage
* T - mapped or not mapped data type
*/
template<class S, class T>
struct storage_columns_count : storage_columns_count_impl<typename S::impl_type, T> {};
/**
* Final specialisation
*/
template<class T>
struct storage_columns_count_impl<storage_impl<>, T, void> : std::integral_constant<int, 0> {};
template<class S, class T>
struct storage_columns_count_impl<S, T, typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> : std::integral_constant<int, S::table_type::columns_count> {};
template<class S, class T>
struct storage_columns_count_impl<S, T, typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> : storage_columns_count_impl<typename S::super, T> {};
/**
* T - table_impl type.
*/
template<class T>
struct table_impl_types;
/**
* type is std::tuple of field types of mapped colums.
*/
template<typename... Args>
struct table_impl_types<table_impl<Args...>> {
using type = std::tuple<typename Args::field_type...>;
};
/**
* S - storage_impl type
* T - mapped or not mapped data type
*/
template<class S, class T, class SFINAE = void>
struct storage_mapped_columns_impl;
/**
* S - storage
* T - mapped or not mapped data type
*/
template<class S, class T>
struct storage_mapped_columns : storage_mapped_columns_impl<typename S::impl_type, T> {};
/**
* Final specialisation
*/
template<class T>
struct storage_mapped_columns_impl<storage_impl<>, T, void> {
using type = std::tuple<>;
};
template<class S, class T>
struct storage_mapped_columns_impl<S, T, typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> {
using table_type = typename S::table_type;
using table_impl_type = typename table_type::impl_type;
using type = typename table_impl_types<table_impl_type>::type;
};
template<class S, class T>
struct storage_mapped_columns_impl<S, T, typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> : storage_mapped_columns_impl<typename S::super, T> {};
}
}
}
namespace sqlite_orm {
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 core_functions::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<class St, class T, class SFINAE = void>
struct column_result_t;
template<class St, class O, class F>
struct column_result_t<St, F O::*, typename std::enable_if<std::is_member_pointer<F O::*>::value && !std::is_member_function_pointer<F O::*>::value>::type> {
using type = F;
};
/**
* Common case for all getter types. Getter types are defined in column.h file
*/
template<class St, class T>
struct column_result_t<St, T, typename std::enable_if<is_getter<T>::value>::type> {
using type = typename getter_traits<T>::field_type;
};
/**
* Common case for all setter types. Setter types are defined in column.h file
*/
template<class St, class T>
struct column_result_t<St, T, typename std::enable_if<is_setter<T>::value>::type> {
using type = typename setter_traits<T>::field_type;
};
template<class St, class T>
struct column_result_t<St, core_functions::length_t<T>, void> {
using type = int;
};
#if SQLITE_VERSION_NUMBER >= 3007016
template<class St, class ...Args>
struct column_result_t<St, core_functions::char_t_<Args...>, void> {
using type = std::string;
};
#endif
template<class St>
struct column_result_t<St, core_functions::random_t, void> {
using type = int;
};
template<class St>
struct column_result_t<St, core_functions::changes_t, void> {
using type = int;
};
template<class St, class T>
struct column_result_t<St, core_functions::abs_t<T>, void> {
using type = std::unique_ptr<double>;
};
template<class St, class T>
struct column_result_t<St, core_functions::lower_t<T>, void> {
using type = std::string;
};
template<class St, class T>
struct column_result_t<St, core_functions::upper_t<T>, void> {
using type = std::string;
};
template<class St, class X>
struct column_result_t<St, core_functions::trim_single_t<X>, void> {
using type = std::string;
};
template<class St, class X, class Y>
struct column_result_t<St, core_functions::trim_double_t<X, Y>, void> {
using type = std::string;
};
template<class St, class X>
struct column_result_t<St, core_functions::ltrim_single_t<X>, void> {
using type = std::string;
};
template<class St, class X, class Y>
struct column_result_t<St, core_functions::ltrim_double_t<X, Y>, void> {
using type = std::string;
};
template<class St, class X>
struct column_result_t<St, core_functions::rtrim_single_t<X>, void> {
using type = std::string;
};
template<class St, class X, class Y>
struct column_result_t<St, core_functions::rtrim_double_t<X, Y>, void> {
using type = std::string;
};
template<class St, class T, class ...Args>
struct column_result_t<St, core_functions::date_t<T, Args...>, void> {
using type = std::string;
};
template<class St, class T, class ...Args>
struct column_result_t<St, core_functions::julianday_t<T, Args...>, void> {
using type = double;
};
template<class St, class T, class ...Args>
struct column_result_t<St, core_functions::datetime_t<T, Args...>, void> {
using type = std::string;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::avg_t<T>, void> {
using type = double;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::count_t<T>, void> {
using type = int;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::count_asterisk_t<T>, void> {
using type = int;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::sum_t<T>, void> {
using type = std::unique_ptr<double>;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::total_t<T>, void> {
using type = double;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::group_concat_single_t<T>, void> {
using type = std::string;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::group_concat_double_t<T>, void> {
using type = std::string;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::max_t<T>, void> {
using type = std::unique_ptr<typename column_result_t<St, T>::type>;
};
template<class St, class T>
struct column_result_t<St, aggregate_functions::min_t<T>, void> {
using type = std::unique_ptr<typename column_result_t<St, T>::type>;
};
template<class St>
struct column_result_t<St, aggregate_functions::count_asterisk_without_type, void> {
using type = int;
};
template<class St, class T>
struct column_result_t<St, distinct_t<T>, void> {
using type = typename column_result_t<St, T>::type;
};
template<class St, class T>
struct column_result_t<St, all_t<T>, void> {
using type = typename column_result_t<St, T>::type;
};
template<class St, class L, class R>
struct column_result_t<St, conc_t<L, R>, void> {
using type = std::string;
};
template<class St, class L, class R>
struct column_result_t<St, add_t<L, R>, void> {
using type = double;
};
template<class St, class L, class R>
struct column_result_t<St, sub_t<L, R>, void> {
using type = double;
};
template<class St, class L, class R>
struct column_result_t<St, mul_t<L, R>, void> {
using type = double;
};
template<class St, class L, class R>
struct column_result_t<St, internal::div_t<L, R>, void> {
using type = double;
};
template<class St, class L, class R>
struct column_result_t<St, mod_t<L, R>, void> {
using type = double;
};
template<class St>
struct column_result_t<St, rowid_t, void> {
using type = int64;
};
template<class St>
struct column_result_t<St, oid_t, void> {
using type = int64;
};
template<class St>
struct column_result_t<St, _rowid_t, void> {
using type = int64;
};
template<class St, class T>
struct column_result_t<St, table_rowid_t<T>, void> {
using type = int64;
};
template<class St, class T>
struct column_result_t<St, table_oid_t<T>, void> {
using type = int64;
};
template<class St, class T>
struct column_result_t<St, table__rowid_t<T>, void> {
using type = int64;
};
template<class St, class T, class C>
struct column_result_t<St, alias_column_t<T, C>, void> {
using type = typename column_result_t<St, C>::type;
};
template<class St, class T, class F>
struct column_result_t<St, column_pointer<T, F>> : column_result_t<St, F, void> {};
template<class St, class ...Args>
struct column_result_t<St, columns_t<Args...>, void> {
using type = std::tuple<typename column_result_t<St, typename std::decay<Args>::type>::type...>;
};
template<class St, class T, class ...Args>
struct column_result_t<St, select_t<T, Args...>> : column_result_t<St, T, void> {};
template<class St, class T>
struct column_result_t<St, T, typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> {
using left_type = typename T::left_type;
using right_type = typename T::right_type;
using left_result = typename column_result_t<St, left_type>::type;
using right_result = typename column_result_t<St, right_type>::type;
static_assert(std::is_same<left_result, right_result>::value, "Compound subselect queries must return same types");
using type = left_result;
};
/**
* Result for the most simple queries like `SELECT 1`
*/
template<class St, class T>
struct column_result_t<St, T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
using type = T;
};
/**
* Result for the most simple queries like `SELECT 'ototo'`
*/
template<class St>
struct column_result_t<St, const char*, void> {
using type = std::string;
};
template<class St, class T, class E>
struct column_result_t<St, as_t<T, E>, void> : column_result_t<St, typename std::decay<E>::type, void> {};
template<class St, class T>
struct column_result_t<St, asterisk_t<T>, void> {
using type = typename storage_traits::storage_mapped_columns<St, T>::type;
};
template<class St, class T, class E>
struct column_result_t<St, conditions::cast_t<T, E>, void> {
using type = T;
};
}
}
#pragma once
#include <vector> // std::vector
#include <string> // std::string
#include <tuple> // std::tuple
#include <type_traits> // std::is_same, std::integral_constant, std::true_type, std::false_type
// #include "column.h"
// #include "tuple_helper.h"
// #include "constraints.h"
namespace sqlite_orm {
namespace internal {
/**
* Common case for table_impl class.
*/
template<typename... Args>
struct table_impl;
/**
* Final superclass for table_impl.
*/
template<>
struct table_impl<>{
static constexpr const int columns_count = 0;
std::vector<std::string> column_names() {
return {};
}
template<class ...Op>
std::vector<std::string> column_names_exept() {
return {};
}
template<class ...Op>
std::vector<std::string> column_names_with() {
return {};
}
template<class L>
void for_each_column(L) {}
template<class L>
void for_each_column_with_constraints(L) {}
template<class F, class L>
void for_each_column_with_field_type(L) {}
template<class Op, class L>
void for_each_column_exept(L) {}
template<class Op, class L>
void for_each_column_with(L) {}
template<class L>
void for_each_primary_key(L) {}
};
/**
* Regular table_impl class.
*/
template<typename H, typename... T>
struct table_impl<H, T...> : private table_impl<T...> {
using column_type = H;
using tail_types = std::tuple<T...>;
using super = table_impl<T...>;
table_impl(H h, T ...t) : super(t...), col(h) {}
column_type col;
static constexpr const int columns_count = 1 + super::columns_count;
/**
* column_names_with implementation. Notice that result will be reversed.
* It is reversed back in `table` class.
* @return vector of column names that have specified Op... conditions.
*/
template<class ...Op>
std::vector<std::string> column_names_with() {
auto res = this->super::template column_names_with<Op...>();
if(this->col.template has_every<Op...>()) {
res.emplace_back(this->col.name);
}
return res;
}
/**
* For each implementation. Calls templated lambda with its column
* and passed call to superclass.
*/
template<class L>
void for_each_column(L l){
this->apply_to_col_if(l, internal::is_column<column_type>{});
this->super::for_each_column(l);
}
/**
* For each implementation. Calls templated lambda with its column
* and passed call to superclass.
*/
template<class L>
void for_each_column_with_constraints(L l){
l(this->col);
this->super::for_each_column_with_constraints(l);
}
template<class F, class L>
void for_each_column_with_field_type(L l) {
this->apply_to_col_if(l, std::is_same<F, typename column_type::field_type>{});
this->super::template for_each_column_with_field_type<F, L>(l);
}
/**
* Working version of `for_each_column_exept`. Calls lambda if column has no option and fire super's function.
*/
template<class Op, class L>
void for_each_column_exept(L l) {
using has_opt = tuple_helper::tuple_contains_type<Op, typename column_type::constraints_type>;
this->apply_to_col_if(l, std::integral_constant<bool, !has_opt::value>{});
this->super::template for_each_column_exept<Op, L>(l);
}
/**
* Working version of `for_each_column_with`. Calls lambda if column has option and fire super's function.
*/
template<class Op, class L>
void for_each_column_with(L l) {
this->apply_to_col_if(l, tuple_helper::tuple_contains_type<Op, typename column_type::constraints_type>{});
this->super::template for_each_column_with<Op, L>(l);
}
/**
* Calls l(this->col) if H is primary_key_t
*/
template<class L>
void for_each_primary_key(L l) {
this->apply_to_col_if(l, internal::is_primary_key<H>{});
this->super::for_each_primary_key(l);
}
template<class L>
void apply_to_col_if(L& l, std::true_type) {
l(this->col);
}
template<class L>
void apply_to_col_if(L&, std::false_type) {}
};
}
}
#pragma once
#include <string> // std::string
#include <type_traits> // std::remove_reference, std::is_same, std::is_base_of
#include <vector> // std::vector
#include <tuple> // std::tuple_size, std::tuple_element
#include <algorithm> // std::reverse, std::find_if
// #include "table_impl.h"
// #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 {
/**
* Table interface class. Implementation is hidden in `table_impl` class.
*/
template<class T, class ...Cs>
struct table_t {
using impl_type = table_impl<Cs...>;
using object_type = T;
static constexpr const int columns_count = impl_type::columns_count;
/**
* Table name.
*/
const std::string name;
/**
* Implementation that stores columns information.
*/
impl_type impl;
table_t(decltype(name) name_, decltype(impl) impl_): name(std::move(name_)), impl(std::move(impl_)) {}
bool _without_rowid = false;
table_t<T, Cs...> 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<class F, class C>
const F* get_object_field_pointer(const object_type &obj, C c) {
const F *res = nullptr;
this->for_each_column_with_field_type<F>([&res, &c, &obj, this](auto &col){
using namespace static_magic;
using column_type = typename std::remove_reference<decltype(col)>::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;
if(!res){
static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col, &c]{
if(compare_any(col.member_pointer, c)){
res = &(obj.*col.member_pointer);
}
})();
}
if(!res){
static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col, &c]{
if(compare_any(col.getter, c)){
res = &((obj).*(col.getter))();
}
})();
}
if(!res){
static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col, &c]{
if(compare_any(col.setter, c)){
res = &((obj).*(col.getter))();
}
})();
}
});
return res;
}
/**
* @return vector of column names of table.
*/
std::vector<std::string> column_names() {
std::vector<std::string> res;
this->impl.for_each_column([&res](auto &c){
res.push_back(c.name);
});
return res;
}
std::vector<std::string> composite_key_columns_names() {
std::vector<std::string> res;
this->impl.for_each_primary_key([this, &res](auto c){
res = this->composite_key_columns_names(c);
});
return res;
}
std::vector<std::string> primary_key_column_names() {
std::vector<std::string> res;
this->impl.template for_each_column_with<constraints::primary_key_t<>>([&res](auto &c){
res.push_back(c.name);
});
if(!res.size()){
res = this->composite_key_columns_names();
}
return res;
}
template<class ...Args>
std::vector<std::string> composite_key_columns_names(constraints::primary_key_t<Args...> pk) {
std::vector<std::string> res;
using pk_columns_tuple = decltype(pk.columns);
res.reserve(std::tuple_size<pk_columns_tuple>::value);
tuple_helper::iterator<std::tuple_size<pk_columns_tuple>::value - 1, Args...>()(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 first argument.
* @return column name or empty string if nothing found.
*/
template<
class F,
class O,
typename = typename std::enable_if<std::is_member_pointer<F O::*>::value && !std::is_member_function_pointer<F O::*>::value>::type>
std::string find_column_name(F O::*m) {
std::string res;
this->template for_each_column_with_field_type<F>([&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<class G>
std::string find_column_name(G getter, typename std::enable_if<is_getter<G>::value>::type * = nullptr) {
std::string res;
using field_type = typename getter_traits<G>::field_type;
this->template for_each_column_with_field_type<field_type>([&res, getter](auto c) {
if(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<class S>
std::string find_column_name(S setter, typename std::enable_if<is_setter<S>::value>::type * = nullptr) {
std::string res;
using field_type = typename setter_traits<S>::field_type;
this->template for_each_column_with_field_type<field_type>([&res, setter](auto c) {
if(c.setter == setter) {
res = c.name;
}
});
return res;
}
/**
* @return vector of column names that have constraints provided as template arguments (not_null, autoincrement).
*/
template<class ...Op>
std::vector<std::string> column_names_with() {
auto res = this->impl.template column_names_with<Op...>();
std::reverse(res.begin(),
res.end());
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 for_each_column_with_constraints 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<class L>
void for_each_column(L l) {
this->impl.for_each_column(l);
}
template<class L>
void for_each_column_with_constraints(L l) {
this->impl.for_each_column_with_constraints(l);
}
template<class F, class L>
void for_each_column_with_field_type(L l) {
this->impl.template for_each_column_with_field_type<F, L>(l);
}
/**
* Iterates all columns exept ones 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<class Op, class L>
void for_each_column_exept(L l) {
this->impl.template for_each_column_exept<Op>(l);
}
/**
* 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<class Op, class L>
void for_each_column_with(L l) {
this->impl.template for_each_column_with<Op>(l);
}
std::vector<table_info> get_table_info() {
std::vector<table_info> res;
res.reserve(size_t(this->columns_count));
this->for_each_column([&res](auto &col){
std::string dft;
using field_type = typename std::remove_reference<decltype(col)>::type::field_type;
if(auto d = col.default_value()) {
auto needQuotes = std::is_base_of<text_printer, type_printer<field_type>>::value;
if(needQuotes){
dft = "'" + *d + "'";
}else{
dft = *d;
}
}
table_info i{
-1,
col.name,
type_printer<field_type>().print(),
col.not_null(),
dft,
col.template has<constraints::primary_key_t<>>(),
};
res.emplace_back(i);
});
std::vector<std::string> compositeKeyColumnNames;
this->impl.for_each_primary_key([this, &compositeKeyColumnNames](auto c){
compositeKeyColumnNames = this->composite_key_columns_names(c);
});
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<int>(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<class ...Cs, class T = typename std::tuple_element<0, std::tuple<Cs...>>::type::object_type>
internal::table_t<T, Cs...> make_table(const std::string &name, Cs&& ...args) {
return {name, internal::table_impl<Cs...>(std::forward<Cs>(args)...)};
}
template<class T, class ...Cs>
internal::table_t<T, Cs...> make_table(const std::string &name, Cs&& ...args) {
return {name, internal::table_impl<Cs...>(std::forward<Cs>(args)...)};
}
}
#pragma once
#include <string> // std::string
#include <sqlite3.h>
#include <cstddef> // std::nullptr_t
#include <system_error> // std::system_error, std::error_code
#include <sstream> // std::stringstream
#include <cstdlib> // std::atoi
#include <type_traits> // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type
#include <utility> // std::pair, std::make_pair
#include <vector> // std::vector
#include <algorithm> // std::find_if
// #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 "sqlite_type.h"
// #include "field_value_holder.h"
#include <type_traits> // std::enable_if
// #include "column.h"
namespace sqlite_orm {
namespace internal {
template<class T, class SFINAE = void>
struct field_value_holder;
template<class T>
struct field_value_holder<T, typename std::enable_if<getter_traits<T>::returns_lvalue>::type> {
using type = typename getter_traits<T>::field_type;
const type &value;
};
template<class T>
struct field_value_holder<T, typename std::enable_if<!getter_traits<T>::returns_lvalue>::type> {
using type = typename getter_traits<T>::field_type;
type value;
};
}
}
namespace sqlite_orm {
namespace internal {
/**
* This is a generic implementation. Used as a tail in storage_impl inheritance chain
*/
template<class ...Ts>
struct storage_impl;
template<class H, class ...Ts>
struct storage_impl<H, Ts...> : public storage_impl<Ts...> {
using table_type = H;
using super = storage_impl<Ts...>;
storage_impl(H h, Ts ...ts) : super(std::forward<Ts>(ts)...), table(std::move(h)) {}
table_type table;
template<class L>
void for_each(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;
this->table.for_each_column_with_constraints([&res](auto c){
if(internal::is_foreign_key<decltype(c)>::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<class O, class F>
std::string column_name_simple(F O::*m) {
return this->table.find_column_name(m);
}
/**
* Same thing as above for getter.
*/
template<class T, typename std::enable_if<is_getter<T>::value>::type>
std::string column_name_simple(T g) {
return this->table.find_column_name(g);
}
/**
* Same thing as above for setter.
*/
template<class T, typename std::enable_if<is_setter<T>::value>::type>
std::string column_name_simple(T s) {
return this->table.find_column_name(s);
}
/**
* Cute function used to find column name by its type and member pointer. Uses SFINAE to
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(F O::*m, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
return this->table.find_column_name(m);
}
/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
return this->super::column_name(m);
}
/**
* Cute function used to find column name by its type and getter pointer. Uses SFINAE to
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(const F& (O::*g)() const, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
return this->table.find_column_name(g);
}
/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(const F& (O::*g)() const, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
return this->super::column_name(g);
}
/**
* Cute function used to find column name by its type and setter pointer. Uses SFINAE to
* skip inequal type O.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(void (O::*s)(F), typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
return this->table.find_column_name(s);
}
/**
* Opposite version of function defined above. Just calls same function in superclass.
*/
template<class O, class F, class HH = typename H::object_type>
std::string column_name(void (O::*s)(F), typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
return this->super::column_name(s);
}
template<class T, class F, class HH = typename H::object_type>
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<std::is_same<T, HH>::value>::type * = nullptr) {
return this->column_name_simple(c.field);
}
template<class T, class F, class HH = typename H::object_type>
std::string column_name(const column_pointer<T, F> &c, typename std::enable_if<!std::is_same<T, HH>::value>::type * = nullptr) {
return this->super::column_name(c);
}
template<class O, class HH = typename H::object_type>
auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
return *this;
}
template<class O, class HH = typename H::object_type>
auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
return this->super::template get_impl<O>();
}
template<class O, class HH = typename H::object_type>
std::string find_table_name(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const {
return this->table.name;
}
template<class O, class HH = typename H::object_type>
std::string find_table_name(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const {
return this->super::template find_table_name<O>();
}
template<class O, class HH = typename H::object_type>
std::string dump(const O &o, typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) {
return this->super::dump(o, nullptr);
}
template<class O, class HH = typename H::object_type>
std::string dump(const O &o, typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) {
std::stringstream ss;
ss << "{ ";
using pair = std::pair<std::string, std::string>;
std::vector<pair> pairs;
this->table.for_each_column([&pairs, &o] (auto &c) {
using field_type = typename std::decay<decltype(c)>::type::field_type;
pair p{c.name, ""};
if(c.member_pointer){
p.second = field_printer<field_type>()(o.*c.member_pointer);
}else{
using getter_type = typename std::decay<decltype(c)>::type::getter_type;
field_value_holder<getter_type> valueHolder{((o).*(c.getter))()};
p.second = field_printer<field_type>()(valueHolder.value);
}
pairs.push_back(std::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();
}
std::vector<table_info> get_table_info(const std::string &tableName, sqlite3 *db) {
std::vector<table_info> res;
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<table_info>*)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;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
return res;
}
void add_column(const table_info &ti, sqlite3 *db) {
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()));
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
/**
* 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) {
std::stringstream ss;
std::vector<std::string> columnNames;
this->table.for_each_column([&columnNames] (auto c) {
columnNames.emplace_back(c.name);
});
auto columnNamesCount = columnNames.size();
ss << "INSERT INTO " << name << " (";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << columnNames[i];
if(i < columnNamesCount - 1) {
ss << ", ";
}else{
ss << " ";
}
}
ss << ") ";
ss << "SELECT ";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << columnNames[i];
if(i < columnNamesCount - 1) {
ss << ", ";
}else{
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()));
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
sync_schema_result schema_status(sqlite3 *db, bool preserve) {
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 = get_table_info(this->table.name, db);
// this vector will contain pointers to columns that gotta be added..
std::vector<table_info*> columnsToAdd;
if(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;
}
static bool get_remove_add_columns(std::vector<table_info*>& columnsToAdd,
std::vector<table_info>& storageTableInfo,
std::vector<table_info>& 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 dbColumnInfoType = to_sqlite_type(dbColumnInfo.type);
auto storageColumnInfoType = to_sqlite_type(storageColumnInfo.type);
if(dbColumnInfoType && storageColumnInfoType) {
auto columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name &&
*dbColumnInfoType == *storageColumnInfoType &&
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() + storageColumnInfoIndex);
--storageColumnInfoIndex;
}else{
// undefined type/types
notEqual = true;
break;
}
}else{
columnsToAdd.push_back(&storageColumnInfo);
}
}
return notEqual;
}
private:
using self = storage_impl<H, Ts...>;
};
template<>
struct storage_impl<>{
template<class O>
std::string find_table_name() const {
return {};
}
template<class L>
void for_each(L) {}
int foreign_keys_count() {
return 0;
}
template<class O>
std::string dump(const O &, sqlite3 *, std::nullptr_t) {
throw std::system_error(std::make_error_code(orm_error_code::type_is_not_mapped_to_storage));
}
bool table_exists(const std::string &tableName, sqlite3 *db) {
auto res = 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;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
return res;
}
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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) {
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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
std::string current_timestamp(sqlite3 *db) {
std::string res;
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<std::string>().extract(argv[0]);
}
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
return res;
}
};
template<class T>
struct is_storage_impl : std::false_type {};
template<class ...Ts>
struct is_storage_impl<storage_impl<Ts...>> : std::true_type {};
}
}
#pragma once
#include <memory> // std::unique/shared_ptr, std::make_unique/shared
#include <string> // std::string
#include <sqlite3.h>
#include <type_traits> // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type
#include <cstddef> // std::ptrdiff_t
#include <iterator> // std::input_iterator_tag, std::iterator_traits, std::distance
#include <system_error> // std::system_error
#include <functional> // std::function
#include <sstream> // std::stringstream
#include <map> // std::map
#include <vector> // std::vector
#include <tuple> // std::tuple_size, std::tuple, std::make_tuple
#include <utility> // std::forward, std::pair
#include <set> // std::set
#include <algorithm> // std::find
// #include "alias.h"
// #include "database_connection.h"
// #include "row_extractor.h"
// #include "statement_finalizer.h"
// #include "error_code.h"
// #include "type_printer.h"
// #include "tuple_helper.h"
// #include "constraints.h"
// #include "table_type.h"
// #include "type_is_nullable.h"
// #include "field_printer.h"
// #include "rowid.h"
// #include "aggregate_functions.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 "transaction_guard.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.
* S - storage type
*/
template<class S>
struct transaction_guard_t {
using storage_type = S;
/**
* 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(storage_type &s): storage(s) {}
~transaction_guard_t() {
if(this->gotta_fire){
if(!this->commit_on_destroy){
this->storage.rollback();
}else{
this->storage.commit();
}
}
}
/**
* Call `COMMIT` explicitly. After this call
* guard will not call `COMMIT` or `ROLLBACK`
* in its destructor.
*/
void commit() {
this->storage.commit();
this->gotta_fire = false;
}
/**
* Call `ROLLBACK` explicitly. After this call
* guard will not call `COMMIT` or `ROLLBACK`
* in its destructor.
*/
void rollback() {
this->storage.rollback();
this->gotta_fire = false;
}
protected:
storage_type &storage;
bool gotta_fire = true;
};
}
}
// #include "pragma.h"
#include <string> // std::string
#include <sqlite3.h>
// #include "error_code.h"
// #include "row_extractor.h"
// #include "journal_mode.h"
namespace sqlite_orm {
template<class S>
struct pragma_t {
using storage_type = S;
pragma_t(storage_type &storage_): storage(storage_) {}
sqlite_orm::journal_mode journal_mode() {
return this->get_pragma<sqlite_orm::journal_mode>("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<decltype(this->_journal_mode)>(value);
}
int synchronous() {
return this->get_pragma<int>("synchronous");
}
void synchronous(int value) {
this->_synchronous = -1;
this->set_pragma("synchronous", value);
this->_synchronous = value;
}
int user_version() {
return this->get_pragma<int>("user_version");
}
void user_version(int value) {
this->set_pragma("user_version", value);
}
int auto_vacuum() {
return this->get_pragma<int>("auto_vacuum");
}
void auto_vacuum(int value) {
this->set_pragma("auto_vacuum", value);
}
friend storage_type;
protected:
storage_type &storage;
int _synchronous = -1;
char _journal_mode = -1; // if != -1 stores static_cast<sqlite_orm::journal_mode>(journal_mode)
template<class T>
T get_pragma(const std::string &name) {
auto connection = this->storage.get_or_create_connection();
auto query = "PRAGMA " + name;
T res;
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv, char **) -> int {
auto &res = *(T*)data;
if(argc){
res = row_extractor<T>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc == SQLITE_OK){
return res;
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* Yevgeniy Zakharov: I wanted to refactored this function with statements and value bindings
* but it turns out that bindings in pragma statements are not supported.
*/
template<class T>
void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) {
std::shared_ptr<internal::database_connection> connection;
if(!db){
connection = this->storage.get_or_create_connection();
db = connection->get_db();
}
std::stringstream ss;
ss << "PRAGMA " << name << " = " << this->storage.string_from_expression(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(connection->get_db()), get_sqlite_error_category()));
}
}
void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) {
std::shared_ptr<internal::database_connection> connection;
if(!db){
connection = this->storage.get_or_create_connection();
db = connection->get_db();
}
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(connection->get_db()), get_sqlite_error_category()));
}
}
};
}
// #include "journal_mode.h"
// #include "limit_accesor.h"
#include <sqlite3.h>
#include <map> // std::map
namespace sqlite_orm {
namespace internal {
template<class S>
struct limit_accesor {
using storage_type = S;
limit_accesor(storage_type &storage_): storage(storage_) {}
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);
}
int worker_threads() {
return this->get(SQLITE_LIMIT_WORKER_THREADS);
}
void worker_threads(int newValue) {
this->set(SQLITE_LIMIT_WORKER_THREADS, newValue);
}
protected:
storage_type &storage;
template<class ...Ts>
friend struct storage_t;
/**
* Stores limit set between connections.
*/
std::map<int, int> limits;
int get(int id) {
auto connection = this->storage.get_or_create_connection();
return sqlite3_limit(connection->get_db(), id, -1);
}
void set(int id, int newValue) {
this->limits[id] = newValue;
auto connection = this->storage.get_or_create_connection();
sqlite3_limit(connection->get_db(), id, newValue);
}
};
}
}
// #include "field_value_holder.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<class ...Ts>
struct storage_t {
using self = storage_t<Ts...>;
using impl_type = storage_impl<Ts...>;
template<class T, class ...Args>
struct view_t {
using mapped_type = T;
storage_t &storage;
std::shared_ptr<internal::database_connection> connection;
const std::string query;
view_t(storage_t &stor, decltype(connection) conn, Args&& ...args):
storage(stor),
connection(std::move(conn)),
query([&args..., &stor]{
std::string q;
stor.template generate_select_asterisk<T>(&q, args...);
return q;
}()){}
struct iterator_t {
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<sqlite3_stmt *> stmt;
view_t<T, Args...> &view;
// shared_ptr is used over unique_ptr here
// so that the iterator can be copyable.
std::shared_ptr<T> current;
void extract_value(std::unique_ptr<T> &temp) {
temp = std::make_unique<T>();
auto &storage = this->view.storage;
auto &impl = storage.template get_impl<T>();
auto index = 0;
impl.table.for_each_column([&index, &temp, this] (auto &c) {
using field_type = typename std::decay<decltype(c)>::type::field_type;
auto value = row_extractor<field_type>().extract(*this->stmt, index++);
if(c.member_pointer){
auto member_pointer = c.member_pointer;
(*temp).*member_pointer = std::move(value);
}else{
((*temp).*(c.setter))(std::move(value));
}
});
}
public:
using value_type = T;
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_t<T, Args...> &view_): stmt(std::make_shared<sqlite3_stmt *>(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};
}
}
T& 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<T> value;
this->extract_value(value);
this->current = std::move(value);
}
return *this->current;
}
T* 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<T> value;
this->extract_value(value);
this->current = std::move(value);
}
return &*this->current;
}
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:{
throw std::system_error(std::error_code(sqlite3_errcode(this->view.connection->get_db()), get_sqlite_error_category()));
}
}
}
}
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);
}
};
size_t size() {
return this->storage.template count<T>();
}
bool empty() {
return !this->size();
}
iterator_t end() {
return {nullptr, *this};
}
iterator_t begin() {
sqlite3_stmt *stmt = nullptr;
auto db = this->connection->get_db();
auto ret = sqlite3_prepare_v2(db, this->query.c_str(), -1, &stmt, nullptr);
if(ret == SQLITE_OK){
return {stmt, *this};
}else{
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
};
std::function<void(sqlite3*)> on_open;
transaction_guard_t<self> transaction_guard() {
this->begin_transaction();
return {*this};
}
template<class S>
friend struct limit_accesor;
/**
* @param filename_ database filename.
*/
storage_t(const std::string &filename_, impl_type impl_):
filename(filename_),
impl(std::move(impl_)),
inMemory(filename_.empty() || filename_ == ":memory:"),
pragma(*this),
limit(*this){
if(inMemory){
this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
this->on_open_internal(this->currentTransaction->get_db());
}
}
storage_t(const storage_t &other):
filename(other.filename),
impl(other.impl),
currentTransaction(other.currentTransaction),
inMemory(other.inMemory),
collatingFunctions(other.collatingFunctions),
pragma(*this),
limit(*this)
{}
protected:
using collating_function = std::function<int(int, const void*, int, const void*)>;
std::string filename;
impl_type impl;
std::shared_ptr<internal::database_connection> currentTransaction;
const bool inMemory;
bool isOpenedForever = false;
std::map<std::string, collating_function> collatingFunctions;
/**
* Check whether connection exists and returns it if yes or creates a new one
* and returns it.
*/
std::shared_ptr<internal::database_connection> get_or_create_connection() {
decltype(this->currentTransaction) connection;
if(!this->currentTransaction){
connection = std::make_shared<internal::database_connection>(this->filename);
this->on_open_internal(connection->get_db());
}else{
connection = this->currentTransaction;
}
return connection;
}
template<class O, class T, class G, class S, class ...Op>
std::string serialize_column_schema(internal::column_t<O, T, G, S, Op...> c) {
std::stringstream ss;
ss << "'" << c.name << "' ";
using field_type = typename decltype(c)::field_type;
using constraints_type = typename decltype(c)::constraints_type;
ss << type_printer<field_type>().print() << " ";
tuple_helper::iterator<std::tuple_size<constraints_type>::value - 1, Op...>()(c.constraints, [&ss](auto &v){
ss << static_cast<std::string>(v) << ' ';
});
if(c.not_null()){
ss << "NOT NULL ";
}
return ss.str();
}
template<class ...Cs>
std::string serialize_column_schema(constraints::primary_key_t<Cs...> fk) {
std::stringstream ss;
ss << static_cast<std::string>(fk) << " (";
std::vector<std::string> columnNames;
columnNames.reserve(std::tuple_size<decltype(fk.columns)>::value);
tuple_helper::iterator<std::tuple_size<decltype(fk.columns)>::value - 1, Cs...>()(fk.columns, [&columnNames, this](auto &c){
columnNames.push_back(this->impl.column_name(c));
});
for(size_t i = 0; i < columnNames.size(); ++i) {
ss << columnNames[i];
if(i < columnNames.size() - 1) {
ss << ", ";
}
}
ss << ") ";
return ss.str();
}
#if SQLITE_VERSION_NUMBER >= 3006019
template<class ...Cs, class ...Rs>
std::string serialize_column_schema(constraints::foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> &fk) {
std::stringstream ss;
std::vector<std::string> columnNames;
using columns_type_t = typename std::decay<decltype(fk)>::type::columns_type;
constexpr const int columnsCount = std::tuple_size<columns_type_t>::value;
columnNames.reserve(columnsCount);
tuple_helper::iterator<columnsCount - 1, Cs...>()(fk.columns, [&columnNames, this](auto &v){
columnNames.push_back(this->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 << " ";
}
ss << ") REFERENCES ";
std::vector<std::string> referencesNames;
using references_type_t = typename std::decay<decltype(fk)>::type::references_type;
constexpr const int referencesCount = std::tuple_size<references_type_t>::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<first_reference_t>::type;
auto refTableName = this->impl.template find_table_name<first_reference_mapped_type>();
ss << refTableName << " ";
}
tuple_helper::iterator<referencesCount - 1, Rs...>()(fk.references, [&referencesNames, this](auto &v){
referencesNames.push_back(this->impl.column_name(v));
});
ss << "( ";
for(size_t i = 0; i < referencesNames.size(); ++i){
ss << referencesNames[i];
if(i < referencesNames.size() - 1){
ss << ",";
}
ss << " ";
}
ss << ") ";
if(fk.on_update){
ss << static_cast<std::string>(fk.on_update) << " " << fk.on_update._action << " ";
}
if(fk.on_delete){
ss << static_cast<std::string>(fk.on_delete) << " " << fk.on_delete._action << " ";
}
return ss.str();
}
#endif
template<class I>
void create_table(sqlite3 *db, const std::string &tableName, I *impl) {
std::stringstream ss;
ss << "CREATE TABLE '" << tableName << "' ( ";
auto columnsCount = impl->table.columns_count;
auto index = 0;
impl->table.for_each_column_with_constraints([columnsCount, &index, &ss, this] (auto c) {
ss << this->serialize_column_schema(c);
if(index < columnsCount - 1) {
ss << ", ";
}
index++;
});
ss << ") ";
if(impl->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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
template<class I>
void backup_table(sqlite3 *db, I *impl) {
// 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 = impl->table.name + "_backup";
if(impl->table_exists(backupTableName, db)){
int suffix = 1;
do{
std::stringstream stream;
stream << suffix;
auto anotherBackupTableName = backupTableName + stream.str();
if(!impl->table_exists(anotherBackupTableName, db)){
backupTableName = anotherBackupTableName;
break;
}
++suffix;
}while(true);
}
this->create_table(db, backupTableName, impl);
impl->copy_table(db, backupTableName);
this->drop_table_internal(impl->table.name, db);
impl->rename_table(db, backupTableName, impl->table.name);
}
template<class O>
void assert_mapped_type() {
using mapped_types_tuples = std::tuple<typename Ts::object_type...>;
static_assert(tuple_helper::has_type<O, mapped_types_tuples>::value, "type is not mapped to a storage");
}
template<class O>
auto& get_impl() {
return this->impl.template get_impl<O>();
}
std::string escape(std::string text) {
for(size_t i = 0; i < text.length(); ) {
if(text[i] == '\''){
text.insert(text.begin() + i, '\'');
i += 2;
}
else
++i;
}
return text;
}
template<class T>
typename std::enable_if<!is_base_of_template<T, compound_operator>::value, std::string>::type string_from_expression(T t, bool /*noTableName*/ = false, bool escape = false) {
auto isNullable = type_is_nullable<T>::value;
if(isNullable && !type_is_nullable<T>()(t)){
return "NULL";
}else{
auto needQuotes = std::is_base_of<text_printer, type_printer<T>>::value;
std::stringstream ss;
if(needQuotes){
ss << "'";
}
std::string text = field_printer<T>()(t);
if(escape){
text = this->escape(text);
}
ss << text;
if(needQuotes){
ss << "'";
}
return ss.str();
}
}
template<class T>
std::string string_from_expression(const alias_holder<T> &holder, bool noTableName = false, bool /*escape*/ = false) {
return T::get();
}
template<class T, class E>
std::string string_from_expression(const as_t<T, E> &als, bool noTableName = false, bool /*escape*/ = false) {
auto tableAliasString = alias_extractor<T>::get();
return this->string_from_expression(als.expression) + " AS " + tableAliasString;
}
template<class T, class C>
std::string string_from_expression(const alias_column_t<T, C> &als, bool noTableName = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << T::get() << "'.";
}
ss << this->string_from_expression(als.column, true);
return ss.str();
}
std::string string_from_expression(const std::string &t, bool /*noTableName*/ = false, bool escape = false) {
std::stringstream ss;
std::string text = t;
if(escape){
text = this->escape(text);
}
ss << "'" << text << "'";
return ss.str();
}
std::string string_from_expression(const char *t, bool /*noTableName*/ = false, bool escape = false) {
std::stringstream ss;
std::string text = t;
if(escape){
text = this->escape(text);
}
ss << "'" << text << "'";
return ss.str();
}
template<class F, class O>
std::string string_from_expression(F O::*m, bool noTableName = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << this->impl.template find_table_name<O>() << "'.";
}
ss << "\"" << this->impl.column_name(m) << "\"";
return ss.str();
}
std::string string_from_expression(const rowid_t &rid, bool /*noTableName*/ = false, bool /*escape*/ = false) {
return static_cast<std::string>(rid);
}
std::string string_from_expression(const oid_t &rid, bool /*noTableName*/ = false, bool /*escape*/ = false) {
return static_cast<std::string>(rid);
}
std::string string_from_expression(const _rowid_t &rid, bool /*noTableName*/ = false, bool /*escape*/ = false) {
return static_cast<std::string>(rid);
}
template<class O>
std::string string_from_expression(const table_rowid_t<O> &rid, bool noTableName = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << this->impl.template find_table_name<O>() << "'.";
}
ss << static_cast<std::string>(rid);
return ss.str();
}
template<class O>
std::string string_from_expression(const table_oid_t<O> &rid, bool noTableName = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << this->impl.template find_table_name<O>() << "'.";
}
ss << static_cast<std::string>(rid);
return ss.str();
}
template<class O>
std::string string_from_expression(const table__rowid_t<O> &rid, bool noTableName = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << this->impl.template find_table_name<O>() << "'.";
}
ss << static_cast<std::string>(rid);
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::group_concat_double_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
auto expr2 = this->string_from_expression(f.y);
ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::group_concat_single_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const conc_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " || " << rhs << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const add_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " + " << rhs << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const sub_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " - " << rhs << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const mul_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " * " << rhs << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const div_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " / " << rhs << ") ";
return ss.str();
}
template<class L, class R>
std::string string_from_expression(const mod_t<L, R> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto lhs = this->string_from_expression(f.l);
auto rhs = this->string_from_expression(f.r);
ss << "(" << lhs << " % " << rhs << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::min_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::max_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::total_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::sum_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::count_asterisk_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
return this->string_from_expression(aggregate_functions::count_asterisk_without_type{});
}
std::string string_from_expression(const aggregate_functions::count_asterisk_without_type &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(f) << "(*) ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::count_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const aggregate_functions::avg_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(a.t);
ss << static_cast<std::string>(a) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const distinct_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const all_t<T> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.t);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class X, class Y>
std::string string_from_expression(const core_functions::rtrim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
auto expr2 = this->string_from_expression(f.y);
ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
return ss.str();
}
template<class X>
std::string string_from_expression(const core_functions::rtrim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class X, class Y>
std::string string_from_expression(const core_functions::ltrim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
auto expr2 = this->string_from_expression(f.y);
ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
return ss.str();
}
template<class X>
std::string string_from_expression(const core_functions::ltrim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
template<class X, class Y>
std::string string_from_expression(const core_functions::trim_double_t<X, Y> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
auto expr2 = this->string_from_expression(f.y);
ss << static_cast<std::string>(f) << "(" << expr << ", " << expr2 << ") ";
return ss.str();
}
template<class X>
std::string string_from_expression(const core_functions::trim_single_t<X> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(f.x);
ss << static_cast<std::string>(f) << "(" << expr << ") ";
return ss.str();
}
std::string string_from_expression(const core_functions::changes_t &ch, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(ch) << "() ";
return ss.str();
}
template<class T>
std::string string_from_expression(const core_functions::length_t<T> &len, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(len.t);
ss << static_cast<std::string>(len) << "(" << expr << ") ";
return ss.str();
}
template<class T, class ...Args>
std::string string_from_expression(const core_functions::datetime_t<T, Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(f) << "(" << this->string_from_expression(f.timestring);
using tuple_t = std::tuple<Args...>;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&ss, this](auto &v){
ss << ", " << this->string_from_expression(v);
});
ss << ") ";
return ss.str();
}
template<class T, class ...Args>
std::string string_from_expression(const core_functions::date_t<T, Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(f) << "(" << this->string_from_expression(f.timestring);
using tuple_t = std::tuple<Args...>;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&ss, this](auto &v){
ss << ", " << this->string_from_expression(v);
}, false);
ss << ") ";
return ss.str();
}
template<class T, class ...Args>
std::string string_from_expression(const core_functions::julianday_t<T, Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(f) << "(" << this->string_from_expression(f.timestring);
using tuple_t = std::tuple<Args...>;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&ss, this](auto &v){
ss << ", " << this->string_from_expression(v);
}, false);
ss << ") ";
return ss.str();
}
std::string string_from_expression(const core_functions::random_t &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(f) << "() ";
return ss.str();
}
#if SQLITE_VERSION_NUMBER >= 3007016
template<class ...Args>
std::string string_from_expression(const core_functions::char_t_<Args...> &f, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
using tuple_t = decltype(f.args);
std::vector<std::string> args;
args.reserve(std::tuple_size<tuple_t>::value);
tuple_helper::tuple_for_each(f.args, [&args, this](auto &v){
auto expression = this->string_from_expression(v);
args.emplace_back(std::move(expression));
});
ss << static_cast<std::string>(f) << "(";
auto lim = int(args.size());
for(auto i = 0; i < lim; ++i) {
ss << args[i];
if(i < lim - 1) {
ss << ", ";
}else{
ss << " ";
}
}
ss << ") ";
return ss.str();
}
#endif
template<class T>
std::string string_from_expression(const core_functions::upper_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(a.t);
ss << static_cast<std::string>(a) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const core_functions::lower_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(a.t);
ss << static_cast<std::string>(a) << "(" << expr << ") ";
return ss.str();
}
template<class T>
std::string string_from_expression(const core_functions::abs_t<T> &a, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
auto expr = this->string_from_expression(a.t);
ss << static_cast<std::string>(a) << "(" << expr << ") ";
return ss.str();
}
template<class T, class F>
std::string string_from_expression(const column_pointer<T, F> &c, bool noTableName = false, bool escape = false) {
std::stringstream ss;
if(!noTableName){
ss << "'" << this->impl.template find_table_name<T>() << "'.";
}
auto &impl = this->get_impl<T>();
ss << "\"" << impl.column_name_simple(c.field) << "\"";
return ss.str();
}
template<class T>
std::vector<std::string> get_column_names(const T &t) {
auto columnName = this->string_from_expression(t);
if(columnName.length()){
return {columnName};
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
}
template<class T>
std::vector<std::string> get_column_names(const internal::asterisk_t<T> &ast) {
std::vector<std::string> res;
res.push_back("*");
return res;
}
template<class ...Args>
std::vector<std::string> get_column_names(const internal::columns_t<Args...> &cols) {
std::vector<std::string> columnNames;
columnNames.reserve(cols.count());
cols.for_each([&columnNames, this](auto &m) {
auto columnName = this->string_from_expression(m);
if(columnName.length()){
columnNames.push_back(columnName);
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
});
return columnNames;
}
/**
* Takes select_t object and returns SELECT query string
*/
template<class T, class ...Args>
std::string string_from_expression(const internal::select_t<T, Args...> &sel, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
if(!is_base_of_template<T, compound_operator>::value){
if(!sel.highest_level){
ss << "( ";
}
ss << "SELECT ";
}
if(get_distinct(sel.col)) {
ss << static_cast<std::string>(distinct(0)) << " ";
}
auto columnNames = this->get_column_names(sel.col);
for(size_t i = 0; i < columnNames.size(); ++i) {
ss << columnNames[i];
if(i < columnNames.size() - 1) {
ss << ",";
}
ss << " ";
}
auto tableNamesSet = this->parse_table_names(sel.col);
internal::join_iterator<Args...>()([&tableNamesSet, this](const auto &c){
using original_join_type = typename std::decay<decltype(c)>::type::type;
using cross_join_type = typename internal::mapped_type_proxy<original_join_type>::type;
auto crossJoinedTableName = this->impl.template find_table_name<cross_join_type>();
auto tableAliasString = alias_extractor<original_join_type>::get();
std::pair<std::string, std::string> tableNameWithAlias(std::move(crossJoinedTableName), std::move(tableAliasString));
tableNamesSet.erase(tableNameWithAlias);
});
if(!tableNamesSet.empty()){
ss << "FROM ";
std::vector<std::pair<std::string, std::string>> tableNames(tableNamesSet.begin(), tableNamesSet.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 << " ";
}
}
using tuple_t = typename std::decay<decltype(sel)>::type::conditions_type;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(sel.conditions, [&ss, this](auto &v){
this->process_single_condition(ss, v);
}, false);
if(!is_base_of_template<T, compound_operator>::value){
if(!sel.highest_level){
ss << ") ";
}
}
return ss.str();
}
template<class T, class E>
std::string string_from_expression(const conditions::cast_t<T, E> &c, bool /*noTableName*/ = false, bool /*escape*/ = false) {
std::stringstream ss;
ss << static_cast<std::string>(c) << " ( " << this->string_from_expression(c.expression) << " AS " << type_printer<T>().print() << ") ";
return ss.str();
}
template<class T>
typename std::enable_if<is_base_of_template<T, compound_operator>::value, std::string>::type string_from_expression(const T &op, bool /*noTableName*/ = false, bool /*escape*/ = false)
{
std::stringstream ss;
ss << this->string_from_expression(op.left) << " ";
ss << static_cast<std::string>(op) << " ";
ss << this->string_from_expression(op.right) << " ";
return ss.str();
}
template<class T>
std::string process_where(const conditions::is_null_t<T> &c) {
std::stringstream ss;
ss << this->string_from_expression(c.t) << " " << static_cast<std::string>(c) << " ";
return ss.str();
}
template<class T>
std::string process_where(const conditions::is_not_null_t<T> &c) {
std::stringstream ss;
ss << this->string_from_expression(c.t) << " " << static_cast<std::string>(c) << " ";
return ss.str();
}
template<class C>
std::string process_where(const conditions::negated_condition_t<C> &c) {
std::stringstream ss;
ss << " " << static_cast<std::string>(c) << " ";
auto cString = this->process_where(c.c);
ss << " (" << cString << " ) ";
return ss.str();
}
template<class L, class R>
std::string process_where(const conditions::and_condition_t<L, R> &c) {
std::stringstream ss;
ss << " (" << this->process_where(c.l) << ") " << static_cast<std::string>(c) << " (" << this->process_where(c.r) << ") ";
return ss.str();
}
template<class L, class R>
std::string process_where(const conditions::or_condition_t<L, R> &c) {
std::stringstream ss;
ss << " (" << this->process_where(c.l) << ") " << static_cast<std::string>(c) << " (" << this->process_where(c.r) << ") ";
return ss.str();
}
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type process_where(const T &c) {
return this->string_from_expression(c);
}
template<class C>
typename std::enable_if<is_base_of_template<C, conditions::binary_condition>::value, std::string>::type process_where(const C &c) {
auto leftString = this->string_from_expression(c.l, false, true);
auto rightString = this->string_from_expression(c.r, false, true);
std::stringstream ss;
ss << leftString << " " << static_cast<std::string>(c) << " " << rightString;
return ss.str();
}
template<class T>
std::string process_where(const conditions::named_collate<T> &col) {
auto res = this->process_where(col.expr);
return res + " " + static_cast<std::string>(col);
}
template<class T>
std::string process_where(const conditions::collate_t<T> &col) {
auto res = this->process_where(col.expr);
return res + " " + static_cast<std::string>(col);
}
template<class L, class A>
std::string process_where(const conditions::in_t<L, A> &inCondition) {
std::stringstream ss;
auto leftString = this->string_from_expression(inCondition.l);
ss << leftString << " " << static_cast<std::string>(inCondition) << " ";
ss << this->string_from_expression(inCondition.arg);
ss << " ";
return ss.str();
}
template<class L, class E>
std::string process_where(const conditions::in_t<L, std::vector<E>> &inCondition) {
std::stringstream ss;
auto leftString = this->string_from_expression(inCondition.l);
ss << leftString << " " << static_cast<std::string>(inCondition) << " ( ";
for(size_t index = 0; index < inCondition.arg.size(); ++index) {
auto &value = inCondition.arg[index];
ss << " " << this->string_from_expression(value);
if(index < inCondition.arg.size() - 1) {
ss << ", ";
}
}
ss << " )";
return ss.str();
}
template<class A, class T>
std::string process_where(const conditions::like_t<A, T> &l) {
std::stringstream ss;
ss << this->string_from_expression(l.a) << " " << static_cast<std::string>(l) << " " << this->string_from_expression(l.t) << " ";
return ss.str();
}
template<class A, class T>
std::string process_where(const conditions::between_t<A, T> &bw) {
std::stringstream ss;
auto expr = this->string_from_expression(bw.expr);
ss << expr << " " << static_cast<std::string>(bw) << " " << this->string_from_expression(bw.b1) << " AND " << this->string_from_expression(bw.b2) << " ";
return ss.str();
}
template<class T>
std::string process_where(const conditions::exists_t<T> &e) {
std::stringstream ss;
ss << static_cast<std::string>(e) << " " << this->string_from_expression(e.t) << " ";
return ss.str();
}
template<class O>
std::string process_order_by(const conditions::order_by_t<O> &orderBy) {
std::stringstream ss;
auto columnName = this->string_from_expression(orderBy.o);
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<class T>
void process_join_constraint(std::stringstream &ss, const conditions::on_t<T> &t) {
ss << static_cast<std::string>(t) << " " << this->process_where(t.t) << " ";
}
template<class F, class O>
void process_join_constraint(std::stringstream &ss, const conditions::using_t<F, O> &u) {
ss << static_cast<std::string>(u) << " (" << this->string_from_expression(u.column, true) << " ) ";
}
void process_single_condition(std::stringstream &ss, const conditions::limit_t &limt) {
ss << static_cast<std::string>(limt) << " ";
if(limt.has_offset) {
if(limt.offset_is_implicit){
ss << limt.off << ", " << limt.lim;
}else{
ss << limt.lim << " OFFSET " << limt.off;
}
}else{
ss << limt.lim;
}
ss << " ";
}
template<class O>
void process_single_condition(std::stringstream &ss, const conditions::cross_join_t<O> &c) {
ss << static_cast<std::string>(c) << " ";
ss << " '" << this->impl.template find_table_name<O>() << "' ";
}
template<class O>
void process_single_condition(std::stringstream &ss, const conditions::natural_join_t<O> &c) {
ss << static_cast<std::string>(c) << " ";
ss << " '" << this->impl.template find_table_name<O>() << "' ";
}
template<class T, class O>
void process_single_condition(std::stringstream &ss, const conditions::inner_join_t<T, O> &l) {
ss << static_cast<std::string>(l) << " ";
auto aliasString = alias_extractor<T>::get();
ss << " '" << this->impl.template find_table_name<typename mapped_type_proxy<T>::type>() << "' ";
if(aliasString.length()){
ss << "'" << aliasString << "' ";
}
this->process_join_constraint(ss, l.constraint);
}
template<class T, class O>
void process_single_condition(std::stringstream &ss, const conditions::left_outer_join_t<T, O> &l) {
ss << static_cast<std::string>(l) << " ";
ss << " '" << this->impl.template find_table_name<T>() << "' ";
this->process_join_constraint(ss, l.constraint);
}
template<class T, class O>
void process_single_condition(std::stringstream &ss, const conditions::left_join_t<T, O> &l) {
ss << static_cast<std::string>(l) << " ";
ss << " '" << this->impl.template find_table_name<T>() << "' ";
this->process_join_constraint(ss, l.constraint);
}
template<class T, class O>
void process_single_condition(std::stringstream &ss, const conditions::join_t<T, O> &l) {
ss << static_cast<std::string>(l) << " ";
ss << " '" << this->impl.template find_table_name<T>() << "' ";
this->process_join_constraint(ss, l.constraint);
}
template<class C>
void process_single_condition(std::stringstream &ss, const conditions::where_t<C> &w) {
ss << static_cast<std::string>(w) << " ";
auto whereString = this->process_where(w.c);
ss << "( " << whereString << ") ";
}
template<class O>
void process_single_condition(std::stringstream &ss, const conditions::order_by_t<O> &orderBy) {
ss << static_cast<std::string>(orderBy) << " ";
auto orderByString = this->process_order_by(orderBy);
ss << orderByString << " ";
}
template<class ...Args>
void process_single_condition(std::stringstream &ss, const conditions::multi_order_by_t<Args...> &orderBy) {
std::vector<std::string> expressions;
using tuple_t = std::tuple<Args...>;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(orderBy.args, [&expressions, this](auto &v){
auto expression = this->process_order_by(v);
expressions.insert(expressions.begin(), expression);
});
ss << static_cast<std::string>(orderBy) << " ";
for(size_t i = 0; i < expressions.size(); ++i) {
ss << expressions[i];
if(i < expressions.size() - 1) {
ss << ", ";
}
}
ss << " ";
}
template<class ...Args>
void process_single_condition(std::stringstream &ss, const conditions::group_by_t<Args...> &groupBy) {
std::vector<std::string> expressions;
using tuple_t = std::tuple<Args...>;
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(groupBy.args, [&expressions, this](auto &v){
auto expression = this->string_from_expression(v);
expressions.push_back(expression);
});
ss << static_cast<std::string>(groupBy) << " ";
for(size_t i = 0; i < expressions.size(); ++i) {
ss << expressions[i];
if(i < expressions.size() - 1) {
ss << ", ";
}
}
ss << " ";
}
template<class T>
void process_single_condition(std::stringstream &ss, const conditions::having_t<T> &hav) {
ss << static_cast<std::string>(hav) << " ";
ss << this->process_where(hav.t) << " ";
}
/**
* Recursion end.
*/
template<class ...Args>
void process_conditions(std::stringstream &, Args .../*args*/) {
//..
}
template<class C, class ...Args>
void process_conditions(std::stringstream &ss, C c, Args&& ...args) {
this->process_single_condition(ss, c);
this->process_conditions(ss, std::forward<Args>(args)...);
}
void on_open_internal(sqlite3 *db) {
#if SQLITE_VERSION_NUMBER >= 3006019
if(this->foreign_keys_count()){
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<journal_mode>(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()));
}
}
for(auto &p : this->limit.limits) {
sqlite3_limit(db, p.first, p.second);
}
if(this->on_open){
this->on_open(db);
}
}
#if SQLITE_VERSION_NUMBER >= 3006019
// returns foreign keys count in storage definition
int foreign_keys_count() {
auto res = 0;
this->impl.for_each([&res](auto impl){
res += impl->foreign_keys_count();
});
return res;
}
#endif
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);
}
public:
template<class T, class ...Args>
view_t<T, Args...> iterate(Args&& ...args) {
this->assert_mapped_type<T>();
auto connection = this->get_or_create_connection();
return {*this, connection, std::forward<Args>(args)...};
}
void create_collation(const std::string &name, collating_function f) {
collating_function *functionPointer = nullptr;
if(f){
functionPointer = &(collatingFunctions[name] = f);
}else{
collatingFunctions.erase(name);
}
// create collations if db is open
if(this->currentTransaction){
auto db = this->currentTransaction->get_db();
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()));
}
}
}
template<class O, class ...Args>
void remove_all(Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::stringstream ss;
ss << "DELETE FROM '" << impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_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(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* Delete routine.
* O is an object's type. Must be specified explicitly.
* @param id id of object to be removed.
*/
template<class O, class I>
void remove(I id) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::stringstream ss;
ss << "DELETE FROM '" << impl.table.name << "' ";
ss << "WHERE ";
auto primaryKeyColumnNames = impl.table.primary_key_column_names();
for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ?";
if(i < primaryKeyColumnNames.size() - 1) {
ss << " AND ";
}else{
ss << " ";
}
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
statement_binder<I>().bind(stmt, index++, id);
if (sqlite3_step(stmt) == SQLITE_DONE) {
// done..
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* 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<class O>
void update(const O &o) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::stringstream ss;
ss << "UPDATE '" << impl.table.name << "' SET ";
std::vector<std::string> setColumnNames;
impl.table.for_each_column([&setColumnNames](auto c) {
if(!c.template has<constraints::primary_key_t<>>()) {
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 = impl.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 << " ";
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
impl.table.for_each_column([&o, stmt, &index] (auto &c) {
if(!c.template has<constraints::primary_key_t<>>()) {
using field_type = typename std::decay<decltype(c)>::type::field_type;
const field_type *value = nullptr;
if(c.member_pointer){
value = &(o.*c.member_pointer);
}else{
value = &((o).*(c.getter))();
}
statement_binder<field_type>().bind(stmt, index++, *value);
}
});
impl.table.for_each_column([&o, stmt, &index] (auto &c) {
if(c.template has<constraints::primary_key_t<>>()) {
using field_type = typename std::decay<decltype(c)>::type::field_type;
const field_type *value = nullptr;
if(c.member_pointer){
value = &(o.*c.member_pointer);
}else{
value = &((o).*(c.getter))();
}
statement_binder<field_type>().bind(stmt, index++, *value);
}
});
if (sqlite3_step(stmt) == SQLITE_DONE) {
// done..
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
template<class ...Args, class ...Wargs>
void update_all(internal::set_t<Args...> set, Wargs ...wh) {
auto connection = this->get_or_create_connection();
std::stringstream ss;
ss << "UPDATE ";
std::set<std::pair<std::string, std::string>> tableNamesSet;
set.for_each([this, &tableNamesSet](auto &asgn) {
auto tableName = this->parse_table_name(asgn.l);
tableNamesSet.insert(tableName.begin(), tableName.end());
});
if(!tableNamesSet.empty()){
if(tableNamesSet.size() == 1){
ss << " '" << tableNamesSet.begin()->first << "' ";
ss << static_cast<std::string>(set) << " ";
std::vector<std::string> setPairs;
set.for_each([this, &setPairs](auto &asgn){
std::stringstream sss;
sss << this->string_from_expression(asgn.l, true) << " = " << this->string_from_expression(asgn.r) << " ";
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 << ", ";
}
}
this->process_conditions(ss, wh...);
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_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(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}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));
}
}
protected:
/**
* O - mapped type
* Args - conditions
* @param query - result query string
* @return impl for O
*/
template<class O, class ...Args>
auto& generate_select_asterisk(std::string *query, Args&& ...args) {
std::stringstream ss;
ss << "SELECT ";
auto &impl = this->get_impl<O>();
auto columnNames = impl.table.column_names();
for(size_t i = 0; i < columnNames.size(); ++i) {
ss
<< "'" << impl.table.name << "'."
<< "\""
<< columnNames[i]
<< "\""
;
if(i < columnNames.size() - 1) {
ss << ", ";
}else{
ss << " ";
}
}
ss << "FROM '" << impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
if(query){
*query = ss.str();
}
return impl;
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const T &) {
return {};
}
template<class F, class O>
std::set<std::pair<std::string, std::string>> parse_table_name(F O::*, std::string alias = {}) {
return {std::make_pair(this->impl.template find_table_name<O>(), std::move(alias))};
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::min_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::max_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::sum_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::total_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::group_concat_double_t<T> &f) {
auto res = this->parse_table_name(f.t);
auto secondSet = this->parse_table_name(f.y);
res.insert(secondSet.begin(), secondSet.end());
return res;
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::group_concat_single_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::count_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::avg_t<T> &a) {
return this->parse_table_name(a.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::length_t<T> &len) {
return this->parse_table_name(len.t);
}
template<class T, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::date_t<T, Args...> &f) {
auto res = this->parse_table_name(f.timestring);
using tuple_t = decltype(f.modifiers);
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&res, this](auto &v){
auto tableNames = this->parse_table_name(v);
res.insert(tableNames.begin(), tableNames.end());
});
return res;
}
template<class T, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::datetime_t<T, Args...> &f) {
auto res = this->parse_table_name(f.timestring);
using tuple_t = decltype(f.modifiers);
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.modifiers, [&res, this](auto &v){
auto tableNames = this->parse_table_name(v);
res.insert(tableNames.begin(), tableNames.end());
});
return res;
}
template<class X>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::trim_single_t<X> &f) {
return this->parse_table_name(f.x);
}
template<class X, class Y>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::trim_double_t<X, Y> &f) {
auto res = this->parse_table_name(f.x);
auto res2 = this->parse_table_name(f.y);
res.insert(res2.begin(), res2.end());
return res;
}
template<class X>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::rtrim_single_t<X> &f) {
return this->parse_table_name(f.x);
}
template<class X, class Y>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::rtrim_double_t<X, Y> &f) {
auto res = this->parse_table_name(f.x);
auto res2 = this->parse_table_name(f.y);
res.insert(res2.begin(), res2.end());
return res;
}
template<class X>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::ltrim_single_t<X> &f) {
return this->parse_table_name(f.x);
}
template<class X, class Y>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::ltrim_double_t<X, Y> &f) {
auto res = this->parse_table_name(f.x);
auto res2 = this->parse_table_name(f.y);
res.insert(res2.begin(), res2.end());
return res;
}
#if SQLITE_VERSION_NUMBER >= 3007016
template<class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::char_t_<Args...> &f) {
std::set<std::pair<std::string, std::string>> res;
using tuple_t = decltype(f.args);
tuple_helper::iterator<std::tuple_size<tuple_t>::value - 1, Args...>()(f.args, [&res, this](auto &v){
auto tableNames = this->parse_table_name(v);
res.insert(tableNames.begin(), tableNames.end());
});
return res;
}
#endif
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::random_t &) {
return {};
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::upper_t<T> &a) {
return this->parse_table_name(a.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::lower_t<T> &a) {
return this->parse_table_name(a.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const core_functions::abs_t<T> &a) {
return this->parse_table_name(a.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const distinct_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const all_t<T> &f) {
return this->parse_table_name(f.t);
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const conc_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const add_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const sub_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const mul_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const div_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class L, class R, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_name(const mod_t<L, R> &f) {
std::set<std::pair<std::string, std::string>> res;
auto leftSet = this->parse_table_names(f.l);
res.insert(leftSet.begin(), leftSet.end());
auto rightSet = this->parse_table_names(f.r);
res.insert(rightSet.begin(), rightSet.end());
return res;
}
template<class T, class F>
std::set<std::pair<std::string, std::string>> parse_table_name(const column_pointer<T, F> &c) {
std::set<std::pair<std::string, std::string>> res;
res.insert({this->impl.template find_table_name<T>(), ""});
return res;
}
template<class T, class C>
std::set<std::pair<std::string, std::string>> parse_table_name(const alias_column_t<T, C> &a) {
return this->parse_table_name(a.column, alias_extractor<T>::get());
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::count_asterisk_t<T> &c) {
auto tableName = this->impl.template find_table_name<T>();
if(!tableName.empty()){
return {std::make_pair(std::move(tableName), "")};
}else{
return {};
}
}
std::set<std::pair<std::string, std::string>> parse_table_name(const aggregate_functions::count_asterisk_without_type &c) {
return {};
}
template<class T>
std::set<std::pair<std::string, std::string>> parse_table_name(const asterisk_t<T> &ast) {
auto tableName = this->impl.template find_table_name<T>();
return {std::make_pair(std::move(tableName), "")};
}
template<class T, class E>
std::set<std::pair<std::string, std::string>> parse_table_name(const conditions::cast_t<T, E> &c) {
return this->parse_table_name(c.expression);
}
template<class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_names(Args...) {
return {};
}
template<class H, class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_names(H h, Args&& ...args) {
auto res = this->parse_table_names(std::forward<Args>(args)...);
auto tableName = this->parse_table_name(h);
res.insert(tableName.begin(), tableName.end());
return res;
}
template<class ...Args>
std::set<std::pair<std::string, std::string>> parse_table_names(const internal::columns_t<Args...> &cols) {
std::set<std::pair<std::string, std::string>> res;
cols.for_each([&res, this](auto &m){
auto tableName = this->parse_table_name(m);
res.insert(tableName.begin(), tableName.end());
});
return res;
}
template<class F, class O, class ...Args>
std::string group_concat_internal(F O::*m, std::unique_ptr<const std::string> y, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::string res;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::group_concat(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName;
if(y){
ss << ",\"" << *y << "\"";
}
ss << ") FROM '"<< impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv,char **) -> int {
auto &res = *(std::string*)data;
if(argc){
res = row_extractor<std::string>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
public:
/**
* Select * with no conditions 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.
*/
template<class O, class C = std::vector<O>, class ...Args>
C get_all(Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
C res;
std::string query;
auto &impl = this->generate_select_asterisk<O>(&query, std::forward<Args>(args)...);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
int stepRes;
do{
stepRes = sqlite3_step(stmt);
switch(stepRes){
case SQLITE_ROW:{
O obj;
auto index = 0;
impl.table.for_each_column([&index, &obj, stmt] (auto c) {
using field_type = typename decltype(c)::field_type;
auto value = row_extractor<field_type>().extract(stmt, index++);
if(c.member_pointer){
obj.*c.member_pointer = std::move(value);
}else{
((obj).*(c.setter))(std::move(value));
}
});
res.push_back(std::move(obj));
}break;
case SQLITE_DONE: break;
default:{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
}while(stepRes != SQLITE_DONE);
return res;
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* 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<class O, class ...Ids>
O get(Ids ...ids) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::unique_ptr<O> res;
std::stringstream ss;
ss << "SELECT ";
auto columnNames = impl.table.column_names();
for(size_t i = 0; i < columnNames.size(); ++i) {
ss << "\"" << columnNames[i] << "\"";
if(i < columnNames.size() - 1) {
ss << ", ";
}else{
ss << " ";
}
}
ss << "FROM '" << impl.table.name << "' WHERE ";
auto primaryKeyColumnNames = impl.table.primary_key_column_names();
if(primaryKeyColumnNames.size()){
for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? ";
if(i < primaryKeyColumnNames.size() - 1) {
ss << "AND ";
}
ss << ' ';
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
auto idsTuple = std::make_tuple(std::forward<Ids>(ids)...);
constexpr const auto idsCount = std::tuple_size<decltype(idsTuple)>::value;
tuple_helper::iterator<idsCount - 1, Ids...>()(idsTuple, [stmt, &index](auto &v){
using field_type = typename std::decay<decltype(v)>::type;
statement_binder<field_type>().bind(stmt, index++, v);
});
auto stepRes = sqlite3_step(stmt);
switch(stepRes){
case SQLITE_ROW:{
O res;
index = 0;
impl.table.for_each_column([&index, &res, stmt] (auto c) {
using field_type = typename decltype(c)::field_type;
auto value = row_extractor<field_type>().extract(stmt, index++);
if(c.member_pointer){
res.*c.member_pointer = std::move(value);
}else{
((res).*(c.setter))(std::move(value));
}
});
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(connection->get_db()), get_sqlite_error_category()));
}
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column));
}
}
/**
* 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<class O, class ...Ids>
std::unique_ptr<O> get_pointer(Ids ...ids) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::unique_ptr<O> res;
std::stringstream ss;
ss << "SELECT ";
auto columnNames = impl.table.column_names();
for(size_t i = 0; i < columnNames.size(); ++i) {
ss << "\"" << columnNames[i] << "\"";
if(i < columnNames.size() - 1) {
ss << ", ";
}else{
ss << " ";
}
}
ss << "FROM '" << impl.table.name << "' WHERE ";
auto primaryKeyColumnNames = impl.table.primary_key_column_names();
if(primaryKeyColumnNames.size() && primaryKeyColumnNames.front().length()){
for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) {
ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ? ";
if(i < primaryKeyColumnNames.size() - 1) {
ss << "AND ";
}
ss << ' ';
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
auto idsTuple = std::make_tuple(std::forward<Ids>(ids)...);
constexpr const auto idsCount = std::tuple_size<decltype(idsTuple)>::value;
tuple_helper::iterator<idsCount - 1, Ids...>()(idsTuple, [stmt, &index](auto &v){
using field_type = typename std::decay<decltype(v)>::type;
statement_binder<field_type>().bind(stmt, index++, v);
});
auto stepRes = sqlite3_step(stmt);
switch(stepRes){
case SQLITE_ROW:{
O res;
index = 0;
impl.table.for_each_column([&index, &res, stmt] (auto c) {
using field_type = typename decltype(c)::field_type;
auto value = row_extractor<field_type>().extract(stmt, index++);
if(c.member_pointer){
res.*c.member_pointer = std::move(value);
}else{
((res).*(c.setter))(std::move(value));
}
});
return std::make_unique<O>(std::move(res));
}break;
case SQLITE_DONE:{
return {};
}break;
default:{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column));
}
}
/**
* 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<class O, class ...Ids>
std::shared_ptr<O> get_no_throw(Ids ...ids) {
return std::shared_ptr<O>(get_pointer<O>(std::forward<Ids>(ids)...));
}
/**
* SELECT COUNT(*) with no conditions routine. https://www.sqlite.org/lang_aggfunc.html#count
* @return Number of O object in table.
*/
template<class O, class ...Args, class R = typename mapped_type_proxy<O>::type>
int count(Args&& ...args) {
this->assert_mapped_type<R>();
auto tableAliasString = alias_extractor<O>::get();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<R>();
int res = 0;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::count()) << "(*) FROM '" << impl.table.name << "' ";
if(!tableAliasString.empty()) {
ss << "'" << tableAliasString << "' ";
}
this->process_conditions(ss, args...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv, char **) -> int {
auto &res = *(int*)data;
if(argc){
res = row_extractor<int>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
return res;
}
/**
* SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count
* @param m member pointer to class mapped to the storage.
*/
template<class F, class O, class ...Args>
int count(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
int res = 0;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::count(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName << ") FROM '"<< impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv,char **) -> int {
auto &res = *(int*)data;
if(argc){
res = row_extractor<int>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
/**
* 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<class F, class O, class ...Args>
double avg(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
double res = 0;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::avg(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName << ") FROM '"<< impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv,char **)->int{
auto &res = *(double*)data;
if(argc){
res = row_extractor<double>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
template<class F, class O>
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<class F, class O, class ...Args,
class Tuple = std::tuple<Args...>,
typename sfinae = typename std::enable_if<std::tuple_size<std::tuple<Args...>>::value >= 1>::type
>
std::string group_concat(F O::*m, Args&& ...args) {
return this->group_concat_internal(m, {}, std::forward<Args>(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<class F, class O, class ...Args>
std::string group_concat(F O::*m, const std::string &y, Args&& ...args) {
return this->group_concat_internal(m, std::make_unique<std::string>(y), std::forward<Args>(args)...);
}
template<class F, class O, class ...Args>
std::string group_concat(F O::*m, const char *y, Args&& ...args) {
return this->group_concat_internal(m, std::make_unique<std::string>(y), std::forward<Args>(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<class F, class O, class ...Args, class Ret = typename column_result_t<self, F O::*>::type>
std::unique_ptr<Ret> max(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::unique_ptr<Ret> res;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::max(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName << ") FROM '" << impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv,char **)->int{
auto &res = *(std::unique_ptr<Ret>*)data;
if(argc){
if(argv[0]){
res = std::make_unique<Ret>(row_extractor<Ret>().extract(argv[0]));
}
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
/**
* 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<class F, class O, class ...Args, class Ret = typename column_result_t<self, F O::*>::type>
std::unique_ptr<Ret> min(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::unique_ptr<Ret> res;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::min(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName << ") FROM '" << impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv,char **)->int{
auto &res = *(std::unique_ptr<Ret>*)data;
if(argc){
if(argv[0]){
res = std::make_unique<Ret>(row_extractor<Ret>().extract(argv[0]));
}
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
/**
* 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<class F, class O, class ...Args, class Ret = typename column_result_t<self, F O::*>::type>
std::unique_ptr<Ret> sum(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = this->get_impl<O>();
std::unique_ptr<Ret> res;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::sum(0)) << "(";
auto columnName = this->string_from_expression(m);
if(columnName.length()){
ss << columnName << ") FROM '"<< impl.table.name << "' ";
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv, char **)->int{
auto &res = *(std::unique_ptr<Ret>*)data;
if(argc){
res = std::make_unique<Ret>(row_extractor<Ret>().extract(argv[0]));
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
/**
* 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<class F, class O, class ...Args>
double total(F O::*m, Args&& ...args) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
double res;
std::stringstream ss;
ss << "SELECT " << static_cast<std::string>(sqlite_orm::total(0)) << "(";
auto columnName = this->string_from_expression(m);
if(!columnName.empty()){
ss << columnName << ") ";
auto tableNamesSet = this->parse_table_names(m);
if(!tableNamesSet.empty()){
ss << "FROM " ;
std::vector<std::pair<std::string, std::string>> tableNames(tableNamesSet.begin(), tableNamesSet.end());
for(size_t i = 0; i < tableNames.size(); ++i) {
ss << "'" << tableNames[i].first << "' ";
if(i < tableNames.size() - 1) {
ss << ",";
}
ss << " ";
}
}
this->process_conditions(ss, std::forward<Args>(args)...);
auto query = ss.str();
auto rc = sqlite3_exec(connection->get_db(),
query.c_str(),
[](void *data, int argc, char **argv, char **)->int{
auto &res = *(double*)data;
if(argc){
res = row_extractor<double>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else{
throw std::system_error(std::make_error_code(orm_error_code::column_not_found));
}
return res;
}
/**
* Select a single column into std::vector<T> or multiple columns into std::vector<std::tuple<...>>.
* For a single column use `auto rows = storage.select(&User::id, where(...));
* For multicolumns user `auto rows = storage.select(columns(&User::id, &User::name), where(...));
*/
template<
class T,
class ...Args,
class R = typename column_result_t<self, T>::type>
std::vector<R> select(T m, Args ...args) {
static_assert(!is_base_of_template<T, compound_operator>::value || std::tuple_size<std::tuple<Args...>>::value == 0,
"Cannot use args with a compound operator");
using select_type = select_t<T, Args...>;
auto query = this->string_from_expression(select_type{std::move(m), std::make_tuple<Args...>(std::forward<Args>(args)...), true});
auto connection = this->get_or_create_connection();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
std::vector<R> res;
int stepRes;
do{
stepRes = sqlite3_step(stmt);
switch(stepRes){
case SQLITE_ROW:{
res.push_back(row_extractor<R>().extract(stmt, 0));
}break;
case SQLITE_DONE: break;
default:{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
}while(stepRes != SQLITE_DONE);
return res;
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* Returns a string representation of object of a class mapped to the storage.
* Type of string has json-like style.
*/
template<class O>
std::string dump(const O &o) {
this->assert_mapped_type<O>();
return this->impl.dump(o);
}
/**
* 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<class O>
void replace(const O &o) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = get_impl<O>();
std::stringstream ss;
ss << "REPLACE INTO '" << impl.table.name << "' (";
auto columnNames = impl.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(";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << "?";
if(i < columnNamesCount - 1) {
ss << ", ";
}else{
ss << ")";
}
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
impl.table.for_each_column([&o, &index, &stmt] (auto c) {
using field_type = typename decltype(c)::field_type;
const field_type *value = nullptr;
if(c.member_pointer){
value = &(o.*c.member_pointer);
}else{
value = &((o).*(c.getter))();
}
statement_binder<field_type>().bind(stmt, index++, *value);
});
if (sqlite3_step(stmt) == SQLITE_DONE) {
//..
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
template<class It>
void replace_range(It from, It to) {
using O = typename std::iterator_traits<It>::value_type;
this->assert_mapped_type<O>();
if(from == to) {
return;
}
auto connection = this->get_or_create_connection();
auto &impl = get_impl<O>();
std::stringstream ss;
ss << "REPLACE INTO '" << impl.table.name << "' (";
auto columnNames = impl.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<int>(std::distance(from, to));
for(auto i = 0; i < valuesCount; ++i) {
ss << valuesString;
if(i < valuesCount - 1) {
ss << ",";
}
ss << " ";
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
for(auto it = from; it != to; ++it) {
auto &o = *it;
impl.table.for_each_column([&o, &index, &stmt] (auto c) {
using field_type = typename decltype(c)::field_type;
const field_type *value = nullptr;
if(c.member_pointer){
value = &(o.*c.member_pointer);
}else{
value = &((o).*(c.getter))();
}
statement_binder<field_type>().bind(stmt, index++, *value);
});
}
if (sqlite3_step(stmt) == SQLITE_DONE) {
//..
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
template<class O, class ...Cols>
int insert(const O &o, columns_t<Cols...> cols) {
constexpr const size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value;
static_assert(colsCount > 0, "Use insert or replace with 1 argument instead");
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = get_impl<O>();
std::stringstream ss;
ss << "INSERT INTO '" << impl.table.name << "' ";
std::vector<std::string> columnNames;
columnNames.reserve(colsCount);
cols.for_each([&columnNames, this](auto &m) {
auto columnName = this->string_from_expression(m, true);
if(columnName.length()){
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 << " ";
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
cols.for_each([&o, &index, &stmt, &impl] (auto &m) {
using column_type = typename std::decay<decltype(m)>::type;
using field_type = typename column_result_t<self, column_type>::type;
const field_type *value = impl.table.template get_object_field_pointer<field_type>(o, m);
statement_binder<field_type>().bind(stmt, index++, *value);
});
if (sqlite3_step(stmt) == SQLITE_DONE) {
return int(sqlite3_last_insert_rowid(connection->get_db()));
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
/**
* 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<class O>
int insert(const O &o) {
this->assert_mapped_type<O>();
auto connection = this->get_or_create_connection();
auto &impl = get_impl<O>();
int res = 0;
std::stringstream ss;
ss << "INSERT INTO '" << impl.table.name << "' ";
std::vector<std::string> columnNames;
auto compositeKeyColumnNames = impl.table.composite_key_columns_names();
impl.table.for_each_column([&impl, &columnNames, &compositeKeyColumnNames] (auto c) {
if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) {
auto it = std::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 << ") ";
}
}
}else{
ss << "DEFAULT ";
}
ss << "VALUES ";
if(columnNamesCount){
ss << "( ";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << "?";
if(i < columnNamesCount - 1) {
ss << ", ";
}else{
ss << ")";
}
}
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
impl.table.for_each_column([&o, &index, &stmt, &impl, &compositeKeyColumnNames] (auto c) {
if(impl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()){
auto it = std::find(compositeKeyColumnNames.begin(),
compositeKeyColumnNames.end(),
c.name);
if(it == compositeKeyColumnNames.end()){
using field_type = typename decltype(c)::field_type;
if(c.member_pointer){
statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer);
}else{
using getter_type = typename decltype(c)::getter_type;
field_value_holder<getter_type> valueHolder{((o).*(c.getter))()};
statement_binder<field_type>().bind(stmt, index++, valueHolder.value);
}
}
}
});
if (sqlite3_step(stmt) == SQLITE_DONE) {
res = int(sqlite3_last_insert_rowid(connection->get_db()));
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
return res;
}
template<class It>
void insert_range(It from, It to) {
using O = typename std::iterator_traits<It>::value_type;
this->assert_mapped_type<O>();
if(from == to) {
return;
}
auto connection = this->get_or_create_connection();
auto &impl = get_impl<O>();
std::stringstream ss;
ss << "INSERT INTO '" << impl.table.name << "' (";
std::vector<std::string> columnNames;
impl.table.for_each_column([&columnNames] (auto c) {
if(!c.template has<constraints::primary_key_t<>>()) {
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 << "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<int>(std::distance(from, to));
for(auto i = 0; i < valuesCount; ++i) {
ss << valuesString;
if(i < valuesCount - 1) {
ss << ",";
}
ss << " ";
}
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_db(), query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
statement_finalizer finalizer{stmt};
auto index = 1;
for(auto it = from; it != to; ++it) {
auto &o = *it;
impl.table.for_each_column([&o, &index, &stmt] (auto c) {
if(!c.template has<constraints::primary_key_t<>>()){
typedef typename decltype(c)::field_type field_type;
const field_type *value = nullptr;
if(c.member_pointer){
value = &(o.*c.member_pointer);
}else{
value = &((o).*(c.getter))();
}
statement_binder<field_type>().bind(stmt, index++, *value);
}
});
}
if (sqlite3_step(stmt) == SQLITE_DONE) {
//..
}else{
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
void drop_index(const std::string &indexName) {
auto connection = this->get_or_create_connection();
std::stringstream ss;
ss << "DROP INDEX '" << indexName + "'";
auto query = ss.str();
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_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(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
void vacuum() {
auto connection = this->get_or_create_connection();
std::string query = "VACUUM";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connection->get_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(connection->get_db()), get_sqlite_error_category()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(connection->get_db()), get_sqlite_error_category()));
}
}
protected:
void drop_table_internal(const std::string &tableName, sqlite3 *db) {
std::stringstream ss;
ss << "DROP TABLE '" << tableName + "'";
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()));
}
}else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
}
public:
/**
* Drops table with given name.
*/
void drop_table(const std::string &tableName) {
auto connection = this->get_or_create_connection();
this->drop_table_internal(tableName, connection->get_db());
}
/**
* sqlite3_changes function.
*/
int changes() {
auto connection = this->get_or_create_connection();
return sqlite3_changes(connection->get_db());
}
/**
* sqlite3_total_changes function.
*/
int total_changes() {
auto connection = this->get_or_create_connection();
return sqlite3_total_changes(connection->get_db());
}
int64 last_insert_rowid() {
auto connection = this->get_or_create_connection();
return sqlite3_last_insert_rowid(connection->get_db());
}
int busy_timeout(int ms) {
auto connection = this->get_or_create_connection();
return sqlite3_busy_timeout(connection->get_db(), ms);
}
/**
* Returns libsqltie3 lib version, not sqlite_orm
*/
std::string libversion() {
return sqlite3_libversion();
}
protected:
template<class ...Tss, class ...Cols>
sync_schema_result sync_table(storage_impl<internal::index_t<Cols...>, Tss...> *impl, sqlite3 *db, bool) {
auto res = sync_schema_result::already_in_sync;
std::stringstream ss;
ss << "CREATE ";
if(impl->table.unique){
ss << "UNIQUE ";
}
using columns_type = typename decltype(impl->table)::columns_type;
using head_t = typename std::tuple_element<0, columns_type>::type;
using indexed_type = typename internal::table_type<head_t>::type;
ss << "INDEX IF NOT EXISTS '" << impl->table.name << "' ON '" << this->impl.template find_table_name<indexed_type>() << "' ( ";
std::vector<std::string> columnNames;
tuple_helper::iterator<std::tuple_size<columns_type>::value - 1, Cols...>()(impl->table.columns, [&columnNames, this](auto &v){
columnNames.push_back(this->impl.column_name(v));
});
for(size_t i = 0; i < columnNames.size(); ++i) {
ss << "'" << columnNames[i] << "'";
if(i < columnNames.size() - 1) {
ss << ",";
}
ss << " ";
}
ss << ") ";
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()));
}
return res;
}
template<class ...Tss, class ...Cs>
sync_schema_result sync_table(storage_impl<table_t<Cs...>, Tss...> *impl, sqlite3 *db, bool preserve) {
auto res = sync_schema_result::already_in_sync;
auto schema_stat = impl->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, impl->table.name, impl);
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 = impl->table.get_table_info();
// now get current table info from db using `PRAGMA table_info` query..
auto dbTableInfo = impl->get_table_info(impl->table.name, db);
// this vector will contain pointers to columns that gotta be added..
std::vector<table_info*> columnsToAdd;
impl->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, impl);
res = decltype(res)::old_columns_removed;
}
if(schema_stat == sync_schema_result::new_columns_added) {
for(auto columnPointer : columnsToAdd) {
impl->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, impl);
for(auto columnPointer : columnsToAdd) {
impl->add_column(*columnPointer, db);
}
res = decltype(res)::new_columns_added_and_old_columns_removed;
}
} else if(schema_stat == sync_schema_result::dropped_and_recreated) {
this->drop_table_internal(impl->table.name, db);
this->create_table(db, impl->table.name, impl);
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<std::string, sync_schema_result> sync_schema(bool preserve = false) {
auto connection = this->get_or_create_connection();
std::map<std::string, sync_schema_result> result;
auto db = connection->get_db();
this->impl.for_each([&result, db, preserve, this](auto impl){
auto res = this->sync_table(impl, db, preserve);
result.insert({impl->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<std::string, sync_schema_result> sync_schema_simulate(bool preserve = false) {
auto connection = this->get_or_create_connection();
std::map<std::string, sync_schema_result> result;
auto db = connection->get_db();
this->impl.for_each([&result, db, preserve](auto impl){
result.insert({impl->table.name, impl->schema_status(db, preserve)});
});
return result;
}
bool transaction(std::function<bool()> f) {
this->begin_transaction();
auto db = this->currentTransaction->get_db();
auto shouldCommit = f();
if(shouldCommit){
this->impl.commit(db);
}else{
this->impl.rollback(db);
}
if(!this->inMemory && !this->isOpenedForever){
this->currentTransaction = nullptr;
}
return shouldCommit;
}
void begin_transaction() {
if(!this->inMemory){
if(!this->isOpenedForever){
if(this->currentTransaction) throw std::system_error(std::make_error_code(orm_error_code::cannot_start_a_transaction_within_a_transaction));
this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
this->on_open_internal(this->currentTransaction->get_db());
}
}
auto db = this->currentTransaction->get_db();
this->impl.begin_transaction(db);
}
void commit() {
if(!this->inMemory){
if(!this->currentTransaction) throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction));
}
auto db = this->currentTransaction->get_db();
this->impl.commit(db);
if(!this->inMemory && !this->isOpenedForever){
this->currentTransaction = nullptr;
}
}
void rollback() {
if(!this->inMemory){
if(!this->currentTransaction) throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction));
}
auto db = this->currentTransaction->get_db();
this->impl.rollback(db);
if(!this->inMemory && !this->isOpenedForever){
this->currentTransaction = nullptr;
}
}
std::string current_timestamp() {
auto connection = this->get_or_create_connection();
return this->impl.current_timestamp(connection->get_db());
}
protected:
#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()));
}
}
bool foreign_keys(sqlite3 *db) {
std::string query = "PRAGMA foreign_keys";
auto res = 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<bool>().extract(argv[0]);
}
return 0;
}, &res, nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()));
}
return res;
}
#endif
public:
#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 connection = this->get_or_create_connection();
return sqlite3_db_release_memory(connection->get_db());
}
#endif
/**
* 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 connection = this->get_or_create_connection();
return this->impl.table_exists(tableName, connection->get_db());
}
/**
* 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<std::string> table_names() {
auto connection = this->get_or_create_connection();
std::vector<std::string> tableNames;
std::string sql = "SELECT name FROM sqlite_master WHERE type='table'";
using Data = std::vector<std::string>;
int res = sqlite3_exec(connection->get_db(), sql.c_str(),
[] (void *data, int argc, char **argv, char ** /*columnName*/) -> int {
auto& tableNames = *(Data*)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(connection->get_db()), get_sqlite_error_category()));
}
return tableNames;
}
void open_forever() {
this->isOpenedForever = true;
if(!this->currentTransaction){
this->currentTransaction = std::make_shared<internal::database_connection>(this->filename);
this->on_open_internal(this->currentTransaction->get_db());
}
}
using pragma_type = pragma_t<self>;
friend pragma_type;
public:
pragma_type pragma;
limit_accesor<self> limit;
};
template<class T>
struct is_storage : std::false_type {};
template<class ...Ts>
struct is_storage<storage_t<Ts...>> : std::true_type {};
}
template<class ...Ts>
internal::storage_t<Ts...> make_storage(const std::string &filename, Ts ...tables) {
return {filename, internal::storage_impl<Ts...>(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)