handle many-to-many relations separately when process entities
This commit is contained in:
@@ -42,9 +42,11 @@ public:
|
||||
template<class Pointer>
|
||||
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
data_type_t type_{};
|
||||
@@ -92,9 +94,11 @@ public:
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
}
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
|
||||
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
|
||||
return;
|
||||
@@ -101,10 +101,15 @@ public:
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_name_stack_.pop();
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
{
|
||||
on_has_many(id, c, "", "", attr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define QUERY_ENTITY_QUERY_BUILDER_HPP
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/query.hpp"
|
||||
#include "matador/sql/query_intermediates.hpp"
|
||||
@@ -10,6 +11,19 @@
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
struct join_data
|
||||
{
|
||||
table join_table;
|
||||
std::unique_ptr<basic_condition> condition;
|
||||
};
|
||||
|
||||
struct entity_query_data {
|
||||
std::string root_table_name;
|
||||
std::vector<column> columns;
|
||||
std::vector<join_data> joins;
|
||||
std::unique_ptr<basic_condition> where_clause;
|
||||
};
|
||||
|
||||
template < typename PrimaryKeyType >
|
||||
class entity_query_builder
|
||||
{
|
||||
@@ -18,95 +32,172 @@ public:
|
||||
: pk_(pk)
|
||||
, schema_(scm) {}
|
||||
|
||||
// determine pk
|
||||
// collect eager relations for joins
|
||||
template<class EntityType>
|
||||
std::optional<query_context> build(connection &db) {
|
||||
EntityType obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
|
||||
std::optional<entity_query_data> build() {
|
||||
const auto info = schema_.info<EntityType>();
|
||||
if (!info) {
|
||||
return std::nullopt;
|
||||
}
|
||||
columns_.clear();
|
||||
table_name_ = info.value().name;
|
||||
query q(db, schema_);
|
||||
return q.select(column_generator::generate<EntityType>(schema_)).from({table_name_}).build();
|
||||
// auto from_intermediate = q.select<EntityType>().from({"t"});
|
||||
// return {};
|
||||
table_info_stack_.push(info.value());
|
||||
entity_query_data_ = { info->name };
|
||||
EntityType obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
|
||||
return std::move(entity_query_data_);
|
||||
}
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
|
||||
{
|
||||
auto b = std::is_integral_v<PrimaryKeyType>;
|
||||
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
push(id);
|
||||
if (is_root_entity() && std::is_integral_v<PrimaryKeyType>) {
|
||||
entity_query_data_.where_clause = make_condition(column{table_info_stack_.top().name, id, ""} == pk_);
|
||||
}
|
||||
}
|
||||
|
||||
void on_primary_key(const char *id, std::string &, size_t)
|
||||
{
|
||||
auto b = std::is_same_v<PrimaryKeyType, std::string>;
|
||||
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
push(id);
|
||||
if (!is_root_entity()) {
|
||||
auto b = std::is_same_v<PrimaryKeyType, std::string>;
|
||||
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void on_revision(const char *id, unsigned long long &/*rev*/)
|
||||
{
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
push(id);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
|
||||
{
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
push(id);
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
column col{id};
|
||||
const auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this , obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
// collect join information (determine primary key of foreign entity)
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
|
||||
} else {
|
||||
push(id);
|
||||
}
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (info) {
|
||||
column lcol(table_name_, id, "");
|
||||
// column rcol(info.value())
|
||||
const auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
// collect join information
|
||||
table_info_stack_.push(info.value());
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
|
||||
} else {
|
||||
push(id);
|
||||
}
|
||||
columns_.emplace_back(table_name_, id, "");
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
|
||||
void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
// collect join information
|
||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
matador::utils::access::process(*this , obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
|
||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column});
|
||||
}
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr)
|
||||
{
|
||||
on_has_many(id, c, "", "", attr);
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
matador::utils::access::process(*this , obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
|
||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column});
|
||||
append_join({info->name, pk->name()}, {id, inverse_join_column});
|
||||
}
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
void push(const std::string &column_name)
|
||||
{
|
||||
char str[4];
|
||||
snprintf(str, 4, "c%02d", ++column_index);
|
||||
entity_query_data_.columns.emplace_back(table_info_stack_.top().name, column_name, str);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_root_entity() const {
|
||||
return table_info_stack_.size() == 1;
|
||||
}
|
||||
|
||||
void append_join(const column &left, const column &right)
|
||||
{
|
||||
entity_query_data_.joins.push_back({
|
||||
{ left.table },
|
||||
make_condition(left == right)
|
||||
});
|
||||
}
|
||||
private:
|
||||
PrimaryKeyType pk_;
|
||||
std::vector<column> columns_;
|
||||
std::stack<table_info> table_info_stack_;
|
||||
const schema &schema_;
|
||||
std::string table_name_;
|
||||
entity_query_data entity_query_data_;
|
||||
int column_index{0};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef QUERY_HAS_MANY_TO_MANY_RELATION_HPP
|
||||
#define QUERY_HAS_MANY_TO_MANY_RELATION_HPP
|
||||
|
||||
#include "matador/sql/entity.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
template < class LocalType, class ForeignType >
|
||||
class has_many_to_many_relation
|
||||
{
|
||||
public:
|
||||
has_many_to_many_relation() = default;
|
||||
has_many_to_many_relation(std::string local_name, std::string remote_name)
|
||||
: local_name_(std::move(local_name))
|
||||
, remote_name_(std::move(remote_name)) {}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::utils::access;
|
||||
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes);
|
||||
field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes);
|
||||
}
|
||||
|
||||
entity<LocalType> local() const { return local_; }
|
||||
entity<LocalType> remote() const { return remote_; }
|
||||
|
||||
private:
|
||||
std::string local_name_;
|
||||
std::string remote_name_;
|
||||
sql::entity<LocalType> local_;
|
||||
sql::entity<ForeignType> remote_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_HAS_MANY_TO_MANY_RELATION_HPP
|
||||
@@ -161,6 +161,10 @@ public:
|
||||
{
|
||||
return where_clause(std::make_unique<Condition>(std::move(cond)));
|
||||
}
|
||||
query_where_intermediate where(std::unique_ptr<basic_condition> &&cond)
|
||||
{
|
||||
return where_clause(std::move(cond));
|
||||
}
|
||||
query_group_by_intermediate group_by(const column &col);
|
||||
query_order_by_intermediate order_by(const column &col);
|
||||
|
||||
@@ -178,6 +182,10 @@ public:
|
||||
{
|
||||
return on_clause(std::make_unique<Condition>(std::move(cond)));
|
||||
}
|
||||
query_on_intermediate on(std::unique_ptr<basic_condition> &&cond)
|
||||
{
|
||||
return on_clause(std::move(cond));
|
||||
}
|
||||
|
||||
private:
|
||||
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
|
||||
|
||||
@@ -41,9 +41,11 @@ public:
|
||||
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
size_t column_index_{};
|
||||
|
||||
@@ -23,6 +23,7 @@ class schema
|
||||
{
|
||||
public:
|
||||
using repository = std::unordered_map<std::type_index, table_info>;
|
||||
using repository_by_name = std::unordered_map<std::string, std::reference_wrapper<table_info>>;
|
||||
using iterator = repository::iterator;
|
||||
using const_iterator = repository::const_iterator;
|
||||
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<table_info> info(std::type_index ti) const;
|
||||
[[nodiscard]] std::optional<table_info> info(const std::string &name) const;
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] std::pair<std::string, std::string> reference() const
|
||||
@@ -77,6 +79,7 @@ public:
|
||||
private:
|
||||
std::string name_;
|
||||
repository repository_;
|
||||
repository_by_name repository_by_name_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,100 @@ namespace matador::sql {
|
||||
|
||||
class dialect;
|
||||
|
||||
struct join_data
|
||||
{
|
||||
table join_table;
|
||||
std::unique_ptr<basic_condition> condition;
|
||||
};
|
||||
|
||||
//class join_collector
|
||||
//{
|
||||
//private:
|
||||
// explicit join_collector(const sql::schema &ts, std::vector<join_data> &joins)
|
||||
// : table_schema_(ts)
|
||||
// , joins_(joins) {}
|
||||
//
|
||||
//public:
|
||||
// template < class Type >
|
||||
// static std::vector<join_data> collect(const sql::schema &ts)
|
||||
// {
|
||||
// const auto info = ts.info<Type>();
|
||||
// if (!info) {
|
||||
// return {};
|
||||
// }
|
||||
// std::vector<join_data> joins;
|
||||
// join_collector collector(ts, joins);
|
||||
// Type obj;
|
||||
// matador::utils::access::process(collector, obj);
|
||||
// return joins;
|
||||
// }
|
||||
//
|
||||
// template < class PrimaryKeyType >
|
||||
// void on_primary_key(const char *id, PrimaryKeyType &, typename std::enable_if<std::is_integral<PrimaryKeyType>::value && !std::is_same<bool, PrimaryKeyType>::value>::type* = 0) {}
|
||||
// void on_primary_key(const char *id, std::string &, size_t) {}
|
||||
// void on_revision(const char *id, unsigned long long &/*rev*/) {}
|
||||
// template<typename Type>
|
||||
// void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
//
|
||||
// template<class Pointer>
|
||||
// void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
// {
|
||||
// if (attr.fetch() == utils::fetch_type::LAZY) {
|
||||
// push(id);
|
||||
// } else {
|
||||
// const auto info = table_schema_.info<typename Pointer::value_type>();
|
||||
// if (!info) {
|
||||
// return;
|
||||
// }
|
||||
// table_name_stack_.push(info.value().name);
|
||||
// typename Pointer::value_type obj;
|
||||
// matador::utils::access::process(*this, obj);
|
||||
// table_name_stack_.pop();
|
||||
// }
|
||||
// }
|
||||
// template<class Pointer>
|
||||
// void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
// {
|
||||
// if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
|
||||
// push(id);
|
||||
// } else {
|
||||
// const auto info = table_schema_.info<typename Pointer::value_type>();
|
||||
// if (!info) {
|
||||
// return;
|
||||
// }
|
||||
// table_name_stack_.push(info.value().name);
|
||||
// typename Pointer::value_type obj;
|
||||
// matador::utils::access::process(*this, obj);
|
||||
// table_name_stack_.pop();
|
||||
// }
|
||||
// }
|
||||
// template<class ContainerType>
|
||||
// void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
|
||||
// {
|
||||
// if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
|
||||
// return;
|
||||
// }
|
||||
// const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
|
||||
// if (!info) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// table_name_stack_.push(info.value().name);
|
||||
// typename ContainerType::value_type::value_type obj;
|
||||
// matador::utils::access::process(*this, obj);
|
||||
// table_name_stack_.pop();
|
||||
// }
|
||||
// template<class ContainerType>
|
||||
// void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
// {
|
||||
// on_has_many(id, c, "", "", attr);
|
||||
// }
|
||||
//
|
||||
//private:
|
||||
// const sql::schema &table_schema_;
|
||||
// std::vector<join_data> &joins_;
|
||||
//};
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
@@ -44,6 +138,9 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
auto columns = sql::column_generator::generate<Type>(*schema_);
|
||||
|
||||
// auto joins = sql
|
||||
c->query(*schema_).select({}).from(info->name);
|
||||
// build pk where condition
|
||||
// - check if type has pk
|
||||
|
||||
@@ -53,9 +53,11 @@ public:
|
||||
values_.emplace_back(fk_value_extractor_.extract(*x));
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
|
||||
Reference in New Issue
Block a user