delete has many to many progress

This commit is contained in:
sascha 2026-05-28 15:45:46 +02:00
parent 73bd6f641c
commit 68d67b17b7
15 changed files with 417 additions and 244 deletions

View File

@ -7,13 +7,11 @@
#include "matador/query/basic_schema.hpp" #include "matador/query/basic_schema.hpp"
#include "matador/query/error_code.hpp" #include "matador/query/error_code.hpp"
#include "matador/query/execute_step.hpp" #include "matador/query/delete_step.hpp"
#include "matador/query/query_contexts.hpp" #include "matador/query/query_contexts.hpp"
#include "matador/query/query_builder_exception.hpp" #include "matador/query/query_builder_exception.hpp"
#include "matador/query/query_builder_utils.hpp" #include "matador/query/query_builder_utils.hpp"
#include "matador/sql/internal/identifier_statement_binder.hpp"
#include "matador/sql/execute_result.hpp"
#include "matador/sql/statement.hpp" #include "matador/sql/statement.hpp"
#include "matador/utils/error.hpp" #include "matador/utils/error.hpp"
@ -22,61 +20,10 @@
#include "matador/utils/result.hpp" #include "matador/utils/result.hpp"
namespace matador::query { namespace matador::query {
template<typename ObjectType>
class delete_step_object final : public execute_step {
public:
delete_step_object(sql::query_context ctx, const object::object_ptr<ObjectType> &ptr)
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &/*conn*/) override {
id_ = ptr_.primary_key();
return utils::ok<void>();
}
utils::result<void, utils::error> execute(sql::statement &stmt) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
sql::identifier_statement_binder binder(stmt, 0);
binder.bind(id_);
if (const auto result = stmt.execute(); !result.is_ok()) {
return utils::failure(result.err());
}
return utils::ok<void>();
}
utils::result<void, utils::error> finalize(object::object_cache &cache, const resolver_service_ptr& /*resolver_service*/) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
cache.erase<ObjectType>(id_);
ptr_.change_state(object::object_state::Transient);
return utils::ok<void>();
}
private:
object::object_ptr<ObjectType> ptr_;
};
struct delete_context {
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
std::vector<std::unique_ptr<execute_step>> steps_{};
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
};
template<typename ObjectType> template<typename ObjectType>
class delete_step_processor { class delete_step_processor {
public: public:
explicit delete_step_processor(delete_context &ctx) explicit delete_step_processor(query_builder_context &ctx)
: ctx_(ctx) {} : ctx_(ctx) {}
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) { utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
@ -97,8 +44,7 @@ public:
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"}); return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
} }
const auto &info = it->second.node().info(); if (const auto &info = it->second.node().info(); !info.has_primary_key()) {
if (!info.has_primary_key()) {
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"}); return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
} }
@ -177,19 +123,50 @@ public:
} }
template<class ForeignType> template<class ForeignType>
void on_has_many_to_many(const char * /*id*/, void on_has_many_to_many(const char *id,
object::collection<object::object_ptr<ForeignType>> &objects, object::collection<object::object_ptr<ForeignType>> &objects,
const char * /*join_column*/, const char *join_column,
const char * /*inverse_join_column*/, const char *inverse_join_column,
const utils::foreign_attributes &attr) { const utils::foreign_attributes &attr) {
on_many_to_many_objects(objects, attr); if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
return;
}
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
const std::type_index foreign_type{typeid(ForeignType)};
const std::type_index local_type{typeid(ObjectType)};
on_many_to_many_objects<relation_value_type>(
id,
objects,
attr,
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
return {std::string{relation_name}, local_type, foreign_type};
});
} }
template<class ForeignType> template<class ForeignType>
void on_has_many_to_many(const char * /*id*/, void on_has_many_to_many(const char *id,
object::collection<object::object_ptr<ForeignType>> &objects, object::collection<object::object_ptr<ForeignType>> &objects,
const utils::foreign_attributes &attr) { const utils::foreign_attributes &attr) {
on_many_to_many_objects(objects, attr); if (id == nullptr) {
return;
}
object::join_columns_collector collector;
if (auto join_columns = collector.collect<ForeignType>(); join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
return;
}
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
const std::type_index foreign_type{typeid(ForeignType)};
const std::type_index local_type{typeid(ObjectType)};
on_many_to_many_objects<relation_value_type>(
id,
objects,
attr,
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
return {std::string{relation_name}, foreign_type, local_type};
});
} }
private: private:
@ -207,28 +184,52 @@ private:
} }
} }
template<class ForeignType> template<class LocalType, class ForeignType, class RelationKeyFactory>
void on_many_to_many_objects(object::collection<object::object_ptr<ForeignType>> &objects, void on_many_to_many_objects(const char *id,
const utils::foreign_attributes &attr) { object::collection<object::object_ptr<ForeignType>> &objects,
const utils::foreign_attributes &attr,
RelationKeyFactory make_relation_key) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) { if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
return; return;
} }
const auto key = make_relation_key(id);
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
return;
}
const auto it = ctx_.schema_.find(std::string{id});
if (it == ctx_.schema_.end()) {
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
}
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
}
if (const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index()); cit == ctx_.contexts_by_type_.end()) {
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
}
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
std::vector<std::unique_ptr<execute_step>> delete_relation_steps;
delete_step_processor<ForeignType> processor{ctx_}; delete_step_processor<ForeignType> processor{ctx_};
for (auto &obj : objects) { for (auto &obj : objects) {
if (!obj) { if (!obj) {
continue; continue;
} }
auto result = processor.build(obj, true); if (obj.is_persistent()) {
if (!result) { auto result = processor.build(obj, true);
throw query_builder_exception(result.release_error()); if (!result) {
throw query_builder_exception(result.release_error());
}
} }
} }
} }
private: private:
delete_context &ctx_; query_builder_context &ctx_;
object::object_ptr<ObjectType> ptr_; object::object_ptr<ObjectType> ptr_;
}; };
@ -245,7 +246,7 @@ public:
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"}); return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"});
} }
delete_context ctx{schema_, contexts_by_type_}; query_builder_context ctx{schema_, contexts_by_type_};
delete_step_processor<ObjectType> processor{ctx}; delete_step_processor<ObjectType> processor{ctx};
const auto result = processor.build(ptr); const auto result = processor.build(ptr);

