From 8f2d07518f31389f437d8ab9f98b1cc5e2885a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Fri, 4 Jul 2025 16:03:04 +0200 Subject: [PATCH] progress on relation completer --- demo/author.hpp | 3 +- demo/book.hpp | 2 +- demo/sandbox.cpp | 43 ++++++++- include/matador/object/object_info.hpp | 12 ++- include/matador/object/schema.hpp | 107 ++++++++++++++++------- include/matador/object/schema_node.hpp | 16 ++-- source/core/object/relation_endpoint.cpp | 2 +- source/core/object/schema.cpp | 26 +++--- source/core/object/schema_node.cpp | 2 +- source/orm/orm/session.cpp | 2 +- 10 files changed, 152 insertions(+), 63 deletions(-) diff --git a/demo/author.hpp b/demo/author.hpp index d14ecbf..0e255df 100644 --- a/demo/author.hpp +++ b/demo/author.hpp @@ -18,7 +18,6 @@ struct author { bool distinguished{false}; matador::object::collection> books; - template void process( Operator& op ) { namespace field = matador::access; @@ -28,7 +27,7 @@ struct author { field::attribute( op, "date_of_birth", date_of_birth, 31 ); field::attribute( op, "year_of_birth", year_of_birth ); field::attribute( op, "distinguished", distinguished ); - field::has_many( op, "books", books, "author_id" ); + field::has_many( op, "books", books, "author_id", matador::utils::default_foreign_attributes ); } }; } diff --git a/demo/book.hpp b/demo/book.hpp index a7c3859..d8ae0fc 100644 --- a/demo/book.hpp +++ b/demo/book.hpp @@ -18,7 +18,7 @@ struct book { namespace field = matador::access; field::primary_key( op, "id", id ); field::attribute( op, "title", title, 511 ); - field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes ); + field::belongs_to( op, "author_id", book_author, matador::utils::default_foreign_attributes ); field::attribute( op, "published_in", published_in ); } }; diff --git a/demo/sandbox.cpp b/demo/sandbox.cpp index e9eeb8f..cb6c2fc 100644 --- a/demo/sandbox.cpp +++ b/demo/sandbox.cpp @@ -5,6 +5,48 @@ #include "author.hpp" #include "book.hpp" + +/* + * node author + * + * relation_endpoints + * 1. - field_name "books" + * - type_index + * - type "has_many" + * - node + * - foreign endpoint (1) "author" + * + * node book + * 2. - field_name "author_id" + * - type_index + * - type "belongs_to" + * - node + * - foreign endpoint (2) "books" + * + * Attach process: + * + * attach + * - relation completer detects "has_many" + * - has_many doesn't find (not yet attached) + * - create endpoint (1) without foreign endpoint + * - create node many_to_many_relation("author_id", "id") + * - 3. create endpoint + * - field name "author_id" + * - type_index + * - type "belongs_to" + * - node > + * - foreign endpoint (1) "author + * - set foreign endpoint of (1) to endpoint (3) + * - attach (internal) node> + * + * attach + * - relation completer detects "belongs_to" + * - belongs_to finds + * - check relation endpoints... + * + * + * + */ using namespace demo; using namespace matador; int main() { @@ -15,5 +57,4 @@ int main() { auto result = schema.attach("authors") .and_then([&schema] { return schema.attach("books"); }); - } \ No newline at end of file diff --git a/include/matador/object/object_info.hpp b/include/matador/object/object_info.hpp index 955e7a0..ad08f1e 100644 --- a/include/matador/object/object_info.hpp +++ b/include/matador/object/object_info.hpp @@ -14,13 +14,15 @@ public: object_info(const std::shared_ptr& node, std::shared_ptr &&ref_column) - : basic_object_info(node, typeid(Type), {}, std::move(ref_column), {}) { + : basic_object_info(node, typeid(Type), {}, std::move(ref_column), {}) + , creator_([]{return std::make_unique(); }){ } object_info(const std::shared_ptr& node, utils::identifier &&pk, std::shared_ptr &&ref_column, object_definition &&definition) - : basic_object_info(node, typeid(Type), std::move(pk), std::move(ref_column), std::move(definition)) { + : basic_object_info(node, typeid(Type), std::move(pk), std::move(ref_column), std::move(definition)) + , creator_([]{return std::make_unique(); }){ } object_info(const std::shared_ptr& node, utils::identifier &&pk, @@ -30,6 +32,12 @@ public: : basic_object_info(node, typeid(Type), std::move(pk), std::move(ref_column), std::move(definition)) , creator_(std::move(creator)){ } + object_info(const std::shared_ptr& node, + object_definition &&definition, + create_func&& creator) + : basic_object_info(node, typeid(Type), std::move(definition)) + , creator_(std::move(creator)){ + } const Type &prototype() const { return prototype_; } std::unique_ptr create() const { return creator_(); } diff --git a/include/matador/object/schema.hpp b/include/matador/object/schema.hpp index 15e37f7..20f6c09 100644 --- a/include/matador/object/schema.hpp +++ b/include/matador/object/schema.hpp @@ -61,7 +61,7 @@ class relation_completer final { public: using value_type = Type; - static void prepare_expected_nodes(schema_node &node) { + static void complete(const std::shared_ptr &node) { relation_completer completer(node); Type obj; @@ -95,13 +95,13 @@ public: void on_has_many_to_many(const char *id, ContainerType &collection, const utils::foreign_attributes &attr); private: - explicit relation_completer(schema_node& node) + explicit relation_completer(const std::shared_ptr& node) : node_(node) - , schema_(node.schema_) + , schema_(node->schema_) , log_(logger::create_logger("relation_completer")) {} private: - schema_node &node_; + std::shared_ptr node_; schema& schema_; logger::logger log_; }; @@ -118,25 +118,16 @@ public: template [[nodiscard]] utils::result attach(const std::string& name, const std::string &parent = "") { - if (has_node(name)) { - return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists")); + // if (has_node(name)) { + // return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists")); + // } + auto node = acquire_node(name); + log_.info("attach node %s", name.c_str()); + relation_completer::complete(node); + if (auto result = attach_node(node, parent); !result) { + return utils::failure(result.err()); } - if (const auto it = expected_node_map_.find(typeid(Type)); it != expected_node_map_.end()) { - const auto node = it->second; - expected_node_map_.erase(it); - node->update_name(name); - - node_map_.insert({node->name(), node})/*.first*/; - type_index_node_map_.insert({node->type_index(), node}); - } else { - // analyze node (collect unknown types by type index) - const auto node = schema_node::make_node(*this, name); - relation_completer::prepare_expected_nodes(*node); - if (auto result = attach_node(node, parent); !result) { - return utils::failure(result.err()); - } - } return utils::ok(); } @@ -204,7 +195,7 @@ public: return utils::failure(result.err()); } - return utils::ok(basic_object_info_ref{result.value()->basic_info()}); + return utils::ok(basic_object_info_ref{result.value()->info()}); } [[nodiscard]] utils::result, utils::error> reference(const std::type_index &type_index) const; @@ -221,6 +212,20 @@ private: [[nodiscard]] utils::result, utils::error> find_node(const std::string &name) const; [[nodiscard]] utils::result, utils::error> find_node(const std::type_index &type_index) const; + template + std::shared_ptr acquire_node(const std::string& name) { + if (const auto it = expected_node_map_.find(typeid(Type)); it != expected_node_map_.end()) { + const auto node = it->second; + expected_node_map_.erase(it); + node->update_name(name); + + return node; + } + + return schema_node::make_node(*this, name); + } + + [[nodiscard]] bool has_node(const std::string &name) const; [[nodiscard]] bool has_node(const std::type_index& index) const; [[nodiscard]] bool has_node(const std::type_index& index, const std::string &name) const; @@ -234,22 +239,40 @@ private: std::string name_; std::shared_ptr root_; - t_node_map node_map_; - t_type_index_node_map type_index_node_map_; + t_node_map nodes_by_name_; + t_type_index_node_map nodes_by_type_; t_type_index_node_map expected_node_map_; logger::logger log_; }; template template -void relation_completer::on_has_many( const char *id, CollectionType&, const char *join_column, const utils::foreign_attributes&, std::enable_if_t::value>* /*unused*/ ) { - using value_type = typename CollectionType::value_type; +void relation_completer::on_has_many( const char *id, CollectionType&, + const char *join_column, + const utils::foreign_attributes&, + std::enable_if_t::value>* /*unused*/ ) { + using value_type = typename CollectionType::value_type::value_type; - const auto node = schema_node::make_relation_node>(schema_, id, [join_column] { - return new many_to_many_relation(join_column, "id"); - }); - - // schema_.attach_node(node, typeid(many_to_many_relation)); + // Check if the object_ptr type is already inserted in the schema (by id) + if (auto result = schema_.find_node(id); !result) { + const std::type_index ti = typeid(many_to_many_relation); + const auto endpoint = node_->info().find_relation_endpoint(ti); + if (endpoint == node_->info().endpoint_end()) { + log_.debug("node '%s' has has many foreign keys '%s' mapped by '%s'", node_->name().c_str(), id, join_column); + const auto node = schema_node::make_relation_node>(schema_, id, [join_column] { + return std::make_unique>(join_column, "id"); + }); + log_.debug("node '%s' many to many type: %s", node_->name().c_str(), typeid(many_to_many_relation).name()); + result = schema_.attach_node(node, ""); + if (!result) { + // Todo: throw internal error + return; + } + node_->info_->register_relation_endpoint(node->type_index(), relation_endpoint(id, relation_type::BELONGS_TO, node)); + node->info_->register_relation_endpoint(node_->type_index(), relation_endpoint(id, relation_type::HAS_MANY, node_)); + } + } else { + } } template @@ -307,7 +330,7 @@ void relation_completer::on_has_one(const char * id, ForeignPointerType &/ schema_.expected_node_map_.insert({ti, schema_node::make_node(schema_, ti.name())}); } else { const auto& foreign_node = result.value(); - if (const auto rit = foreign_node->basic_info().find_relation_endpoint(ti); rit != foreign_node->basic_info().endpoint_end()) { + if (const auto rit = foreign_node->info().find_relation_endpoint(ti); rit != foreign_node->info().endpoint_end()) { if (rit->second.is_has_many()) { } else if (rit->second.is_has_one()) { @@ -322,8 +345,24 @@ void relation_completer::on_has_one(const char * id, ForeignPointerType &/ template template -void relation_completer::on_belongs_to( const char* id, ForeignPointerType& obj, const utils::foreign_attributes& attr ) { - +void relation_completer::on_belongs_to(const char* id, ForeignPointerType& /*obj*/, const utils::foreign_attributes& /*attr*/) { + auto ti = std::type_index(typeid(typename ForeignPointerType::value_type)); + if (auto result = schema_.find_node(ti); !result) { + log_.debug("node '%s' has foreign key '%s' belongs to '%s'", node_->name().c_str(), id, typeid(typename ForeignPointerType::value_type).name()); + } else { + const auto foreign_node = result.value(); + auto it = foreign_node->info().find_relation_endpoint(node_->type_index()); + if (it != foreign_node->info().endpoint_end()) { + auto endpoint = it->second; + } + using TP = many_to_many_relation; + log_.debug("node '%s', many to many type: %s", node_->name().c_str(), typeid(TP).name()); + log_.debug("foreign node '%s', many to many type: %s", foreign_node->name().c_str(), ti.name()); + if (foreign_node->type_index() == ti) { + log_.debug("node '%s' has foreign key '%s' belongs to '%s': already inserted", node_->name().c_str(), id, result.value()->name().c_str()); + } else { + } + } } } diff --git a/include/matador/object/schema_node.hpp b/include/matador/object/schema_node.hpp index 0efe5af..5b486c3 100644 --- a/include/matador/object/schema_node.hpp +++ b/include/matador/object/schema_node.hpp @@ -26,7 +26,8 @@ public: node, std::move(pk_info.pk), std::make_shared(pk_info.pk_column_name, name, pk_info.type, utils::constraints::FOREIGN_KEY), - object_definition{attribute_definition_generator::generate(tree)} + object_definition{attribute_definition_generator::generate(tree)}, + []{ return std::make_unique(); } ); node->info_ = std::move(info); @@ -37,11 +38,12 @@ public: static std::shared_ptr make_relation_node(object::schema& tree, const std::string& name, CreatorFunc &&creator) { auto node = std::shared_ptr(new schema_node(tree, name)); - // auto info = std::make_unique>( - // node, - // object_definition{attribute_definition_generator::generate(tree)} - // ); - // node->info_ = std::move(info); + auto info = std::make_unique>( + node, + object_definition{attribute_definition_generator::generate(tree)}, + std::move(creator) + ); + node->info_ = std::move(info); return node; } @@ -60,7 +62,7 @@ public: [[nodiscard]] node_ptr next() const; [[nodiscard]] node_ptr prev() const; - [[nodiscard]] const basic_object_info& basic_info() const; + [[nodiscard]] const basic_object_info& info() const; void update_name(const std::string& name); diff --git a/source/core/object/relation_endpoint.cpp b/source/core/object/relation_endpoint.cpp index bf137b6..c3c8823 100644 --- a/source/core/object/relation_endpoint.cpp +++ b/source/core/object/relation_endpoint.cpp @@ -3,7 +3,7 @@ #include "matador/object/schema_node.hpp" namespace matador::object { -relation_endpoint::relation_endpoint(std::string field_name, relation_type type, const std::shared_ptr &node) +relation_endpoint::relation_endpoint(std::string field_name, const relation_type type, const std::shared_ptr &node) : field_name_(std::move(field_name)) , type_(type) , node_(node) { diff --git a/source/core/object/schema.cpp b/source/core/object/schema.cpp index 55009aa..8501237 100644 --- a/source/core/object/schema.cpp +++ b/source/core/object/schema.cpp @@ -40,11 +40,11 @@ std::string schema::name() const { utils::result, utils::error> schema::reference(const std::type_index &type_index) const { const auto result = find_node(type_index); if (result) { - return utils::ok((*result)->basic_info().reference_column()); + return utils::ok((*result)->info().reference_column()); } if (const auto it = expected_node_map_.find(type_index); it != expected_node_map_.end()) { - return utils::ok(it->second->basic_info().reference_column()); + return utils::ok(it->second->info().reference_column()); } return utils::failure(result.err()); @@ -69,8 +69,8 @@ utils::result, utils::error> schema::attach_node(co push_back_child(parent_node, node); // Todo: check return value - node_map_.insert({node->name(), node})/*.first*/; - type_index_node_map_.insert({node->type_index(), node}); + nodes_by_name_.insert({node->name(), node})/*.first*/; + nodes_by_type_.insert({node->type_index(), node}); return utils::ok(node); } @@ -88,24 +88,24 @@ utils::result, utils::error> schema::attach_node(co push_back_child(*result, node); // Todo: check return value - node_map_.insert({node->name(), node})/*.first*/; - type_index_node_map_.insert({node->type_index(), node}); + nodes_by_name_.insert({node->name(), node})/*.first*/; + nodes_by_type_.insert({node->type_index(), node}); return utils::ok(node); } utils::result, utils::error> schema::find_node(const std::string &name) const { // first search in the prototype map - const auto i = node_map_.find(name); - if (i == node_map_.end()) { + const auto i = nodes_by_name_.find(name); + if (i == nodes_by_name_.end()) { return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by name '" + name + "'")); } return utils::ok(i->second); } utils::result, utils::error> schema::find_node(const std::type_index &type_index) const { - const auto i = type_index_node_map_.find(type_index); - if (i == type_index_node_map_.end()) { + const auto i = nodes_by_type_.find(type_index); + if (i == nodes_by_type_.end()) { return utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by type '" + std::string(type_index.name()) + "'")); } @@ -129,14 +129,14 @@ void schema::push_back_child(const node_ptr &parent, const node_ptr &child) { } bool schema::has_node(const std::string &name) const { - return node_map_.count(name) > 0; + return nodes_by_name_.count(name) > 0; } bool schema::has_node(const std::type_index &index) const { - return type_index_node_map_.count(index) > 0; + return nodes_by_type_.count(index) > 0; } bool schema::has_node(const std::type_index &index, const std::string &name) const { - return node_map_.count(name) > 0 || type_index_node_map_.count(index) > 0; + return nodes_by_name_.count(name) > 0 || nodes_by_type_.count(index) > 0; } } // namespace matador::object diff --git a/source/core/object/schema_node.cpp b/source/core/object/schema_node.cpp index f50b43c..d217048 100644 --- a/source/core/object/schema_node.cpp +++ b/source/core/object/schema_node.cpp @@ -31,7 +31,7 @@ std::type_index schema_node::type_index() const { return info_->type_index(); } -const basic_object_info &schema_node::basic_info() const { +const basic_object_info &schema_node::info() const { return *info_; } diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index 8b9a0a7..959b53f 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -21,7 +21,7 @@ utils::result session::create_schema() const { auto c = pool_.acquire(); for (const auto &t : *schema_) { auto result = query::query::create() - .table(t.name(), t.basic_info().definition().columns()) + .table(t.name(), t.info().definition().columns()) .execute(*c); if ( !result ) { return utils::failure(result.err());