Compare commits

..

No commits in common. "fa7ff832c974c36289122a6891691ff90e338cd7" and "31e9c7e9ac18789ec8a65369976a782fb4a5af90" have entirely different histories.

22 changed files with 111 additions and 499 deletions

43
Todo.md
View File

@ -3,45 +3,4 @@
- Add is_valid() method to connection & connection_impl - Add is_valid() method to connection & connection_impl
- Read in entity fields - Read in entity fields
- Add special handling for update in backends - Add special handling for update in backends
- Add ODBC/SQL Server backend - Add ODBC/SQL Server backend
Fetch eager strategies
======================
ONE TO ONE/MANY
*person* *address*
- has one address - belongs to person
=> join "address" on "person.id" == "address.person_id"
*address* *person*
- belongs to person - has one address
=> join "person" on "address.person_id" == "person.id"
*book* *author*
- belongs to author - has many books
- => join "author" on "book.author_id" == "author.id"
HAS MANY TO ONE (WITHOUT RELATION TABLE)
*author* *book*
- has many books - belongs to author
- => join "book" on "author.id" == "book.author_id"
if "has many" type has primary key & field "author_id"
if table name belongs to entity template type?
HAS MANY TO MANY (WITHOUT RELATION TABLE)
*student* *student_course* *course*
- has many courses - belongs to student
- belongs to course - has many students
=> join "student_course" on "student.id" == "student_course.student_id"
join "student_course" on "course.id" == "student_course.course_id"
if has many type hasn't primary key (is relation table)

View File

@ -33,7 +33,7 @@ public:
LIKE LIKE
}; };
virtual std::string evaluate(const dialect &dialect, query_context &query) const = 0; virtual std::string evaluate(dialect &dialect, query_context &query) const = 0;
static std::unordered_map<operand_t, std::string> operands; static std::unordered_map<operand_t, std::string> operands;
}; };

View File

@ -21,9 +21,9 @@ struct column
column(const char *name); // NOLINT(*-explicit-constructor) column(const char *name); // NOLINT(*-explicit-constructor)
column(std::string name); // NOLINT(*-explicit-constructor) column(std::string name); // NOLINT(*-explicit-constructor)
column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor) column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor)
column(std::string table_name, std::string name, std::string as = ""); column(std::string table_name, std::string name, std::string as);
column(std::string table_name, const char* name, std::string as = ""); column(std::string table_name, const char* name, std::string as);
column(struct table &t, const char* name, std::string as = ""); column(struct table &t, const char* name, std::string as);
[[nodiscard]] bool equals(const column &x) const; [[nodiscard]] bool equals(const column &x) const;

View File

@ -42,11 +42,9 @@ public:
template<class Pointer> template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
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*/) {} void on_has_many(const char *, ContainerType &, 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: private:
data_type_t type_{}; data_type_t type_{};
@ -94,11 +92,9 @@ public:
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
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*/) {} void on_has_many(const char *, ContainerType &, 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: private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti); std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);

View File

@ -86,7 +86,7 @@ public:
} }
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &attr) 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_) { if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
return; return;
@ -101,15 +101,10 @@ public:
matador::utils::access::process(*this, obj); matador::utils::access::process(*this, obj);
table_name_stack_.pop(); table_name_stack_.pop();
} }
template<class ContainerType> template<class ContainerType>
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) void on_has_many(const char *id, ContainerType &c, 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: private:

View File

@ -37,7 +37,7 @@ public:
placeholder value; placeholder value;
std::string evaluate(const dialect &d, query_context &query) const override; std::string evaluate(dialect &d, query_context &query) const override;
}; };
template<class T> template<class T>
@ -54,7 +54,7 @@ public:
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value); return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value);
@ -74,7 +74,7 @@ public:
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " '" + value + "'"; return d.prepare_identifier(field_) + " " + operand + " '" + value + "'";
@ -95,7 +95,7 @@ public:
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_); return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_);
} }
@ -114,7 +114,7 @@ public:
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_); return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_);
} }
@ -156,7 +156,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override { std::string evaluate(dialect &d, query_context &query) const override {
auto count = size(); auto count = size();
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
@ -226,7 +226,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override; std::string evaluate(dialect &d, query_context &query) const override;
private: private:
query_context &query_; query_context &query_;
@ -261,7 +261,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition BETWEEN part of the query * @return A condition BETWEEN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override { std::string evaluate(dialect &d, query_context &query) const override {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second); return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second);
@ -303,7 +303,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
// ensure the numbering order for host vars // ensure the numbering order for host vars
auto cl = left.evaluate(d, query); auto cl = left.evaluate(d, query);
@ -346,7 +346,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
return operand + " (" + cond.evaluate(d) + ")"; return operand + " (" + cond.evaluate(d) + ")";
} }
@ -369,7 +369,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(dialect &d, query_context &query) const override
{ {
return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_); return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_);
} }