View File

@ -0,0 +1,84 @@
#ifndef MATADOR_DELETE_STEP_HPP
#define MATADOR_DELETE_STEP_HPP
#include "matador/query/error_code.hpp"
#include "matador/query/execute_step.hpp"
#include "matador/sql/internal/identifier_statement_binder.hpp"
#include "matador/sql/statement.hpp"
#include "matador/object/object_ptr.hpp"
namespace matador::query {
template<typename ObjectType>
class delete_step_object final : public execute_step {
public:
delete_step_object(sql::query_context ctx, const object::object_ptr<ObjectType> &ptr)
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &/*conn*/) override {
id_ = ptr_.primary_key();
return utils::ok<void>();
}
utils::result<void, utils::error> execute(sql::statement &stmt) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
sql::identifier_statement_binder binder(stmt, 0);
binder.bind(id_);
if (const auto result = stmt.execute(); !result.is_ok()) {
return utils::failure(result.err());
}
return utils::ok<void>();
}
utils::result<void, utils::error> finalize(object::object_cache &cache, const resolver_service_ptr& /*resolver_service*/) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
cache.erase<ObjectType>(id_);
ptr_.change_state(object::object_state::Transient);
return utils::ok<void>();
}
private:
object::object_ptr<ObjectType> ptr_;
};
template <typename ObjectType>
class delete_step_relation : public execute_step {
public:
delete_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &) override {
return utils::ok<void>();
}
utils::result<void, utils::error> execute(sql::statement &stmt) override {
stmt.bind(*ptr_);
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
return utils::failure(exec_result.err());
}
return utils::ok<void>();
}
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
return utils::ok<void>();
}
private:
object::object_ptr<ObjectType> ptr_;
};
}
#endif //MATADOR_DELETE_STEP_HPP

View File

