discord-bot/libs/sqlite_orm-1.8.2/dev/table.h

311 lines
14 KiB
C++

#pragma once
#include <string> // std::string
#include <type_traits> // std::remove_reference, std::is_same, std::decay
#include <vector> // std::vector
#include <tuple> // std::tuple_size, std::tuple_element
#include <utility> // std::forward, std::move
#include "functional/cxx_universal.h"
#include "functional/cxx_type_traits_polyfill.h"
#include "functional/cxx_functional_polyfill.h"
#include "functional/static_magic.h"
#include "functional/mpl.h"
#include "functional/index_sequence_util.h"
#include "tuple_helper/tuple_filter.h"
#include "tuple_helper/tuple_traits.h"
#include "tuple_helper/tuple_iteration.h"
#include "member_traits/member_traits.h"
#include "typed_comparator.h"
#include "type_traits.h"
#include "constraints.h"
#include "table_info.h"
#include "column.h"
namespace sqlite_orm {
namespace internal {
struct basic_table {
/**
* Table name.
*/
std::string name;
};
/**
* Table definition.
*/
template<class O, bool WithoutRowId, class... Cs>
struct table_t : basic_table {
using object_type = O;
using elements_type = std::tuple<Cs...>;
static constexpr bool is_without_rowid_v = WithoutRowId;
using is_without_rowid = polyfill::bool_constant<is_without_rowid_v>;
elements_type elements;
#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
table_t(std::string name_, elements_type elements_) :
basic_table{std::move(name_)}, elements{std::move(elements_)} {}
#endif
table_t<O, true, Cs...> without_rowid() const {
return {this->name, this->elements};
}
/**
* Returns foreign keys count in table definition
*/
constexpr int foreign_keys_count() const {
#if SQLITE_VERSION_NUMBER >= 3006019
using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>;
return int(fk_index_sequence::size());
#else
return 0;
#endif
}
/**
* Function used to get field value from object by mapped member pointer/setter/getter.
*
* For a setter the corresponding getter has to be searched,
* so the method returns a pointer to the field as returned by the found getter.
* Otherwise the method invokes the member pointer and returns its result.
*/
template<class M, satisfies_not<is_setter, M> = true>
decltype(auto) object_field_value(const object_type& object, M memberPointer) const {
return polyfill::invoke(memberPointer, object);
}
template<class M, satisfies<is_setter, M> = true>
const member_field_type_t<M>* object_field_value(const object_type& object, M memberPointer) const {
using field_type = member_field_type_t<M>;
const field_type* res = nullptr;
iterate_tuple(this->elements,
col_index_sequence_with_field_type<elements_type, field_type>{},
call_as_template_base<column_field>([&res, &memberPointer, &object](const auto& column) {
if(compare_any(column.setter, memberPointer)) {
res = &polyfill::invoke(column.member_pointer, object);
}
}));
return res;
}
const basic_generated_always::storage_type*
find_column_generated_storage_type(const std::string& name) const {
const basic_generated_always::storage_type* result = nullptr;
#if SQLITE_VERSION_NUMBER >= 3031000
iterate_tuple(this->elements,
col_index_sequence_with<elements_type, is_generated_always>{},
[&result, &name](auto& column) {
if(column.name != name) {
return;
}
using generated_op_index_sequence =
filter_tuple_sequence_t<std::remove_const_t<decltype(column.constraints)>,
is_generated_always>;
constexpr size_t opIndex = first_index_sequence_value(generated_op_index_sequence{});
result = &get<opIndex>(column.constraints).storage;
});
#else
(void)name;
#endif
return result;
}
template<class G, class S>
bool exists_in_composite_primary_key(const column_field<G, S>& column) const {
bool res = false;
this->for_each_primary_key([&column, &res](auto& primaryKey) {
using colrefs_tuple = decltype(primaryKey.columns);
using same_type_index_sequence =
filter_tuple_sequence_t<colrefs_tuple,
check_if_is_type<member_field_type_t<G>>::template fn,
member_field_type_t>;
iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) {
if(compare_any(memberPointer, column.member_pointer) ||
compare_any(memberPointer, column.setter)) {
res = true;
}
});
});
return res;
}
/**
* Call passed lambda with all defined primary keys.
*/
template<class L>
void for_each_primary_key(L&& lambda) const {
using pk_index_sequence = filter_tuple_sequence_t<elements_type, is_primary_key>;
iterate_tuple(this->elements, pk_index_sequence{}, lambda);
}
std::vector<std::string> composite_key_columns_names() const {
std::vector<std::string> res;
this->for_each_primary_key([this, &res](auto& primaryKey) {
res = this->composite_key_columns_names(primaryKey);
});
return res;
}
std::vector<std::string> primary_key_column_names() const {
using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>;
if(pkcol_index_sequence::size() > 0) {
return create_from_tuple<std::vector<std::string>>(this->elements,
pkcol_index_sequence{},
&column_identifier::name);
} else {
return this->composite_key_columns_names();
}
}
template<class L>
void for_each_primary_key_column(L&& lambda) const {
iterate_tuple(this->elements,
col_index_sequence_with<elements_type, is_primary_key>{},
call_as_template_base<column_field>([&lambda](const auto& column) {
lambda(column.member_pointer);
}));
this->for_each_primary_key([&lambda](auto& primaryKey) {
iterate_tuple(primaryKey.columns, lambda);
});
}
template<class... Args>
std::vector<std::string> composite_key_columns_names(const primary_key_t<Args...>& primaryKey) const {
return create_from_tuple<std::vector<std::string>>(primaryKey.columns,
[this, empty = std::string{}](auto& memberPointer) {
if(const std::string* columnName =
this->find_column_name(memberPointer)) {
return *columnName;
} else {
return empty;
}
});
}
/**
* Searches column name by class member pointer passed as the first argument.
* @return column name or empty string if nothing found.
*/
template<class M, satisfies<std::is_member_pointer, M> = true>
const std::string* find_column_name(M m) const {
const std::string* res = nullptr;
using field_type = member_field_type_t<M>;
iterate_tuple(this->elements,
col_index_sequence_with_field_type<elements_type, field_type>{},
[&res, m](auto& c) {
if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) {
res = &c.name;
}
});
return res;
}
/**
* Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints.
*/
constexpr int non_generated_columns_count() const {
#if SQLITE_VERSION_NUMBER >= 3031000
using non_generated_col_index_sequence =
col_index_sequence_excluding<elements_type, is_generated_always>;
return int(non_generated_col_index_sequence::size());
#else
return this->count_columns_amount();
#endif
}
/**
* Counts and returns amount of columns. Skips constraints.
*/
constexpr int count_columns_amount() const {
using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>;
return int(col_index_sequence::size());
}
/**
* Call passed lambda with all defined foreign keys.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
*/
template<class L>
void for_each_foreign_key(L&& lambda) const {
using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>;
iterate_tuple(this->elements, fk_index_sequence{}, lambda);
}
template<class Target, class L>
void for_each_foreign_key_to(L&& lambda) const {
using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>;
using filtered_index_sequence = filter_tuple_sequence_t<elements_type,
check_if_is_type<Target>::template fn,
target_type_t,
fk_index_sequence>;
iterate_tuple(this->elements, filtered_index_sequence{}, lambda);
}
/**
* Call passed lambda with all defined columns.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
*/
template<class L>
void for_each_column(L&& lambda) const {
using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>;
iterate_tuple(this->elements, col_index_sequence{}, lambda);
}
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
*/
template<template<class...> class OpTraitFn, class L>
void for_each_column_excluding(L&& lambda) const {
iterate_tuple(this->elements, col_index_sequence_excluding<elements_type, OpTraitFn>{}, lambda);
}
/**
* Call passed lambda with columns not having the specified constraint trait `OpTrait`.
* @param lambda Lambda called for each column.
*/
template<class OpTraitFnCls, class L, satisfies<mpl::is_metafunction_class, OpTraitFnCls> = true>
void for_each_column_excluding(L&& lambda) const {
this->for_each_column_excluding<OpTraitFnCls::template fn>(lambda);
}
std::vector<table_xinfo> get_table_info() const;
};
template<class T>
struct is_table : std::false_type {};
template<class O, bool W, class... Cs>
struct is_table<table_t<O, W, Cs...>> : std::true_type {};
}
/**
* Factory function for a table definition.
*
* The mapped object type is determined implicitly from the first column definition.
*/
template<class... Cs, class T = typename std::tuple_element_t<0, std::tuple<Cs...>>::object_type>
internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) {
SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(
return {std::move(name), std::make_tuple<Cs...>(std::forward<Cs>(args)...)});
}
/**
* Factory function for a table definition.
*
* The mapped object type is explicitly specified.
*/
template<class T, class... Cs>
internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) {
SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(
return {std::move(name), std::make_tuple<Cs...>(std::forward<Cs>(args)...)});
}
}