From a2c5bca709db7c0e2433ccb09202171672c41405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 19 Feb 2026 22:32:15 +0100 Subject: [PATCH] has many to many relation inserts --- .../matador/query/insert_query_builder.hpp | 82 ++++++++++++++++++- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/include/matador/query/insert_query_builder.hpp b/include/matador/query/insert_query_builder.hpp index b33bab6..e01f89f 100644 --- a/include/matador/query/insert_query_builder.hpp +++ b/include/matador/query/insert_query_builder.hpp @@ -59,6 +59,28 @@ struct pk_unset_checker { static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {} }; +struct pk_value_extractor { + std::uint64_t value{0}; + + template + void on_primary_key(const char * /*id*/, V &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) { + value = static_cast(pk); + } + static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {} + template + static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {} + template + static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {} + template + static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {} + template + static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {} + template + static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {} + template + static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {} +}; + class insert_query_builder { public: using step_query_t = std::variant; @@ -91,6 +113,12 @@ public: build_for(ptr); + // relation inserts must run after all entity inserts were collected + for (auto &s : relation_steps_) { + steps_.push_back(std::move(s)); + } + relation_steps_.clear(); + return utils::ok(steps_); } @@ -110,10 +138,53 @@ public: void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) { on_foreign_object(obj, attr); } - template - static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {} - template - static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {} + template + static void on_has_many(const char * /*id*/, Collection &con, const char * /*join_column*/, const utils::foreign_attributes & ) {} + template + void on_has_many_to_many(const char *id, Collection &con, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes & ) { + if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) { + return; + } + if (current_entity_pk_ == 0ULL) { + // Without a local PK we cannot create relation rows + // (PK may be assigned later by Identity; that case needs a different mechanism than requested here) + return; + } + + const auto rel_it = schema_.find(std::string{id}); + if (rel_it == schema_.end()) { + throw query_builder_exception(query_build_error::UnknownType); + } + const table &rel_table = rel_it->second.table(); + + for (auto &obj : con) { + if (!obj) { + continue; + } + + // Ensure target exists as dependency (deps first) + if (!obj.is_persistent()) { + using dep_t = std::remove_reference_t; + build_for(obj); + } + + // Extract FK value from the foreign object + pk_value_extractor fk{}; + access::process(fk, *obj); + if (fk.value == 0ULL) { + continue; + } + + // Build INSERT into relation table with the 2 FK columns + insert_step rel_step{}; + rel_step.query = executable_query{ + query::query::insert() + .into(rel_table, {table_column{&rel_table, join_column}, table_column{&rel_table, inverse_join_column}}) + .values({utils::database_type{current_entity_pk_}, utils::database_type{fk.value}}) + }; + relation_steps_.push_back(std::move(rel_step)); + } + } template static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {} @@ -204,7 +275,10 @@ private: const basic_schema &schema_; std::vector steps_; + std::vector relation_steps_; std::unordered_set, visit_key_hash> visited_; + + std::uint64_t current_entity_pk_{0}; }; } #endif //MATADOR_INSERT_QUERY_BUILDER_HPP \ No newline at end of file