@ -56,49 +56,10 @@ private:
std::string join_column_; std::string join_column_;
}; };
struct processing_many_to_many_key {
std::string id;
std::type_index local_type{typeid(void)};
std::type_index foreign_type{typeid(void)};
bool operator==(processing_many_to_many_key const &other) const {
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
}
};
template<class LocalType, typename ForeignType>
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
}
struct processing_many_to_many_key_hash {
size_t operator()(const processing_many_to_many_key &p) const noexcept {
size_t seed = std::hash<std::type_index>{}(p.local_type);
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
const size_t id_hash = std::hash<std::string>{}(p.id);
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
return seed;
}
};
struct insert_context {
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
std::vector<std::unique_ptr<execute_step>> steps_{};
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
};
template < typename ObjectType > template < typename ObjectType >
class insert_step_processor { class insert_step_processor {
public: public:
explicit insert_step_processor(insert_context &ctx) explicit insert_step_processor(query_builder_context &ctx)
: ctx_{ctx} : ctx_{ctx}
{} {}
@ -247,7 +208,6 @@ public:
attr, attr,
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key { [foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
return {std::string{relation_name}, local_type, foreign_type}; return {std::string{relation_name}, local_type, foreign_type};
// return make_processing_many_to_many_key<ObjectType, ForeignType>(relation_name);
}, },
[this, join_column, inverse_join_column](const auto &obj) { [this, join_column, inverse_join_column](const auto &obj) {
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj); return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
@ -275,7 +235,6 @@ public:
attr, attr,
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key { [foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
return {std::string{relation_name}, foreign_type, local_type}; return {std::string{relation_name}, foreign_type, local_type};
return make_processing_many_to_many_key<ForeignType, ObjectType>(relation_name);
}, },
[this, join_columns = std::move(join_columns)](const auto &obj) { [this, join_columns = std::move(join_columns)](const auto &obj) {
return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_); return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
@ -297,12 +256,12 @@ private:
} }
} }
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory> template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
void insert_many_to_many_relations(const char *id, void insert_many_to_many_relations(const char *id,
object::collection<object::object_ptr<ForeignType>> &objects, object::collection<object::object_ptr<ForeignType>> &objects,
const utils::foreign_attributes &attr, const utils::foreign_attributes &attr,
RelationKeyFactory make_relation_key, RelationKeyFactory make_relation_key,
RelationFactory make_relation) { RelationFactory make_relation) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
return; return;
} }
@ -343,7 +302,7 @@ private:
auto rel = make_relation(obj); auto rel = make_relation(obj);
access::process(*this, *rel); // access::process(*this, *rel);
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel)); insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
} }
@ -365,7 +324,7 @@ private:
} }
private: private:
insert_context& ctx_; query_builder_context& ctx_;
object::object_ptr<ObjectType> ptr_; object::object_ptr<ObjectType> ptr_;
}; };
@ -382,7 +341,7 @@ public:
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"}); return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
} }
insert_context ctx{schema_, contexts_by_type_}; query_builder_context ctx{schema_, contexts_by_type_};
insert_step_processor<ObjectType> processor{ctx}; insert_step_processor<ObjectType> processor{ctx};
const auto result = processor.build(ptr); const auto result = processor.build(ptr);

View File

@ -18,5 +18,43 @@ struct entity_visit_key_hash {
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2)); return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
} }
}; };
struct processing_many_to_many_key {
std::string id;
std::type_index local_type{typeid(void)};
std::type_index foreign_type{typeid(void)};
bool operator==(processing_many_to_many_key const &other) const {
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
}
};
template<class LocalType, typename ForeignType>
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
}
struct processing_many_to_many_key_hash {
size_t operator()(const processing_many_to_many_key &p) const noexcept {
size_t seed = std::hash<std::type_index>{}(p.local_type);
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
const size_t id_hash = std::hash<std::string>{}(p.id);
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
return seed;
}
};
struct query_builder_context {
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
std::vector<std::unique_ptr<execute_step>> steps_{};
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
};
} }
#endif //MATADOR_QUERY_BUILDER_UTILS_HPP #endif //MATADOR_QUERY_BUILDER_UTILS_HPP

View File

