diff --git a/demo/sandbox.cpp b/demo/sandbox.cpp index eba9ee1..867832a 100644 --- a/demo/sandbox.cpp +++ b/demo/sandbox.cpp @@ -104,8 +104,8 @@ using namespace demo; using namespace matador; int main() { - logger::default_min_log_level(logger::log_level::LVL_DEBUG); - logger::add_log_sink(logger::create_stdout_sink()); + // logger::default_min_log_level(logger::log_level::LVL_DEBUG); + // logger::add_log_sink(logger::create_stdout_sink()); { // has_many with builtin-type diff --git a/demo/work.cpp b/demo/work.cpp index 1c939dd..4eb1c26 100644 --- a/demo/work.cpp +++ b/demo/work.cpp @@ -97,7 +97,7 @@ int main() { .and_then([&ses] { return ses.attach("id_payloads"); }) .and_then([&ses] { return ses.attach("tasks"); }) ; - // ses.dump_schema(std::cout); + ses.dump_schema(std::cout); if (!result) { std::cout << "error: " << result.err().message() << std::endl; diff --git a/include/matador/object/basic_object_info.hpp b/include/matador/object/basic_object_info.hpp index bbc28ff..806ba41 100644 --- a/include/matador/object/basic_object_info.hpp +++ b/include/matador/object/basic_object_info.hpp @@ -30,7 +30,7 @@ public: [[nodiscard]] bool has_primary_key() const; [[nodiscard]] const utils::identifier& primary_key() const; - void register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint); + endpoint_iterator register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint); void unregister_relation_endpoint(const std::type_index &type); [[nodiscard]] const_endpoint_iterator find_relation_endpoint(const std::type_index &type) const; diff --git a/include/matador/object/foreign_node_completer.hpp b/include/matador/object/foreign_node_completer.hpp index 207142b..9911370 100644 --- a/include/matador/object/foreign_node_completer.hpp +++ b/include/matador/object/foreign_node_completer.hpp @@ -44,7 +44,7 @@ public: template void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); template - static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {} + void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); template void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr); @@ -91,6 +91,11 @@ void foreign_node_completer::on_belongs_to( const char* /*id*/, ForeignPointerTy attach_node(); } +template +void foreign_node_completer::on_has_one( const char*, ForeignPointerType&, const utils::foreign_attributes& ) { + attach_node(); +} + template void foreign_node_completer::on_has_many( const char* /*id*/, CollectionType&, const char* /*join_column*/, const utils::foreign_attributes& /*attr*/, std::enable_if_t::value>* ) { attach_node(); diff --git a/include/matador/object/internal/shadow_schema.hpp b/include/matador/object/internal/shadow_schema.hpp index bd86349..b598fc1 100644 --- a/include/matador/object/internal/shadow_schema.hpp +++ b/include/matador/object/internal/shadow_schema.hpp @@ -20,9 +20,10 @@ private: public: explicit shadow_schema(object::schema& s); - object::schema& schema() const; + [[nodiscard]] object::schema& schema() const; [[nodiscard]] bool schema_contains(const std::type_index& ti) const; [[nodiscard]] utils::result find_node(const std::type_index &type_index) const; + [[nodiscard]] utils::result find_node(const std::string &name) const; [[nodiscard]] utils::result attach_node(const std::shared_ptr &node) const; [[nodiscard]] utils::result detach_node(const std::shared_ptr &node) const; diff --git a/include/matador/object/relation_completer.hpp b/include/matador/object/relation_completer.hpp index 852fe3f..66771d2 100644 --- a/include/matador/object/relation_completer.hpp +++ b/include/matador/object/relation_completer.hpp @@ -126,7 +126,20 @@ void relation_completer::on_has_many(const char *id, CollectionType &, // Todo: throw internal error or attach node return; } - if (const auto endpoint = nodes_.top()->info().find_relation_endpoint(typeid(relation_value_type)); endpoint == nodes_.top()->info().endpoint_end()) { + auto local_it = nodes_.top()->info().find_relation_endpoint(typeid(value_type)); + if (local_it == nodes_.top()->info().endpoint_end()) { + const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, result.value()); + local_it = nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); + } + auto foreign_it = result.value()->info().find_relation_endpoint(typeid(value_type)); + if (foreign_it == result.value()->info().endpoint_end()) { + const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, nodes_.top()); + foreign_it = result.value()->info_->register_relation_endpoint(typeid(Type), foreign_endpoint); + } + link_relation_endpoints(local_it->second, foreign_it->second); + + /* + if (const auto endpoint = nodes_.top()->info().find_relation_endpoint(typeid(relation_value_type)); endpoint == nodes_.top()->info().endpoint_end()) { // Endpoint was not found log_.debug("node '%s' has has many foreign keys '%s' mapped by '%s'", nodes_.top()->name().c_str(), id, join_column); result = schema_node::make_relation_node( @@ -159,6 +172,7 @@ void relation_completer::on_has_many(const char *id, CollectionType &, } } + */ } template @@ -179,10 +193,13 @@ void relation_completer::on_has_many(const char *id, CollectionType &, con // Todo: throw internal exception } - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, nodes_.top()); - const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, result.value()); + // const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, nodes_.top()); + // const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, result.value()); + const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, result.value()); + const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, nodes_.top()); + nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); - foreign_endpoint->node_->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + result.value()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); link_relation_endpoints(local_endpoint, foreign_endpoint); } @@ -194,20 +211,18 @@ void relation_completer::on_has_many_to_many(const char *id, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) { using relation_value_type = many_to_many_relation; - auto result = schema_.find_node(typeid(relation_value_type)); - if (result) { - // Found relation node. - // Complete relation links - const auto &foreign_node = result.value(); - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, nodes_.top()); - nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); - const auto it = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); - if (it == foreign_node->info().endpoint_end()) { - // Todo: Throw error - return; - } - link_relation_endpoints(local_endpoint, it->second); - } else { + using value_type = typename CollectionType::value_type::value_type; + + // Check if the object_ptr type is already inserted in the schema (by id) + auto result = schema_.find_node(typeid(value_type)); + if (!result) { + // Todo: throw internal error or attach node + return; + } + + const auto foreign_node = result.value(); + result = schema_.find_node(id); + if (!result) { // Relation not found. auto creator = [join_column, inverse_join_column] { return std::make_unique(join_column, inverse_join_column); @@ -220,15 +235,21 @@ void relation_completer::on_has_many_to_many(const char *id, } auto& node = result.value(); - auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, nodes_.top()); - auto join_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, node); - auto inverse_join_endpoint = std::make_shared(inverse_join_column, relation_type::BELONGS_TO, - node); + const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); + const auto join_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, nodes_.top()); + const auto inverse_join_endpoint = std::make_shared(inverse_join_column, relation_type::BELONGS_TO, foreign_node); + const auto foreign_endpoint = std::make_shared(id, relation_type::HAS_MANY, node); + // register relation endpoint in local node nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); + // register relation endpoint in foreign node + foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + // register endpoints in relation node node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint); + node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), inverse_join_endpoint); + + // link endpoints link_relation_endpoints(local_endpoint, join_endpoint); - node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), - inverse_join_endpoint); + link_relation_endpoints(foreign_endpoint, inverse_join_endpoint); } } @@ -251,28 +272,24 @@ template void relation_completer::on_has_one(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) { - const auto ti = std::type_index(typeid(typename ForeignPointerType::value_type)); - if (const auto result = schema_.find_node(ti); !result) { + using value_type = typename ForeignPointerType::value_type; + const auto ti = std::type_index(typeid(value_type)); + const auto result = schema_.find_node(ti); + if (!result) { // Node was not found - // Create node without the foreign relation endpoint - log_.debug("node '%s' has foreign key '%s' has one '%s'", nodes_.top()->name().c_str(), id, ti.name()); - nodes_.top()->info_-> - register_relation_endpoint(ti, std::make_shared(id, relation_type::HAS_ONE, nodes_.top())); - } else { - const auto &foreign_node = result.value(); - if (const auto it = foreign_node->info().find_relation_endpoint(nodes_.top()->type_index()); - it != foreign_node->info().endpoint_end()) { - if (it->second->is_belongs_to()) { - it->second->node_ = foreign_node; - const auto endpoint = std::make_shared(id, relation_type::HAS_ONE, nodes_.top()); - nodes_.top()->info_->register_relation_endpoint(ti, endpoint); - link_relation_endpoints(endpoint, it->second); - } else { - // Todo: Throw internal error - return; - } - } + // Todo: Throw internal error + return; } + + auto local_it = nodes_.top()->info().find_relation_endpoint(typeid(value_type)); + if (local_it == nodes_.top()->info().endpoint_end()) { + const auto local_endpoint = std::make_shared(id, relation_type::HAS_ONE, result.value()); + local_it = nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); + } + if (const auto foreign_it = result.value()->info().find_relation_endpoint(typeid(Type)); foreign_it != result.value()->info().endpoint_end()) { + link_relation_endpoints(local_it->second, foreign_it->second); + } + } template @@ -282,40 +299,39 @@ void relation_completer::on_belongs_to(const char *id, const utils::foreign_attributes & /*attr*/) { using value_type = typename ForeignPointerType::value_type; const auto ti = std::type_index(typeid(value_type)); - if (auto result = schema_.find_node(ti); !result) { + auto result = schema_.find_node(ti); + if (!result) { // Type was not found - // Create node without the foreign relation endpoint - log_.debug("node '%s' has foreign key '%s' belongs to '%s'", nodes_.top()->name().c_str(), id, ti.name()); - nodes_.top()->info_->register_relation_endpoint( - ti, std::make_shared(id, relation_type::BELONGS_TO, node_ptr{})); - } else { - // Type was found - const auto &foreign_node = result.value(); - // Check foreign node and relation endpoint - if (const auto it = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); - it != foreign_node->info().endpoint_end()) { - // Found corresponding relation endpoint in the foreign node - if (it->second->is_has_one()) { - const auto endpoint = std::make_shared(id, relation_type::BELONGS_TO, nodes_.top()); - nodes_.top()->info_->register_relation_endpoint(ti, endpoint); - link_relation_endpoints(endpoint, it->second); - } else if (it->second->foreign_endpoint()->node().type_index() == typeid(many_to_many_relation)) { - // Endpoint is a "many_to_many_relation". This means there - // is a "many_to_many_relation" node attached. Because of being a - // "belongs_to"-relation the "many_to_many_relation" can be removed - // (detach), and the endpoints must be adjusted - const auto foreign_endpoint = it->second->foreign_endpoint(); - const auto detach_result = schema_.detach_node(foreign_endpoint->node_); - foreign_endpoint->node_ = nodes_.top(); - nodes_.top()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); - } else { - // check type - } - } else { - // Relation node was not found, create only endpoint. - const auto endpoint = std::make_shared(id, relation_type::BELONGS_TO, nodes_.top()); + // Todo: Throw internal error + return; + } + // Type was found + const auto &foreign_node = result.value(); + // Check foreign node and relation endpoint + if (const auto it = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); + it != foreign_node->info().endpoint_end()) { + // Found corresponding relation endpoint in the foreign node + if (it->second->is_has_one()) { + const auto endpoint = std::make_shared(id, relation_type::BELONGS_TO, foreign_node); nodes_.top()->info_->register_relation_endpoint(ti, endpoint); + link_relation_endpoints(endpoint, it->second); + } else if (it->second->foreign_endpoint()->node().type_index() == typeid(many_to_many_relation)) { + // Endpoint is a "many_to_many_relation". This means there + // is a "many_to_many_relation" node attached. Because of being a + // "belongs_to"-relation the "many_to_many_relation" can be removed + // (detach), and the endpoints must be adjusted + const auto foreign_endpoint = it->second->foreign_endpoint(); + const auto detach_result = schema_.detach_node(foreign_endpoint->node_); + foreign_endpoint->node_ = foreign_node; + // foreign_endpoint->node_ = nodes_.top(); + nodes_.top()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + } else { + // check type } + } else { + // Relation node was not found, create only endpoint. + const auto endpoint = std::make_shared(id, relation_type::BELONGS_TO, foreign_node); + nodes_.top()->info_->register_relation_endpoint(ti, endpoint); } } diff --git a/include/matador/object/schema.hpp b/include/matador/object/schema.hpp index 6737231..717a359 100644 --- a/include/matador/object/schema.hpp +++ b/include/matador/object/schema.hpp @@ -48,8 +48,10 @@ public: } else if (!has_node(name)) { it->second->update_name(name); nodes_by_name_[name] = it->second; + relation_completer::complete(it->second); log_.info("attach: update node name to '%s' (type: %s)", it->second->name().c_str(), it->second->type_index().name()); } else { + // remove_node( ) return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists")); } diff --git a/source/core/object/basic_object_info.cpp b/source/core/object/basic_object_info.cpp index 807e519..8187dee 100644 --- a/source/core/object/basic_object_info.cpp +++ b/source/core/object/basic_object_info.cpp @@ -55,8 +55,8 @@ const utils::identifier& basic_object_info::primary_key() const { return identifier_.value(); } -void basic_object_info::register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint) { - relation_endpoints_.insert(std::make_pair(type, endpoint)); +basic_object_info::endpoint_iterator basic_object_info::register_relation_endpoint(const std::type_index &type, const std::shared_ptr &endpoint) { + return relation_endpoints_.insert(std::make_pair(type, endpoint)).first; } void basic_object_info::unregister_relation_endpoint(const std::type_index &type) { diff --git a/source/core/object/internal/shadow_schema.cpp b/source/core/object/internal/shadow_schema.cpp index a2e3923..09a2692 100644 --- a/source/core/object/internal/shadow_schema.cpp +++ b/source/core/object/internal/shadow_schema.cpp @@ -19,6 +19,10 @@ utils::result shadow_schema::find_node( c return schema_.find_node(type_index); } +utils::result shadow_schema::find_node( const std::string& name ) const { + return schema_.find_node(name); +} + utils::result shadow_schema::attach_node( const std::shared_ptr& node ) const { return schema_.attach_node(node, ""); } diff --git a/source/core/object/schema.cpp b/source/core/object/schema.cpp index 35deb42..8fc7e23 100644 --- a/source/core/object/schema.cpp +++ b/source/core/object/schema.cpp @@ -64,11 +64,11 @@ utils::result, utils::error> schema::refer void schema::dump(std::ostream &os) const { for (const auto &node : *this) { - os << "node [" << node->name() << "] (" << node->type_index().name() << ")\n"; + os << "node [" << node->name() << "] (" /*<< node->type_index().name()*/ << ")\n"; for (auto it = node->info().endpoint_begin(); it != node->info().endpoint_end(); ++it) { - os << " " << /*node.name() << "::" <<*/ it->second->field_name() << " (" << it->second->type_name() << ")"; + os << " " /*<< node->name() << "::"*/ << it->second->field_name() << " (" << it->second->type_name() << ")"; if (it->second->foreign_endpoint()) { - os << " <---> " << it->second->foreign_endpoint()->node().name() << "::" << it->second->foreign_endpoint()->field_name() << " (" << it->second->foreign_endpoint()->type_name() << ")\n"; + os << " <---> " << it->second->node().name() << "::" << it->second->foreign_endpoint()->field_name() << " (" << it->second->foreign_endpoint()->type_name() << ")\n"; } else { os << "\n"; } diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index e5bfd64..56674cf 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -27,17 +27,31 @@ utils::result session::create_schema() const { for (auto it = node->info().endpoint_begin(); it != node->info().endpoint_end(); ++it) { dependency_graph[node->name()].push_back(it->second->node().name()); - in_degree[it->second->node().name()] = std::make_pair(0, it->second->node_ptr()); - in_degree[it->second->node().name()].first++; + auto n = it->second->node_ptr(); + auto nn = node->name(); + if (it->second->is_has_one()) { + continue; + } + if (const auto dit = in_degree.find(it->second->node().name()); dit == in_degree.end()) { + in_degree[it->second->node().name()] = std::make_pair(1, it->second->node_ptr()); + } else { + in_degree[it->second->node().name()].first++; + } } // Ensure the current node exists in the graph representation - if (in_degree.find(node->name()) == in_degree.end()) { - in_degree[node->name()].first = 0; + in_degree[node->name()] = std::make_pair(0, node); } } + for (const auto &it : dependency_graph) { + std::cout << "Dependency graph " << it.first << std::endl; + for (const auto &neighbor: it.second) { + std::cout << " " << neighbor << std::endl; + } + std::cout << std::endl; + } // Step 2: Perform topological sorting (Kahn's Algorithm) std::queue zero_in_degree; std::vector sorted_order; @@ -48,6 +62,11 @@ utils::result session::create_schema() const { } } + for (const auto &it : in_degree) { + std::cout << "In degree table " << it.second.second->name() << " (" << it.second.first << ")" << std::endl; + } + + while (!zero_in_degree.empty()) { auto current = zero_in_degree.front(); zero_in_degree.pop(); @@ -65,7 +84,7 @@ utils::result session::create_schema() const { // Step 3: Check for cycles if (sorted_order.size() != in_degree.size()) { - throw std::logic_error("Cycle detected in table dependencies"); + // throw std::logic_error("Cycle detected in table dependencies"); } // Step 4: Create tables in the sorted order @@ -86,15 +105,15 @@ utils::result session::create_schema() const { // } } - auto c = pool_.acquire(); - for (const auto &node: *schema_) { - auto result = query::query::create() - .table(node->name(), node->info().definition().columns()) - .execute(*c); - if (!result) { - return utils::failure(result.err()); - } - } + // auto c = pool_.acquire(); + // for (const auto &node: *schema_) { + // auto result = query::query::create() + // .table(node->name(), node->info().definition().columns()) + // .execute(*c); + // if (!result) { + // return utils::failure(result.err()); + // } + // } return utils::ok();