diff --git a/demo/work.cpp b/demo/work.cpp index 3808d72..1317650 100644 --- a/demo/work.cpp +++ b/demo/work.cpp @@ -76,22 +76,22 @@ int main() { orm::session ses(pool); - auto result = ses.attach("collection_centers"); - // .and_then([&ses] { return ses.attach("user_directories"); }) - // .and_then([&ses] { return ses.attach("ldap_group_schema_settings"); }) - // .and_then([&ses] { return ses.attach("ldap_import_settings"); }) - // .and_then([&ses] { return ses.attach("ldap_user_schema_settings"); }) - // .and_then([&ses] { return ses.attach("internal_user_directories"); }) - // .and_then([&ses] { return ses.attach("ldap_user_directories"); } ) - // .and_then([&ses] { return ses.attach("login_histories"); }) - // .and_then([&ses] { return ses.attach("scenarios"); }) - // .and_then([&ses] { return ses.attach("users"); }) - // .and_then([&ses] { return ses.attach("user_sessions"); }) - // .and_then([&ses] { return ses.attach("jobs"); }) - // .and_then([&ses] { return ses.attach("payloads"); }) - // .and_then([&ses] { return ses.attach("id_list_payloads"); }) - // .and_then([&ses] { return ses.attach("id_payloads"); }) - // .and_then([&ses] { return ses.attach("tasks"); }) + auto result = ses.attach("collection_centers") + .and_then([&ses] { return ses.attach("user_directories"); }) + .and_then([&ses] { return ses.attach("ldap_group_schema_settings"); }) + .and_then([&ses] { return ses.attach("ldap_import_settings"); }) + .and_then([&ses] { return ses.attach("ldap_user_schema_settings"); }) + .and_then([&ses] { return ses.attach("internal_user_directories"); }) + .and_then([&ses] { return ses.attach("ldap_user_directories"); } ) + .and_then([&ses] { return ses.attach("login_histories"); }) + .and_then([&ses] { return ses.attach("scenarios"); }) + .and_then([&ses] { return ses.attach("users"); }) + .and_then([&ses] { return ses.attach("user_sessions"); }) + .and_then([&ses] { return ses.attach("jobs"); }) + .and_then([&ses] { return ses.attach("payloads"); }) + .and_then([&ses] { return ses.attach("id_list_payloads"); }) + .and_then([&ses] { return ses.attach("id_payloads"); }) + .and_then([&ses] { return ses.attach("tasks"); }); // .and_then([&ses] { return ses.create_schema(); }); if (!result) { @@ -99,7 +99,7 @@ int main() { return 0; } - schema.dump(std::cout); + ses.dump_schema(std::cout); // const std::string dns{"sqlite://demo.db"}; // sql::connection c(dns); // diff --git a/include/matador/logger/log_domain.hpp b/include/matador/logger/log_domain.hpp index 297be46..8ac9de7 100644 --- a/include/matador/logger/log_domain.hpp +++ b/include/matador/logger/log_domain.hpp @@ -93,7 +93,7 @@ public: * @param source Source of the log message * @param message Message to log */ - void log(log_level lvl, const std::string &source, const char *message); + void log(log_level lvl, const std::string &source, const char *message) const; /** * Clears the list of log sinks @@ -101,7 +101,7 @@ public: void clear(); private: - void get_time_stamp(char* timestamp_buffer); + void get_time_stamp(char* timestamp_buffer) const; private: static std::map level_strings; @@ -111,7 +111,7 @@ private: log_level_range log_level_range_; - std::mutex mutex_; + mutable std::mutex mutex_; }; } diff --git a/include/matador/logger/logger.hpp b/include/matador/logger/logger.hpp index 04082db..55305ed 100644 --- a/include/matador/logger/logger.hpp +++ b/include/matador/logger/logger.hpp @@ -58,7 +58,7 @@ public: * @param args The arguments to be replaced in the message */ template - void fatal(const std::string &what, ARGS const &... args) { fatal(what.c_str(), args...); } + void fatal(const std::string &what, ARGS const &... args) const { fatal(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -69,7 +69,7 @@ public: * @param args The arguments to be replaced in the message */ template - void fatal(const char *what, ARGS const &... args) { log(log_level::LVL_FATAL, what, args...); } + void fatal(const char *what, ARGS const &... args) const { log(log_level::LVL_FATAL, what, args...); } /** * Writes a log message string with log level LVL_FATAL @@ -80,7 +80,7 @@ public: * @param args The arguments to be replaced in the message */ template - void error(const std::string &what, ARGS const &... args) { error(what.c_str(), args...); } + void error(const std::string &what, ARGS const &... args) const { error(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -91,7 +91,7 @@ public: * @param args The arguments to be replaced in the message */ template - void error(const char *what, ARGS const &... args) { log(log_level::LVL_ERROR, what, args...); } + void error(const char *what, ARGS const &... args) const { log(log_level::LVL_ERROR, what, args...); } /** * Writes a log message string with log level LVL_FATAL @@ -102,7 +102,7 @@ public: * @param args The arguments to be replaced in the message */ template - void warn(const std::string &what, ARGS const &... args) { warn(what.c_str(), args...); } + void warn(const std::string &what, ARGS const &... args) const { warn(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -113,7 +113,7 @@ public: * @param args The arguments to be replaced in the message */ template - void warn(const char *what, ARGS const &... args) { log(log_level::LVL_WARN, what, args...); } + void warn(const char *what, ARGS const &... args) const { log(log_level::LVL_WARN, what, args...); } /** * Writes a log message string with log level LVL_FATAL @@ -124,7 +124,7 @@ public: * @param args The arguments to be replaced in the message */ template - void info(const std::string &what, ARGS const &... args) { info(what.c_str(), args...); } + void info(const std::string &what, ARGS const &... args) const { info(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -135,7 +135,7 @@ public: * @param args The arguments to be replaced in the message */ template - void info(const char *what, ARGS const &... args) { log(log_level::LVL_INFO, what, args...); } + void info(const char *what, ARGS const &... args) const { log(log_level::LVL_INFO, what, args...); } /** * Writes a log message string with log level LVL_FATAL @@ -146,7 +146,7 @@ public: * @param args The arguments to be replaced in the message */ template - void debug(const std::string &what, ARGS const &... args) { debug(what.c_str(), args...); } + void debug(const std::string &what, ARGS const &... args) const { debug(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -157,7 +157,7 @@ public: * @param args The arguments to be replaced in the message */ template - void debug(const char *what, ARGS const &... args) { log(log_level::LVL_DEBUG, what, args...); } + void debug(const char *what, ARGS const &... args) const { log(log_level::LVL_DEBUG, what, args...); } /** * Writes a log message string with log level LVL_FATAL @@ -168,7 +168,7 @@ public: * @param args The arguments to be replaced in the message */ template - void trace(const std::string &what, ARGS const &... args) { trace(what.c_str(), args...); } + void trace(const std::string &what, ARGS const &... args) const { trace(what.c_str(), args...); } /** * Writes a log message represented by a char pointer with log level LVL_FATAL @@ -179,7 +179,7 @@ public: * @param args The arguments to be replaced in the message */ template - void trace(const char *what, ARGS const &... args) { log(log_level::LVL_TRACE, what, args...); } + void trace(const char *what, ARGS const &... args) const { log(log_level::LVL_TRACE, what, args...); } /** * Writes a log message represented by a char pointer @@ -191,7 +191,7 @@ public: * @param args The arguments to be replaced in the message */ template - void log(log_level lvl, const char *what, ARGS const &... args); + void log(log_level lvl, const char *what, ARGS const &... args) const; /** * Writes a log message represented by a char pointer @@ -207,14 +207,14 @@ public: * * @return Represented log source name */ - const std::string& source() const; + [[nodiscard]] const std::string& source() const; /** * Returns the name of the connected log domain * * @return The name of the log domain */ - std::string domain() const; + [[nodiscard]] std::string domain() const; private: std::string source_; @@ -222,8 +222,7 @@ private: }; template -void logger::log(log_level lvl, const char *what, ARGS const &... args) -{ +void logger::log(log_level lvl, const char *what, ARGS const &... args) const { char message_buffer[16384]; #ifdef _MSC_VER diff --git a/include/matador/object/foreign_node_completer.hpp b/include/matador/object/foreign_node_completer.hpp new file mode 100644 index 0000000..3c74d2d --- /dev/null +++ b/include/matador/object/foreign_node_completer.hpp @@ -0,0 +1,110 @@ +#ifndef FOREIGN_NODE_COMPLETER_HPP +#define FOREIGN_NODE_COMPLETER_HPP + +#include "matador/object/internal/shadow_schema.hpp" +#include "matador/object/schema_node.hpp" +#include "matador/object/join_columns_collector.hpp" +#include "matador/object/object_ptr.hpp" + +#include "matador/logger/log_manager.hpp" + +#include + +namespace matador::object { + +/** + * Processes the given node and ensures + * that all foreign nodes needed by the given node + * relations are attached in schema. + * + * @tparam Type Type of node to complete + */ +class foreign_node_completer final { +private: + using node_ptr = std::shared_ptr; + +public: + template + static void complete(const std::shared_ptr &node) { + internal::shadow_schema shadow(node->schema_); + foreign_node_completer completer(shadow); + + completer.complete_node(node); + } + + template + static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, std::enable_if_t && !std::is_same_v> * = nullptr) {} + static void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t /*size*/) {} + static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} + template + static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template + static void on_attribute(const char * /*id*/, std::optional &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + + 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*/) {} + + template + void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr); + template + void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) {} + + template + void on_has_many_to_many(const char *id, CollectionType &collection, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr); + template + void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr); + +private: + explicit foreign_node_completer(internal::shadow_schema &shadow); + + template + void complete_node(const std::shared_ptr &node) { + nodes_.push(node); + + Type obj; + access::process(*this, obj); + nodes_.pop(); + } + + template + void attach_node() { + if (schema_.schema_contains(typeid(Type))) { + return; + } + const auto node = schema_node::make_node(schema_.schema(), ""); + if (auto result = schema_.attach_node(node)) { + complete(result.value()); + } + } +private: + std::stack nodes_; + internal::shadow_schema &schema_; + logger::logger log_; + join_columns_collector join_columns_collector_{}; +}; + + +template +void foreign_node_completer::on_belongs_to( const char* id, 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(); +} + +template +void foreign_node_completer::on_has_many_to_many( const char* id, CollectionType& collection, const char* join_column, const char* inverse_join_column, const utils::foreign_attributes& attr ) { + attach_node(); +} + +template +void foreign_node_completer::on_has_many_to_many( const char* id, CollectionType& collection, const utils::foreign_attributes& attr ) { + attach_node(); +} + +} +#endif //FOREIGN_NODE_COMPLETER_HPP diff --git a/include/matador/object/internal/shadow_schema.hpp b/include/matador/object/internal/shadow_schema.hpp new file mode 100644 index 0000000..e940e46 --- /dev/null +++ b/include/matador/object/internal/shadow_schema.hpp @@ -0,0 +1,33 @@ +#ifndef SHADOW_SCHEMA_HPP +#define SHADOW_SCHEMA_HPP + +#include "matador/utils/result.hpp" +#include "matador/utils/error.hpp" + +#include +#include + +namespace matador::object { +class schema; +class schema_node; +} + +namespace matador::object::internal { +class shadow_schema { +private: + using node_ptr = std::shared_ptr; + +public: + explicit shadow_schema(object::schema& s); + + schema& schema() const; + bool schema_contains(const std::type_index& ti) const; + [[nodiscard]] utils::result find_node(const std::type_index &type_index) const; + [[nodiscard]] utils::result attach_node(const std::shared_ptr &node) const; + [[nodiscard]] utils::result detach_node(const std::shared_ptr &node) const; + +private: + object::schema& schema_; +}; +} +#endif //SHADOW_SCHEMA_HPP diff --git a/include/matador/object/relation_completer.hpp b/include/matador/object/relation_completer.hpp new file mode 100644 index 0000000..f48ce45 --- /dev/null +++ b/include/matador/object/relation_completer.hpp @@ -0,0 +1,338 @@ +#ifndef RELATION_COMPLETER_HPP +#define RELATION_COMPLETER_HPP + +#include "matador/object/internal/shadow_schema.hpp" +#include "matador/object/many_to_many_relation.hpp" +#include "matador/object/join_columns_collector.hpp" +#include "matador/object/object_ptr.hpp" +#include "matador/object/schema_node.hpp" + +#include "matador/logger/log_manager.hpp" + +#include + +namespace matador::object { + +/* + * 1. has_many (MM) + * no belongs to + * relation table is needed + * - element type is a foreign table (FT), + * then relation table must look like follows: + * relation_table + * where MM and FT must be defined as belongs to + * - element type if a builtin type BT (i.e. string, int, etc.), + * then the relation table must look like follows: + * relation_table + * where MM as belongs to and BT as given type + * + * 2. has_many_to_many (MM1, MM2) + * relation_table is needed + * relation_table + * where MM1 and MM2 must be defined as belongs to + * + * 3. hans_many (MM) <-> belongs_to (BT) + * belongs_to has foreign key to the has_many side + * no relation table needed + * + * 4. has_one to belongs_to + * no relation table is needed + * + * 5. has_many (MM) <-> has_one (HO) + * invalid relation -> error + * + * 6. has_one + * no has_many or belongs_to + * invalid relation -> error + */ +template +class relation_completer final { +private: + using node_ptr = std::shared_ptr; + +public: + using endpoint_ptr = std::shared_ptr; + + static void complete(const std::shared_ptr &node) { + internal::shadow_schema shadow(node->schema_); + relation_completer completer(shadow); + + completer.complete_node_relations(node); + } + + template + static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, std::enable_if_t && !std::is_same_v> * = nullptr) {} + static void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t /*size*/) {} + static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} + template + static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template + static void on_attribute(const char * /*id*/, std::optional &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + + template + void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr); + template + 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); + template + void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr); + + template + void on_has_many_to_many(const char *id, CollectionType &collection, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr); + template + void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr); + +private: + explicit relation_completer(internal::shadow_schema &shadow) + : schema_(shadow) + , log_(logger::create_logger("relation_completer")) { + } + + void complete_node_relations(const std::shared_ptr &node) { + nodes_.push(node); + + Type obj; + access::process(*this, obj); + nodes_.pop(); + } + + static void register_relation_endpoints(const endpoint_ptr &endpoint, + const endpoint_ptr &other_endpoint); + static void link_relation_endpoints(const endpoint_ptr &endpoint, + const endpoint_ptr &other_endpoint); + +private: + std::stack nodes_; + internal::shadow_schema &schema_; + logger::logger log_; + join_columns_collector join_columns_collector_{}; +}; + +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*/) { + // Shortcut to a value type of object_ptr::value_type in a collection + using value_type = typename CollectionType::value_type::value_type; + using relation_value_type = many_to_many_relation; + + // 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; + } + 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); + const auto node = schema_node::make_relation_node( + schema_.schema(), id, [join_column] { + return std::make_unique(join_column, "id"); + }); + result = schema_.attach_node(node); + if (!result) { + // Todo: throw internal error + return; + } + 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, node); + nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); + foreign_endpoint->node_->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + link_relation_endpoints(local_endpoint, foreign_endpoint); + } else { + const auto &foreign_node = result.value(); + if (const auto rit = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); + rit != foreign_node->info().endpoint_end()) { + if (rit->second->is_belongs_to()) { + rit->second->node_ = foreign_node; + const auto localEndpoint = std::make_shared(id, relation_type::HAS_MANY, nodes_.top()); + nodes_.top()->info_->register_relation_endpoint(nodes_.top()->type_index(), localEndpoint); + link_relation_endpoints(localEndpoint, rit->second); + } else { + // Todo: throw internal error relation node has invalid type + } + } else { + // Todo: throw internal error couldn't find relation node + } + + } +} + +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; + using relation_value_type = many_to_relation; + + const auto node = schema_node::make_relation_node( + schema_.schema(), id, [join_column] { + return std::make_unique(join_column, "value"); + }); + + const auto result = schema_.attach_node(node); + if (!result) { + // 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, node); + nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); + foreign_endpoint->node_->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); + link_relation_endpoints(local_endpoint, foreign_endpoint); +} + +template +template +void relation_completer::on_has_many_to_many(const char *id, + CollectionType &/*collection*/, + const char *join_column, + 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 { + // Relation not not found. + auto creator = [join_column, inverse_join_column] { + return std::make_unique(join_column, inverse_join_column); + }; + + auto node = schema_node::make_relation_node(schema_.schema(), id, std::move(creator)); + + 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); + nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); + node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint); + link_relation_endpoints(local_endpoint, join_endpoint); + node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), + inverse_join_endpoint); + result = schema_.attach_node(node); + if (!result) { + // Todo: throw internal error + return; + } + } +} + +template +template +void relation_completer::on_has_many_to_many(const char *id, + CollectionType &collection, + const utils::foreign_attributes &attr) { + const auto join_columns = join_columns_collector_.collect(); + on_has_many_to_many( + id, + collection, + join_columns.inverse_join_column.c_str(), + join_columns.join_column.c_str(), + attr); +} + +template +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) { + // 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; + } + } + } +} + +template +template +void relation_completer::on_belongs_to(const char *id, + ForeignPointerType & /*obj*/, + 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) { + // 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()); + nodes_.top()->info_->register_relation_endpoint(ti, endpoint); + } + } +} + +template +void relation_completer::register_relation_endpoints(const endpoint_ptr &endpoint, + const endpoint_ptr &other_endpoint) { + endpoint->node_->info_->register_relation_endpoint(other_endpoint->node_->type_index(), endpoint); + other_endpoint->node_->info_->register_relation_endpoint(endpoint->node_->type_index(), other_endpoint); + link_relation_endpoints(endpoint, other_endpoint); +} + +template +void relation_completer::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) { + endpoint->link_foreign_endpoint(other_endpoint); + other_endpoint->link_foreign_endpoint(endpoint); +} +} +#endif //RELATION_COMPLETER_HPP diff --git a/include/matador/object/relation_endpoint.hpp b/include/matador/object/relation_endpoint.hpp index 891ba3b..bf82aa2 100644 --- a/include/matador/object/relation_endpoint.hpp +++ b/include/matador/object/relation_endpoint.hpp @@ -39,7 +39,7 @@ public: void link_foreign_endpoint(const std::shared_ptr& endpoint); private: - template + template friend class relation_completer; std::string field_name_; diff --git a/include/matador/object/schema.hpp b/include/matador/object/schema.hpp index 1efe156..b2514fd 100644 --- a/include/matador/object/schema.hpp +++ b/include/matador/object/schema.hpp @@ -3,9 +3,9 @@ #include "matador/logger/log_manager.hpp" -#include "matador/object/join_columns_collector.hpp" -#include "matador/object/many_to_many_relation.hpp" #include "matador/object/error_code.hpp" +#include "matador/object/foreign_node_completer.hpp" +#include "matador/object/relation_completer.hpp" #include "matador/object/schema_node.hpp" #include "matador/object/schema_node_iterator.hpp" @@ -15,123 +15,16 @@ #include "matador/logger/logger.hpp" #include -#include #include #include namespace matador::object { +namespace internal { +class shadow_schema; +} + utils::error make_error(error_code ec, const std::string &msg); -class schema; - -/* - * 1. has_many (MM) - * no belongs to - * relation table is needed - * - element type is a foreign table (FT), - * then relation table must look like follows: - * relation_table - * where MM and FT must be defined as belongs to - * - element type if a builtin type BT (i.e. string, int, etc.), - * then the relation table must look like follows: - * relation_table - * where MM as belongs to and BT as given type - * - * 2. has_many_to_many (MM1, MM2) - * relation_table is needed - * relation_table - * where MM1 and MM2 must be defined as belongs to - * - * 3. hans_many (MM) <-> belongs_to (BT) - * belongs_to has foreign key to the has_many side - * no relation table needed - * - * 4. has_one to belongs_to - * no relation table is needed - * - * 5. has_many (MM) <-> has_one (HO) - * invalid relation -> error - * - * 6. has_one - * no has_many or belongs_to - * invalid relation -> error - */ -template -class relation_completer final { -public: - using value_type = Type; - using endpoint_ptr = std::shared_ptr; - - static void complete(const std::shared_ptr &node) { - relation_completer completer(node); - - Type obj; - access::process(completer, obj); - } - - template - static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, - std::enable_if_t && !std::is_same_v> * = nullptr) { - } - - static void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t /*size*/) { - } - - static void on_revision(const char * /*id*/, uint64_t &/*rev*/) { - } - - template - static void on_attribute(const char * /*id*/, AttributeType &/*val*/, - const utils::field_attributes &/*attr*/ = utils::null_attributes) { - } - - template - static void on_attribute(const char * /*id*/, std::optional &/*val*/, - const utils::field_attributes &/*attr*/ = utils::null_attributes) { - } - - template - void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr); - template - 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); - template - void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, - std::enable_if_t::value> * = nullptr); - - template - void on_has_many_to_many(const char *id, CollectionType &collection, const char *join_column, - const char *inverse_join_column, const utils::foreign_attributes &attr); - template - void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr); - -private: - explicit relation_completer(const std::shared_ptr &node) - : node_(node) - , schema_(node->schema_) - , log_(logger::create_logger("relation_completer")) { - } - - static void register_relation_endpoints(const endpoint_ptr &endpoint, - const endpoint_ptr &other_endpoint); - static void link_relation_endpoints(const endpoint_ptr &endpoint, - const endpoint_ptr &other_endpoint); - - template - void ensure_type_is_known(); - -private: - std::shared_ptr node_; - schema &schema_; - logger::logger log_; - join_columns_collector join_columns_collector_; -}; - - class schema { public: typedef const_schema_node_iterator const_iterator; /**< Shortcut for the list const iterator. */ @@ -145,14 +38,20 @@ 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")); - // } - auto node = schema_node::make_node(*this, name); - relation_completer::complete(node); - if (auto result = attach_node(node, parent); !result) { - return utils::failure(result.err()); - } + if (const auto it = nodes_by_type_.find(typeid(Type)); it == nodes_by_type_.end() ) { + auto node = schema_node::make_node(*this, name); + foreign_node_completer::complete(node); + relation_completer::complete(node); + if (auto result = attach_node(node, parent); !result) { + return utils::failure(result.err()); + } + } else if (!has_node(name)) { + it->second->update_name(name); + nodes_by_name_[name] = it->second; + log_.info("attach: update node name to '%s' (type: %s)", it->second->name().c_str(), it->second->type_index().name()); + } else { + return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists")); + } return utils::ok(); } @@ -162,16 +61,22 @@ public: const auto ti = std::type_index(typeid(SuperType)); auto result = find_node(ti); if (!result) { - return utils::failure(make_error(error_code::NodeNotFound, - "Parent node '" + std::string(ti.name()) + "' not found")); + return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(ti.name()) + "' not found")); } return attach(name, (*result)->name()); } + /** + * Detaches a given node from the schema. If the + * node is a parent of other nodes, these nodes are + * detached as well. + * + * @param node Node to detach from schema + * @return Result object indicating success or failure + */ [[nodiscard]] utils::result detach(const node_ptr &node); - // [[nodiscard]] utils::result detach(const std::string& name); /** * Return the first schema node. @@ -209,6 +114,14 @@ public: */ [[nodiscard]] std::string name() const; + [[nodiscard]] bool contains(const std::string &name) const; + [[nodiscard]] bool contains(const std::type_index &index) const; + template < typename Type > + [[nodiscard]] bool contains() const { + return contains(std::type_index(typeid(Type))); + } + + template [[nodiscard]] utils::result, utils::error> info() const { auto result = find_node(std::type_index(typeid(Type))); @@ -238,10 +151,8 @@ private: using t_node_map = std::unordered_map; using t_type_index_node_map = std::unordered_map; - [[nodiscard]] utils::result attach_node(const std::shared_ptr &node, - const std::string &parent); - [[nodiscard]] utils::result attach_node(const std::shared_ptr &node, - const std::type_index &type_index); + [[nodiscard]] utils::result attach_node(const node_ptr &node, const std::string &parent); + // [[nodiscard]] utils::result attach_node(const node_ptr &node, const std::type_index &type_index); [[nodiscard]] utils::result find_node(const std::string &name) const; [[nodiscard]] utils::result find_node(const std::type_index &type_index) const; template @@ -257,8 +168,8 @@ private: void remove_node(const node_ptr &node); private: - template - friend class relation_completer; + friend class internal::shadow_schema; + friend class foreign_node_completer; std::string name_; std::shared_ptr root_; @@ -267,241 +178,6 @@ private: t_type_index_node_map nodes_by_type_; 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*/) { - // Shortcut to value type of object_ptr::value_type in collection - using value_type = typename CollectionType::value_type::value_type; - - // Check if the object_ptr type is already inserted in the schema (by id) - if (auto result = schema_.find_node(id); !result) { - // Type was not found. - // Ensure value_type is known to the schema. Name will be added later - ensure_type_is_known(); - // Create and attach the relation node. - const std::type_index ti = typeid(many_to_many_relation); - if (const auto endpoint = node_->info().find_relation_endpoint(ti); endpoint == node_->info().endpoint_end()) { - // Endpoint was not found - 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"); - }); - result = schema_.attach_node(node, ""); - if (!result) { - // Todo: throw internal error - return; - } - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node_); - const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, node); - node_->info_->register_relation_endpoint(typeid(value_type), local_endpoint); - foreign_endpoint->node_->info_->register_relation_endpoint(node_->type_index(), foreign_endpoint); - link_relation_endpoints(local_endpoint, foreign_endpoint); - } - } else { - // Type was found. - // Check if the relation node is already attached. - const auto &foreign_node = result.value(); - if (const auto rit = foreign_node->info_->find_relation_endpoint(node_->type_index()); - rit != foreign_node->info().endpoint_end()) { - if (rit->second->is_belongs_to()) { - rit->second->node_ = foreign_node; - const auto endpoint = std::make_shared(id, relation_type::HAS_MANY, node_); - node_->info_->register_relation_endpoint(node_->type_index(), endpoint); - link_relation_endpoints(endpoint, rit->second); - } else { - // Todo: throw internal error relation node has invalid type - } - } else { - // Todo: throw internal error couldn't find relation node - } - } -} - -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; - using relation_value_type = many_to_relation; - - const auto node = schema_node::make_relation_node( - schema_, id, [join_column] { - return std::make_unique(join_column, "value"); - }); - - const auto result = schema_.attach_node(node, ""); - if (!result) { - // Todo: throw internal exception - } - - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node_); - const auto foreign_endpoint = std::make_shared(join_column, relation_type::BELONGS_TO, node); - node_->info_->register_relation_endpoint(typeid(value_type), local_endpoint); - foreign_endpoint->node_->info_->register_relation_endpoint(node_->type_index(), foreign_endpoint); - link_relation_endpoints(local_endpoint, foreign_endpoint); -} - -template -template -void relation_completer::on_has_many_to_many(const char *id, - CollectionType &/*collection*/, - const char *join_column, - const char *inverse_join_column, - const utils::foreign_attributes &/*attr*/) { - auto result = schema_.find_node(id); - if (result) { - const auto &foreign_node = result.value(); - const auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node_); - node_->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); - const auto it = foreign_node->info_->find_relation_endpoint(node_->type_index()); - if (it == foreign_node->info().endpoint_end()) { - // Todo: Throw error - return; - } - link_relation_endpoints(local_endpoint, it->second); - } else { - using relation_value_type = many_to_many_relation; - auto creator = [join_column, inverse_join_column] { - return std::make_unique(join_column, inverse_join_column); - }; - - auto node = schema_node::make_relation_node(schema_, id, std::move(creator)); - - auto local_endpoint = std::make_shared(id, relation_type::HAS_MANY, node_); - 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); - node_->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint); - node->info_->register_relation_endpoint(node_->type_index(), join_endpoint); - link_relation_endpoints(local_endpoint, join_endpoint); - node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), - inverse_join_endpoint); - result = schema_.attach_node(node, ""); - if (!result) { - // Todo: throw internal error - return; - } - } -} - -template -template -void relation_completer::on_has_many_to_many(const char *id, - CollectionType &collection, - const utils::foreign_attributes &attr) { - const auto join_columns = join_columns_collector_.collect(); - on_has_many_to_many( - id, - collection, - join_columns.inverse_join_column.c_str(), - join_columns.join_column.c_str(), - attr); -} - -template -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.is_ok()) { - // Node was not found - // Create node without the foreign relation endpoint - log_.debug("node '%s' has foreign key '%s' has one '%s'", node_->name().c_str(), id, ti.name()); - node_->info_-> - register_relation_endpoint(ti, std::make_shared(id, relation_type::HAS_ONE, node_)); - } else { - const auto &foreign_node = result.value(); - if (const auto it = foreign_node->info().find_relation_endpoint(typeid(Type)); - 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, node_); - node_->info_->register_relation_endpoint(ti, endpoint); - link_relation_endpoints(endpoint, it->second); - } else { - // Todo: Throw internal error - return; - } - } - } -} - -template -template -void relation_completer::on_belongs_to(const char *id, - ForeignPointerType & /*obj*/, - 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) { - // Type was not found - // Create node without the foreign relation endpoint - log_.debug("node '%s' has foreign key '%s' belongs to '%s'", node_->name().c_str(), id, ti.name()); - node_->info_->register_relation_endpoint( - ti, std::make_shared(id, relation_type::BELONGS_TO, schema::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(typeid(Type)); - 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, node_); - node_->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(foreign_endpoint->node_); - foreign_endpoint->node_ = node_; - node_->info_->register_relation_endpoint(node_->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, node_); - node_->info_->register_relation_endpoint(ti, endpoint); - } - } -} - -template -void relation_completer::register_relation_endpoints(const endpoint_ptr &endpoint, - const endpoint_ptr &other_endpoint) { - endpoint->node_->info_->register_relation_endpoint(other_endpoint->node_->type_index(), endpoint); - other_endpoint->node_->info_->register_relation_endpoint(endpoint->node_->type_index(), other_endpoint); - link_relation_endpoints(endpoint, other_endpoint); -} - -template -void relation_completer::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) { - endpoint->link_foreign_endpoint(other_endpoint); - other_endpoint->link_foreign_endpoint(endpoint); -} - -template -template -void relation_completer::ensure_type_is_known() { - if (auto result = schema_.find_node(); result) { - return; - } - auto result = schema_.attach_node(schema_node::make_node(schema_, ""), ""); - relation_completer::complete(result.value()); -} } #endif //SCHEMA_HPP diff --git a/include/matador/object/schema_node.hpp b/include/matador/object/schema_node.hpp index b007587..e268bde 100644 --- a/include/matador/object/schema_node.hpp +++ b/include/matador/object/schema_node.hpp @@ -83,8 +83,9 @@ private: private: friend class schema; - template + template friend class relation_completer; + friend class foreign_node_completer; friend class const_schema_node_iterator; object::schema &schema_; diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index 4d2e898..810e4ea 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -121,6 +121,8 @@ public: [[nodiscard]] const sql::dialect& dialect() const; + void dump_schema(std::ostream &os) const; + private: friend class query_select; diff --git a/include/matador/utils/primary_key_attribute.hpp b/include/matador/utils/primary_key_attribute.hpp new file mode 100644 index 0000000..53acd6d --- /dev/null +++ b/include/matador/utils/primary_key_attribute.hpp @@ -0,0 +1,39 @@ +#ifndef PRIMARY_KEY_ATTRIBUTE_HPP +#define PRIMARY_KEY_ATTRIBUTE_HPP + +namespace matador::utils { +class primary_key_attribute final { +public: + /** + * Creates primary_key_attribute instance + * with size 0 (zero). + */ + primary_key_attribute() = default; + /** + * Creates primary_key_attribute instance + * with given size. + * + * @param size Size of the attribute + */ + primary_key_attribute(size_t size); // NOLINT(*-explicit-constructor) + ~primary_key_attribute() = default; + primary_key_attribute(const primary_key_attribute &) = default; + primary_key_attribute(primary_key_attribute &&) = default; + primary_key_attribute &operator=(const primary_key_attribute &) = default; + primary_key_attribute &operator=(primary_key_attribute &&) = default; + + primary_key_attribute& operator=(size_t size); + + /** + * Returns the size of the field + * + * @return Size of the field + */ + [[nodiscard]] size_t size() const; + +private: + size_t size_ = 0; +}; + +} +#endif //PRIMARY_KEY_ATTRIBUTE_HPP diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index 53ac056..4114771 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -17,12 +17,15 @@ add_library(matador-core STATIC ../../include/matador/object/attribute_definition.hpp ../../include/matador/object/basic_object_info.hpp ../../include/matador/object/error_code.hpp + ../../include/matador/object/foreign_node_completer.hpp + ../../include/matador/object/internal/shadow_schema.hpp ../../include/matador/object/many_to_many_relation.hpp ../../include/matador/object/object_definition.hpp ../../include/matador/object/object_info.hpp ../../include/matador/object/object_proxy.hpp ../../include/matador/object/object_ptr.hpp ../../include/matador/object/primary_key_resolver.hpp + ../../include/matador/object/relation_completer.hpp ../../include/matador/object/relation_endpoint.hpp ../../include/matador/object/schema.hpp ../../include/matador/object/schema_node.hpp @@ -53,6 +56,7 @@ add_library(matador-core STATIC ../../include/matador/utils/macro_map.hpp ../../include/matador/utils/os.hpp ../../include/matador/utils/placeholder.hpp + ../../include/matador/utils/primary_key_attribute.hpp ../../include/matador/utils/result.hpp ../../include/matador/utils/singleton.hpp ../../include/matador/utils/string.hpp @@ -72,6 +76,8 @@ add_library(matador-core STATIC object/attribute_definition.cpp object/basic_object_info.cpp object/error_code.cpp + object/foreign_node_completer.cpp + object/internal/shadow_schema.cpp object/object_definition.cpp object/primary_key_resolver.cpp object/relation_endpoint.cpp @@ -88,6 +94,7 @@ add_library(matador-core STATIC utils/leader_follower_thread_pool.cpp utils/library.cpp utils/os.cpp + utils/primary_key_attribute.cpp utils/string.cpp utils/thread_pool.cpp utils/types.cpp diff --git a/source/core/logger/log_domain.cpp b/source/core/logger/log_domain.cpp index ddb6a3f..badb1d5 100644 --- a/source/core/logger/log_domain.cpp +++ b/source/core/logger/log_domain.cpp @@ -86,8 +86,7 @@ void log_domain::add_sink(sink_ptr sink) sinks.push_back(std::move(sink)); } -void log_domain::log(log_level lvl, const std::string &source, const char *message) -{ +void log_domain::log(log_level lvl, const std::string &source, const char *message) const { if (lvl < log_level_range_.max_level || lvl > log_level_range_.min_level) { return; } @@ -114,8 +113,7 @@ void log_domain::clear() sinks.clear(); } -void log_domain::get_time_stamp(char* const timestamp_buffer) -{ +void log_domain::get_time_stamp(char* const timestamp_buffer) const { std::lock_guard l(mutex_); details::gettimestamp(timestamp_buffer, 80); } diff --git a/source/core/object/foreign_node_completer.cpp b/source/core/object/foreign_node_completer.cpp new file mode 100644 index 0000000..9f45dc6 --- /dev/null +++ b/source/core/object/foreign_node_completer.cpp @@ -0,0 +1,11 @@ +#include "matador/object/foreign_node_completer.hpp" + +#include "matador/object/schema.hpp" + +#include "matador/logger/logger.hpp" + +namespace matador::object { +foreign_node_completer::foreign_node_completer(internal::shadow_schema &shadow) +: schema_(shadow) +, log_(logger::create_logger("node_completer")) {} +} \ No newline at end of file diff --git a/source/core/object/internal/shadow_schema.cpp b/source/core/object/internal/shadow_schema.cpp new file mode 100644 index 0000000..a2e3923 --- /dev/null +++ b/source/core/object/internal/shadow_schema.cpp @@ -0,0 +1,29 @@ +#include "matador/object/internal/shadow_schema.hpp" + +#include "matador/object/schema.hpp" +#include "matador/object/schema_node.hpp" + +namespace matador::object::internal { +shadow_schema::shadow_schema( object::schema& s ) +: schema_(s) {} + +schema& shadow_schema::schema() const { + return schema_; +} + +bool shadow_schema::schema_contains( const std::type_index& ti ) const { + return schema_.contains(ti); +} + +utils::result shadow_schema::find_node( const std::type_index& type_index ) const { + return schema_.find_node(type_index); +} + +utils::result shadow_schema::attach_node( const std::shared_ptr& node ) const { + return schema_.attach_node(node, ""); +} + +utils::result shadow_schema::detach_node( const std::shared_ptr& node ) const { + return schema_.detach(node); +} +} \ No newline at end of file diff --git a/source/core/object/schema.cpp b/source/core/object/schema.cpp index bf342be..a76b4dc 100644 --- a/source/core/object/schema.cpp +++ b/source/core/object/schema.cpp @@ -25,25 +25,6 @@ utils::result schema::detach(const node_ptr &node) { return utils::ok(); } -// utils::result schema::detach(const std::string& name) { -// const auto nit = nodes_by_name_.find(name); -// if (nit == nodes_by_name_.end()) { -// return utils::failure(make_error(error_code::NodeNotFound, "Node '" + name + "' not found.")); -// } -// -// const auto ti = nit->second->type_index(); -// nodes_by_name_.erase(nit); -// -// const auto it = nodes_by_type_.find(ti); -// if (it == nodes_by_type_.end()) { -// return utils::failure(make_error(error_code::NodeNotFound, "Node '" + name + "' not found.")); -// } -// -// nodes_by_type_.erase(it); -// -// return utils::ok(); -// } - schema::const_iterator schema::begin() const { return const_iterator(root_->first_child_->next_sibling_); } @@ -64,6 +45,14 @@ std::string schema::name() const { return name_; } +bool schema::contains( const std::string& name ) const { + return nodes_by_name_.count(name) > 0; +} + +bool schema::contains( const std::type_index& index ) const { + return nodes_by_type_.count(index) > 0; +} + utils::result, utils::error> schema::reference(const std::type_index &type_index) const { const auto result = find_node(type_index); if (result) { @@ -93,7 +82,7 @@ utils::result schema::attach_node(const std::sha return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists.")); } - log_.info("attach node '%s' (type: %s)", node->name().c_str(), node->type_index().name()); + log_.info("attach: insert node '%s' (type: %s)", node->name().c_str(), node->type_index().name()); // set node to root node auto parent_node = root_; @@ -114,24 +103,24 @@ utils::result schema::attach_node(const std::sha return utils::ok(node); } -utils::result schema::attach_node(const std::shared_ptr &node, - const std::type_index &type_index) { - if (has_node(node)) { - return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists.")); - } - auto result = find_node(type_index); - if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) { - return result; - } - - insert_node(*result, node); - - // Todo: check return value - nodes_by_name_.insert({node->name(), node})/*.first*/; - nodes_by_type_.insert({node->type_index(), node}); - - return utils::ok(node); -} +// utils::result schema::attach_node(const std::shared_ptr &node, +// const std::type_index &type_index) { +// if (has_node(node)) { +// return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists.")); +// } +// auto result = find_node(type_index); +// if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) { +// return result; +// } +// +// insert_node(*result, node); +// +// // Todo: check return value +// nodes_by_name_.insert({node->name(), node})/*.first*/; +// nodes_by_type_.insert({node->type_index(), node}); +// +// return utils::ok(node); +// } utils::result schema::find_node(const std::string &name) const { // first search in the prototype map diff --git a/source/core/utils/primary_key_attribute.cpp b/source/core/utils/primary_key_attribute.cpp new file mode 100644 index 0000000..a92573c --- /dev/null +++ b/source/core/utils/primary_key_attribute.cpp @@ -0,0 +1,15 @@ +#include "matador/utils/primary_key_attribute.hpp" + +namespace matador::utils { +primary_key_attribute::primary_key_attribute(const size_t size) +: size_(size) {} + +primary_key_attribute& primary_key_attribute::operator=(const size_t size) { + size_ = size; + return *this; +} + +size_t primary_key_attribute::size() const { + return size_; +} +} \ No newline at end of file diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index 959b53f..0276ecc 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -110,6 +110,10 @@ const class sql::dialect &session::dialect() const return dialect_; } +void session::dump_schema( std::ostream& os ) const { + schema_->dump(os); +} + query::fetchable_query session::build_select_query(entity_query_data &&data) { return query::query::select(data.columns) .from(*data.root_table)