View File

@ -2,7 +2,6 @@
#define QUERY_ENTITY_QUERY_BUILDER_HPP #define QUERY_ENTITY_QUERY_BUILDER_HPP
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/query_context.hpp" #include "matador/sql/query_context.hpp"
#include "matador/sql/query.hpp" #include "matador/sql/query.hpp"
#include "matador/sql/query_intermediates.hpp" #include "matador/sql/query_intermediates.hpp"
@ -11,19 +10,6 @@
namespace matador::sql { 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 > template < typename PrimaryKeyType >
class entity_query_builder class entity_query_builder
{ {
@ -32,172 +18,95 @@ public:
: pk_(pk) : pk_(pk)
, schema_(scm) {} , schema_(scm) {}
// determine pk
// collect eager relations for joins
template<class EntityType> template<class EntityType>
std::optional<entity_query_data> build() { std::optional<query_context> build(connection &db) {
EntityType obj;
matador::utils::access::process(*this, obj);
const auto info = schema_.info<EntityType>(); const auto info = schema_.info<EntityType>();
if (!info) { if (!info) {
return std::nullopt; return std::nullopt;
} }
table_info_stack_.push(info.value()); columns_.clear();
entity_query_data_ = { info->name }; table_name_ = info.value().name;
EntityType obj; query q(db, schema_);
matador::utils::access::process(*this, obj); return q.select(column_generator::generate<EntityType>(schema_)).from({table_name_}).build();
// auto from_intermediate = q.select<EntityType>().from({"t"});
return std::move(entity_query_data_); // return {};
} }
template < class V > 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) 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)
{ {
push(id); auto b = std::is_integral_v<PrimaryKeyType>;
if (is_root_entity() && std::is_integral_v<PrimaryKeyType>) { std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
entity_query_data_.where_clause = make_condition(column{table_info_stack_.top().name, id, ""} == pk_); columns_.emplace_back(table_name_, id, "");
}
} }
void on_primary_key(const char *id, std::string &, size_t) void on_primary_key(const char *id, std::string &, size_t)
{ {
push(id); auto b = std::is_same_v<PrimaryKeyType, std::string>;
if (!is_root_entity()) { std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
auto b = std::is_same_v<PrimaryKeyType, std::string>; columns_.emplace_back(table_name_, id, "");
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
}
} }
void on_revision(const char *id, unsigned long long &/*rev*/) void on_revision(const char *id, unsigned long long &/*rev*/)
{ {
push(id); columns_.emplace_back(table_name_, id, "");
} }
template<typename Type> template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{ {
push(id); columns_.emplace_back(table_name_, id, "");
} }
template<class Pointer> template<class Pointer>
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr) void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename Pointer::value_type>(); column col{id};
if (!info) {
return;
}
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(); // collect join information (determine primary key of foreign entity)
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> template<class Pointer>
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr) void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename Pointer::value_type>(); auto info = schema_.info<typename Pointer::value_type>();
if (!info) { if (info) {
return; column lcol(table_name_, id, "");
// column rcol(info.value())
} }
table_info_stack_.push(info.value()); // collect join information
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> template<class ContainerType>
void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename ContainerType::value_type::value_type>(); // collect join information
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> template<class ContainerType>
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) void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { on_has_many(id, c, "", "", attr);
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: private:
PrimaryKeyType pk_; PrimaryKeyType pk_;
std::stack<table_info> table_info_stack_; std::vector<column> columns_;
const schema &schema_; const schema &schema_;
entity_query_data entity_query_data_; std::string table_name_;
int column_index{0};
}; };
} }

