From 68d67b17b7df05f84b3dabbce8f9b83dda6469b4 Mon Sep 17 00:00:00 2001 From: sascha Date: Thu, 28 May 2026 15:45:46 +0200 Subject: [PATCH] delete has many to many progress --- .../matador/query/delete_query_builder.hpp | 143 +++++++++--------- include/matador/query/delete_step.hpp | 84 ++++++++++ .../matador/query/insert_query_builder.hpp | 61 ++------ include/matador/query/query_builder_utils.hpp | 38 +++++ include/matador/query/schema.hpp | 4 - include/matador/query/schema_utils.hpp | 13 ++ include/matador/sql/statement.hpp | 3 +- source/orm/CMakeLists.txt | 15 +- source/orm/query/schema_utils.cpp | 57 +++++++ source/orm/query/session.cpp | 49 +----- test/orm/CMakeLists.txt | 5 +- test/orm/query/DeleteQueryBuilderTest.cpp | 100 ++++++++++++ test/orm/query/InsertQueryBuilderTest.cpp | 69 +-------- test/orm/query/QueryFixture.cpp | 11 ++ test/orm/query/QueryFixture.hpp | 9 ++ 15 files changed, 417 insertions(+), 244 deletions(-) create mode 100644 include/matador/query/delete_step.hpp create mode 100644 include/matador/query/schema_utils.hpp create mode 100644 source/orm/query/schema_utils.cpp create mode 100644 test/orm/query/DeleteQueryBuilderTest.cpp diff --git a/include/matador/query/delete_query_builder.hpp b/include/matador/query/delete_query_builder.hpp index 42aab52..fbdb693 100644 --- a/include/matador/query/delete_query_builder.hpp +++ b/include/matador/query/delete_query_builder.hpp @@ -7,13 +7,11 @@ #include "matador/query/basic_schema.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_builder_exception.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/utils/error.hpp" @@ -22,61 +20,10 @@ #include "matador/utils/result.hpp" namespace matador::query { -template -class delete_step_object final : public execute_step { -public: - delete_step_object(sql::query_context ctx, const object::object_ptr &ptr) - : execute_step(std::move(ctx)) - , ptr_(ptr) {} - - utils::result prepare(sql::executor &/*conn*/) override { - id_ = ptr_.primary_key(); - return utils::ok(); - } - - utils::result 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(); - } - - utils::result 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(id_); - ptr_.change_state(object::object_state::Transient); - - return utils::ok(); - } - -private: - object::object_ptr ptr_; -}; - -struct delete_context { - const basic_schema &schema_; - const std::unordered_map &contexts_by_type_; - - std::vector> steps_{}; - std::vector> relation_steps_{}; - std::unordered_set, entity_visit_key_hash> visited_{}; -}; - template class delete_step_processor { public: - explicit delete_step_processor(delete_context &ctx) + explicit delete_step_processor(query_builder_context &ctx) : ctx_(ctx) {} utils::result build(object::object_ptr ptr, const bool as_relation_step = false) { @@ -97,8 +44,7 @@ public: return utils::failure(utils::error{error_code::UnknownType, "Unknown type"}); } - const auto &info = it->second.node().info(); - if (!info.has_primary_key()) { + if (const auto &info = it->second.node().info(); !info.has_primary_key()) { return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"}); } @@ -177,19 +123,50 @@ public: } template - void on_has_many_to_many(const char * /*id*/, + void on_has_many_to_many(const char *id, object::collection> &objects, - const char * /*join_column*/, - const char * /*inverse_join_column*/, + const char *join_column, + const char *inverse_join_column, 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; + const std::type_index foreign_type{typeid(ForeignType)}; + const std::type_index local_type{typeid(ObjectType)}; + on_many_to_many_objects( + 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 - void on_has_many_to_many(const char * /*id*/, + void on_has_many_to_many(const char *id, object::collection> &objects, 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(); join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) { + return; + } + + using relation_value_type = object::many_to_many_relation; + const std::type_index foreign_type{typeid(ForeignType)}; + const std::type_index local_type{typeid(ObjectType)}; + on_many_to_many_objects( + 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: @@ -207,28 +184,52 @@ private: } } - template - void on_many_to_many_objects(object::collection> &objects, - const utils::foreign_attributes &attr) { + template + void on_many_to_many_objects(const char *id, + object::collection> &objects, + const utils::foreign_attributes &attr, + RelationKeyFactory make_relation_key) { if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) { 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> delete_relation_steps; delete_step_processor processor{ctx_}; for (auto &obj : objects) { if (!obj) { continue; } - auto result = processor.build(obj, true); - if (!result) { - throw query_builder_exception(result.release_error()); + if (obj.is_persistent()) { + auto result = processor.build(obj, true); + if (!result) { + throw query_builder_exception(result.release_error()); + } } } } private: - delete_context &ctx_; + query_builder_context &ctx_; object::object_ptr ptr_; }; @@ -245,7 +246,7 @@ public: 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 processor{ctx}; const auto result = processor.build(ptr); diff --git a/include/matador/query/delete_step.hpp b/include/matador/query/delete_step.hpp new file mode 100644 index 0000000..9cec2f6 --- /dev/null +++ b/include/matador/query/delete_step.hpp @@ -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 +class delete_step_object final : public execute_step { +public: + delete_step_object(sql::query_context ctx, const object::object_ptr &ptr) + : execute_step(std::move(ctx)) + , ptr_(ptr) {} + + utils::result prepare(sql::executor &/*conn*/) override { + id_ = ptr_.primary_key(); + return utils::ok(); + } + + utils::result 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(); + } + + utils::result 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(id_); + ptr_.change_state(object::object_state::Transient); + + return utils::ok(); + } + +private: + object::object_ptr ptr_; +}; + +template +class delete_step_relation : public execute_step { +public: + delete_step_relation(sql::query_context ctx, const object::object_ptr& ptr) + : execute_step(std::move(ctx)) + , ptr_(ptr) {} + + utils::result prepare(sql::executor &) override { + return utils::ok(); + } + utils::result 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(); + } + + utils::result finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override { + return utils::ok(); + } + +private: + object::object_ptr ptr_; +}; + +} + +#endif //MATADOR_DELETE_STEP_HPP diff --git a/include/matador/query/insert_query_builder.hpp b/include/matador/query/insert_query_builder.hpp index 38045eb..e9cf024 100644 --- a/include/matador/query/insert_query_builder.hpp +++ b/include/matador/query/insert_query_builder.hpp @@ -56,49 +56,10 @@ private: 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 -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{}(p.local_type); - - const size_t foreign_hash = std::hash{}(p.foreign_type); - seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2); - - const size_t id_hash = std::hash{}(p.id); - seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2); - - return seed; - } -}; - -struct insert_context { - const basic_schema &schema_; - const std::unordered_map &contexts_by_type_; - - std::vector> steps_{}; - std::vector> relation_steps_{}; - std::unordered_set, entity_visit_key_hash> visited_{}; - std::unordered_set processing_many_to_many_relations_{}; - -}; - template < typename ObjectType > class insert_step_processor { public: - explicit insert_step_processor(insert_context &ctx) + explicit insert_step_processor(query_builder_context &ctx) : ctx_{ctx} {} @@ -247,7 +208,6 @@ public: attr, [foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key { return {std::string{relation_name}, local_type, foreign_type}; - // return make_processing_many_to_many_key(relation_name); }, [this, join_column, inverse_join_column](const auto &obj) { return object::make_object(join_column, inverse_join_column, ptr_, obj); @@ -275,7 +235,6 @@ public: attr, [foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key { return {std::string{relation_name}, foreign_type, local_type}; - return make_processing_many_to_many_key(relation_name); }, [this, join_columns = std::move(join_columns)](const auto &obj) { return object::make_object(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_); @@ -297,12 +256,12 @@ private: } } - template - void insert_many_to_many_relations(const char *id, - object::collection> &objects, - const utils::foreign_attributes &attr, - RelationKeyFactory make_relation_key, - RelationFactory make_relation) { + template + void insert_many_to_many_relations(const char *id, + object::collection> &objects, + const utils::foreign_attributes &attr, + RelationKeyFactory make_relation_key, + RelationFactory make_relation) { if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { return; } @@ -343,7 +302,7 @@ private: auto rel = make_relation(obj); - access::process(*this, *rel); + // access::process(*this, *rel); insert_relation_steps.push_back(std::make_unique>(cit->second.insert, rel)); } @@ -365,7 +324,7 @@ private: } private: - insert_context& ctx_; + query_builder_context& ctx_; object::object_ptr ptr_; }; @@ -382,7 +341,7 @@ public: 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 processor{ctx}; const auto result = processor.build(ptr); diff --git a/include/matador/query/query_builder_utils.hpp b/include/matador/query/query_builder_utils.hpp index a8d18d5..80b885e 100644 --- a/include/matador/query/query_builder_utils.hpp +++ b/include/matador/query/query_builder_utils.hpp @@ -18,5 +18,43 @@ struct entity_visit_key_hash { 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 +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{}(p.local_type); + + const size_t foreign_hash = std::hash{}(p.foreign_type); + seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2); + + const size_t id_hash = std::hash{}(p.id); + seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2); + + return seed; + } +}; + +struct query_builder_context { + const basic_schema &schema_; + const std::unordered_map &contexts_by_type_; + + std::vector> steps_{}; + std::vector> relation_steps_{}; + std::unordered_set, entity_visit_key_hash> visited_{}; + std::unordered_set processing_many_to_many_relations_{}; +}; } #endif //MATADOR_QUERY_BUILDER_UTILS_HPP diff --git a/include/matador/query/schema.hpp b/include/matador/query/schema.hpp index d2e8dae..c767453 100644 --- a/include/matador/query/schema.hpp +++ b/include/matador/query/schema.hpp @@ -352,10 +352,6 @@ utils::result query_object_resolver_producer utils::result query_joined_object_resolver_producer::build_query(const sql::dialect &d) { - // producer_creator pc(repo_, typeid(Type)); - // Type obj; - // access::process(pc, obj); - select_query_builder qb(repo_); const auto *join_column = table_[collection_name()]; const auto result = qb.build(*join_column == utils::_); diff --git a/include/matador/query/schema_utils.hpp b/include/matador/query/schema_utils.hpp new file mode 100644 index 0000000..89e5340 --- /dev/null +++ b/include/matador/query/schema_utils.hpp @@ -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 diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 48d555a..1e4e1ef 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -3,11 +3,10 @@ #include "matador/sql/abstract_sql_logger.hpp" #include "matador/sql/error_code.hpp" +#include "matador/sql/execute_result.hpp" #include "matador/sql/query_result.hpp" #include "matador/sql/interface/statement_proxy.hpp" -#include "matador/object/basic_repository.hpp" - #include "matador/utils/error.hpp" #include "matador/utils/result.hpp" diff --git a/source/orm/CMakeLists.txt b/source/orm/CMakeLists.txt index 500a870..0971121 100644 --- a/source/orm/CMakeLists.txt +++ b/source/orm/CMakeLists.txt @@ -16,7 +16,10 @@ add_library(matador-orm STATIC ../../include/matador/query/criteria/logical_criteria.hpp ../../include/matador/query/criteria_evaluator.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/execute_step.hpp ../../include/matador/query/expression/abstract_column_expression.hpp ../../include/matador/query/expression/binary_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/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/fetchable_query.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/query.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_column.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_utils.hpp ../../include/matador/query/schema.hpp + ../../include/matador/query/schema_utils.hpp ../../include/matador/query/select_query_builder.hpp ../../include/matador/query/sequence_pk_generator.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/statement_impl.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_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_result_binder.hpp ../../include/matador/sql/internal/pk_reader.hpp @@ -188,6 +193,7 @@ add_library(matador-orm STATIC query/query_part.cpp query/query_utils.cpp query/schema.cpp + query/schema_utils.cpp query/select_query_builder.cpp query/sequence_pk_generator.cpp query/session.cpp @@ -208,9 +214,9 @@ add_library(matador-orm STATIC sql/interface/query_result_reader.cpp sql/interface/statement_impl.cpp sql/interface/statement_proxy.cpp - sql/internal/joined_collection_resolver_producer.cpp sql/internal/identifier_reader.cpp sql/internal/identifier_statement_binder.cpp + sql/internal/joined_collection_resolver_producer.cpp sql/internal/object_resolver_producer.cpp sql/internal/object_result_binder.cpp sql/internal/query_result_pk_resolver.cpp @@ -221,9 +227,6 @@ add_library(matador-orm STATIC sql/resolver_service.cpp sql/statement.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 diff --git a/source/orm/query/schema_utils.cpp b/source/orm/query/schema_utils.cpp new file mode 100644 index 0000000..d3a1c4a --- /dev/null +++ b/source/orm/query/schema_utils.cpp @@ -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 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; +} +} diff --git a/source/orm/query/session.cpp b/source/orm/query/session.cpp index 5046ccb..c4da8a6 100644 --- a/source/orm/query/session.cpp +++ b/source/orm/query/session.cpp @@ -3,9 +3,8 @@ #include "matador/sql/backend_provider.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/schema_utils.hpp" #include @@ -23,51 +22,7 @@ session::session(session_context&& ctx, const basic_schema &scm) , resolver_service_(ctx.resolver_service) { using namespace matador::utils; for (const auto &[type, node] : schema_) { - query_contexts queries; - - // 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 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_); - } + query_contexts queries = to_query_contexts(node, dialect_); queries.insert.resolver = resolver_service_; queries.update_one.resolver = resolver_service_; diff --git a/test/orm/CMakeLists.txt b/test/orm/CMakeLists.txt index 53f22eb..523a3fb 100644 --- a/test/orm/CMakeLists.txt +++ b/test/orm/CMakeLists.txt @@ -3,6 +3,8 @@ CPMAddPackage("gh:catchorg/Catch2@3.14.0") list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) add_executable(OrmTests + ../utils/RecordingObserver.cpp + ../utils/RecordingObserver.hpp backend/test_backend_service.cpp backend/test_backend_service.hpp backend/test_connection.cpp @@ -16,6 +18,7 @@ add_executable(OrmTests query/ColumnExpressionTest.cpp query/ColumnGeneratorTest.cpp query/CriteriaTests.cpp + query/DeleteQueryBuilderTest.cpp query/GeneratorTests.cpp query/InsertQueryBuilderTest.cpp query/QueryBuilderTest.cpp @@ -32,8 +35,6 @@ add_executable(OrmTests sql/StatementCacheTest.cpp utils/auto_reset_event.cpp utils/auto_reset_event.hpp - ../utils/RecordingObserver.hpp - ../utils/RecordingObserver.cpp ) target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) diff --git a/test/orm/query/DeleteQueryBuilderTest.cpp b/test/orm/query/DeleteQueryBuilderTest.cpp new file mode 100644 index 0000000..6d71155 --- /dev/null +++ b/test/orm/query/DeleteQueryBuilderTest.cpp @@ -0,0 +1,100 @@ +#include + +#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("airplanes") + .and_then( [&scm] { return scm.attach("flights"); } ); + REQUIRE(result); + + const auto contexts_by_type = to_contexts_by_name(scm, db->dialect()); + delete_query_builder dqb(scm, contexts_by_type); + + const auto a380 = make_object(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("books") + .and_then( [&scm] { return scm.attach("authors"); } ); + REQUIRE(result.is_ok()); + + auto s_king = make_object(1, "Steven", "King", "21.9.1947", 1956, false); + + s_king->books.push_back(make_object(2, "Carrie", object_ptr{}, 1974)); + s_king->books.push_back(make_object(3, "The Shining", object_ptr{}, 1977)); + s_king->books.push_back(make_object(4, "It", object_ptr{}, 1986)); + s_king->books.push_back(make_object(5, "Misery", object_ptr{}, 1987)); + s_king->books.push_back(make_object(6, "The Dark Tower: The Gunslinger", object_ptr{}, 1982)); + + const auto contexts_by_type = to_contexts_by_name(scm, db->dialect()); + delete_query_builder 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("recipes") + .and_then( [&scm] { return scm.attach("ingredients"); } ); + REQUIRE(result.is_ok()); + + const std::vector ingredients { + make_object(1, "Apple"), + make_object(2, "Strawberry"), + make_object(3, "Pineapple"), + make_object(4, "Sugar"), + make_object(5, "Flour"), + make_object(6, "Butter"), + make_object(7, "Beans") + }; + + std::vector recipes { + make_object(1, "Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}), + make_object(2, "Strawberry Cake", std::vector{ingredients[5], ingredients[6]}), + make_object(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 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); +} \ No newline at end of file diff --git a/test/orm/query/InsertQueryBuilderTest.cpp b/test/orm/query/InsertQueryBuilderTest.cpp index 1217960..ce1ec2f 100644 --- a/test/orm/query/InsertQueryBuilderTest.cpp +++ b/test/orm/query/InsertQueryBuilderTest.cpp @@ -6,13 +6,11 @@ #include "matador/sql/connection.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/insert_query_builder.hpp" -#include "../backend/test_backend_service.hpp" +#include "QueryFixture.hpp" #include "../../models/author.hpp" #include "../../models/book.hpp" @@ -24,58 +22,15 @@ using namespace matador::object; using namespace matador::sql; using namespace matador::query; using namespace matador::utils; +using namespace matador::test; -std::unordered_map to_contexts_by_name(const schema& scm, const dialect& d) { - std::unordered_map 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()); - connection db("noop://noop.db"); - +TEST_CASE_METHOD(QueryFixture, "insert query builder test", "[query][insert_query_builder]") { schema scm; auto result = scm.attach("airplanes") .and_then( [&scm] { return scm.attach("flights"); } ); 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 iqb(scm, contexts_by_type); const auto a380 = make_object(1, "Boeing", "A380" ); @@ -86,11 +41,7 @@ TEST_CASE("insert query builder test", "[query][insert_query_builder]") { REQUIRE(stmts.size() == 1); } -TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_many]") { - using namespace matador::test; - backend_provider::instance().register_backend("noop", std::make_unique()); - connection db("noop://noop.db"); - +TEST_CASE_METHOD(QueryFixture, "Test insert builder has many", "[query][insert_query_builder][has_many]") { schema scm; const auto result = scm.attach("books") .and_then( [&scm] { return scm.attach("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(5, "Misery", object_ptr{}, 1987)); s_king->books.push_back(make_object(6, "The Dark Tower: The Gunslinger", object_ptr{}, 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 iqb(scm, contexts_by_type); 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); } -TEST_CASE("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()); - connection db("noop://noop.db"); - +TEST_CASE_METHOD(QueryFixture, "Test insert builder has many to many", "[query][insert_query_builder][many_to_many]") { schema scm; const auto result = scm.attach("recipes") .and_then( [&scm] { return scm.attach("ingredients"); } ); @@ -141,7 +88,7 @@ TEST_CASE("Test insert builder has many to many", "[query][insert_query_builder] make_object(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 iqb(scm, contexts_by_type); auto build_result = iqb.build(recipes[0]); diff --git a/test/orm/query/QueryFixture.cpp b/test/orm/query/QueryFixture.cpp index 294b02e..873416b 100644 --- a/test/orm/query/QueryFixture.cpp +++ b/test/orm/query/QueryFixture.cpp @@ -4,6 +4,9 @@ #include "matador/sql/interface/connection_impl.hpp" +#include "matador/query/basic_schema.hpp" +#include "matador/query/schema_utils.hpp" + namespace matador::test { QueryFixture::QueryFixture() { @@ -12,4 +15,12 @@ QueryFixture::QueryFixture() { db = std::make_unique("noop://noop.db"); } +std::unordered_map QueryFixture::to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) { + std::unordered_map 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; +} } \ No newline at end of file diff --git a/test/orm/query/QueryFixture.hpp b/test/orm/query/QueryFixture.hpp index f3c9793..cafd373 100644 --- a/test/orm/query/QueryFixture.hpp +++ b/test/orm/query/QueryFixture.hpp @@ -3,8 +3,14 @@ #include "matador/sql/connection.hpp" +#include "matador/query/query_contexts.hpp" + #include +namespace matador::query { +class basic_schema; +} + namespace matador::test { class QueryFixture { @@ -12,6 +18,9 @@ public: QueryFixture(); ~QueryFixture() = default; +protected: + static std::unordered_map to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) ; + protected: std::unique_ptr db; };