342 lines
13 KiB
C++
342 lines
13 KiB
C++
#ifndef MATADOR_GENERATOR_HPP
|
|
#define MATADOR_GENERATOR_HPP
|
|
|
|
#include <matador/utils/placeholder.hpp>
|
|
#include <matador/utils/primary_key_attribute.hpp>
|
|
#include <matador/utils/field_attributes.hpp>
|
|
#include <matador/utils/foreign_attributes.hpp>
|
|
|
|
#include "matador/object/repository.hpp"
|
|
|
|
#include "matador/query/fk_value_extractor.hpp"
|
|
#include "matador/query/internal/column_value_pair.hpp"
|
|
|
|
#include "matador/query/table.hpp"
|
|
|
|
#include <vector>
|
|
|
|
namespace matador::query::generator {
|
|
enum class column_generator_options {
|
|
None = 0,
|
|
GenerateAlias = 1 << 0,
|
|
ForceLazy = 1 << 1
|
|
};
|
|
|
|
// template<typename EnumType>
|
|
// class enum_flags {
|
|
// public:
|
|
// explicit enum_flags(EnumType value) : value_(value) {}
|
|
//
|
|
// operator EnumType() const { return value_; }
|
|
//
|
|
// enum_flags operator|=(EnumType other) { value_ |= other; return *this; }
|
|
// enum_flags operator&=(EnumType other) { value_ |= other; return *this; })
|
|
// private:
|
|
// EnumType value_;
|
|
// };
|
|
|
|
inline column_generator_options operator|(column_generator_options a, column_generator_options b) { return static_cast<column_generator_options>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); }
|
|
inline column_generator_options operator&(column_generator_options a, column_generator_options b) { return static_cast<column_generator_options>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); }
|
|
inline column_generator_options& operator|= (column_generator_options& a, column_generator_options b) { return reinterpret_cast<column_generator_options&>(reinterpret_cast<int&>(a) |= static_cast<int>(b)); }
|
|
inline column_generator_options& operator&= (column_generator_options& a, column_generator_options b) { return reinterpret_cast<column_generator_options&>(reinterpret_cast<int&>(a) &= static_cast<int>(b)); }
|
|
|
|
inline bool is_column_generator_option_set(const column_generator_options source, const column_generator_options needle) { return static_cast<int>(source & needle) > 0; }
|
|
|
|
constexpr auto default_column_generator_options = column_generator_options::ForceLazy;
|
|
|
|
class column_generator {
|
|
public:
|
|
explicit column_generator(const std::string &table_name = "",
|
|
column_generator_options options = default_column_generator_options);
|
|
explicit column_generator(const object::repository &repo,
|
|
const std::string &table_name = "",
|
|
column_generator_options options = default_column_generator_options);
|
|
|
|
template< class Type >
|
|
std::vector<column> generate() {
|
|
Type obj;
|
|
return generate(obj);
|
|
}
|
|
|
|
template< class Type >
|
|
std::vector<column> generate(const Type &obj) {
|
|
result_.clear();
|
|
access::process(*this, obj);
|
|
|
|
return result_;
|
|
}
|
|
|
|
template < class V >
|
|
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
|
push(id);
|
|
}
|
|
void on_revision(const char *id, uint64_t &/*rev*/);
|
|
|
|
template<typename Type>
|
|
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {
|
|
push(id);
|
|
}
|
|
|
|
template<class Pointer>
|
|
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &attr) {
|
|
on_foreign_key(id, x, attr);
|
|
}
|
|
template<class Pointer>
|
|
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &attr) {
|
|
on_foreign_key(id, x, attr);
|
|
}
|
|
|
|
template<class ContainerType>
|
|
void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &attr) {
|
|
if (attr.fetch() == utils::fetch_type::Lazy || is_column_generator_option_set(options_, column_generator_options::ForceLazy)) {
|
|
return;
|
|
}
|
|
if (!repo_) {
|
|
return;
|
|
}
|
|
const auto info = repo_->get().info<typename ContainerType::value_type::value_type>();
|
|
if (!info) {
|
|
return;
|
|
}
|
|
|
|
if (seen_tables.count(info->get().name()) == 0) {
|
|
auto it = seen_tables.insert(info->get().name()).first;
|
|
table_stack_.push(std::make_shared<table>(info.value().get().name()));
|
|
typename ContainerType::value_type::value_type obj;
|
|
access::process(*this, obj);
|
|
table_stack_.pop();
|
|
seen_tables.erase(it);
|
|
}
|
|
}
|
|
|
|
template<class ContainerType>
|
|
void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) {
|
|
if (attr.fetch() == utils::fetch_type::Lazy || is_column_generator_option_set(options_, column_generator_options::ForceLazy)) {
|
|
return;
|
|
}
|
|
push(join_column);
|
|
push(inverse_join_column);
|
|
}
|
|
|
|
template<class ContainerType>
|
|
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
|
|
|
private:
|
|
template<class Pointer>
|
|
void on_foreign_key(const char *id, Pointer &, const utils::foreign_attributes &attr) {
|
|
if (attr.fetch() == utils::fetch_type::Lazy || is_column_generator_option_set(options_, column_generator_options::ForceLazy)) {
|
|
push(id);
|
|
} else {
|
|
if (!repo_) {
|
|
return;
|
|
}
|
|
const auto info = repo_->get().info<typename Pointer::value_type>();
|
|
if (!info) {
|
|
return;
|
|
}
|
|
if (seen_tables.count(info->get().name()) == 0) {
|
|
auto it = seen_tables.insert(info->get().name()).first;
|
|
table_stack_.push(std::make_shared<table>(info.value().get().name()));
|
|
typename Pointer::value_type obj;
|
|
access::process(*this, obj);
|
|
table_stack_.pop();
|
|
seen_tables.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void push(const std::string &column_name);
|
|
|
|
private:
|
|
std::optional<std::reference_wrapper<const object::repository>> repo_;
|
|
std::vector<column> result_;
|
|
std::stack<std::shared_ptr<table>> table_stack_;
|
|
std::unordered_set<std::string> seen_tables;
|
|
int column_index{0};
|
|
column_generator_options options_{false};
|
|
};
|
|
|
|
class placeholder_generator final {
|
|
public:
|
|
template< class Type >
|
|
std::vector<utils::placeholder> generate() {
|
|
Type obj;
|
|
return generate(obj);
|
|
}
|
|
|
|
template< class Type >
|
|
std::vector<utils::placeholder> generate(const Type &obj) {
|
|
result_.clear();
|
|
access::process(*this, obj);
|
|
|
|
return result_;
|
|
}
|
|
|
|
template < class V >
|
|
void on_primary_key(const char * /*id*/, V &/*x*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
|
result_.emplace_back(utils::_);
|
|
}
|
|
void on_revision(const char *id, uint64_t &/*rev*/);
|
|
|
|
template<typename Type>
|
|
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {
|
|
result_.emplace_back(utils::_);
|
|
}
|
|
|
|
template<class Type, template < class ... > class Pointer>
|
|
void on_belongs_to(const char * /*id*/, Pointer<Type> &/*x*/, const utils::foreign_attributes &/*attr*/) {
|
|
result_.emplace_back(utils::_);
|
|
}
|
|
template<class Type, template < class ... > class Pointer>
|
|
void on_has_one(const char * /*id*/, Pointer<Type> &/*x*/, const utils::foreign_attributes &/*attr*/) {
|
|
result_.emplace_back(utils::_);
|
|
}
|
|
template<class ContainerType>
|
|
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class ContainerType>
|
|
static void on_has_many_to_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class ContainerType>
|
|
static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
|
|
|
private:
|
|
std::vector<utils::placeholder> result_;
|
|
};
|
|
enum class column_value_generator_options {
|
|
None = 0,
|
|
ValuesAsPlaceHolder = 1 << 0
|
|
};
|
|
|
|
inline column_value_generator_options operator|(column_value_generator_options a, column_value_generator_options b) { return static_cast<column_value_generator_options>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); }
|
|
inline column_value_generator_options operator&(column_value_generator_options a, column_value_generator_options b) { return static_cast<column_value_generator_options>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); }
|
|
inline column_value_generator_options& operator|= (column_value_generator_options& a, column_value_generator_options b) { return reinterpret_cast<column_value_generator_options&>(reinterpret_cast<int&>(a) |= static_cast<int>(b)); }
|
|
inline column_value_generator_options& operator&= (column_value_generator_options& a, column_value_generator_options b) { return reinterpret_cast<column_value_generator_options&>(reinterpret_cast<int&>(a) &= static_cast<int>(b)); }
|
|
|
|
inline bool is_column_value_generator_option_set(const column_value_generator_options source, const column_value_generator_options needle) { return static_cast<int>(source & needle) > 0; }
|
|
|
|
class column_value_generator final {
|
|
public:
|
|
explicit column_value_generator(column_value_generator_options options = column_value_generator_options::None);
|
|
template< class Type >
|
|
std::vector<internal::column_value_pair> generate() {
|
|
Type obj;
|
|
return generate(obj);
|
|
}
|
|
|
|
template< class Type >
|
|
std::vector<internal::column_value_pair> generate(const Type &obj) {
|
|
result_.clear();
|
|
access::process(*this, obj);
|
|
|
|
return result_;
|
|
}
|
|
|
|
template < class V >
|
|
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
|
push_back(id, x);
|
|
}
|
|
void on_revision(const char *id, uint64_t &/*rev*/);
|
|
|
|
template<typename Type>
|
|
void on_attribute(const char *id, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) {
|
|
push_back(id, x);
|
|
}
|
|
|
|
template<class Type, template < class ... > class Pointer>
|
|
void on_belongs_to(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/) {
|
|
push_back(id, fk_value_extractor_.extract(*x));
|
|
}
|
|
template<class Type, template < class ... > class Pointer>
|
|
void on_has_one(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/) {
|
|
push_back(id, fk_value_extractor_.extract(*x));
|
|
}
|
|
template<class ContainerType>
|
|
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class ContainerType>
|
|
static void on_has_many_to_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class ContainerType>
|
|
static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
|
|
|
template<typename Type>
|
|
void push_back(const char* id, const Type& value) {
|
|
if (is_column_value_generator_option_set(options_, column_value_generator_options::ValuesAsPlaceHolder)) {
|
|
result_.emplace_back(id, utils::_);
|
|
} else {
|
|
result_.emplace_back(id, value);
|
|
}
|
|
}
|
|
|
|
private:
|
|
detail::fk_value_extractor fk_value_extractor_{};
|
|
std::vector<internal::column_value_pair> result_;
|
|
column_value_generator_options options_{column_value_generator_options::None};
|
|
};
|
|
|
|
template<typename Type>
|
|
std::vector<utils::placeholder> placeholders() {
|
|
placeholder_generator generator;
|
|
return generator.generate<Type>();
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<utils::placeholder> placeholders(const Type &obj) {
|
|
placeholder_generator generator;
|
|
return generator.generate(obj);
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<internal::column_value_pair> column_value_pairs(const Type &obj, const column_value_generator_options options = column_value_generator_options::None) {
|
|
column_value_generator generator(options);
|
|
return generator.generate(obj);
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<internal::column_value_pair> column_value_pairs() {
|
|
Type obj;
|
|
return column_value_pairs(obj, column_value_generator_options::ValuesAsPlaceHolder);
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<column> columns(const object::repository &repo,
|
|
const std::string &table_name = "",
|
|
const column_generator_options options = default_column_generator_options) {
|
|
column_generator generator(repo, table_name, options);
|
|
return generator.generate<Type>();
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<column> columns(const object::repository &repo,
|
|
const column_generator_options options) {
|
|
std::string table_name;
|
|
if (const auto result = repo.info<Type>()) {
|
|
table_name = result.value().get().name();
|
|
}
|
|
column_generator generator(repo, table_name, options);
|
|
return generator.generate<Type>();
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<column> columns(const Type &obj,
|
|
const object::repository &repo,
|
|
const std::string &table_name = "",
|
|
const column_generator_options options = default_column_generator_options) {
|
|
column_generator generator(repo, table_name, options);
|
|
return generator.generate(obj);
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<column> columns(const std::string &table_name = "",
|
|
const column_generator_options options = default_column_generator_options) {
|
|
column_generator generator(table_name, options);
|
|
return generator.generate<Type>();
|
|
}
|
|
|
|
template<typename Type>
|
|
std::vector<column> columns(const Type &obj,
|
|
const std::string &table_name = "",
|
|
const column_generator_options options = default_column_generator_options) {
|
|
column_generator generator(table_name, options);
|
|
return generator.generate(obj);
|
|
}
|
|
|
|
}
|
|
#endif //MATADOR_GENERATOR_HPP
|