View File

@ -1,38 +0,0 @@
#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

View File

@ -161,10 +161,6 @@ public:
{ {
return where_clause(std::make_unique<Condition>(std::move(cond))); 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_group_by_intermediate group_by(const column &col);
query_order_by_intermediate order_by(const column &col); query_order_by_intermediate order_by(const column &col);
@ -182,10 +178,6 @@ public:
{ {
return on_clause(std::make_unique<Condition>(std::move(cond))); 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: private:
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond); query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);

View File

@ -41,11 +41,9 @@ public:
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
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*/) {} void on_has_many(const char *, ContainerType &, 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: private:
size_t column_index_{}; size_t column_index_{};

View File

@ -23,7 +23,6 @@ class schema
{ {
public: public:
using repository = std::unordered_map<std::type_index, table_info>; 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 iterator = repository::iterator;
using const_iterator = repository::const_iterator; using const_iterator = repository::const_iterator;
@ -51,7 +50,6 @@ public:
} }
[[nodiscard]] std::optional<table_info> info(std::type_index ti) const; [[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> template<typename Type>
[[nodiscard]] std::pair<std::string, std::string> reference() const [[nodiscard]] std::pair<std::string, std::string> reference() const
@ -79,7 +77,6 @@ public:
private: private:
std::string name_; std::string name_;
repository repository_; repository repository_;
repository_by_name repository_by_name_;
}; };
} }

View File

@ -13,100 +13,6 @@ namespace matador::sql {
class dialect; 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 class session
{ {
public: public:
@ -138,9 +44,6 @@ public:
return {}; return {};
} }
auto columns = sql::column_generator::generate<Type>(*schema_);
// auto joins = sql
c->query(*schema_).select({}).from(info->name); c->query(*schema_).select({}).from(info->name);
// build pk where condition // build pk where condition
// - check if type has pk // - check if type has pk

View File

@ -53,11 +53,9 @@ public:
values_.emplace_back(fk_value_extractor_.extract(*x)); values_.emplace_back(fk_value_extractor_.extract(*x));
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
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*/) {} void on_has_many(const char *, ContainerType &, 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: private:
template<typename Type> template<typename Type>

View File

@ -64,19 +64,24 @@ void belongs_to(Operator &op, const char *id, Type &value, const foreign_attribu
op.on_belongs_to(id, value, attr); op.on_belongs_to(id, value, attr);
} }
template<class Operator, class Type, template<class ...> class ContainerType> //template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, ContainerType<Type> &container, const char *join_column, const foreign_attributes &attr) { //void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const foreign_attributes &attr) {
op.on_has_many(container, join_column, attr); // op.on_has_many(id, container, attr);
} //}
template<class Operator, class Type, template<class ...> class ContainerType> 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) { void has_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) {
op.on_has_many_to_many(id, container, join_column, inverse_join_column, 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, 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> template<class Operator, class Type, template<class ...> class ContainerType>
void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) { 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_to_many(id, container, attr); op.on_has_many(id, container, left_column, right_column, attr);
} }
} }

View File