@ -352,10 +352,6 @@ utils::result<sql::query_context, utils::error> query_object_resolver_producer<T
template<typename Type> template<typename Type>
utils::result<sql::query_context, utils::error> query_joined_object_resolver_producer<Type>::build_query(const sql::dialect &d) { utils::result<sql::query_context, utils::error> query_joined_object_resolver_producer<Type>::build_query(const sql::dialect &d) {
// producer_creator pc(repo_, typeid(Type));
// Type obj;
// access::process(pc, obj);
select_query_builder qb(repo_); select_query_builder qb(repo_);
const auto *join_column = table_[collection_name()]; const auto *join_column = table_[collection_name()];
const auto result = qb.build<Type>(*join_column == utils::_); const auto result = qb.build<Type>(*join_column == utils::_);

View File

@ -0,0 +1,13 @@
#ifndef MATADOR_SCHEMA_UTILS_HPP
#define MATADOR_SCHEMA_UTILS_HPP
#include "matador/query/query_contexts.hpp"
#include "matador/query/basic_schema.hpp"
namespace matador::sql {
class dialect;
}
namespace matador::query {
query_contexts to_query_contexts(const schema_node &node, const sql::dialect &d);
}
#endif //MATADOR_SCHEMA_UTILS_HPP

View File

@ -3,11 +3,10 @@
#include "matador/sql/abstract_sql_logger.hpp" #include "matador/sql/abstract_sql_logger.hpp"
#include "matador/sql/error_code.hpp" #include "matador/sql/error_code.hpp"
#include "matador/sql/execute_result.hpp"
#include "matador/sql/query_result.hpp" #include "matador/sql/query_result.hpp"
#include "matador/sql/interface/statement_proxy.hpp" #include "matador/sql/interface/statement_proxy.hpp"
#include "matador/object/basic_repository.hpp"
#include "matador/utils/error.hpp" #include "matador/utils/error.hpp"
#include "matador/utils/result.hpp" #include "matador/utils/result.hpp"

View File

@ -16,7 +16,10 @@ add_library(matador-orm STATIC
../../include/matador/query/criteria/logical_criteria.hpp ../../include/matador/query/criteria/logical_criteria.hpp
../../include/matador/query/criteria_evaluator.hpp ../../include/matador/query/criteria_evaluator.hpp
../../include/matador/query/database.hpp ../../include/matador/query/database.hpp
../../include/matador/query/delete_query_builder.hpp
../../include/matador/query/delete_step.hpp
../../include/matador/query/error_code.hpp ../../include/matador/query/error_code.hpp
../../include/matador/query/execute_step.hpp
../../include/matador/query/expression/abstract_column_expression.hpp ../../include/matador/query/expression/abstract_column_expression.hpp
../../include/matador/query/expression/binary_column_expression.hpp ../../include/matador/query/expression/binary_column_expression.hpp
../../include/matador/query/expression/column_expression.hpp ../../include/matador/query/expression/column_expression.hpp
@ -31,7 +34,7 @@ add_library(matador-orm STATIC
../../include/matador/query/identity_pk_generator.hpp ../../include/matador/query/identity_pk_generator.hpp
../../include/matador/query/insert_query_builder.hpp ../../include/matador/query/insert_query_builder.hpp
../../include/matador/query/insert_query_builder.hpp ../../include/matador/query/insert_query_builder.hpp
../../include/matador/query/execute_step.hpp ../../include/matador/query/insert_step.hpp
../../include/matador/query/intermediates/executable_query.hpp ../../include/matador/query/intermediates/executable_query.hpp
../../include/matador/query/intermediates/fetchable_query.hpp ../../include/matador/query/intermediates/fetchable_query.hpp
../../include/matador/query/intermediates/query_alter_intermediate.hpp ../../include/matador/query/intermediates/query_alter_intermediate.hpp
@ -71,6 +74,7 @@ add_library(matador-orm STATIC
../../include/matador/query/meta_table_macro.hpp ../../include/matador/query/meta_table_macro.hpp
../../include/matador/query/query.hpp ../../include/matador/query/query.hpp
../../include/matador/query/query_builder.hpp ../../include/matador/query/query_builder.hpp
../../include/matador/query/query_builder_utils.hpp
../../include/matador/query/query_collection_resolver.hpp ../../include/matador/query/query_collection_resolver.hpp
../../include/matador/query/query_column.hpp ../../include/matador/query/query_column.hpp
../../include/matador/query/query_contexts.hpp ../../include/matador/query/query_contexts.hpp
@ -80,6 +84,7 @@ add_library(matador-orm STATIC
../../include/matador/query/query_part.hpp ../../include/matador/query/query_part.hpp
../../include/matador/query/query_utils.hpp ../../include/matador/query/query_utils.hpp
../../include/matador/query/schema.hpp ../../include/matador/query/schema.hpp
../../include/matador/query/schema_utils.hpp
../../include/matador/query/select_query_builder.hpp ../../include/matador/query/select_query_builder.hpp
../../include/matador/query/sequence_pk_generator.hpp ../../include/matador/query/sequence_pk_generator.hpp
../../include/matador/query/session.hpp ../../include/matador/query/session.hpp
@ -104,9 +109,9 @@ add_library(matador-orm STATIC
../../include/matador/sql/interface/query_result_reader.hpp ../../include/matador/sql/interface/query_result_reader.hpp
../../include/matador/sql/interface/statement_impl.hpp ../../include/matador/sql/interface/statement_impl.hpp
../../include/matador/sql/interface/statement_proxy.hpp ../../include/matador/sql/interface/statement_proxy.hpp
../../include/matador/sql/internal/joined_collection_resolver_producer.hpp
../../include/matador/sql/internal/identifier_reader.hpp ../../include/matador/sql/internal/identifier_reader.hpp
../../include/matador/sql/internal/identifier_statement_binder.hpp ../../include/matador/sql/internal/identifier_statement_binder.hpp
../../include/matador/sql/internal/joined_collection_resolver_producer.hpp
../../include/matador/sql/internal/object_resolver_producer.hpp ../../include/matador/sql/internal/object_resolver_producer.hpp
../../include/matador/sql/internal/object_result_binder.hpp ../../include/matador/sql/internal/object_result_binder.hpp
../../include/matador/sql/internal/pk_reader.hpp ../../include/matador/sql/internal/pk_reader.hpp
@ -188,6 +193,7 @@ add_library(matador-orm STATIC
query/query_part.cpp query/query_part.cpp
query/query_utils.cpp query/query_utils.cpp
query/schema.cpp query/schema.cpp
query/schema_utils.cpp
query/select_query_builder.cpp query/select_query_builder.cpp
query/sequence_pk_generator.cpp query/sequence_pk_generator.cpp
query/session.cpp query/session.cpp
@ -208,9 +214,9 @@ add_library(matador-orm STATIC
sql/interface/query_result_reader.cpp sql/interface/query_result_reader.cpp
sql/interface/statement_impl.cpp sql/interface/statement_impl.cpp
sql/interface/statement_proxy.cpp sql/interface/statement_proxy.cpp
sql/internal/joined_collection_resolver_producer.cpp
sql/internal/identifier_reader.cpp sql/internal/identifier_reader.cpp
sql/internal/identifier_statement_binder.cpp sql/internal/identifier_statement_binder.cpp
sql/internal/joined_collection_resolver_producer.cpp
sql/internal/object_resolver_producer.cpp sql/internal/object_resolver_producer.cpp
sql/internal/object_result_binder.cpp sql/internal/object_result_binder.cpp
sql/internal/query_result_pk_resolver.cpp sql/internal/query_result_pk_resolver.cpp
@ -221,9 +227,6 @@ add_library(matador-orm STATIC
sql/resolver_service.cpp sql/resolver_service.cpp
sql/statement.cpp sql/statement.cpp
sql/statement_cache.cpp sql/statement_cache.cpp
../../include/matador/query/delete_query_builder.hpp
../../include/matador/query/query_builder_utils.hpp
../../include/matador/query/insert_step.hpp
) )
target_include_directories(matador-orm target_include_directories(matador-orm

View File

@ -0,0 +1,57 @@
#include "matador/query/schema_utils.hpp"
#include "matador/query/query.hpp"
#include "matador/query/criteria.hpp"
using namespace matador::utils;
namespace matador::query {
query_contexts to_query_contexts(const schema_node& node, const sql::dialect &d) {
query_contexts queries;
// SELECT all
queries.select_all = select(node.table())
.from(node.name())
.compile(d);
if (node.table().has_primary_key()) {
// SELECT one
queries.select_one = select(node.table())
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(d);
// UPDATE one
auto update_set = query::update(node.table());
for (const auto &col: node.table().columns()) {
update_set.set(col, _);
}
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
.compile(d);
// DELETE one
queries.delete_one = query::remove()
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(d);
}
// INSERT one
std::vector<table_column> columns;
for (const auto &col: node.table().columns()) {
if (col.is_primary_key() && utils::is_constraint_set(col.attributes().options(), constraints::Identity)) {
continue;
}
columns.push_back(col);
}
if (node.pk_generator().type() == utils::generator_type::Identity) {
queries.insert = query::insert()
.into(node.name(), columns)
.values(generator::placeholders(columns.size()))
.returning(node.table().primary_key_column()->as(node.table().primary_key_column()->column_name()))
.compile(d);
} else {
queries.insert = query::insert()
.into(node.name(), columns)
.values(generator::placeholders(columns.size()))
.compile(d);
}
return queries;
}
}

View File

@ -3,9 +3,8 @@
#include "matador/sql/backend_provider.hpp" #include "matador/sql/backend_provider.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/query/query.hpp"
#include "matador/query/generator.hpp"
#include "matador/query/basic_schema.hpp" #include "matador/query/basic_schema.hpp"
#include "matador/query/schema_utils.hpp"
#include <stdexcept> #include <stdexcept>
@ -23,51 +22,7 @@ session::session(session_context&& ctx, const basic_schema &scm)
, resolver_service_(ctx.resolver_service) { , resolver_service_(ctx.resolver_service) {
using namespace matador::utils; using namespace matador::utils;
for (const auto &[type, node] : schema_) { for (const auto &[type, node] : schema_) {
query_contexts queries; query_contexts queries = to_query_contexts(node, dialect_);
// SELECT all
queries.select_all = select(node.table())
.from(node.name())
.compile(dialect_);
if (node.table().has_primary_key()) {
// SELECT one
queries.select_one = select(node.table())
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(dialect_);
// UPDATE one
auto update_set = query::update(node.table());
for (const auto &col: node.table().columns()) {
update_set.set(col, _);
}
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
.compile(dialect_);
// DELETE one
queries.delete_one = query::remove()
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(dialect_);
}
// INSERT one
std::vector<table_column> columns;
for (const auto &col: node.table().columns()) {
if (col.is_primary_key() && utils::is_constraint_set(col.attributes().options(), constraints::Identity)) {
continue;
}
columns.push_back(col);
}
if (node.pk_generator().type() == utils::generator_type::Identity) {
queries.insert = query::insert()
.into(node.name(), columns)
.values(generator::placeholders(columns.size()))
.returning(node.table().primary_key_column()->as(node.table().primary_key_column()->column_name()))
.compile(dialect_);
} else {
queries.insert = query::insert()
.into(node.name(), columns)
.values(generator::placeholders(columns.size()))
.compile(dialect_);
}
queries.insert.resolver = resolver_service_; queries.insert.resolver = resolver_service_;
queries.update_one.resolver = resolver_service_; queries.update_one.resolver = resolver_service_;

View File

@ -3,6 +3,8 @@ CPMAddPackage("gh:catchorg/Catch2@3.14.0")
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
add_executable(OrmTests add_executable(OrmTests
../utils/RecordingObserver.cpp
../utils/RecordingObserver.hpp
backend/test_backend_service.cpp backend/test_backend_service.cpp
backend/test_backend_service.hpp backend/test_backend_service.hpp
backend/test_connection.cpp backend/test_connection.cpp
@ -16,6 +18,7 @@ add_executable(OrmTests
query/ColumnExpressionTest.cpp query/ColumnExpressionTest.cpp
query/ColumnGeneratorTest.cpp query/ColumnGeneratorTest.cpp
query/CriteriaTests.cpp query/CriteriaTests.cpp
query/DeleteQueryBuilderTest.cpp
query/GeneratorTests.cpp query/GeneratorTests.cpp
query/InsertQueryBuilderTest.cpp query/InsertQueryBuilderTest.cpp
query/QueryBuilderTest.cpp query/QueryBuilderTest.cpp
@ -32,8 +35,6 @@ add_executable(OrmTests
sql/StatementCacheTest.cpp sql/StatementCacheTest.cpp
utils/auto_reset_event.cpp utils/auto_reset_event.cpp
utils/auto_reset_event.hpp utils/auto_reset_event.hpp
../utils/RecordingObserver.hpp
../utils/RecordingObserver.cpp
) )
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)

View File

@ -0,0 +1,100 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/object/object_ptr.hpp"
#include "matador/sql/backend_provider.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/interface/connection_impl.hpp"
#include "matador/query/schema.hpp"
#include "matador/query/delete_query_builder.hpp"
#include "QueryFixture.hpp"
#include "../../models/author.hpp"
#include "../../models/book.hpp"
#include "../../models/airplane.hpp"
#include "../../models/flight.hpp"
#include "../../models/recipe.hpp"
using namespace matador::object;
using namespace matador::sql;
using namespace matador::query;
using namespace matador::utils;
using namespace matador::test;
TEST_CASE_METHOD(QueryFixture, "delete query builder test", "[query][delete_query_builder]") {
schema scm;
auto result = scm.attach<airplane>("airplanes")
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
REQUIRE(result);
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
delete_query_builder<airplane> dqb(scm, contexts_by_type);
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
auto build_result = dqb.build(a380);
REQUIRE(build_result.is_ok());
const auto& stmts = *build_result;
REQUIRE(stmts.size() == 1);
}
TEST_CASE_METHOD(QueryFixture, "Test delete builder has many", "[query][delete_query_builder][has_many]") {
schema scm;
const auto result = scm.attach<book>("books")
.and_then( [&scm] { return scm.attach<author>("authors"); } );
REQUIRE(result.is_ok());
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
s_king->books.push_back(make_object<book>(2, "Carrie", object_ptr<author>{}, 1974));
s_king->books.push_back(make_object<book>(3, "The Shining", object_ptr<author>{}, 1977));
s_king->books.push_back(make_object<book>(4, "It", object_ptr<author>{}, 1986));
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
delete_query_builder<author> dqb(scm, contexts_by_type);
auto build_result = dqb.build(s_king);
REQUIRE(build_result.is_ok());
const auto& stmts = *build_result;
REQUIRE_FALSE(stmts.empty());
REQUIRE(stmts.size() == 6);
}
TEST_CASE_METHOD(QueryFixture, "Test delete builder has many to many", "[query][delete_query_builder][many_to_many]") {
schema scm;
const auto result = scm.attach<recipe>("recipes")
.and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } );
REQUIRE(result.is_ok());
const std::vector ingredients {
make_object<ingredient>(1, "Apple"),
make_object<ingredient>(2, "Strawberry"),
make_object<ingredient>(3, "Pineapple"),
make_object<ingredient>(4, "Sugar"),
make_object<ingredient>(5, "Flour"),
make_object<ingredient>(6, "Butter"),
make_object<ingredient>(7, "Beans")
};
std::vector recipes {
make_object<recipe>(1, "Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}),
make_object<recipe>(2, "Strawberry Cake", std::vector{ingredients[5], ingredients[6]}),
make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
};
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
delete_query_builder<recipe> dqb(scm, contexts_by_type);
auto build_result = dqb.build(recipes[0]);
REQUIRE(build_result.is_ok());
const auto& stmts = *build_result;
REQUIRE_FALSE(stmts.empty());
REQUIRE(stmts.size() == 7);
}

View File

@ -6,13 +6,11 @@
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/interface/connection_impl.hpp" #include "matador/sql/interface/connection_impl.hpp"
#include "matador/query/query_contexts.hpp"
#include "matador/query/query.hpp"
#include "matador/query/schema.hpp" #include "matador/query/schema.hpp"
#include "matador/query/insert_query_builder.hpp" #include "matador/query/insert_query_builder.hpp"
#include "../backend/test_backend_service.hpp" #include "QueryFixture.hpp"
#include "../../models/author.hpp" #include "../../models/author.hpp"
#include "../../models/book.hpp" #include "../../models/book.hpp"
@ -24,58 +22,15 @@ using namespace matador::object;
using namespace matador::sql; using namespace matador::sql;
using namespace matador::query; using namespace matador::query;
using namespace matador::utils; using namespace matador::utils;
using namespace matador::test;
std::unordered_map<std::type_index, query_contexts> to_contexts_by_name(const schema& scm, const dialect& d) { TEST_CASE_METHOD(QueryFixture, "insert query builder test", "[query][insert_query_builder]") {
std::unordered_map<std::type_index, query_contexts> contexts_by_type;
for (const auto &[type, node] : scm) {
query_contexts queries;
// SELECT all
queries.select_all = select(node.table())
.from(node.name())
.compile(d);
if (node.table().has_primary_key()) {
// SELECT one
queries.select_one = select(node.table())
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(d);
// UPDATE one
auto update_set = update(node.table());
for (const auto &col: node.table().columns()) {
update_set.set(col, _);
}
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
.compile(d);
// DELETE one
queries.delete_one = remove()
.from(node.name())
.where(*node.table().primary_key_column() == _)
.compile(d);
}
// INSERT one
queries.insert = insert()
.into(node.name(), node.table())
.values(generator::placeholders(node.table().columns().size()))
.compile(d);
contexts_by_type[node.node().type_index()] = queries;
}
return contexts_by_type;
}
TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
using namespace matador::test;
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
connection db("noop://noop.db");
schema scm; schema scm;
auto result = scm.attach<airplane>("airplanes") auto result = scm.attach<airplane>("airplanes")
.and_then( [&scm] { return scm.attach<flight>("flights"); } ); .and_then( [&scm] { return scm.attach<flight>("flights"); } );
REQUIRE(result); REQUIRE(result);
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect()); const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
insert_query_builder<airplane> iqb(scm, contexts_by_type); insert_query_builder<airplane> iqb(scm, contexts_by_type);
const auto a380 = make_object<airplane>(1, "Boeing", "A380" ); const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
@ -86,11 +41,7 @@ TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
REQUIRE(stmts.size() == 1); REQUIRE(stmts.size() == 1);
} }
TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_many]") { TEST_CASE_METHOD(QueryFixture, "Test insert builder has many", "[query][insert_query_builder][has_many]") {
using namespace matador::test;
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
connection db("noop://noop.db");
schema scm; schema scm;
const auto result = scm.attach<book>("books") const auto result = scm.attach<book>("books")
.and_then( [&scm] { return scm.attach<author>("authors"); } ); .and_then( [&scm] { return scm.attach<author>("authors"); } );
@ -104,7 +55,7 @@ TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_man
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987)); s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982)); s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect()); const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
insert_query_builder<author> iqb(scm, contexts_by_type); insert_query_builder<author> iqb(scm, contexts_by_type);
auto build_result = iqb.build(s_king); auto build_result = iqb.build(s_king);
@ -115,11 +66,7 @@ TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_man
REQUIRE(stmts.size() == 6); REQUIRE(stmts.size() == 6);
} }
TEST_CASE("Test insert builder has many to many", "[query][insert_query_builder][many_to_many]") { TEST_CASE_METHOD(QueryFixture, "Test insert builder has many to many", "[query][insert_query_builder][many_to_many]") {
using namespace matador::test;
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
connection db("noop://noop.db");
schema scm; schema scm;
const auto result = scm.attach<recipe>("recipes") const auto result = scm.attach<recipe>("recipes")
.and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } ); .and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } );
@ -141,7 +88,7 @@ TEST_CASE("Test insert builder has many to many", "[query][insert_query_builder]
make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]}) make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
}; };
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect()); const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
insert_query_builder<recipe> iqb(scm, contexts_by_type); insert_query_builder<recipe> iqb(scm, contexts_by_type);
auto build_result = iqb.build(recipes[0]); auto build_result = iqb.build(recipes[0]);

