diff --git a/include/matador/query/insert_query_builder.hpp b/include/matador/query/insert_query_builder.hpp index 9990581..4e351e1 100644 --- a/include/matador/query/insert_query_builder.hpp +++ b/include/matador/query/insert_query_builder.hpp @@ -69,11 +69,13 @@ public: return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"}); } + relation_steps_.clear(); + processing_many_to_many_relations_.clear(); steps_.clear(); visited_.clear(); ptr_ = ptr; - const auto result = build_for(ptr, steps_); + const auto result = build_insert_steps(ptr, steps_); ptr_.reset(); if (!result) { return utils::failure(result.err()); @@ -108,6 +110,9 @@ public: object::collection> &objects, const char *join_column, const utils::foreign_attributes &attr) { + if (join_column == nullptr) { + return; + } if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { return; } @@ -115,18 +120,24 @@ public: has_many_linker linker(ptr_, join_column); for (auto &obj : objects) { if (obj.is_transient()) { - build_for(obj, relation_steps_); + const auto result = build_insert_steps(obj, relation_steps_); + if (!result) { + throw query_builder_exception(error_code::InvalidObject, "Invalid object"); + } } access::process(linker, *obj); } - } + template void on_has_many(const char *id, object::collection &objects, const char *join_column, const utils::foreign_attributes &attr) { + if (id == nullptr || join_column == nullptr) { + return; + } if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { return; } @@ -159,106 +170,42 @@ public: return; } - if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { - return; - } - - if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) { - return; - } - - const auto it = schema_.find(std::string{id}); - if (it == schema_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); - } - using relation_value_type = object::many_to_many_relation; - if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) { - throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type"); - } - - const auto cit = contexts_by_type_.find(it->second.node().info().type_index()); - if (cit == contexts_by_type_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); - } - - std::ignore = processing_many_to_many_relations_.insert(id); - std::vector> insert_relation_steps; - for (auto &obj : objects) { - if (!obj) { - continue; - } - - // Ensure target exists as dependency (deps first) - if (obj.is_transient()) { - build_for(obj, relation_steps_); - } - - auto rel = object::make_object(join_column, inverse_join_column, ptr_, obj); - - access::process(*this, *rel); - - insert_relation_steps.push_back(std::make_unique>(cit->second.insert, rel)); - } - relation_steps_.insert(relation_steps_.end(), std::make_move_iterator(insert_relation_steps.begin()), std::make_move_iterator(insert_relation_steps.end())); - processing_many_to_many_relations_.erase(id); + insert_many_to_many_relations( + id, + objects, + attr, + [this, join_column, inverse_join_column](const auto &obj) { + return object::make_object(join_column, inverse_join_column, ptr_, obj); + }); } template void on_has_many_to_many(const char *id, object::collection> &objects, const utils::foreign_attributes &attr) { - if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { - return; - } - - if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) { + if (id == nullptr) { return; } object::join_columns_collector collector; auto join_columns = collector.collect(); - - const auto it = schema_.find(std::string{id}); - if (it == schema_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); + if (join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) { + return; } using relation_value_type = object::many_to_many_relation; - - if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) { - throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type"); - } - - const auto cit = contexts_by_type_.find(it->second.node().info().type_index()); - if (cit == contexts_by_type_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); - } - - std::ignore = processing_many_to_many_relations_.insert(id); - std::vector> insert_relation_steps; - for (auto &obj : objects) { - if (!obj) { - continue; - } - - // Ensure target exists as dependency (deps first) - if (obj.is_transient()) { - build_for(obj, relation_steps_); - } - - auto rel = object::make_object(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_); - - access::process(*this, *rel); - - insert_relation_steps.push_back(std::make_unique>(cit->second.insert, rel)); - } - relation_steps_.insert(relation_steps_.end(), std::make_move_iterator(insert_relation_steps.begin()), std::make_move_iterator(insert_relation_steps.end())); - processing_many_to_many_relations_.erase(id); + insert_many_to_many_relations( + id, + objects, + attr, + [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_); + }); } private: template - static std::pair make_visit_key(const object::object_ptr &ptr) { - return {std::type_index(typeid(EntityType)), static_cast(&(*ptr))}; + static std::pair make_visit_key(const EntityType &ptr) { + return {std::type_index(typeid(EntityType)), static_cast(&ptr)}; } struct visit_key_hash { @@ -271,12 +218,12 @@ private: }; template - utils::result build_for(const object::object_ptr &ptr, std::vector> &steps) { + utils::result build_insert_steps(const object::object_ptr &ptr, std::vector> &steps) { if (!ptr) { return utils::failure(utils::error{error_code::InvalidObject, "Object is null"}); } - const auto key = make_visit_key(ptr); + const auto key = make_visit_key(*ptr); if (visited_.find(key) != visited_.end()) { return utils::ok(); } @@ -315,20 +262,81 @@ private: return utils::ok(); } + template + void insert_many_to_many_relations(const char *id, + object::collection> &objects, + const utils::foreign_attributes &attr, + RelationFactory make_relation) { + if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { + return; + } + + if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) { + return; + } + + const auto it = schema_.find(std::string{id}); + if (it == schema_.end()) { + throw query_builder_exception(error_code::UnknownType, "Unknown type"); + } + + if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) { + throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type"); + } + + const auto cit = contexts_by_type_.find(it->second.node().info().type_index()); + if (cit == contexts_by_type_.end()) { + throw query_builder_exception(error_code::UnknownType, "Unknown type"); + } + + std::ignore = processing_many_to_many_relations_.insert(id); + std::vector> insert_relation_steps; + for (auto &obj : objects) { + if (!obj) { + continue; + } + + // Ensure target exists as dependency (deps first) + if (obj.is_transient()) { + const auto result = build_insert_steps(obj, relation_steps_); + if (!result) { + throw query_builder_exception(error_code::InvalidObject, "Invalid object"); + } + } + + auto rel = make_relation(obj); + + access::process(*this, *rel); + + insert_relation_steps.push_back(std::make_unique>(cit->second.insert, rel)); + } + relation_steps_.insert( + relation_steps_.end(), + std::make_move_iterator(insert_relation_steps.begin()), + std::make_move_iterator(insert_relation_steps.end())); + processing_many_to_many_relations_.erase(id); + } template - void on_foreign_object(Pointer &obj, const utils::foreign_attributes & /*attr*/) { + void on_foreign_object(Pointer &obj, const utils::foreign_attributes &attr) { + if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) { + return; + } + if (!obj) { return; } - // Dependency only matters if the referenced object must be inserted if (obj.is_persistent()) { return; } using dep_t = std::remove_reference_t; - build_for(obj, steps_); + const auto result = build_insert_steps(obj, steps_); + if (!result) { + throw query_builder_exception(error_code::InvalidObject, "Invalid object"); + } } + private: const basic_schema &schema_; const std::unordered_map &contexts_by_type_;