@ -91,7 +91,6 @@ set(SQL_HEADER
../include/matador/sql/field.hpp ../include/matador/sql/field.hpp
../include/matador/sql/entity_query_builder.hpp ../include/matador/sql/entity_query_builder.hpp
../include/matador/sql/table_definition.hpp ../include/matador/sql/table_definition.hpp
../include/matador/sql/has_many_to_many_relation.hpp
) )
set(QUERY_SOURCES set(QUERY_SOURCES

View File

@ -6,7 +6,7 @@ condition<column, placeholder, std::enable_if<true>::type>::condition(const colu
: basic_column_condition(fld, op), value(val) : basic_column_condition(fld, op), value(val)
{} {}
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(const dialect &d, query_context &query) const std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d, query_context &query) const
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars); return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars);
@ -16,7 +16,7 @@ condition<column, query_context>::condition(column col, basic_condition::operand
: basic_column_condition(std::move(col), op), query_(q) : basic_column_condition(std::move(col), op), query_(q)
{} {}
std::string condition<column, query_context>::evaluate(const dialect &d, query_context &query) const std::string condition<column, query_context>::evaluate(dialect &d, query_context &query) const
{ {
std::string result(d.prepare_identifier(field_) + " " + operand + " ("); std::string result(d.prepare_identifier(field_) + " " + operand + " (");
result += (")"); result += (")");

View File

@ -37,7 +37,7 @@ std::string dialect::prepare_identifier_string(const std::string &col) const
for (auto &part : parts) { for (auto &part : parts) {
escape_quotes_in_identifier(part); escape_quotes_in_identifier(part);
// quote_identifier(part); quote_identifier(part);
} }
return utils::join(parts, "."); return utils::join(parts, ".");
} }

View File

@ -15,10 +15,7 @@ std::string schema::name() const
const table_info& schema::attach(const std::type_index ti, const table_info& table) const table_info& schema::attach(const std::type_index ti, const table_info& table)
{ {
return repository_.try_emplace(ti, table).first->second;
auto &ref = repository_.try_emplace(ti, table).first->second;
repository_by_name_.try_emplace(ref.name, std::ref(ref));
return ref;
} }
std::optional<table_info> schema::info(std::type_index ti) const std::optional<table_info> schema::info(std::type_index ti) const
@ -30,15 +27,6 @@ std::optional<table_info> schema::info(std::type_index ti) const
return it->second; return it->second;
} }
std::optional<table_info> schema::info(const std::string &name) const
{
const auto it = repository_by_name_.find(name);
if (it == repository_by_name_.end()) {
return std::nullopt;
}
return it->second;
}
std::pair<std::string, std::string> schema::reference(const std::type_index &ti) const std::pair<std::string, std::string> schema::reference(const std::type_index &ti) const
{ {
const auto it = repository_.find(ti); const auto it = repository_.find(ti);

View File

@ -1,123 +1,33 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <matador/sql/connection.hpp> #include <matador/sql/connection.hpp>
#include <matador/sql/dialect.hpp>
#include <matador/sql/entity_query_builder.hpp> #include <matador/sql/entity_query_builder.hpp>
#include "models/author.hpp" #include "models/author.hpp"
#include "models/book.hpp" #include "models/book.hpp"
#include "models/recipe.hpp"
#include "models/order.hpp" struct schiff {
std::string id;
template<typename Operator>
void process(Operator &op)
{
namespace field = matador::utils::access;
field::primary_key(op, "id", id, 255);
}
};
using namespace matador::sql; using namespace matador::sql;
TEST_CASE("Create sql query data for entity with eager belongs to", "[query][entity][builder]") { TEST_CASE("Create sql query for entity", "[query][entity][builder]") {
using namespace matador::test; connection noop("noop://noop.db");
connection db("noop://noop.db");
schema scm("noop"); schema scm("noop");
scm.attach<author>("authors"); scm.attach<matador::test::author>("authors");
scm.attach<book>("books"); scm.attach<matador::test::book>("books");
entity_query_builder eqb(17, scm); entity_query_builder eqb(17, scm);
auto data = eqb.build<book>(); auto context = eqb.build<matador::test::book>(noop);
std::cout << "SQL: " << context.value().sql << "\n";
REQUIRE(data.has_value()); context = eqb.build<schiff>(noop);
REQUIRE(data->root_table_name == "books");
REQUIRE(data->joins.size() == 1);
REQUIRE(data->columns.size() == 9);
REQUIRE(data->where_clause);
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "books", "books.author_id = authors.id"}
};
query_context qc;
size_t index{0};
for (const auto &jd : data->joins) {
REQUIRE(jd.join_table.name == expected_join_data[index].first);
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
++index;
}
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == "books.id = 17");
auto &jd = data->joins.front();
auto context = db.query(scm)
.select(data->columns)
.from(data->root_table_name)
.join_left(jd.join_table).on(std::move(jd.condition))
.where(std::move(data->where_clause))
.build();
}
TEST_CASE("Create sql query data for entity with eager has many belongs to", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<product>("products");
scm.attach<order_details>("order_details");
scm.attach<order>("orders");
entity_query_builder eqb(17, scm);
auto data = eqb.build<order>();
REQUIRE(data.has_value());
REQUIRE(data->root_table_name == "orders");
REQUIRE(data->joins.size() == 1);
REQUIRE(data->columns.size() == 15);
REQUIRE(data->where_clause);
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "orders", "orders.order_id = order_details.order_id"}
};
query_context qc;
size_t index{0};
for (const auto &jd : data->joins) {
REQUIRE(jd.join_table.name == expected_join_data[index].first);
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
++index;
}
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == "orders.order_id = 17");
}
TEST_CASE("Create sql query data for entity with eager many to many", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<recipe>("recipes");
scm.attach<ingredient>("ingredients");
scm.attach<recipe_ingredient>("recipe_ingredients");
entity_query_builder eqb(17, scm);
auto data = eqb.build<ingredient>();
REQUIRE(data.has_value());
REQUIRE(data->root_table_name == "ingredients");
REQUIRE(data->joins.size() == 2);
REQUIRE(data->columns.size() == 4);
REQUIRE(data->where_clause);
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "ingredients", "ingredients.id = recipe_ingredients.ingredient_id"},
{ "recipes", "recipes.id = recipe_ingredients.recipe_id"}
};
query_context qc;
size_t index{0};
for (const auto &jd : data->joins) {
REQUIRE(jd.join_table.name == expected_join_data[index].first);
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
++index;
}
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == "ingredients.id = 17");
} }