View File

@ -4,6 +4,9 @@
#include "matador/sql/interface/connection_impl.hpp" #include "matador/sql/interface/connection_impl.hpp"
#include "matador/query/basic_schema.hpp"
#include "matador/query/schema_utils.hpp"
namespace matador::test { namespace matador::test {
QueryFixture::QueryFixture() { QueryFixture::QueryFixture() {
@ -12,4 +15,12 @@ QueryFixture::QueryFixture() {
db = std::make_unique<sql::connection>("noop://noop.db"); db = std::make_unique<sql::connection>("noop://noop.db");
} }
std::unordered_map<std::type_index, query::query_contexts> QueryFixture::to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) {
std::unordered_map<std::type_index, query::query_contexts> contexts_by_type;
for (const auto &[type, node] : scm) {
contexts_by_type[node.node().type_index()] = to_query_contexts(node, d);
}
return contexts_by_type;
}
} }

View File

@ -3,8 +3,14 @@
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/query/query_contexts.hpp"
#include <memory> #include <memory>
namespace matador::query {
class basic_schema;
}
namespace matador::test { namespace matador::test {
class QueryFixture { class QueryFixture {
@ -12,6 +18,9 @@ public:
QueryFixture(); QueryFixture();
~QueryFixture() = default; ~QueryFixture() = default;
protected:
static std::unordered_map<std::type_index, query::query_contexts> to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) ;
protected: protected:
std::unique_ptr<sql::connection> db; std::unique_ptr<sql::connection> db;
}; };