handle many-to-many relations separately when process entities

This commit is contained in:
2024-04-07 14:45:57 +02:00
parent 30e2e0c221
commit fa7ff832c9
19 changed files with 484 additions and 96 deletions
@@ -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);
+8 -3
View File
@@ -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:
+126 -35
View File
@@ -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);
+4 -2
View File
@@ -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_{};
+3
View File
@@ -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_;
};
}
+97
View File
@@ -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
+4 -2
View File
@@ -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>
+8 -13
View File
@@ -64,24 +64,19 @@ void belongs_to(Operator &op, const char *id, Type &value, const foreign_attribu
op.on_belongs_to(id, value, attr);
}
//template<class Operator, class Type, template<class ...> class ContainerType>
//void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const foreign_attributes &attr) {
// op.on_has_many(id, container, attr);
//}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) {
op.on_has_many(id, container, attr);
void has_many(Operator &op, ContainerType<Type> &container, const char *join_column, const foreign_attributes &attr) {
op.on_has_many(container, join_column, attr);
}
//template<class Operator, class Type, template<class ...> class ContainerType>
//void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const char *left_column, const char *right_column, const foreign_attributes &attr) {
// op.on_has_many(id, container, left_column, right_column, attr);
//}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const char *join_column, const char *inverse_join_column, const foreign_attributes &attr) {
op.on_has_many_to_many(id, container, join_column, inverse_join_column, attr);
}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, ContainerType<Type> &container, const char *left_column, const char *right_column, const foreign_attributes &attr) {
op.on_has_many(id, container, left_column, right_column, attr);
void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) {
op.on_has_many_to_many(id, container, attr);
}
}