View File

@ -3,14 +3,10 @@
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/sql/entity.hpp"
#include <string> #include <string>
namespace matador::test { namespace matador::test {
struct book;
struct author struct author
{ {
unsigned long id{}; unsigned long id{};
@ -31,7 +27,7 @@ struct author
field::attribute(op, "date_of_birth", date_of_birth, 31); field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute(op, "year_of_birth", year_of_birth); field::attribute(op, "year_of_birth", year_of_birth);
field::attribute(op, "distinguished", distinguished); field::attribute(op, "distinguished", distinguished);
field::has_many(op, books, "author_id", utils::fetch_type::LAZY); field::has_many(op, "books", books, "author_id", "id", utils::fetch_type::LAZY);
} }
}; };

View File

@ -42,7 +42,7 @@ struct order
field::attribute(op, "ship_region", ship_region, 255); field::attribute(op, "ship_region", ship_region, 255);
field::attribute(op, "ship_postal_code", ship_postal_code, 255); field::attribute(op, "ship_postal_code", ship_postal_code, 255);
field::attribute(op, "ship_country", ship_country, 255); field::attribute(op, "ship_country", ship_country, 255);
field::has_many(op, order_details_, "order_id", utils::fetch_type::EAGER); field::has_many(op, "order_details", order_details_, utils::fetch_type::EAGER);
} }
}; };

View File

@ -6,7 +6,6 @@
#include "matador/utils/foreign_attributes.hpp" #include "matador/utils/foreign_attributes.hpp"
#include "matador/sql/entity.hpp" #include "matador/sql/entity.hpp"
#include "matador/sql/has_many_to_many_relation.hpp"
#include <string> #include <string>
@ -24,7 +23,7 @@ struct ingredient
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, 255);
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER); field::has_many(op, "recipes", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER);
} }
}; };
@ -39,15 +38,21 @@ struct recipe
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, 255);
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::fetch_type::LAZY); field::has_many(op, "ingredients", ingredients, "recipe_id", "ingredient_id", utils::fetch_type::EAGER);
} }
}; };
class recipe_ingredient : public sql::has_many_to_many_relation<recipe, ingredient> struct recipe_ingredient
{ {
public: sql::entity<recipe> recipe_;
recipe_ingredient() sql::entity<ingredient> ingredient_;
: has_many_to_many_relation("recipe_id", "ingredient_id") {}
template<class Operator>
void process(Operator &op) {
namespace field = matador::utils::access;
field::belongs_to(op, "recipe_id", recipe_, utils::default_foreign_attributes);
field::belongs_to(op, "ingredient_id", ingredient_, utils::default_foreign_attributes);
}
}; };
} }