From 76263318666b9ab2094cc3eae8887b673c65a9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 15 Jan 2026 12:40:54 +0100 Subject: [PATCH] implemented lazy loading for object_ptr and belongs_to --- backends/postgres/src/postgres_connection.cpp | 4 +- backends/postgres/src/postgres_statement.cpp | 2 +- include/matador/object/object_proxy.hpp | 114 +++-- include/matador/object/object_ptr.hpp | 55 ++- include/matador/object/object_resolver.hpp | 35 ++ .../object/object_resolver_factory.hpp | 27 ++ include/matador/object/relation_completer.hpp | 1 - include/matador/orm/session.hpp | 396 ++++++++++-------- include/matador/query/basic_schema.hpp | 64 +++ .../query/criteria/criteria_operators.hpp | 6 + include/matador/query/generator.hpp | 12 +- .../query/intermediates/fetchable_query.hpp | 24 +- .../query_insert_intermediate.hpp | 2 +- include/matador/query/query.hpp | 3 +- .../query_builder.hpp} | 88 ++-- .../query_builder_exception.hpp | 4 +- .../matador/query/query_object_resolver.hpp | 62 +++ include/matador/query/schema.hpp | 82 ++-- include/matador/query/table_column.hpp | 2 - include/matador/sql/connection.hpp | 6 +- include/matador/sql/connection_pool.hpp | 2 + include/matador/sql/executor.hpp | 3 + include/matador/sql/field.hpp | 3 + .../matador/sql/interface/statement_impl.hpp | 4 +- .../matador/sql/interface/statement_proxy.hpp | 2 + .../sql/internal/identifier_reader.hpp | 51 +++ .../internal/identifier_statement_binder.hpp | 33 ++ .../sql/internal/query_result_impl.hpp | 37 +- .../sql/internal/query_result_pk_resolver.hpp | 8 +- .../sql/internal/resolver_producer.hpp | 24 ++ .../internal/statement_object_resolver.hpp | 35 ++ .../matador/sql/producer_resolver_factory.hpp | 21 + include/matador/sql/query_context.hpp | 44 +- include/matador/sql/query_record_result.hpp | 132 ++++++ include/matador/sql/query_result.hpp | 78 ++-- include/matador/sql/record.hpp | 3 + include/matador/sql/statement.hpp | 51 ++- include/matador/sql/statement_cache.hpp | 2 +- include/matador/utils/identifier.hpp | 62 ++- .../utils/identifier_to_value_converter.hpp | 29 ++ include/matador/utils/value.hpp | 3 + source/core/CMakeLists.txt | 13 +- .../utils/identifier_to_value_converter.cpp | 53 +++ source/core/utils/value.cpp | 8 + source/orm/CMakeLists.txt | 23 +- source/orm/orm/session.cpp | 25 +- source/orm/orm/session_query_builder.cpp | 83 ---- source/orm/query/basic_schema.cpp | 92 ++++ .../orm/query/criteria/criteria_operators.cpp | 15 + source/orm/query/generator.cpp | 2 +- .../query/intermediates/fetchable_query.cpp | 23 +- .../orm/query/internal/query_result_impl.cpp | 20 +- source/orm/query/query_builder.cpp | 81 ++++ .../query_builder_exception.cpp | 6 +- source/orm/query/query_object_resolver.cpp | 16 + source/orm/query/schema.cpp | 80 +--- source/orm/sql/connection.cpp | 30 +- source/orm/sql/connection_pool.cpp | 20 +- source/orm/sql/field.cpp | 11 + source/orm/sql/internal/identifier_reader.cpp | 6 + .../internal/identifier_statement_binder.cpp | 56 +++ source/orm/sql/internal/resolver_producer.cpp | 10 + source/orm/sql/producer_resolver_factory.cpp | 14 + source/orm/sql/query_result.cpp | 9 +- source/orm/sql/record.cpp | 20 +- source/orm/sql/statement.cpp | 11 +- test/backends/QueryBasicTest.cpp | 6 +- test/backends/QueryStatementTests.cpp | 40 +- test/backends/QueryTest.cpp | 144 ++++--- test/backends/SessionFixture.cpp | 10 +- test/backends/SessionFixture.hpp | 2 +- test/backends/SessionTest.cpp | 63 ++- test/backends/StatementTest.cpp | 40 +- test/models/author.hpp | 7 + test/models/book.hpp | 3 + test/models/department.hpp | 6 + test/models/product.hpp | 9 +- test/models/shipment.hpp | 8 + test/orm/backend/test_connection.cpp | 13 +- test/orm/backend/test_statement.cpp | 5 +- test/orm/orm/SessionQueryBuilderTest.cpp | 18 +- 81 files changed, 1790 insertions(+), 927 deletions(-) create mode 100644 include/matador/object/object_resolver.hpp create mode 100644 include/matador/object/object_resolver_factory.hpp create mode 100644 include/matador/query/basic_schema.hpp rename include/matador/{orm/session_query_builder.hpp => query/query_builder.hpp} (74%) rename include/matador/{orm => query}/query_builder_exception.hpp (82%) create mode 100644 include/matador/query/query_object_resolver.hpp create mode 100644 include/matador/sql/internal/identifier_reader.hpp create mode 100644 include/matador/sql/internal/identifier_statement_binder.hpp create mode 100644 include/matador/sql/internal/resolver_producer.hpp create mode 100644 include/matador/sql/internal/statement_object_resolver.hpp create mode 100644 include/matador/sql/producer_resolver_factory.hpp create mode 100644 include/matador/sql/query_record_result.hpp create mode 100644 include/matador/utils/identifier_to_value_converter.hpp create mode 100644 source/core/utils/identifier_to_value_converter.cpp delete mode 100644 source/orm/orm/session_query_builder.cpp create mode 100644 source/orm/query/basic_schema.cpp create mode 100644 source/orm/query/query_builder.cpp rename source/orm/{orm => query}/query_builder_exception.cpp (55%) create mode 100644 source/orm/query/query_object_resolver.cpp create mode 100644 source/orm/sql/internal/identifier_reader.cpp create mode 100644 source/orm/sql/internal/identifier_statement_binder.cpp create mode 100644 source/orm/sql/internal/resolver_producer.cpp create mode 100644 source/orm/sql/producer_resolver_factory.cpp diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index 879e170..9b02188 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -109,7 +109,9 @@ utils::result, utils::error> postgres_co prototype.at(i).change_type(type); } - return utils::ok(std::make_unique(std::make_unique(res), prototype)); + return utils::ok(std::make_unique(std::make_unique(res), + std::move(prototype), + context.resolver_factory)); } std::string postgres_connection::generate_statement_name(const sql::query_context &query) { diff --git a/backends/postgres/src/postgres_statement.cpp b/backends/postgres/src/postgres_statement.cpp index 6a49cdb..b104a5e 100644 --- a/backends/postgres/src/postgres_statement.cpp +++ b/backends/postgres/src/postgres_statement.cpp @@ -60,7 +60,7 @@ utils::result, utils::error> postgres_st return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql)); } - return utils::ok(std::make_unique(std::make_unique(res), query_.prototype)); + return utils::ok(std::make_unique(std::make_unique(res), query_.prototype, query_.resolver_factory)); } std::unique_ptr postgres_statement::create_binder() const { diff --git a/include/matador/object/object_proxy.hpp b/include/matador/object/object_proxy.hpp index 2362b7c..4934604 100644 --- a/include/matador/object/object_proxy.hpp +++ b/include/matador/object/object_proxy.hpp @@ -3,92 +3,76 @@ #include "matador/object/primary_key_resolver.hpp" +#include "matador/object/object_resolver.hpp" + #include +#include +#include +#include namespace matador::object { - -class basic_object_proxy { -public: - virtual ~basic_object_proxy() = default; - - [[nodiscard]] virtual void *get() const = 0; -}; - -template < typename Type > -class object_proxy; - -template < typename Type > -class object_resolver { -public: - virtual ~object_resolver() = default; - - virtual Type* resolve(const object_proxy &proxy) const = 0; -}; - -template < typename Type > -class static_object_resolver final : public object_resolver { -public: - static_object_resolver() = default; - explicit static_object_resolver(Type* obj) - : obj_(obj) {} - explicit static_object_resolver(std::unique_ptr obj) - : obj_(std::move(obj)) {} - - Type* resolve(const object_proxy &/*proxy*/) const override { - return obj_.get(); - } - -private: - std::unique_ptr obj_{}; -}; - - template -class object_proxy final : public basic_object_proxy { +class object_proxy final { public: object_proxy() = default; - explicit object_proxy(Type* obj) - : resolver_(std::make_unique>(obj)) - , pk_(primary_key_resolver::resolve_object(*obj).pk) {} - [[nodiscard]] void *get() const override { return static_cast(pointer()); } - - Type* operator->() const { return pointer(); } - Type& operator*() { return *pointer(); } - const Type& operator*() const { return *pointer(); } - - Type* pointer() const { return resolve(); } - - Type& ref() { return *pointer(); } - const Type& ref() const { return *pointer(); } - - void reset(Type* obj) { - pk_ = primary_key_resolver::resolve_object(*obj).pk; - resolver_ = std::make_unique>(obj); + // Lazy + object_proxy(std::weak_ptr> resolver, utils::identifier id) + : resolver_(resolver) + , pk_(std::move(id)) { } - void reset(std::unique_ptr obj) { - pk_ = primary_key_resolver::resolve_object(*obj).pk; - resolver_ = std::make_unique>(std::move(obj)); + + // Eager + object_proxy(std::weak_ptr> resolver, std::shared_ptr obj) + : obj_(obj) + , resolver_(resolver) + , pk_(primary_key_resolver::resolve_object(*obj).pk) { } - void reset(std::unique_ptr> &&resolver) { - resolver_ = std::move(resolver); + + // Transient + explicit object_proxy(std::shared_ptr obj) + : obj_(obj) + , pk_(primary_key_resolver::resolve_object(*obj).pk) { } + [[nodiscard]] void *raw_pointer() const { return static_cast(pointer()); } + + Type *operator->() { return pointer(); } + Type &operator*() { return *pointer(); } + const Type &operator*() const { return *pointer(); } + + Type *pointer() const { return resolve(); } + [[nodiscard]] bool empty() const { return resolver_ == nullptr; } [[nodiscard]] bool valid() const { return !empty(); } [[nodiscard]] bool has_primary_key() const { return !pk_.is_null(); } - [[nodiscard]] const utils::identifier& primary_key() const { return pk_; } + [[nodiscard]] const utils::identifier &primary_key() const { return pk_; } void primary_key(const utils::identifier &pk) { pk_ = pk; } private: Type* resolve() const { - return resolver_->resolve(*this); + if (obj_) { + return obj_.get(); + } + + std::lock_guard lock(mutex_); + auto resolver = resolver_.lock(); + if (!resolver) { + return nullptr; + // Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception + // throw std::runtime_error("Detached proxy (session expired)"); + } + + const_cast&>(obj_) = resolver->resolve(pk_); + + return obj_.get(); } private: - std::unique_ptr> resolver_{std::make_unique>()}; - utils::identifier pk_{}; + std::shared_ptr obj_{}; + std::weak_ptr> resolver_{}; + utils::identifier pk_{}; + mutable std::mutex mutex_{}; }; - } #endif //OBJECT_PROXY_HPP diff --git a/include/matador/object/object_ptr.hpp b/include/matador/object/object_ptr.hpp index c32f57c..c3d345d 100644 --- a/include/matador/object/object_ptr.hpp +++ b/include/matador/object/object_ptr.hpp @@ -12,44 +12,53 @@ template class object_ptr { public: object_ptr() - : ptr_(std::make_shared>()) {} - explicit object_ptr(Type *obj) - : ptr_(std::make_shared>(obj)) {} - object_ptr(const object_ptr &other) : ptr_(other.ptr_) {} - object_ptr(object_ptr &&other) noexcept : ptr_(std::move(other.ptr_)) {} + : proxy_(std::make_shared>()) {} + explicit object_ptr(std::shared_ptr obj) + : proxy_(std::make_shared>(obj)) {} + explicit object_ptr(std::shared_ptr> obj) + : proxy_(std::move(obj)) {} + object_ptr(const object_ptr &other) = default; + object_ptr(object_ptr &&other) noexcept = default; object_ptr &operator=(const object_ptr &other) = default; object_ptr &operator=(object_ptr &&other) = default; - using value_type = Type; - Type* operator->() const { return ptr_->pointer(); } - Type& operator*() { return ptr_->ref(); } - const Type& operator*() const { return ptr_->ref(); } + bool operator==(const object_ptr &other) const { + return get() == other.get(); + } + bool operator!=(const object_ptr &other) const { return !operator==(other); } - [[nodiscard]] bool empty() const { return ptr_->pointer() == nullptr; } - void reset(Type *obj) { - ptr_->reset(obj); + using value_type = Type; + + Type *operator->() const { return get(); } + Type &operator*() { return *get(); } + const Type &operator*() const { return *get(); } + + [[nodiscard]] bool empty() const { return get() == nullptr; } + + Type *get() const { + return proxy_ ? proxy_->pointer() : nullptr; } - Type* get() const { return static_cast(ptr_->pointer()); } + void reset() { proxy_.reset(); } operator bool() { return valid(); } - bool valid() { return ptr_ != nullptr; } + bool valid() { return proxy_ != nullptr; } + + [[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); } + [[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); } + void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); } - [[nodiscard]] bool has_primary_key() const { return ptr_->has_primary_key(); } - [[nodiscard]] const utils::identifier& primary_key() const { return ptr_->primary_key(); } - void primary_key(const utils::identifier &pk) { ptr_->primary_key(pk); } private: - std::shared_ptr> ptr_{}; + std::shared_ptr > proxy_{}; }; template -struct is_object_ptr : std::false_type -{}; +struct is_object_ptr : std::false_type { +}; template -struct is_object_ptr> : std::true_type -{}; - +struct is_object_ptr > : std::true_type { +}; } #endif //OBJECT_PTR_HPP diff --git a/include/matador/object/object_resolver.hpp b/include/matador/object/object_resolver.hpp new file mode 100644 index 0000000..7ffea49 --- /dev/null +++ b/include/matador/object/object_resolver.hpp @@ -0,0 +1,35 @@ +#ifndef MATADOR_OBJECT_LOADER_HPP +#define MATADOR_OBJECT_LOADER_HPP + +#include +#include + +namespace matador::utils { +class identifier; +} + +namespace matador::object { + +class abstract_object_resolver { +public: + virtual ~abstract_object_resolver() = default; + + [[nodiscard]] const std::type_index& type() const { return type_; } + +protected: + explicit abstract_object_resolver(const std::type_index& ti) : type_(ti) {} + +public: + const std::type_index type_; +}; + + +template +class object_resolver : public abstract_object_resolver { +public: + object_resolver() : abstract_object_resolver(typeid(Type)) {} + + virtual std::shared_ptr resolve(const utils::identifier& id) = 0; +}; +} +#endif //MATADOR_OBJECT_LOADER_HPP \ No newline at end of file diff --git a/include/matador/object/object_resolver_factory.hpp b/include/matador/object/object_resolver_factory.hpp new file mode 100644 index 0000000..ac8390d --- /dev/null +++ b/include/matador/object/object_resolver_factory.hpp @@ -0,0 +1,27 @@ +#ifndef MATADOR_OBJECT_RESOLVER_FACTORY_HPP +#define MATADOR_OBJECT_RESOLVER_FACTORY_HPP + +#include "matador/object/object_resolver.hpp" + +#include + +namespace matador::object { +class object_resolver_factory { +public: + virtual ~object_resolver_factory() = default; + + template + std::shared_ptr> resolver() { + const auto res = acquire_resolver(std::type_index(typeid(Type))); + if (!res) { + return std::dynamic_pointer_cast>(res); + } + + return std::dynamic_pointer_cast>(res); + } + + virtual std::shared_ptr acquire_resolver(const std::type_index &type) = 0; + virtual void register_resolver(std::shared_ptr &&resolver) = 0; +}; +} +#endif //MATADOR_OBJECT_RESOLVER_FACTORY_HPP \ No newline at end of file diff --git a/include/matador/object/relation_completer.hpp b/include/matador/object/relation_completer.hpp index 122522f..675e1b8 100644 --- a/include/matador/object/relation_completer.hpp +++ b/include/matador/object/relation_completer.hpp @@ -5,7 +5,6 @@ #include "matador/object/foreign_node_completer.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/repository_node.hpp" #include "matador/logger/log_manager.hpp" diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index 4d434fd..f0be5d5 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -2,8 +2,8 @@ #define QUERY_SESSION_HPP #include "matador/orm/error_code.hpp" -#include "matador/orm/session_query_builder.hpp" +#include "matador/query/query_builder.hpp" #include "matador/query/criteria.hpp" #include "matador/query/query.hpp" #include "matador/query/generator.hpp" @@ -18,70 +18,83 @@ #include "matador/object/object_ptr.hpp" #include +#include namespace matador::orm { - utils::error make_error(error_code ec, const std::string &msg); struct session_context { + session_context(utils::message_bus &bus, std::string dns, const size_t count, const size_t cache_size = 500) + : bus(bus) + , dns(std::move(dns)) + , connection_count(count) + , cache_size(cache_size) { + } utils::message_bus &bus; - sql::connection_pool &pool; + std::string dns; + size_t connection_count{}; size_t cache_size{500}; -}; - -template -class lazy_object_resolver final : public object::object_resolver { -public: - explicit lazy_object_resolver(sql::statement &&stmt) : stmt_(std::move(stmt)) {} - - Type* resolve(const object::object_proxy& proxy) const override { - return proxy.resolve(); - } - -private: - sql::statement stmt_; + std::shared_ptr resolver_factory = std::make_shared(); }; class prototype_builder final { public: explicit prototype_builder(const std::unordered_map &statements_per_column) - : statements_per_column_(statements_per_column) {} - - - template < typename Type > - Type build() const { - Type obj; - access::process(*this, obj); - - return obj; + : statements_per_column_(statements_per_column) { } - template < class V > - static void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {} - static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} + template - static void on_attribute(const char * /*id*/, Type &/*obj*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + Type build() const { + Type obj; + access::process(*this, obj); + + return obj; + } + + template + static void on_primary_key(const char * /*id*/, V &/*pk*/, + const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) { + } + + static void on_revision(const char * /*id*/, uint64_t &/*rev*/) { + } + + template + static void on_attribute(const char * /*id*/, Type &/*obj*/, + const utils::field_attributes &/*attr*/ = utils::null_attributes) { + } + template void on_belongs_to(const char *id, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) { - const auto it = statements_per_column_.find(id); - if (it == statements_per_column_.end()) { - return; - } - - + const auto it = statements_per_column_.find(id); + if (it == statements_per_column_.end()) { + return; + } } + template - void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {} + void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) { + } + template - void on_has_many(const char * /*id*/, ContainerType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} + void on_has_many(const char * /*id*/, ContainerType &, const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/) { + } + template - void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {} + void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const char * /*join_column*/, + const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) { + } + template - void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const utils::foreign_attributes &/*attr*/) {} + void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const utils::foreign_attributes &/*attr*/) { + } private: const std::unordered_map &statements_per_column_; }; + class session final : public sql::executor { public: session(session_context &&ctx, const query::schema &scm); @@ -95,8 +108,10 @@ public: */ template utils::result, utils::error> insert(Type *obj); - template< class Type, typename... Args > - utils::result, utils::error> insert(Args&&... args); + template + utils::result, utils::error> insert(object::object_ptr obj); + template + utils::result, utils::error> insert(Args &&... args); template utils::result, utils::error> update(const object::object_ptr &obj); @@ -112,33 +127,40 @@ public: utils::result drop_table() const; utils::result drop_table(const std::string &table_name) const; - [[nodiscard]] utils::result, utils::error> fetch_all(const sql::query_context &q) const; + [[nodiscard]] utils::result, utils::error> + fetch_all(const sql::query_context &q) const; [[nodiscard]] utils::result execute(const std::string &sql) const; [[nodiscard]] std::vector describe_table(const std::string &table_name) const; [[nodiscard]] bool table_exists(const std::string &table_name) const; - [[nodiscard]] utils::result, utils::error> fetch(const sql::query_context &ctx) const override; + [[nodiscard]] utils::result, utils::error> fetch( + const sql::query_context &ctx) const override; [[nodiscard]] utils::result execute(const sql::query_context &ctx) const override; [[nodiscard]] utils::result prepare(const sql::query_context &ctx) override; - [[nodiscard]] std::string str( const sql::query_context& ctx ) const override; - [[nodiscard]] const sql::dialect& dialect() const override; + [[nodiscard]] std::string str(const sql::query_context &ctx) const override; + [[nodiscard]] const sql::dialect &dialect() const override; + [[nodiscard]] std::shared_ptr resolver_factory() const override; + [[nodiscard]] const class query::basic_schema &schema() const; private: friend class query_select; - static query::fetchable_query build_select_query(entity_query_data &&data); + static query::fetchable_query build_select_query(query::entity_query_data &&data); private: + sql::connection_pool pool_; mutable sql::statement_cache cache_; const sql::dialect &dialect_; - const query::schema& schema_; - mutable std::unordered_map> prototypes_; + const query::basic_schema &schema_; + mutable std::unordered_map > prototypes_; + std::shared_ptr resolver_factory_; }; template utils::result, utils::error> session::insert(Type *obj) { + std::shared_ptr ptr(obj); const auto it = schema_.find(typeid(Type)); if (it == schema_.end()) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); @@ -149,171 +171,199 @@ utils::result, utils::error> session::insert(Type *obj) .values(query::generator::placeholders()) .prepare(*this); if (!res) { - return utils::failure(res.err()); + return utils::failure(res.err()); } if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) { return utils::failure(insert_result.err()); } - return utils::ok(object::object_ptr{obj}); + return utils::ok(object::object_ptr{ptr}); } -template -utils::result, utils::error> session::insert( Args&&... args ) { - return insert(new Type(std::forward(args)...)); +template +utils::result, utils::error> session::insert(object::object_ptr obj) { + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { + return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + } + + auto res = query::query::insert() + .into(it->second.name(), query::generator::columns(schema_)) + .values(query::generator::placeholders()) + .prepare(*this); + if (!res) { + return utils::failure(res.err()); + } + + if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) { + return utils::failure(insert_result.err()); + } + return utils::ok(obj); +} + +template +utils::result, utils::error> session::insert(Args &&... args) { + return insert(new Type(std::forward(args)...)); } class pk_object_binder final { public: - explicit pk_object_binder(sql::statement &stmt, size_t position) - : stmt_(stmt) - , binding_position_(position){} + explicit pk_object_binder(sql::statement &stmt, const size_t position) + : stmt_(stmt) + , binding_position_(position) { + } - template < class Type > - sql::statement& bind(Type &obj) { - access::process(*this, obj); - return stmt_; - } + template + sql::statement &bind(Type &obj) { + access::process(*this, obj); + return stmt_; + } - template - void on_primary_key(const char * /*id*/, Type &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) { - stmt_.bind(binding_position_, x); - } + template + void on_primary_key(const char * /*id*/, Type &x, const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) { + stmt_.bind(binding_position_, x); + } - static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} - template < class Type > - static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} - template < class Pointer > - static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} - template < class Pointer > - static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} + static void on_revision(const char * /*id*/, uint64_t &/*rev*/) { + } - template - static void on_has_many(const char * /*id*/, - ContainerType &/*c*/, - const char * /*join_column*/, - const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} - template - static void on_has_many_to_many(const char * /*id*/, - ContainerType &/*c*/, - const char * /*join_column*/, - const char * /*inverse_join_column*/, - const utils::foreign_attributes &/*attr*/) {} - template - static void on_has_many_to_many(const char * /*id*/, - ContainerType &/*c*/, - const utils::foreign_attributes &/*attr*/) {} + template + static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template + static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} + template + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} + + template + static void on_has_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {} + + template + static void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + + template + static void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} private: - sql::statement &stmt_; - size_t binding_position_{0}; - sql::object_pk_binder pk_binder_; + sql::statement &stmt_; + size_t binding_position_{0}; + sql::object_pk_binder pk_binder_{}; }; template -utils::result, utils::error> session::update( const object::object_ptr& obj ) { - const auto it = schema_.find(typeid(Type)); - if (it == schema_.end()) { - return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - } - using namespace matador::utils; - using namespace matador::query; +utils::result, utils::error> session::update(const object::object_ptr &obj) { + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { + return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + } + using namespace matador::utils; + using namespace matador::query; - const auto col = table_column(it->second.node().info().primary_key_attribute()->name()); - auto res = matador::query::query::update(it->second.name()) - .set(generator::column_value_pairs()) - .where(col == _) - .prepare(*this); - if (!res) { - return utils::failure(res.err()); - } + const auto col = table_column(it->second.node().info().primary_key_attribute()->name()); + auto res = matador::query::query::update(it->second.name()) + .set(generator::column_value_pairs()) + .where(col == _) + .prepare(*this); + if (!res) { + return utils::failure(res.err()); + } - res->bind(*obj); - pk_object_binder binder(res.value(), res->bind_pos()); - if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) { - return utils::failure(update_result.err()); - } - return utils::ok(object::object_ptr{obj}); + res->bind(*obj); + pk_object_binder binder(res.value(), res->bind_pos()); + if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) { + return utils::failure(update_result.err()); + } + return utils::ok(object::object_ptr{obj}); } template -utils::result session::remove( const object::object_ptr& obj ) { - const auto it = schema_.find(typeid(Type)); - if (it == schema_.end()) { - return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - } - using namespace matador::utils; - using namespace matador::query; +utils::result session::remove(const object::object_ptr &obj) { + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { + return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + } + using namespace matador::utils; + using namespace matador::query; - const auto col = table_column(it->second.node().info().primary_key_attribute()->name()); - auto res = matador::query::query::remove() - .from( it->second.name() ) - .where(col == _) - .prepare(*this); - if (!res) { - return utils::failure(res.err()); - } + const auto col = table_column(it->second.node().info().primary_key_attribute()->name()); + auto res = matador::query::query::remove() + .from(it->second.name()) + .where(col == _) + .prepare(*this); + if (!res) { + return utils::failure(res.err()); + } - pk_object_binder binder(res.value(), res->bind_pos()); - if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) { - return utils::failure(update_result.err()); - } - return utils::ok(); + pk_object_binder binder(res.value(), res->bind_pos()); + if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) { + return utils::failure(update_result.err()); + } + return utils::ok(); } template -utils::result, utils::error> session::find(const PrimaryKeyType& pk) { - const auto it = schema_.find(typeid(Type)); - if (it == schema_.end()) { - return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - } - const auto& info = it->second.node().info(); - // if (!info) { - // return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - // } - if (!info.has_primary_key()) { - return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key.")); - } +utils::result, utils::error> session::find(const PrimaryKeyType &pk) { + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { + return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + } + const auto &info = it->second.node().info(); + if (!info.has_primary_key()) { + return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key.")); + } - session_query_builder eqb(schema_, *this); - const query::table_column c(&it->second.table(),info.primary_key_attribute()->name()); - using namespace matador::query; - auto data = eqb.build(c == utils::_); - if (!data.is_ok()) { - return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info.name() + ".")); - } + query::query_builder eqb(schema_, *this); + const query::table_column c(&it->second.table(), info.primary_key_attribute()->name()); + using namespace matador::query; + auto data = eqb.build(c == utils::_); + if (!data.is_ok()) { + return utils::failure(make_error(error_code::FailedToBuildQuery, + "Failed to build query for type " + info.name() + ".")); + } - auto res = build_select_query(data.release()).prepare(*this); + auto res = build_select_query(data.release()).prepare(*this); - if (!res) { - return utils::failure(res.err()); - } - auto stmt_result = res->bind(0, const_cast(pk)).template fetch_one(); - if (stmt_result && !stmt_result.value()) { - return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info.name() + " with primary key " + std::to_string(pk) + ".")); - } - return utils::ok(object::object_ptr{ stmt_result->release() }); + if (!res) { + return utils::failure(res.err()); + } + auto stmt_result = res->bind(0, const_cast(pk)) + .template fetch_one(); + if (!stmt_result) { + return utils::failure(make_error(error_code::FailedToFindObject, + "Failed to find object of type " + info.name() + " with primary key " + + std::to_string(pk) + ".")); + } + return utils::ok(*stmt_result); } template utils::result, utils::error> session::find(query::criteria_ptr clause) { - const auto it = schema_.find(typeid(Type)); - if (it == schema_.end()) { - return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - } + const auto it = schema_.find(typeid(Type)); + if (it == schema_.end()) { + return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + } - session_query_builder eqb(schema_, *this); - auto data = eqb.build(std::move(clause)); - if (!data.is_ok()) { - return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + it->second.name() + ".")); - } + query::query_builder eqb(schema_, *this); + auto data = eqb.build(std::move(clause)); + if (!data.is_ok()) { + return utils::failure(make_error(error_code::FailedToBuildQuery, + "Failed to build query for type " + it->second.name() + ".")); + } - auto result = build_select_query(data.release()).prepare(*this); - if (!result.is_ok()) { - return utils::failure(result.err()); - } + auto result = build_select_query(data.release()).prepare(*this); + if (!result.is_ok()) { + return utils::failure(result.err()); + } - return result->template fetch(); + return result->template fetch(); } template diff --git a/include/matador/query/basic_schema.hpp b/include/matador/query/basic_schema.hpp new file mode 100644 index 0000000..c38bcbc --- /dev/null +++ b/include/matador/query/basic_schema.hpp @@ -0,0 +1,64 @@ +#ifndef MATADOR_BASIC_SCHEMA_HPP +#define MATADOR_BASIC_SCHEMA_HPP + +#include "matador/object/repository.hpp" + +#include "matador/sql/producer_resolver_factory.hpp" + +#include "matador/query/table.hpp" + +#include +#include +#include + +namespace matador::query { +class schema_node final { +public: + schema_node(class table tab, const object::repository_node& node); + + [[nodiscard]] const std::string& name() const; + [[nodiscard]] const class table& table() const; + [[nodiscard]] const object::repository_node& node() const; + +private: + class table table_; + const object::repository_node& node_; +}; + +class basic_schema { +public: + using schema_node_map = std::unordered_map; + using iterator = std::unordered_map::iterator; + using const_iterator = std::unordered_map::const_iterator; + + basic_schema(); + explicit basic_schema(const std::string &name); + + iterator begin(); + iterator end(); + [[nodiscard]] const_iterator begin() const; + [[nodiscard]] const_iterator end() const; + + [[nodiscard]] size_t size() const; + [[nodiscard]] bool empty() const; + + iterator find(const std::type_index& ti); + iterator find(const std::string& name); + + [[nodiscard]] const_iterator find(const std::type_index& ti) const; + [[nodiscard]] const_iterator find(const std::string& name) const; + + [[nodiscard]] bool contains(const std::type_index &index) const; + + void initialize_executor(sql::executor &exec) const; + +protected: + template + friend class schema_observer; + + object::repository repo_; + std::unordered_map schema_nodes_; + std::unordered_map> resolver_producers_; +}; +} +#endif //MATADOR_BASIC_SCHEMA_HPP \ No newline at end of file diff --git a/include/matador/query/criteria/criteria_operators.hpp b/include/matador/query/criteria/criteria_operators.hpp index aa08db4..78feae9 100644 --- a/include/matador/query/criteria/criteria_operators.hpp +++ b/include/matador/query/criteria/criteria_operators.hpp @@ -7,6 +7,9 @@ #include "matador/utils/placeholder.hpp" #include "matador/utils/value.hpp" +namespace matador::utils { +class identifier; +} namespace matador::sql { struct query_context; } @@ -57,6 +60,9 @@ criteria_ptr operator>=(const table_column &col, utils::placeholder p); criteria_ptr operator<(const table_column &col, utils::placeholder p); criteria_ptr operator<=(const table_column &col, utils::placeholder p); +criteria_ptr operator==(const table_column &col, const utils::identifier &id); +criteria_ptr operator!=(const table_column &col, const utils::identifier &id); + criteria_ptr operator&&(criteria_ptr left, criteria_ptr right); criteria_ptr operator||(criteria_ptr left, criteria_ptr right); diff --git a/include/matador/query/generator.hpp b/include/matador/query/generator.hpp index ae92363..28d5d58 100644 --- a/include/matador/query/generator.hpp +++ b/include/matador/query/generator.hpp @@ -10,7 +10,7 @@ #include "matador/query/internal/column_value_pair.hpp" #include "matador/query/table.hpp" -#include "matador/query/schema.hpp" +#include "matador/query/basic_schema.hpp" #include @@ -45,7 +45,7 @@ constexpr auto default_column_generator_options = column_generator_options::Forc class column_generator { public: - explicit column_generator(const schema &repo, + explicit column_generator(const basic_schema &repo, const table* tab, column_generator_options options = default_column_generator_options); @@ -141,7 +141,7 @@ private: void push(const std::string &column_name); private: - const schema &repo_; + const basic_schema &repo_; std::vector result_; std::stack table_stack_; std::unordered_set seen_tables; @@ -289,7 +289,7 @@ std::vector column_value_pairs() { } template -std::vector columns(const schema &repo, +std::vector columns(const basic_schema &repo, const table &tab, const column_generator_options options = default_column_generator_options) { column_generator generator(repo, &tab, options); @@ -297,7 +297,7 @@ std::vector columns(const schema &repo, } template -std::vector columns(const schema &repo, +std::vector columns(const basic_schema &repo, const column_generator_options options = default_column_generator_options) { const auto it = repo.find(typeid(Type)); if (it == repo.end()) { @@ -309,7 +309,7 @@ std::vector columns(const schema &repo, template std::vector columns(const Type &obj, - const schema &repo, + const basic_schema &repo, const column_generator_options options = default_column_generator_options) { const auto it = repo.find(typeid(Type)); if (it == repo.end()) { diff --git a/include/matador/query/intermediates/fetchable_query.hpp b/include/matador/query/intermediates/fetchable_query.hpp index 958e3df..3cc6264 100644 --- a/include/matador/query/intermediates/fetchable_query.hpp +++ b/include/matador/query/intermediates/fetchable_query.hpp @@ -3,13 +3,15 @@ #include "query_intermediate.hpp" -#include "../query_compiler.hpp" +#include "matador/query/query_compiler.hpp" +#include "matador/sql/executor.hpp" -#include "../../sql/query_result.hpp" -#include "../../sql/record.hpp" +#include "matador/sql/query_result.hpp" +#include "matador/sql/query_record_result.hpp" +#include "matador/sql/record.hpp" -#include "../../utils/error.hpp" -#include "../../utils/result.hpp" +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" namespace matador::sql { class executor; @@ -32,14 +34,15 @@ public: } const auto prototype = result.value()->prototype(); - return utils::ok(sql::query_result(result.release(), [prototype] { + auto resolver = exec.resolver_factory()->resolver(); + return utils::ok(sql::query_result(result.release(), resolver, [prototype] { return sql::detail::create_prototype(prototype); })); } [[nodiscard]] utils::result, utils::error> fetch_all(const sql::executor &exec) const; template < class Type > - utils::result, utils::error> fetch_one(const sql::executor &exec) + utils::result, utils::error> fetch_one(sql::executor &exec) { auto result = fetch(exec); if (!result.is_ok()) { @@ -47,15 +50,16 @@ public: } const auto prototype = result.value()->prototype(); - auto objects = sql::query_result(result.release(), [prototype] { + auto resolver = exec.resolver_factory()->resolver(); + auto objects = sql::query_result(result.release(), resolver, [prototype] { return sql::detail::create_prototype(prototype); }); auto first = objects.begin(); if (first == objects.end()) { - return utils::ok(std::unique_ptr{nullptr}); + return utils::ok(object::object_ptr{}); } - return utils::ok(std::unique_ptr{first.release()}); + return utils::ok(first.optr()); } [[nodiscard]] utils::result, utils::error> fetch_one(const sql::executor &exec) const; diff --git a/include/matador/query/intermediates/query_insert_intermediate.hpp b/include/matador/query/intermediates/query_insert_intermediate.hpp index 47481d3..4232eb8 100644 --- a/include/matador/query/intermediates/query_insert_intermediate.hpp +++ b/include/matador/query/intermediates/query_insert_intermediate.hpp @@ -3,10 +3,10 @@ #include "matador/query/intermediates/query_intermediate.hpp" #include "matador/query/intermediates/query_into_intermediate.hpp" -#include "matador/query/schema.hpp" #include "matador/query/generator.hpp" namespace matador::query { +class schema; class query_insert_intermediate : public query_intermediate { public: diff --git a/include/matador/query/query.hpp b/include/matador/query/query.hpp index 25f1c14..5f640d6 100644 --- a/include/matador/query/query.hpp +++ b/include/matador/query/query.hpp @@ -4,13 +4,12 @@ #include "matador/query/query_intermediates.hpp" #include "matador/query/generator.hpp" -#include "matador/query/schema.hpp" namespace matador::sql { class connection; } namespace matador::query { - +class schema; table_column alias(const std::string &column, const std::string &as); table_column alias(table_column &&col, const std::string &as); table_column count(const std::string &column); diff --git a/include/matador/orm/session_query_builder.hpp b/include/matador/query/query_builder.hpp similarity index 74% rename from include/matador/orm/session_query_builder.hpp rename to include/matador/query/query_builder.hpp index 93ca21f..85ddf7a 100644 --- a/include/matador/orm/session_query_builder.hpp +++ b/include/matador/query/query_builder.hpp @@ -1,13 +1,11 @@ #ifndef QUERY_ENTITY_QUERY_BUILDER_HPP #define QUERY_ENTITY_QUERY_BUILDER_HPP -#include "matador/orm/query_builder_exception.hpp" +#include "matador/query/query_builder_exception.hpp" #include "matador/query/criteria.hpp" #include "matador/query/query.hpp" -#include "matador/query/schema.hpp" -#include "matador/sql/executor.hpp" #include "matador/sql/statement.hpp" #include "matador/object/join_columns_collector.hpp" @@ -21,45 +19,45 @@ #include #include -namespace matador::orm { +namespace matador::query { struct entity_query_data { - const query::table* root_table{nullptr}; + const table* root_table{nullptr}; std::string pk_column_name{}; - std::vector columns{}; + std::vector columns{}; std::unordered_map lazy_loading_statements{}; - std::vector joins{}; - query::criteria_ptr where_clause{}; + std::vector joins{}; + criteria_ptr where_clause{}; }; -class criteria_transformer final : public query::criteria_visitor { +class criteria_transformer final : public criteria_visitor { public: - criteria_transformer(const query::schema &repo, const std::unordered_map& tables_by_name); - void visit( const query::between_criteria& node ) override; - void visit( const query::binary_criteria& node ) override; - void visit( const query::binary_column_criteria& node ) override; - void visit( const query::collection_criteria& node ) override; - void visit( const query::collection_query_criteria& node ) override; - void visit( const query::like_criteria& node ) override; - void visit( const query::logical_criteria& node ) override; - void visit( const query::not_criteria& node ) override; + criteria_transformer(const basic_schema &repo, const std::unordered_map& tables_by_name); + void visit( const between_criteria& node ) override; + void visit( const binary_criteria& node ) override; + void visit( const binary_column_criteria& node ) override; + void visit( const collection_criteria& node ) override; + void visit( const collection_query_criteria& node ) override; + void visit( const like_criteria& node ) override; + void visit( const logical_criteria& node ) override; + void visit( const not_criteria& node ) override; private: - void update_criteria_column(const query::abstract_column_criteria& node) const; + void update_criteria_column(const abstract_column_criteria& node) const; private: - const query::schema &repo_; - const std::unordered_map& tables_by_name_; + const basic_schema &repo_; + const std::unordered_map& tables_by_name_; }; -class session_query_builder final { +class query_builder final { public: - session_query_builder(const query::schema &scm, sql::executor &exec) + query_builder(const basic_schema &scm, sql::executor &exec) : schema_(scm) , executor_(exec){} template - utils::result build(query::criteria_ptr clause = {}) { + utils::result build(criteria_ptr clause = {}) { const auto it = schema_.find(typeid(EntityType)); if (it == schema_.end()) { return utils::failure(query_build_error::UnknownType); @@ -144,8 +142,8 @@ public: } append_join( - query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - query::table_column{&next->second, join_column} + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&next->second, join_column} ); @@ -159,7 +157,7 @@ public: // if (next != processed_tables_.end()) { // return; // } - // table_info_stack_.push({*result, std::make_shared(info.name(), build_alias('t', ++table_index))}); + // table_info_stack_.push({*result, std::make_shared(info.name(), build_alias('t', ++table_index))}); // next = processed_tables_.insert({info.name(), table_info_stack_.top().table}).first; // typename ContainerType::value_type::value_type obj; // access::process(*this , obj); @@ -170,8 +168,8 @@ public: // } // // append_join( - // query::column{table_info_stack_.top().table.get(), table_info_stack_.top().info.get().primary_key_attribute()->name()}, - // query::column{next->second.get(), join_column} + // column{table_info_stack_.top().table.get(), table_info_stack_.top().info.get().primary_key_attribute()->name()}, + // column{next->second.get(), join_column} // ); } @@ -210,12 +208,12 @@ public: } append_join( - query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - query::table_column{&relation->second, join_column} + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&relation->second, join_column} ); append_join( - query::table_column{&relation->second, inverse_join_column}, - query::table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} + table_column{&relation->second, inverse_join_column}, + table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} ); } @@ -257,12 +255,12 @@ public: const auto join_columns = join_columns_collector_.collect(); append_join( - query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, - query::table_column{&relation->second, join_columns.inverse_join_column} + table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()}, + table_column{&relation->second, join_columns.inverse_join_column} ); append_join( - query::table_column{&relation->second, join_columns.join_column}, - query::table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} + table_column{&relation->second, join_columns.join_column}, + table_column{&next->second, result->second.node().info().primary_key_attribute()->name()} ); } @@ -272,17 +270,17 @@ private: void push(const std::string &column_name); static std::string build_alias(char prefix, unsigned int count); [[nodiscard]] bool is_root_entity() const; - void append_join(const query::table_column &left, const query::table_column &right); + void append_join(const table_column &left, const table_column &right); private: struct table_info { const object::basic_object_info &info; - query::table table; + class table table; }; std::stack table_info_stack_{}; - std::unordered_map processed_tables_{}; - const query::schema &schema_; + std::unordered_map processed_tables_{}; + const basic_schema &schema_; entity_query_data entity_query_data_{}; unsigned int column_index{0}; unsigned int table_index{0}; @@ -291,7 +289,7 @@ private: }; template -void session_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) { +void query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) { const auto it = schema_.find(typeid(typename Pointer::value_type)); if (it == schema_.end()) { throw query_builder_exception{query_build_error::UnknownType}; @@ -315,15 +313,15 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u table_info_stack_.pop(); append_join( - query::table_column{&table_info_stack_.top().table, id}, - query::table_column{&next->second, info.primary_key_attribute()->name()} + table_column{&table_info_stack_.top().table, id}, + table_column{&next->second, info.primary_key_attribute()->name()} ); } else { push(id); using namespace matador::utils; using namespace matador::query; // create select query - auto result = matador::query::query::select(generator::columns(schema_, foreign_table, generator::column_generator_options::ForceLazy)) + auto result = query::query::select(generator::columns(schema_, foreign_table, generator::column_generator_options::ForceLazy)) .from(foreign_table) .where(table_column(&foreign_table, info.primary_key_attribute()->name(), "") == _) .prepare(executor_); diff --git a/include/matador/orm/query_builder_exception.hpp b/include/matador/query/query_builder_exception.hpp similarity index 82% rename from include/matador/orm/query_builder_exception.hpp rename to include/matador/query/query_builder_exception.hpp index 46e11ba..586f19b 100644 --- a/include/matador/orm/query_builder_exception.hpp +++ b/include/matador/query/query_builder_exception.hpp @@ -5,7 +5,7 @@ #include -namespace matador::orm { +namespace matador::query { enum class query_build_error : std::uint8_t { Ok = 0, @@ -17,7 +17,7 @@ enum class query_build_error : std::uint8_t { class query_builder_exception final : public std::exception { public: - explicit query_builder_exception(const query_build_error error, utils::error &&err = {}); + explicit query_builder_exception(query_build_error error, utils::error &&err = {}); [[nodiscard]] query_build_error error_type() const; [[nodiscard]] const utils::error &error() const; diff --git a/include/matador/query/query_object_resolver.hpp b/include/matador/query/query_object_resolver.hpp new file mode 100644 index 0000000..360d348 --- /dev/null +++ b/include/matador/query/query_object_resolver.hpp @@ -0,0 +1,62 @@ +#ifndef MATADOR_QUERY_OBJECT_LOADER_HPP +#define MATADOR_QUERY_OBJECT_LOADER_HPP + +#include "matador/sql/internal/identifier_statement_binder.hpp" +#include "matador/sql/statement.hpp" + +#include "matador/object/object_resolver.hpp" + +#include "matador/query/table.hpp" +#include "matador/query/query_builder.hpp" + +namespace matador::sql { +class executor; +} + +namespace matador::query { +template +class query_object_resolver : public object::object_resolver { +public: + explicit query_object_resolver(const basic_schema &repo, sql::executor& exec, const table &tab, std::string pk_name) + : executor_(exec) + , schema_(repo) + , table_(tab) + , pk_name_(std::move(pk_name)) {} + + std::shared_ptr resolve(const utils::identifier &id) override; +protected: + sql::executor& executor_; + const basic_schema &schema_; + const table &table_; + std::string pk_name_; + std::type_index index{typeid(Type)}; +}; + +utils::result prepare_statement(sql::executor& exec, entity_query_data &&data); + +template +std::shared_ptr query_object_resolver::resolve(const utils::identifier &id) { + query_builder qb(schema_, executor_); + const auto *pk_column = table_[pk_name_]; + auto builder_result = qb.build(*pk_column == utils::_); + + if (!builder_result) { + return nullptr; + } + auto stmt = prepare_statement(executor_, builder_result.release()); + if (!stmt) { + return nullptr; + } + + sql::identifier_statement_binder binder(*stmt); + binder.bind(id); + + auto result = stmt->template fetch_one_raw(); + if (!result) { + return nullptr; + } + return *result; +} + +} +#endif //MATADOR_QUERY_OBJECT_LOADER_HPP \ No newline at end of file diff --git a/include/matador/query/schema.hpp b/include/matador/query/schema.hpp index d1fc108..dd83356 100644 --- a/include/matador/query/schema.hpp +++ b/include/matador/query/schema.hpp @@ -2,19 +2,41 @@ #define MATADOR_SCHEMA_HPP #include "matador/object/observer.hpp" -#include "matador/object/repository.hpp" #include "matador/sql/query_context.hpp" +#include "matador/sql/internal/resolver_producer.hpp" -#include "matador/query/table.hpp" +#include "matador/query/query_object_resolver.hpp" +#include "matador/query/basic_schema.hpp" namespace matador::sql { class connection_pool; class connection; +class executor; } namespace matador::query { +template +class query_resolver_producer : public sql::resolver_producer { +public: + query_resolver_producer() = default; + query_resolver_producer(const basic_schema& repo, const table& tab, std::string pk_name) + : resolver_producer(typeid(Type)) + , repo_(repo) + , table_(tab) + , pk_name_(std::move(pk_name)) {} + + std::shared_ptr produce(sql::executor &exec) override { + return std::make_shared>(repo_, exec, table_, std::move(pk_name_)); + } + +private: + const basic_schema& repo_; + const table& table_; + std::string pk_name_; +}; + class schema; using schema_ref = std::reference_wrapper; @@ -52,25 +74,14 @@ private: schema& schema_; }; -class schema_node final { +class schema final : public basic_schema { public: - schema_node(class table tab, const object::repository_node& node); + using basic_schema::basic_schema; + using basic_schema::iterator; + using basic_schema::const_iterator; + using basic_schema::contains; + using basic_schema::find; - [[nodiscard]] const std::string& name() const; - [[nodiscard]] const class table& table() const; - [[nodiscard]] const object::repository_node& node() const; - -private: - class table table_; - const object::repository_node& node_; -}; -class schema final { -public: - using iterator = std::unordered_map::iterator; - using const_iterator = std::unordered_map::const_iterator; - - schema() = default; - explicit schema(const std::string &name); template [[nodiscard]] utils::result attach(const std::string &name, Observers&&... observers) { @@ -97,28 +108,15 @@ public: [[nodiscard]] static utils::result, utils::error> describe_table(const std::string &table_name, const sql::connection &conn) ; [[nodiscard]] utils::result table_exists(const std::string &table_name, const sql::connection &conn) const; - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - - [[nodiscard]] size_t size() const; - [[nodiscard]] bool empty() const; + template + iterator find() { return basic_schema::find(typeid(Type)); } template - iterator find() { return find(typeid(Type)); } - iterator find(const std::type_index& ti); - iterator find(const std::string& name); + const_iterator find() const { return basic_schema::find(typeid(Type)); } - template - const_iterator find() const { return find(typeid(Type)); } - const_iterator find(const std::type_index& ti) const; - const_iterator find(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))); + return basic_schema::contains(std::type_index(typeid(Type))); } const object::repository &repo() const { return repo_; } @@ -130,14 +128,11 @@ private: [[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ; [[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ; - void insert_table(const std::type_index& ti, const object::repository_node &node); + iterator insert_table(const std::type_index& ti, const object::repository_node &node); private: template friend class schema_observer; - - object::repository repo_; - std::unordered_map schema_nodes_; }; template @@ -151,7 +146,12 @@ utils::result schema::drop_table(const sql::connection &conn } template void schema_observer::on_attach(const object::repository_node &node, const Type &/*prototype*/) const { - schema_.insert_table(typeid(Type), node); + const auto it = schema_.insert_table(typeid(Type), node); + + if (it->second.node().info().has_primary_key()) { + auto producer = std::make_unique>(schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name()); + schema_.resolver_producers_[typeid(Type)] = std::move(producer); + } } template diff --git a/include/matador/query/table_column.hpp b/include/matador/query/table_column.hpp index 8386c24..61c37be 100644 --- a/include/matador/query/table_column.hpp +++ b/include/matador/query/table_column.hpp @@ -6,9 +6,7 @@ #include "matador/utils/basic_types.hpp" #include "matador/utils/field_attributes.hpp" -#include #include -#include namespace matador::query { diff --git a/include/matador/sql/connection.hpp b/include/matador/sql/connection.hpp index 872e5e0..20e3cec 100644 --- a/include/matador/sql/connection.hpp +++ b/include/matador/sql/connection.hpp @@ -25,7 +25,8 @@ public: * @param info The database connection info data * @param sql_logger The logging handler */ - explicit connection(const connection_info& info, const logger_ptr &sql_logger = null_logger); + explicit connection(const connection_info& info, logger_ptr sql_logger = null_logger); + explicit connection(const connection_info& info, std::shared_ptr resolver_factory, logger_ptr sql_logger = null_logger); /** * @brief Creates a database connection from a connection string. * @@ -33,6 +34,7 @@ public: * @param sql_logger The logging handler */ explicit connection(const std::string &dns, const logger_ptr &sql_logger = null_logger); + explicit connection(const std::string &dns, std::shared_ptr resolver_factory, const logger_ptr &sql_logger = null_logger); /** * Copies a given connection * @@ -138,6 +140,7 @@ public: [[nodiscard]] std::string str( const query_context& ctx ) const override; [[nodiscard]] const class dialect &dialect() const override; + [[nodiscard]] std::shared_ptr resolver_factory() const override; private: [[nodiscard]] utils::result, utils::error> perform_prepare(const query_context &ctx) const; @@ -149,6 +152,7 @@ private: std::unique_ptr connection_; std::shared_ptr logger_ = std::make_shared(); + std::shared_ptr resolver_factory_ = std::make_shared(); }; } diff --git a/include/matador/sql/connection_pool.hpp b/include/matador/sql/connection_pool.hpp index 7ba124b..92cd4dd 100644 --- a/include/matador/sql/connection_pool.hpp +++ b/include/matador/sql/connection_pool.hpp @@ -46,6 +46,7 @@ private: class connection_pool { public: connection_pool(const std::string &dns, size_t count); + connection_pool(const std::string &dns, size_t count, std::function &&connection_creator); connection_ptr acquire(); connection_ptr try_acquire(); @@ -74,6 +75,7 @@ private: connection_map idle_connections_; const connection_info info_; + std::function connection_creator_; }; } diff --git a/include/matador/sql/executor.hpp b/include/matador/sql/executor.hpp index 94ddb4a..9c10fae 100644 --- a/include/matador/sql/executor.hpp +++ b/include/matador/sql/executor.hpp @@ -1,6 +1,8 @@ #ifndef EXECUTOR_HPP #define EXECUTOR_HPP +#include "matador/sql/producer_resolver_factory.hpp" + #include "matador/utils/error.hpp" #include "matador/utils/result.hpp" @@ -20,6 +22,7 @@ public: [[nodiscard]] virtual utils::result, utils::error> fetch(const query_context &ctx) const = 0; [[nodiscard]] virtual utils::result prepare(const query_context &ctx) = 0; [[nodiscard]] virtual std::string str(const query_context &ctx) const = 0; + [[nodiscard]] virtual std::shared_ptr resolver_factory() const = 0; }; } diff --git a/include/matador/sql/field.hpp b/include/matador/sql/field.hpp index 4bac0d9..428b369 100644 --- a/include/matador/sql/field.hpp +++ b/include/matador/sql/field.hpp @@ -28,6 +28,9 @@ public: field(field &&x) noexcept; field& operator=(field &&x) noexcept; + bool operator==(const field& rhs) const; + bool operator!=(const field& rhs) const; + template field& operator=(Type value) { value_ = std::move(value); diff --git a/include/matador/sql/interface/statement_impl.hpp b/include/matador/sql/interface/statement_impl.hpp index 237178a..c64803a 100644 --- a/include/matador/sql/interface/statement_impl.hpp +++ b/include/matador/sql/interface/statement_impl.hpp @@ -7,11 +7,13 @@ #include "matador/sql/object_parameter_binder.hpp" #include "matador/utils/data_type_traits.hpp" +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" #include namespace matador::sql { - +class query_result_impl; class sql_error; class statement_impl { diff --git a/include/matador/sql/interface/statement_proxy.hpp b/include/matador/sql/interface/statement_proxy.hpp index b57546c..8dc27ce 100644 --- a/include/matador/sql/interface/statement_proxy.hpp +++ b/include/matador/sql/interface/statement_proxy.hpp @@ -40,6 +40,8 @@ public: [[nodiscard]] std::unique_ptr create_binder() const; protected: + friend class statement; + std::unique_ptr statement_; }; diff --git a/include/matador/sql/internal/identifier_reader.hpp b/include/matador/sql/internal/identifier_reader.hpp new file mode 100644 index 0000000..b95f013 --- /dev/null +++ b/include/matador/sql/internal/identifier_reader.hpp @@ -0,0 +1,51 @@ +#ifndef MATADOR_IDENTIFIER_READER_HPP +#define MATADOR_IDENTIFIER_READER_HPP + +#include "matador/utils/access.hpp" +#include "matador/utils/identifier.hpp" +#include "matador/utils/primary_key_attribute.hpp" + +namespace matador::sql { +class query_result_reader; +} + +namespace matador::sql::internal { +class identifier_reader final { +public: + explicit identifier_reader(query_result_reader &reader); + + template + utils::identifier read(const Type &obj, const size_t column_index) { + identifier_.clear(); + column_index_ = column_index; + access::process(*this, obj); + return identifier_; + } + + template + void on_primary_key(const char *id, ValueType &value, const utils::primary_key_attribute& attr = utils::default_pk_attributes) { + utils::data_type_traits::read_value(reader_, id, column_index_, value, attr.size()); + identifier_ = value; + } + + static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} + template < class Type > + static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template < class Pointer > + static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + template < class Pointer > + static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} + template + static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} + template + static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {} + template + static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const utils::foreign_attributes &/*attr*/) {} + +private: + query_result_reader &reader_; + size_t column_index_{}; + utils::identifier identifier_; +}; +} +#endif //MATADOR_IDENTIFIER_READER_HPP \ No newline at end of file diff --git a/include/matador/sql/internal/identifier_statement_binder.hpp b/include/matador/sql/internal/identifier_statement_binder.hpp new file mode 100644 index 0000000..78135a6 --- /dev/null +++ b/include/matador/sql/internal/identifier_statement_binder.hpp @@ -0,0 +1,33 @@ +#ifndef MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP +#define MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP + +#include "matador/utils/identifier.hpp" + +namespace matador::sql { +class statement; + +class identifier_statement_binder : public utils::identifier_serializer { +public: + explicit identifier_statement_binder(statement &stmt, size_t index = 0); + + void bind(const utils::identifier &id); + + void serialize(int8_t &, const utils::field_attributes &) override; + void serialize(int16_t &, const utils::field_attributes &) override; + void serialize(int32_t &, const utils::field_attributes &) override; + void serialize(int64_t &, const utils::field_attributes &) override; + void serialize(uint8_t &, const utils::field_attributes &) override; + void serialize(uint16_t &, const utils::field_attributes &) override; + void serialize(uint32_t &, const utils::field_attributes &) override; + void serialize(uint64_t &, const utils::field_attributes &) override; + void serialize(const char *, const utils::field_attributes &) override; + void serialize(std::string &, const utils::field_attributes &) override; + void serialize(utils::null_type_t &, const utils::field_attributes &) override; + +private: + statement &stmt_; + size_t index_{}; +}; + +} +#endif //MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP \ No newline at end of file diff --git a/include/matador/sql/internal/query_result_impl.hpp b/include/matador/sql/internal/query_result_impl.hpp index c2ae1a7..960e599 100644 --- a/include/matador/sql/internal/query_result_impl.hpp +++ b/include/matador/sql/internal/query_result_impl.hpp @@ -10,16 +10,17 @@ #include "matador/sql/interface/query_result_reader.hpp" #include "matador/sql/internal/query_result_pk_resolver.hpp" +#include "matador/sql/internal/identifier_reader.hpp" #include "matador/sql/record.hpp" #include "matador/object/attribute.hpp" +#include "matador/object/object_proxy.hpp" +#include "matador/object/object_resolver_factory.hpp" -#include #include #include #include #include -#include namespace matador::utils { class value; @@ -73,9 +74,9 @@ private: class query_result_impl { public: query_result_impl(std::unique_ptr &&reader, - std::vector &&prototype, size_t column_index = 0); - query_result_impl(std::unique_ptr &&reader, - const std::vector &prototype, size_t column_index = 0); + std::vector prototype, + const std::shared_ptr& resolver_factory, + size_t column_index = 0); template void on_primary_key(const char *id, ValueType &value, const utils::primary_key_attribute& attr = utils::default_pk_attributes) { @@ -122,17 +123,19 @@ public: } template - void on_has_many(const char * /*id*/, ContainerType &cont, const char * /*join_column*/, - const utils::foreign_attributes &attr) { + void on_has_many(const char * /*id*/, ContainerType &cont, const char * /*join_column*/, const utils::foreign_attributes &attr) { + using value_type = typename ContainerType::value_type::value_type; + auto resolver = resolver_factory_->resolver(); + if (attr.fetch() == utils::fetch_type::Lazy) { // pk_reader_.read(*id, column_index_++); } else { - const auto ti = std::type_index(typeid(typename ContainerType::value_type::value_type)); - auto obj = std::make_unique(); + const auto ti = std::type_index(typeid(value_type)); + auto obj = std::make_shared(); type_stack_.push(ti); access::process(*this, *obj); type_stack_.pop(); - auto ptr = typename ContainerType::value_type(obj.release()); + auto ptr = typename ContainerType::value_type(std::make_shared>(resolver, obj)); const auto pk = ptr.primary_key(); if (ptr.primary_key().is_valid()) { cont.push_back(ptr); @@ -194,16 +197,18 @@ private: template void on_foreign_key(Pointer &x, const utils::foreign_attributes &attr) { - if (x.empty()) { - x.reset(new typename Pointer::value_type); - } + const auto resolver = resolver_factory_->resolver(); if (attr.fetch() == utils::fetch_type::Lazy) { - pk_reader_.read(*x, column_index_++); + typename Pointer::value_type obj; + auto pk = id_reader_.read(obj, column_index_++); + x = Pointer(std::make_shared>(resolver, pk)); } else { + auto obj = std::make_shared(); const auto ti = std::type_index(typeid(*x)); type_stack_.push(ti); - access::process(*this, *x); + access::process(*this, *obj); type_stack_.pop(); + x = Pointer(std::make_shared>(resolver, obj)); } } @@ -211,6 +216,8 @@ protected: size_t column_index_ = 0; std::vector prototype_; std::unique_ptr reader_; + std::shared_ptr resolver_factory_; + internal::identifier_reader id_reader_; detail::pk_reader pk_reader_; std::stack type_stack_; utils::identifier current_pk_{}; diff --git a/include/matador/sql/internal/query_result_pk_resolver.hpp b/include/matador/sql/internal/query_result_pk_resolver.hpp index 286cf3f..0c296a5 100644 --- a/include/matador/sql/internal/query_result_pk_resolver.hpp +++ b/include/matador/sql/internal/query_result_pk_resolver.hpp @@ -50,9 +50,9 @@ public: template static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} + static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {} template - static void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} + static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const utils::foreign_attributes &/*attr*/) {} private: template @@ -60,8 +60,8 @@ private: if (fetch == utils::fetch_type::Lazy) { ++column_index_; } else { - const Type obj; - type_stack_.push(typeid(Type)); + const Type obj{}; + type_stack_.emplace(typeid(Type)); access::process(*this, obj); type_stack_.pop(); } diff --git a/include/matador/sql/internal/resolver_producer.hpp b/include/matador/sql/internal/resolver_producer.hpp new file mode 100644 index 0000000..477ed9f --- /dev/null +++ b/include/matador/sql/internal/resolver_producer.hpp @@ -0,0 +1,24 @@ +#ifndef MATADOR_RESOLVER_PRODUCER_HPP +#define MATADOR_RESOLVER_PRODUCER_HPP + +#include "matador/object/object_resolver.hpp" + +#include + +namespace matador::sql { +class executor; +class resolver_producer { +public: + virtual ~resolver_producer() = default; + virtual std::shared_ptr produce(executor &exec) = 0; + + [[nodiscard]] const std::type_index& type() const; + +protected: + explicit resolver_producer(const std::type_index &type); + +private: + std::type_index type_; +}; +} +#endif //MATADOR_RESOLVER_PRODUCER_HPP \ No newline at end of file diff --git a/include/matador/sql/internal/statement_object_resolver.hpp b/include/matador/sql/internal/statement_object_resolver.hpp new file mode 100644 index 0000000..547857c --- /dev/null +++ b/include/matador/sql/internal/statement_object_resolver.hpp @@ -0,0 +1,35 @@ +#ifndef MATADOR_STATEMENT_OBJECT_RESOLVER_HPP +#define MATADOR_STATEMENT_OBJECT_RESOLVER_HPP + +#include "matador/object/object_resolver.hpp" + +#include "matador/sql/statement.hpp" +#include "matador/sql/internal/identifier_statement_binder.hpp" + +namespace matador::sql { +template +class statement_object_resolver : public object::object_resolver { +public: + explicit statement_object_resolver(statement stmt) + : statement_(std::move(stmt)) {} + + std::shared_ptr resolve(const utils::identifier &id) override; +protected: + statement statement_; + std::type_index index{typeid(Type)}; +}; + +template +std::shared_ptr statement_object_resolver::resolve(const utils::identifier &id) { + identifier_statement_binder binder(statement_); + binder.bind(id); + + auto result = statement_.template fetch_one(); + if (!result) { + return nullptr; + } + return std::make_shared(*result); +} + +} +#endif //MATADOR_STATEMENT_OBJECT_RESOLVER_HPP \ No newline at end of file diff --git a/include/matador/sql/producer_resolver_factory.hpp b/include/matador/sql/producer_resolver_factory.hpp new file mode 100644 index 0000000..a7856c0 --- /dev/null +++ b/include/matador/sql/producer_resolver_factory.hpp @@ -0,0 +1,21 @@ +#ifndef MATADOR_RESOLVER_FACTORY_HPP +#define MATADOR_RESOLVER_FACTORY_HPP + +#include "matador/object/object_resolver_factory.hpp" + +#include "matador/sql/internal/resolver_producer.hpp" + +namespace matador::sql { +class executor; + +class producer_resolver_factory : public object::object_resolver_factory { +public: + + std::shared_ptr acquire_resolver(const std::type_index &type) override; + void register_resolver(std::shared_ptr &&resolver) override; + +private: + std::unordered_map> resolvers_; +}; +} +#endif //MATADOR_RESOLVER_FACTORY_HPP \ No newline at end of file diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index 844eafa..7dfcf76 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -2,31 +2,29 @@ #define QUERY_QUERY_DATA_HPP #include "matador/object/attribute.hpp" +#include "matador/object/object_resolver_factory.hpp" #include "matador/utils/types.hpp" -#include - namespace matador::sql { - enum class sql_command { - SQL_UNKNOWN, - SQL_CREATE, - SQL_CREATE_TABLE, - SQL_CREATE_SCHEMA, - SQL_CREATE_DATABASE, - SQL_UPDATE, - SQL_INSERT, - SQL_DELETE, - SQL_SELECT, - SQL_DROP, - SQL_DROP_TABLE, - SQL_DROP_SCHEMA, - SQL_DROP_DATABASE, - SQL_ALTER, - SQL_ALTER_TABLE, - SQL_ALTER_SCHEMA - }; + SQL_UNKNOWN, + SQL_CREATE, + SQL_CREATE_TABLE, + SQL_CREATE_SCHEMA, + SQL_CREATE_DATABASE, + SQL_UPDATE, + SQL_INSERT, + SQL_DELETE, + SQL_SELECT, + SQL_DROP, + SQL_DROP_TABLE, + SQL_DROP_SCHEMA, + SQL_DROP_DATABASE, + SQL_ALTER, + SQL_ALTER_TABLE, + SQL_ALTER_SCHEMA +}; struct sql_command_info { std::string sql; @@ -40,14 +38,10 @@ struct query_context { std::string schema_name{}; std::string table_name{}; std::vector prototype{}; - // std::vector result_vars{}; std::vector bind_vars{}; std::vector bind_types{}; - - // std::unordered_map column_aliases{}; - // std::unordered_map table_aliases{}; + std::shared_ptr resolver_factory{}; }; - } #endif //QUERY_QUERY_DATA_HPP diff --git a/include/matador/sql/query_record_result.hpp b/include/matador/sql/query_record_result.hpp new file mode 100644 index 0000000..96abf63 --- /dev/null +++ b/include/matador/sql/query_record_result.hpp @@ -0,0 +1,132 @@ +#ifndef MATADOR_QUERY_RECORD_RESULT_HPP +#define MATADOR_QUERY_RECORD_RESULT_HPP + +#include "matador/sql/query_result.hpp" + +namespace matador::sql { + +template <> +class query_result; + +template <> +class query_result_iterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = record; + using difference_type = std::ptrdiff_t; + using self = query_result_iterator; /**< Shortcut for this class. */ + using pointer = value_type*; /**< Shortcut for the pointer type. */ + using reference = value_type&; /**< Shortcut for the reference type */ + +public: + query_result_iterator() = default; + explicit query_result_iterator(query_result *res) + : result_(res) + {} + query_result_iterator(query_result *res, record rec) + : record_(std::move(rec)) + , result_(res) + {} + query_result_iterator(query_result_iterator&& x) noexcept + : record_(std::move(x.record_)) + , result_(x.result_) + {} + query_result_iterator& operator=(query_result_iterator&& x) noexcept { + result_ = x.result_; + record_ = std::move(x.record_); + return *this; + } + + ~query_result_iterator() = default; + + bool operator==(const query_result_iterator& rhs) const { + return record_ == rhs.record_; + } + + bool operator!=(const query_result_iterator& rhs) const { + return !(*this == rhs); + } + + self& operator++(); + self operator++(int); + + pointer operator->() { return &record_; } + reference operator*() { return record_; } + record release() { return std::move(record_); } + +private: + record record_; + query_result *result_{nullptr}; +}; + +template<> +class query_result final { +public: + using iterator = query_result_iterator; + + query_result(std::unique_ptr &&impl, const std::vector &prototype) + : impl_(std::move(impl)) + , prototype_(prototype) {} + + iterator begin() { return std::move(++iterator(this)); } + iterator end() { return {}; } + +private: + friend class query_result_iterator; + + [[nodiscard]] record create() const; + void bind(const record& obj) const; + bool fetch(record& obj) const; + +protected: + std::unique_ptr impl_; + std::vector prototype_; +}; + +inline record query_result::create() const { + record rec; + int index{0}; + for (const auto &col: prototype_) { + rec.append({ + col.name(), + col.type(), + col.attributes().options(), + col.attributes().size(), + index++ + }); + } + return rec; + +} + +inline void query_result::bind(const record &obj) const { + impl_->bind(obj); +} + +inline bool query_result::fetch(record &obj) const { + return impl_->fetch(obj); +} + +inline query_result_iterator::self & query_result_iterator::operator++() { + record_ = result_->create(); + result_->bind(record_); + if (!result_->fetch(record_)) { + record_.clear(); + } + + return *this; +} + +inline query_result_iterator::self query_result_iterator::operator++(int) { + self tmp(result_, record_); + + record_ = result_->create(); + result_->bind(record_); + if (!result_->fetch(record_)) { + record_.clear(); + } + + return tmp; +} +} +#endif //MATADOR_QUERY_RECORD_RESULT_HPP \ No newline at end of file diff --git a/include/matador/sql/query_result.hpp b/include/matador/sql/query_result.hpp index 826d39e..2f9d250 100644 --- a/include/matador/sql/query_result.hpp +++ b/include/matador/sql/query_result.hpp @@ -2,22 +2,24 @@ #define QUERY_QUERY_RESULT_HPP #include "matador/object/attribute.hpp" +#include "matador/object/object_ptr.hpp" #include "matador/sql/internal/query_result_impl.hpp" #include #include -namespace matador::sql { +#include "matador/object/object.hpp" +namespace matador::sql { class record; +class query_result_impl; template < typename Type > class query_result; template < typename Type > -class query_result_iterator -{ +class query_result_iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = Type; @@ -31,7 +33,7 @@ public: explicit query_result_iterator(query_result *res) : result_(res) {} - query_result_iterator(query_result *res, std::unique_ptr obj) + query_result_iterator(query_result *res, object::object_ptr obj) : obj_(std::move(obj)) , result_(res) {} @@ -48,19 +50,20 @@ public: ~query_result_iterator() = default; - bool operator==(const query_result_iterator& rhs) { + bool operator==(const query_result_iterator& rhs) const { return obj_ == rhs.obj_; } - bool operator!=(const query_result_iterator& rhs) { - return obj_ != rhs.obj_; + bool operator!=(const query_result_iterator& rhs) const { + return !operator==(rhs); } self& operator++() { - obj_.reset(result_->create()); - result_->bind(*obj_); - if (!result_->fetch(*obj_)) { - obj_.reset(); + auto obj = result_->create(); + result_->bind(*obj); + obj_.reset(); + if (result_->fetch(*obj)) { + obj_ = object::object_ptr(std::make_shared>(result_->resolver_, std::move(obj))); } return *this; @@ -69,10 +72,11 @@ public: self operator++(int) { const self tmp(result_, obj_); - obj_.reset(result_->create()); - result_->bind(*obj_); - if (!result_->fetch(*obj_)) { - obj_.reset(); + auto obj = result_->create(); + result_->bind(*obj); + obj_.reset(); + if (result_->fetch(*obj)) { + obj_ = object::object_ptr(std::make_shared>(result_->resolver_, std::move(obj))); } return tmp; @@ -82,50 +86,37 @@ public: return obj_.get(); } - reference operator*() { - return *obj_; + object::object_ptr operator*() { + return obj_; } - pointer get() { - return obj_.get(); - } - - pointer release() { - return obj_.release(); + object::object_ptr optr() { + return obj_; } private: - std::unique_ptr obj_; + object::object_ptr obj_; query_result *result_{nullptr}; }; namespace detail { - template < typename Type > -Type* create_prototype(const std::vector &/*prototype*/) { - return new Type{}; +std::shared_ptr create_prototype(const std::vector &/*prototype*/) { + return std::make_shared(); } -template <> -record* create_prototype(const std::vector &prototype); - +record create_prototype(const std::vector &prototype); } template class query_result final { public: using iterator = query_result_iterator; - using creator_func = std::function; + using creator_func = std::function()>; -public: - // explicit query_result(std::unique_ptr &&impl) - // : impl_(std::move(impl)) - // , creator_([this] { - // return detail::create_prototype(impl_->prototype()); - // }) {} - - query_result(std::unique_ptr &&impl, creator_func&& creator) + query_result(std::unique_ptr &&impl, const std::shared_ptr> &resolver, creator_func&& creator) : impl_(std::move(impl)) + , resolver_(resolver) , creator_(std::move(creator)) {} iterator begin() { return std::move(++iterator(this)); } @@ -134,17 +125,18 @@ public: private: friend class query_result_iterator; - Type* create(); + std::shared_ptr create(); void bind(const Type& obj); bool fetch(Type& obj); protected: std::unique_ptr impl_; + std::shared_ptr> resolver_; creator_func creator_; }; template -Type *query_result::create() { +std::shared_ptr query_result::create() { return creator_(); } @@ -157,6 +149,6 @@ template bool query_result::fetch(Type &obj) { return impl_->fetch(obj); } - -} // namespace matador::sql +} +// namespace matador::sql #endif //QUERY_QUERY_RESULT_HPP diff --git a/include/matador/sql/record.hpp b/include/matador/sql/record.hpp index 7650b82..9c4ac8b 100644 --- a/include/matador/sql/record.hpp +++ b/include/matador/sql/record.hpp @@ -28,6 +28,9 @@ public: record& operator=(record&&) noexcept = default; ~record() = default; + bool operator==(const record& rhs) const; + bool operator!=(const record& rhs) const; + template void process(Operator &op) { for(auto &f : fields_) { diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 8943fa9..b641f89 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -2,20 +2,20 @@ #define QUERY_STATEMENT_HPP #include "matador/sql/abstract_sql_logger.hpp" +#include "matador/sql/error_code.hpp" #include "matador/sql/query_result.hpp" - #include "matador/sql/interface/statement_proxy.hpp" +#include "matador/object/basic_repository.hpp" + #include "matador/utils/error.hpp" #include "matador/utils/result.hpp" +#include #include + namespace matador::sql { -namespace detail { -template -class identifier_binder; -} class statement_impl; class statement_proxy; @@ -104,7 +104,10 @@ public: * @return The query result set */ template - utils::result, utils::error> fetch_one(); + utils::result, utils::error> fetch_one(); + + template + utils::result, utils::error> fetch_one_raw(); /** * Fetches the first result of a prepared statement. * The type is a record representing an unknown variable type. @@ -140,10 +143,6 @@ public: [[nodiscard]] utils::result, utils::error> fetch_internal() const; -private: - template - friend class detail::identifier_binder; - private: std::shared_ptr statement_proxy_; std::unique_ptr bindings_; @@ -165,31 +164,51 @@ statement &statement::bind(const Type &obj) { template utils::result, utils::error> statement::fetch() { std::cout << statement_proxy_->sql() << std::endl; - return statement_proxy_->fetch(*bindings_).and_then([](std::unique_ptr &&value) { + return statement_proxy_->fetch(*bindings_).and_then([this](std::unique_ptr &&value) { + auto resolver = statement_proxy_->statement_->query_.resolver_factory->resolver(); const auto prototype = value->prototype(); - return utils::ok(query_result(std::forward(value), [prototype] { + return utils::ok(query_result(std::forward(value), resolver, [prototype] { return detail::create_prototype(prototype); })); }); } template -utils::result, utils::error> statement::fetch_one() { +utils::result, utils::error> statement::fetch_one() { std::cout << statement_proxy_->sql() << std::endl; auto result = statement_proxy_->fetch(*bindings_); if (!result.is_ok()) { return utils::failure(result.err()); } + auto resolver = statement_proxy_->statement_->query_.resolver_factory->resolver(); const auto prototype = result.value()->prototype(); - auto records = query_result(result.release(), [prototype] { + auto records = query_result(result.release(), resolver, [prototype] { return detail::create_prototype(prototype); }); auto first = records.begin(); if (first == records.end()) { - return utils::ok(std::unique_ptr{nullptr}); + return utils::failure(utils::error{ + error_code::FETCH_FAILED, + "Failed to find entity." + }); } - return utils::ok(std::unique_ptr{first.release()}); + return utils::ok(first.optr()); +} + +template +utils::result, utils::error> statement::fetch_one_raw() { + auto result = statement_proxy_->fetch(*bindings_); + if (!result.is_ok()) { + return utils::failure(result.err()); + } + + auto obj = std::make_shared(); + (*result)->bind(*obj); + if (!(*result)->fetch(*obj)) { + return utils::ok(std::shared_ptr{}); + } + return utils::ok(obj); } } diff --git a/include/matador/sql/statement_cache.hpp b/include/matador/sql/statement_cache.hpp index ef567b0..0c57f2b 100644 --- a/include/matador/sql/statement_cache.hpp +++ b/include/matador/sql/statement_cache.hpp @@ -77,7 +77,7 @@ private: std::mutex mutex_; connection_pool &pool_; - const sql::dialect &dialect_; + const dialect &dialect_; utils::message_bus &bus_; }; diff --git a/include/matador/utils/identifier.hpp b/include/matador/utils/identifier.hpp index fa3c8d0..08b433a 100644 --- a/include/matador/utils/identifier.hpp +++ b/include/matador/utils/identifier.hpp @@ -11,9 +11,7 @@ #include namespace matador::utils { - -class identifier_serializer -{ +class identifier_serializer { public: virtual ~identifier_serializer() = default; @@ -25,7 +23,7 @@ public: virtual void serialize(uint16_t &, const field_attributes &) = 0; virtual void serialize(uint32_t &, const field_attributes &) = 0; virtual void serialize(uint64_t &, const field_attributes &) = 0; - virtual void serialize(const char*, const field_attributes &) = 0; + virtual void serialize(const char *, const field_attributes &) = 0; virtual void serialize(std::string &, const field_attributes &) = 0; virtual void serialize(null_type_t &, const field_attributes &) = 0; }; @@ -34,13 +32,13 @@ template struct identifier_type_traits; template -struct identifier_type_traits>> { +struct identifier_type_traits > > { static bool is_valid(Type value) { return value > 0; } static std::string to_string(const Type value) { return std::to_string(value); } }; template -struct identifier_type_traits>> { +struct identifier_type_traits > > { static bool is_valid(const Type &value) { return !value.empty(); } static std::string to_string(const Type &value) { return value; } }; @@ -52,27 +50,23 @@ struct identifier_type_traits { }; template<> -struct identifier_type_traits { +struct identifier_type_traits { static bool is_valid(const char *value); static std::string to_string(const char *value); }; namespace detail { - template size_t hash(const Type &value) { return std::hash()(value); } size_t hash(const char *value); - } -class identifier -{ +class identifier { private: - struct base - { + struct base { base(const std::type_index &ti, basic_type type); base(const base &x) = delete; base &operator=(const base &x) = delete; @@ -93,25 +87,25 @@ private: }; template - struct pk final : base - { + struct pk final : base { using self = pk; explicit pk(const IdType &id) : base(std::type_index(typeid(IdType)), data_type_traits::type(1)) - , id_(id) - , size_(sizeof(IdType)) {} + , id_(id) + , size_(sizeof(IdType)) { + } [[nodiscard]] base *copy() const override { return new self(id_); } [[nodiscard]] bool equal_to(const base &x) const override { - return static_cast(x).id_ == id_; + return static_cast(x).id_ == id_; } [[nodiscard]] bool less(const base &x) const override { - return id_ < static_cast(x).id_; + return id_ < static_cast(x).id_; } [[nodiscard]] bool is_valid() const override { @@ -134,8 +128,7 @@ private: size_t size_{}; }; - struct null_pk final : base - { + struct null_pk final : base { null_pk(); [[nodiscard]] base *copy() const override; [[nodiscard]] bool equal_to(const base &x) const override; @@ -149,26 +142,29 @@ private: public: identifier(); + template explicit identifier(const Type &id) - : id_(std::make_shared>(id)) {} + : id_(std::make_shared >(id)) { + } + explicit identifier(const char *id) - : id_(std::make_shared>(id)) {} + : id_(std::make_shared >(id)) { + } + identifier(const identifier &x); identifier &operator=(const identifier &x); - identifier(identifier &&x) noexcept ; + identifier(identifier &&x) noexcept; identifier &operator=(identifier &&x) noexcept; template - identifier& operator=(const Type &value) - { - id_ = std::make_shared>(value); + identifier &operator=(const Type &value) { + id_ = std::make_shared >(value); return *this; } - identifier& operator=(const char *value) - { - id_ = std::make_shared>(value); + identifier &operator=(const char *value) { + id_ = std::make_shared >(value); return *this; } @@ -208,7 +204,7 @@ public: friend std::ostream &operator<<(std::ostream &out, const identifier &id); private: - explicit identifier(const std::shared_ptr& id); + explicit identifier(const std::shared_ptr &id); private: std::shared_ptr id_; @@ -217,10 +213,10 @@ private: static identifier null_identifier{}; /// @cond MATADOR_DEV -struct id_pk_hash -{ +struct id_pk_hash { size_t operator()(const identifier &id) const; }; + /// @endcond } diff --git a/include/matador/utils/identifier_to_value_converter.hpp b/include/matador/utils/identifier_to_value_converter.hpp new file mode 100644 index 0000000..38d25ff --- /dev/null +++ b/include/matador/utils/identifier_to_value_converter.hpp @@ -0,0 +1,29 @@ +#ifndef MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP +#define MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP + +#include "matador/utils/identifier.hpp" +#include "matador/utils/value.hpp" + +namespace matador::utils { +class identifier_to_value_converter final : public identifier_serializer { +public: + [[nodiscard]] value convert(const identifier &id); + + void serialize(int8_t &, const field_attributes &) override; + void serialize(int16_t &, const field_attributes &) override; + void serialize(int32_t &, const field_attributes &) override; + void serialize(int64_t &, const field_attributes &) override; + void serialize(uint8_t &, const field_attributes &) override; + void serialize(uint16_t &, const field_attributes &) override; + void serialize(uint32_t &, const field_attributes &) override; + void serialize(uint64_t &, const field_attributes &) override; + void serialize(const char *, const field_attributes &) override; + void serialize(std::string &, const field_attributes &) override; + void serialize(null_type_t &, const field_attributes &) override; + +private: + value value_; +}; +} + +#endif //MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP \ No newline at end of file diff --git a/include/matador/utils/value.hpp b/include/matador/utils/value.hpp index b220abe..d3a2e50 100644 --- a/include/matador/utils/value.hpp +++ b/include/matador/utils/value.hpp @@ -44,6 +44,9 @@ public: value(value &&x) noexcept; value& operator=(value &&x) noexcept; + bool operator==(const value& rhs) const; + bool operator!=(const value& rhs) const; + template std::optional as() const { if (std::holds_alternative(value_)) { diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index 35d309c..355359b 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -15,14 +15,19 @@ add_library(matador-core STATIC ../../include/matador/net/socket_interrupter.hpp ../../include/matador/object/attribute.hpp ../../include/matador/object/basic_object_info.hpp + ../../include/matador/object/basic_repository.hpp ../../include/matador/object/error_code.hpp ../../include/matador/object/foreign_node_completer.hpp + ../../include/matador/object/internal/observer_list_copy_creator.hpp + ../../include/matador/object/internal/observer_list_creator.hpp ../../include/matador/object/many_to_many_relation.hpp ../../include/matador/object/object.hpp ../../include/matador/object/object_generator.hpp ../../include/matador/object/object_info.hpp ../../include/matador/object/object_proxy.hpp ../../include/matador/object/object_ptr.hpp + ../../include/matador/object/object_resolver.hpp + ../../include/matador/object/object_resolver_factory.hpp ../../include/matador/object/observer.hpp ../../include/matador/object/primary_key_resolver.hpp ../../include/matador/object/relation_completer.hpp @@ -31,7 +36,6 @@ add_library(matador-core STATIC ../../include/matador/object/repository_node.hpp ../../include/matador/object/repository_node_iterator.hpp ../../include/matador/object/restriction.hpp - ../../include/matador/sql/statement_cache.hpp ../../include/matador/utils/access.hpp ../../include/matador/utils/attribute_reader.hpp ../../include/matador/utils/attribute_writer.hpp @@ -53,6 +57,7 @@ add_library(matador-core STATIC ../../include/matador/utils/file.hpp ../../include/matador/utils/foreign_attributes.hpp ../../include/matador/utils/identifier.hpp + ../../include/matador/utils/identifier_to_value_converter.hpp ../../include/matador/utils/leader_follower_thread_pool.hpp ../../include/matador/utils/library.hpp ../../include/matador/utils/macro_map.hpp @@ -78,6 +83,7 @@ add_library(matador-core STATIC logger/rotating_file_sink.cpp object/attribute.cpp object/basic_object_info.cpp + object/basic_repository.cpp object/error_code.cpp object/foreign_node_completer.cpp object/object.cpp @@ -95,6 +101,7 @@ add_library(matador-core STATIC utils/file.cpp utils/foreign_attributes.cpp utils/identifier.cpp + utils/identifier_to_value_converter.cpp utils/leader_follower_thread_pool.cpp utils/library.cpp utils/message_bus.cpp @@ -106,10 +113,6 @@ add_library(matador-core STATIC utils/uuid.cpp utils/value.cpp utils/version.cpp - ../../include/matador/object/internal/observer_list_creator.hpp - ../../include/matador/object/internal/observer_list_copy_creator.hpp - ../../include/matador/object/basic_repository.hpp - object/basic_repository.cpp ) target_link_libraries(matador-core ${CMAKE_DL_LIBS}) diff --git a/source/core/utils/identifier_to_value_converter.cpp b/source/core/utils/identifier_to_value_converter.cpp new file mode 100644 index 0000000..5a5d6fc --- /dev/null +++ b/source/core/utils/identifier_to_value_converter.cpp @@ -0,0 +1,53 @@ +#include "matador/utils/identifier_to_value_converter.hpp" + +namespace matador::utils { +value identifier_to_value_converter::convert(const identifier &id) { + id.serialize(*this); + + return value_; +} + +void identifier_to_value_converter::serialize(int8_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(int16_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(int32_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(int64_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(uint8_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(uint16_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(uint32_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(uint64_t &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(const char *x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(std::string &x, const field_attributes &) { + value_ = x; +} + +void identifier_to_value_converter::serialize(null_type_t &/*x*/, const field_attributes &) { + value_.type(basic_type::Null); +} +} diff --git a/source/core/utils/value.cpp b/source/core/utils/value.cpp index e2ba757..7a46e55 100644 --- a/source/core/utils/value.cpp +++ b/source/core/utils/value.cpp @@ -39,6 +39,14 @@ value &value::operator=(value &&x) noexcept { return *this; } +bool value::operator==(const value &rhs) const { + return type_ == rhs.type_ && value_ == rhs.value_; +} + +bool value::operator!=(const value &rhs) const { + return !operator==(rhs); +} + std::string value::str() const { return as().value_or(""); } diff --git a/source/orm/CMakeLists.txt b/source/orm/CMakeLists.txt index 13d414e..462e177 100644 --- a/source/orm/CMakeLists.txt +++ b/source/orm/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(matador-orm STATIC ../../include/matador/orm/error_code.hpp ../../include/matador/orm/session.hpp - ../../include/matador/orm/session_query_builder.hpp ../../include/matador/query/attribute_string_writer.hpp + ../../include/matador/query/basic_schema.hpp ../../include/matador/query/builder.hpp ../../include/matador/query/criteria.hpp ../../include/matador/query/criteria/abstract_column_criteria.hpp @@ -50,10 +50,13 @@ add_library(matador-orm STATIC ../../include/matador/query/join_data.hpp ../../include/matador/query/key_value_generator.hpp ../../include/matador/query/meta_table_macro.hpp + ../../include/matador/query/meta_table_macro.hpp ../../include/matador/query/query.hpp + ../../include/matador/query/query_builder.hpp ../../include/matador/query/query_compiler.hpp ../../include/matador/query/query_data.hpp ../../include/matador/query/query_intermediates.hpp + ../../include/matador/query/query_object_resolver.hpp ../../include/matador/query/query_part.hpp ../../include/matador/query/query_utils.hpp ../../include/matador/query/schema.hpp @@ -76,19 +79,25 @@ add_library(matador-orm STATIC ../../include/matador/sql/interface/query_result_reader.hpp ../../include/matador/sql/interface/statement_impl.hpp ../../include/matador/sql/interface/statement_proxy.hpp + ../../include/matador/sql/internal/identifier_reader.hpp + ../../include/matador/sql/internal/identifier_statement_binder.hpp ../../include/matador/sql/internal/object_result_binder.hpp ../../include/matador/sql/internal/query_result_impl.hpp ../../include/matador/sql/internal/query_result_pk_resolver.hpp + ../../include/matador/sql/internal/resolver_producer.hpp + ../../include/matador/sql/internal/statement_object_resolver.hpp + ../../include/matador/sql/producer_resolver_factory.hpp ../../include/matador/sql/query_context.hpp + ../../include/matador/sql/query_record_result.hpp ../../include/matador/sql/query_result.hpp ../../include/matador/sql/record.hpp ../../include/matador/sql/sql_functions.hpp ../../include/matador/sql/statement.hpp + ../../include/matador/sql/statement_cache.hpp orm/error_code.cpp - orm/query_builder_exception.cpp orm/session.cpp - orm/session_query_builder.cpp query/attribute_string_writer.cpp + query/basic_schema.cpp query/builder.cpp query/criteria/abstract_column_criteria.cpp query/criteria/between_criteria.cpp @@ -133,7 +142,10 @@ add_library(matador-orm STATIC query/internal/query_result_impl.cpp query/key_value_generator.cpp query/query.cpp + query/query_builder.cpp + query/query_builder_exception.cpp query/query_compiler.cpp + query/query_object_resolver.cpp query/query_part.cpp query/query_utils.cpp query/schema.cpp @@ -153,14 +165,17 @@ add_library(matador-orm STATIC sql/interface/query_result_reader.cpp sql/interface/statement_impl.cpp sql/interface/statement_proxy.cpp + sql/internal/identifier_reader.cpp + sql/internal/identifier_statement_binder.cpp sql/internal/object_result_binder.cpp sql/internal/query_result_pk_resolver.cpp + sql/internal/resolver_producer.cpp sql/object_parameter_binder.cpp + sql/producer_resolver_factory.cpp sql/query_result.cpp sql/record.cpp sql/statement.cpp sql/statement_cache.cpp - ../../include/matador/query/meta_table_macro.hpp ) target_include_directories(matador-orm diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index 7798094..b931d3e 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -2,6 +2,7 @@ #include "matador/sql/backend_provider.hpp" #include "matador/sql/dialect.hpp" +#include "matador/sql/query_record_result.hpp" #include "matador/query/query.hpp" @@ -13,9 +14,11 @@ utils::error make_error(const error_code ec, const std::string &msg) { } session::session(session_context&& ctx, const query::schema &scm) -: cache_(ctx.bus, ctx.pool, ctx.cache_size) -, dialect_(sql::backend_provider::instance().connection_dialect(ctx.pool.info().type)) -, schema_(scm) { +: pool_(ctx.dns, ctx.connection_count, [ctx](const sql::connection_info& info) { return sql::connection(info, ctx.resolver_factory); }) +, cache_(ctx.bus, pool_, ctx.cache_size) +, dialect_(sql::backend_provider::instance().connection_dialect(pool_.info().type)) +, schema_(scm) +, resolver_factory_(ctx.resolver_factory) { } utils::result session::drop_table(const std::string &table_name) const { @@ -54,10 +57,8 @@ utils::result, utils::error> session::fetch_all(c if (!res) { return utils::failure(res.err()); } - const auto prototype = res.value()->prototype(); - return utils::ok(sql::query_result{std::move(*res), [prototype] { - return sql::detail::create_prototype(prototype); - }}); + const auto prototype = res.value()->prototype(); + return utils::ok(sql::query_result{std::move(*res), prototype}); } utils::result session::execute(const std::string &sql) const { @@ -84,6 +85,14 @@ const class sql::dialect &session::dialect() const { return dialect_; } +std::shared_ptr session::resolver_factory() const { + return resolver_factory_; +} + +const query::basic_schema & session::schema() const { + return schema_; +} + utils::result, utils::error> session::fetch(const sql::query_context& ctx) const { if (const auto result = cache_.acquire(ctx); !result) { return utils::failure(result.err()); @@ -112,7 +121,7 @@ std::string session::str(const sql::query_context& ctx) const { return ctx.sql; } -query::fetchable_query session::build_select_query(entity_query_data &&data) { +query::fetchable_query session::build_select_query(query::entity_query_data &&data) { return query::query::select(data.columns) .from(*data.root_table) .join_left(data.joins) diff --git a/source/orm/orm/session_query_builder.cpp b/source/orm/orm/session_query_builder.cpp deleted file mode 100644 index 6f4490a..0000000 --- a/source/orm/orm/session_query_builder.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "matador/orm/session_query_builder.hpp" - -#include - -namespace matador::orm { -criteria_transformer::criteria_transformer(const query::schema& repo, const std::unordered_map& tables_by_name) -: repo_(repo) -, tables_by_name_(tables_by_name) {} - -void criteria_transformer::visit( const query::between_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const query::binary_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const query::binary_column_criteria& /*node*/ ) {} - -void criteria_transformer::visit( const query::collection_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const query::collection_query_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const query::like_criteria& node ) { - update_criteria_column(node); -} - -void criteria_transformer::visit( const query::logical_criteria& node ) { - node.left_clause()->accept(*this); - node.right_clause()->accept(*this); -} - -void criteria_transformer::visit( const query::not_criteria& node ) { - node.clause()->accept(*this); -} - -void criteria_transformer::update_criteria_column(const query::abstract_column_criteria& node) const { - if (node.col().table() == nullptr) { - return; - } - const auto it = tables_by_name_.find(node.col().table()->name()); - if (it == tables_by_name_.end()) { - return; - } - - const_cast(node.col()).table(&it->second); -} - -void session_query_builder::on_revision(const char *id, uint64_t &/*rev*/) { - push(id); -} - -void session_query_builder::push(const std::string &column_name) { - const auto it = processed_tables_.find(table_info_stack_.top().info.name()); - if (it == processed_tables_.end()) { - throw query_builder_exception{query_build_error::UnexpectedError}; - } - entity_query_data_.columns.emplace_back(&it->second, column_name, build_alias('c', ++column_index)); -} - -std::string session_query_builder::build_alias(const char prefix, const unsigned int count) { - char str[4]; - snprintf(str, 4, "%c%02d", prefix, count); - - return str; -} - -[[nodiscard]] bool session_query_builder::is_root_entity() const { - return table_info_stack_.size() == 1; -} - -void session_query_builder::append_join(const query::table_column &left, const query::table_column &right) { - using namespace matador::query; - entity_query_data_.joins.push_back({ - right.table(), - std::make_unique(left, binary_operator::EQUALS, right) - }); -} -} diff --git a/source/orm/query/basic_schema.cpp b/source/orm/query/basic_schema.cpp new file mode 100644 index 0000000..c4385b8 --- /dev/null +++ b/source/orm/query/basic_schema.cpp @@ -0,0 +1,92 @@ +#include "matador/query/basic_schema.hpp" + +#include "matador/sql/executor.hpp" + +namespace matador::query { +schema_node::schema_node(class table tab, const object::repository_node& node) +: table_(std::move(tab)) +, node_(std::move(node)) { +} + +const std::string & schema_node::name() const { + return table_.name(); +} + +const table &schema_node::table() const { + return table_; +} + +const object::repository_node& schema_node::node() const { + return node_; +} + +basic_schema::basic_schema() +: basic_schema("") {} + +basic_schema::basic_schema(const std::string &name) +: repo_(name) { +} + +basic_schema::iterator basic_schema::begin() { + return schema_nodes_.begin(); +} + +basic_schema::iterator basic_schema::end() { + return schema_nodes_.end(); +} + +basic_schema::const_iterator basic_schema::begin() const { + return schema_nodes_.begin(); +} + +basic_schema::const_iterator basic_schema::end() const { + return schema_nodes_.end(); +} + +size_t basic_schema::size() const { + return schema_nodes_.size(); +} + +bool basic_schema::empty() const { + return schema_nodes_.empty(); +} + +basic_schema::iterator basic_schema::find(const std::type_index &ti) { + return schema_nodes_.find(ti); +} + +basic_schema::const_iterator basic_schema::find(const std::type_index &ti) const { + return schema_nodes_.find(ti); +} + +basic_schema::iterator basic_schema::find(const std::string &name) { + const auto result = repo_.basic_info(name); + if (!result) { + return schema_nodes_.end(); + } + + return schema_nodes_.find(result->get().type_index()); +} + +basic_schema::const_iterator basic_schema::find(const std::string &name) const { + const auto result = repo_.basic_info(name); + if (!result) { + return schema_nodes_.end(); + } + + return schema_nodes_.find(result->get().type_index()); +} + +bool basic_schema::contains(const std::type_index &index) const { + return schema_nodes_.count(index) == 1; +} + +void basic_schema::initialize_executor(sql::executor &exec) const { + auto factory = std::make_shared(); + for (const auto &[key, producer] : resolver_producers_) { + auto resolver = producer->produce(exec); + exec.resolver_factory()->register_resolver(std::move(resolver)); + } +} + +} \ No newline at end of file diff --git a/source/orm/query/criteria/criteria_operators.cpp b/source/orm/query/criteria/criteria_operators.cpp index 143621a..c36d513 100644 --- a/source/orm/query/criteria/criteria_operators.cpp +++ b/source/orm/query/criteria/criteria_operators.cpp @@ -5,7 +5,22 @@ #include "matador/query/criteria/logical_criteria.hpp" #include "matador/query/criteria/not_criteria.hpp" +#include "matador/utils/identifier.hpp" +#include "matador/utils/identifier_to_value_converter.hpp" + namespace matador::query { +criteria_ptr operator==(const table_column &col, const utils::identifier &id) { + utils::identifier_to_value_converter conv; + + return std::make_unique(col, binary_operator::EQUALS, conv.convert(id)); +} + +criteria_ptr operator!=(const table_column &col, const utils::identifier &id) { + utils::identifier_to_value_converter conv; + + return std::make_unique(col, binary_operator::NOT_EQUALS, conv.convert(id)); +} + criteria_ptr operator==(const table_column &col, utils::placeholder p) { return std::make_unique(col, binary_operator::EQUALS, p); } diff --git a/source/orm/query/generator.cpp b/source/orm/query/generator.cpp index db0159b..76385ea 100644 --- a/source/orm/query/generator.cpp +++ b/source/orm/query/generator.cpp @@ -1,7 +1,7 @@ #include "matador/query/generator.hpp" namespace matador::query::generator { -column_generator::column_generator(const schema& repo, const table* tab, const column_generator_options options) +column_generator::column_generator(const basic_schema& repo, const table* tab, const column_generator_options options) : repo_(std::cref(repo)) , options_(options) { table_stack_.push(tab); diff --git a/source/orm/query/intermediates/fetchable_query.cpp b/source/orm/query/intermediates/fetchable_query.cpp index 420c486..61a0390 100644 --- a/source/orm/query/intermediates/fetchable_query.cpp +++ b/source/orm/query/intermediates/fetchable_query.cpp @@ -3,6 +3,7 @@ #include "matador/sql/executor.hpp" #include "matador/sql/field.hpp" #include "matador/sql/statement.hpp" +#include "matador/sql/query_record_result.hpp" namespace matador::query { @@ -29,29 +30,27 @@ utils::result, utils::error> fetchable_query::fet return exec.fetch(ctx) .and_then([](auto &&res) { const auto prototype = res->prototype(); - return utils::ok(sql::query_result(std::forward(res), [prototype] { - return detail::create_prototype(prototype); - })); + return utils::ok(sql::query_result(std::forward(res), prototype)); }); } utils::result, utils::error> fetchable_query::fetch_one(const sql::executor &exec) const { query_compiler compiler; - auto result = exec.fetch(compiler.compile(*context_, exec.dialect(), std::nullopt)); + auto ctx = compiler.compile(*context_, exec.dialect(), std::nullopt); + ctx.resolver_factory = exec.resolver_factory(); + auto result = exec.fetch(ctx); if (!result.is_ok()) { return utils::failure(result.err()); } const auto prototype = result.value()->prototype(); - sql::query_result records(std::move(*result), [prototype] { - return detail::create_prototype(prototype); - }); + sql::query_result records(std::move(*result), prototype); auto first = records.begin(); if (first == records.end()) { return utils::ok(std::optional{std::nullopt}); } - return utils::ok(std::optional{*first.get()}); + return utils::ok(std::optional{first.release()}); } std::string fetchable_query::str(const sql::executor &exec) const { @@ -64,11 +63,15 @@ sql::query_context fetchable_query::compile(const sql::dialect &d) const { } utils::result, utils::error> fetchable_query::fetch(const sql::executor &exec) const { - return exec.fetch(compile(exec.dialect())); + auto ctx = compile(exec.dialect()); + ctx.resolver_factory = exec.resolver_factory(); + return exec.fetch(ctx); } utils::result fetchable_query::prepare(sql::executor &exec) const { - return exec.prepare(compile(exec.dialect())); + auto ctx = compile(exec.dialect()); + ctx.resolver_factory = exec.resolver_factory(); + return exec.prepare(ctx); } } \ No newline at end of file diff --git a/source/orm/query/internal/query_result_impl.cpp b/source/orm/query/internal/query_result_impl.cpp index f985bbe..e5c436b 100644 --- a/source/orm/query/internal/query_result_impl.cpp +++ b/source/orm/query/internal/query_result_impl.cpp @@ -8,21 +8,15 @@ detail::pk_reader::pk_reader(query_result_reader &reader) } query_result_impl::query_result_impl(std::unique_ptr &&reader, - std::vector &&prototype, + std::vector prototype, + const std::shared_ptr& resolver_factory, const size_t column_index) : column_index_(column_index) - , prototype_(std::move(prototype)) - , reader_(std::move(reader)) - , pk_reader_(*reader_) { -} - -query_result_impl::query_result_impl(std::unique_ptr &&reader, - const std::vector &prototype, - const size_t column_index) -: column_index_(column_index) - , prototype_(prototype) - , reader_(std::move(reader)) - , pk_reader_(*reader_) { +, prototype_(std::move(prototype)) +, reader_(std::move(reader)) +, resolver_factory_(resolver_factory) +, id_reader_(*reader_) +, pk_reader_(*reader_) { } void query_result_impl::on_revision(const char *id, uint64_t &rev) { diff --git a/source/orm/query/query_builder.cpp b/source/orm/query/query_builder.cpp new file mode 100644 index 0000000..508aaca --- /dev/null +++ b/source/orm/query/query_builder.cpp @@ -0,0 +1,81 @@ +#include "matador/query/query_builder.hpp" + +namespace matador::query { +criteria_transformer::criteria_transformer(const basic_schema& repo, const std::unordered_map& tables_by_name) +: repo_(repo) +, tables_by_name_(tables_by_name) {} + +void criteria_transformer::visit( const between_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const binary_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const binary_column_criteria& /*node*/ ) {} + +void criteria_transformer::visit( const collection_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const collection_query_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const like_criteria& node ) { + update_criteria_column(node); +} + +void criteria_transformer::visit( const logical_criteria& node ) { + node.left_clause()->accept(*this); + node.right_clause()->accept(*this); +} + +void criteria_transformer::visit( const not_criteria& node ) { + node.clause()->accept(*this); +} + +void criteria_transformer::update_criteria_column(const abstract_column_criteria& node) const { + if (node.col().table() == nullptr) { + return; + } + const auto it = tables_by_name_.find(node.col().table()->name()); + if (it == tables_by_name_.end()) { + return; + } + + const_cast(node.col()).table(&it->second); +} + +void query_builder::on_revision(const char *id, uint64_t &/*rev*/) { + push(id); +} + +void query_builder::push(const std::string &column_name) { + const auto it = processed_tables_.find(table_info_stack_.top().info.name()); + if (it == processed_tables_.end()) { + throw query_builder_exception{query_build_error::UnexpectedError}; + } + entity_query_data_.columns.emplace_back(&it->second, column_name, build_alias('c', ++column_index)); +} + +std::string query_builder::build_alias(const char prefix, const unsigned int count) { + char str[4]; + snprintf(str, 4, "%c%02d", prefix, count); + + return str; +} + +[[nodiscard]] bool query_builder::is_root_entity() const { + return table_info_stack_.size() == 1; +} + +void query_builder::append_join(const table_column &left, const table_column &right) { + using namespace matador::query; + entity_query_data_.joins.push_back({ + right.table(), + std::make_unique(left, binary_operator::EQUALS, right) + }); +} +} diff --git a/source/orm/orm/query_builder_exception.cpp b/source/orm/query/query_builder_exception.cpp similarity index 55% rename from source/orm/orm/query_builder_exception.cpp rename to source/orm/query/query_builder_exception.cpp index 6edc195..ec0fc62 100644 --- a/source/orm/orm/query_builder_exception.cpp +++ b/source/orm/query/query_builder_exception.cpp @@ -1,8 +1,8 @@ -#include "matador/orm/query_builder_exception.hpp" +#include "matador/query/query_builder_exception.hpp" -namespace matador::orm { +namespace matador::query { -query_builder_exception::query_builder_exception( const query_build_error error, utils::error&& err ) +query_builder_exception::query_builder_exception(const query_build_error error, utils::error&& err) : error_type_(error) , error_( std::move(err) ) {} diff --git a/source/orm/query/query_object_resolver.cpp b/source/orm/query/query_object_resolver.cpp new file mode 100644 index 0000000..a6ecb44 --- /dev/null +++ b/source/orm/query/query_object_resolver.cpp @@ -0,0 +1,16 @@ +#include "matador/query/query_object_resolver.hpp" + +#include "matador/query/criteria.hpp" +#include "matador/query/query.hpp" + +namespace matador::query { +utils::result prepare_statement(sql::executor &exec, entity_query_data &&data) { + return query::query::select(data.columns) + .from(*data.root_table) + .join_left(data.joins) + .where(std::move(data.where_clause)) + .order_by({data.root_table, data.pk_column_name}) + .asc() + .prepare(exec); +} +} diff --git a/source/orm/query/schema.cpp b/source/orm/query/schema.cpp index 410f3f5..6963cd9 100644 --- a/source/orm/query/schema.cpp +++ b/source/orm/query/schema.cpp @@ -11,26 +11,6 @@ #include "matador/sql/dialect.hpp" namespace matador::query { -schema_node::schema_node(class table tab, const object::repository_node& node) -: table_(std::move(tab)) -, node_(std::move(node)) { -} - -const std::string & schema_node::name() const { - return table_.name(); -} - -const table &schema_node::table() const { - return table_; -} - -const object::repository_node& schema_node::node() const { - return node_; -} - -schema::schema(const std::string &name) -: repo_(name) { -} utils::result schema::create(const sql::connection &conn) const { // Step 1: Build dependency graph @@ -64,7 +44,7 @@ utils::result schema::create(const sql::connection &conn) co std::vector fk_sql_commands; - const auto q = query::query::create().schema(repo_.name()).compile(conn); + const auto q = query::create().schema(repo_.name()).compile(conn); std::cout << q.sql << std::endl; // create plain tables without constraints @@ -184,60 +164,6 @@ utils::result schema::table_exists(const std::string &table_ return conn.exists(repo_.name(), table_name); } -schema::iterator schema::begin() { - return schema_nodes_.begin(); -} - -schema::iterator schema::end() { - return schema_nodes_.end(); -} - -schema::const_iterator schema::begin() const { - return schema_nodes_.begin(); -} - -schema::const_iterator schema::end() const { - return schema_nodes_.end(); -} - -size_t schema::size() const { - return schema_nodes_.size(); -} - -bool schema::empty() const { - return schema_nodes_.empty(); -} - -schema::iterator schema::find(const std::type_index &ti) { - return schema_nodes_.find(ti); -} - -schema::const_iterator schema::find(const std::type_index &ti) const { - return schema_nodes_.find(ti); -} - -schema::iterator schema::find(const std::string &name) { - const auto result = repo_.basic_info(name); - if (!result) { - return schema_nodes_.end(); - } - - return schema_nodes_.find(result->get().type_index()); -} - -schema::const_iterator schema::find(const std::string &name) const { - const auto result = repo_.basic_info(name); - if (!result) { - return schema_nodes_.end(); - } - - return schema_nodes_.find(result->get().type_index()); -} - -bool schema::contains(const std::type_index &index) const { - return schema_nodes_.count(index) == 1; -} - void schema::dump(std::ostream &os) const { repo_.dump(os); } @@ -269,11 +195,11 @@ sql::query_context schema::build_drop_constraint_context(const object::repositor .compile(conn); } -void schema::insert_table(const std::type_index &ti, const object::repository_node &node) { +schema::iterator schema::insert_table(const std::type_index &ti, const object::repository_node &node) { std::vector columns; for (const auto &attr: node.info().attributes()) { columns.emplace_back(nullptr, attr.name(), attr.type(), attr.attributes()); } - std::ignore = schema_nodes_.insert({ti, schema_node{table(node.name(), columns), node}}).first; + return schema_nodes_.insert({ti, schema_node{table(node.name(), columns), node}}).first; } } // namespace matador::query diff --git a/source/orm/sql/connection.cpp b/source/orm/sql/connection.cpp index 2772761..cef072f 100644 --- a/source/orm/sql/connection.cpp +++ b/source/orm/sql/connection.cpp @@ -3,7 +3,6 @@ #include "matador/sql/backend_provider.hpp" #include "matador/sql/dialect.hpp" #include "matador/sql/dialect_token.hpp" -#include "matador/sql/error_code.hpp" #include "matador/sql/interface/connection_impl.hpp" @@ -28,16 +27,29 @@ public: }; } -connection::connection(const connection_info& info, const std::shared_ptr &sql_logger) -: logger_(sql_logger) -{ +connection::connection(const connection_info& info, logger_ptr sql_logger) +: logger_(std::move(sql_logger)) { connection_.reset(backend_provider::instance().create_connection(info.type, info)); } -connection::connection(const std::string& dns, const std::shared_ptr &sql_logger) +connection::connection(const connection_info &info, + std::shared_ptr resolver_factory, + logger_ptr sql_logger) +: logger_(std::move(sql_logger)) +, resolver_factory_(std::move(resolver_factory)) { + connection_.reset(backend_provider::instance().create_connection(info.type, info)); +} + +connection::connection(const std::string& dns, const logger_ptr &sql_logger) : connection(connection_info::parse(dns), sql_logger) {} +connection::connection(const std::string &dns, + std::shared_ptr resolver_factory, + const logger_ptr &sql_logger) +: connection(connection_info::parse(dns), std::move(resolver_factory), sql_logger) +{} + connection::connection(const connection &x) { if (x.connection_) { throw std::runtime_error("couldn't copy connection with valid connection impl"); @@ -58,11 +70,13 @@ connection &connection::operator=(const connection &x) { connection::connection( connection&& x ) noexcept : connection_(std::move(x.connection_)) , logger_(std::move(x.logger_)) +, resolver_factory_(std::move(x.resolver_factory_)) {} connection & connection::operator=(connection &&x) noexcept { connection_ = std::move(x.connection_); logger_ = std::move(x.logger_); + resolver_factory_ = std::move(x.resolver_factory_); return *this; } @@ -71,7 +85,7 @@ connection::~connection() { if (!connection_) { return; } - if (connection_->is_open()) { + if (const auto result = connection_->is_open(); *result) { connection_->close(); } connection_impl* impl = connection_.release(); @@ -219,6 +233,10 @@ const class dialect &connection::dialect() const return connection_->dialect(); } +std::shared_ptr connection::resolver_factory() const { + return resolver_factory_; +} + utils::result, utils::error> connection::perform_prepare(const query_context& ctx) const { if (ctx.command != sql_command::SQL_CREATE_TABLE && (ctx.prototype.empty() || has_unknown_columns(ctx.prototype))) { if (const auto result = describe(ctx.table_name); result.is_ok()) { diff --git a/source/orm/sql/connection_pool.cpp b/source/orm/sql/connection_pool.cpp index 913e2b7..b94dbc4 100644 --- a/source/orm/sql/connection_pool.cpp +++ b/source/orm/sql/connection_pool.cpp @@ -53,21 +53,19 @@ bool connection_ptr::valid() const { return connection_ != nullptr; } -connection_pool::connection_pool(const std::string& dns, size_t count) -: info_(connection_info::parse(dns)) { +connection_pool::connection_pool(const std::string& dns, const size_t count) +: connection_pool(dns, count, [](const connection_info &info) { return connection{info}; }) +{} + +connection_pool::connection_pool(const std::string &dns, size_t count, std::function &&connection_creator) +: info_(connection_info::parse(dns)) +, connection_creator_(std::move(connection_creator)) { connection_repo_.reserve(count); while (count) { - // connection c(info_); - // auto&& cc = std::move(c); - // const auto ic = identifiable_connection{count, std::move(cc)}; - // connection_repo_.push_back(ic); - // connection_repo_.emplace_back(count, std::move(cc)); - connection_repo_.emplace_back(count, connection{info_}); + connection_repo_.emplace_back(count, connection_creator_(info_)); auto &conn = connection_repo_.back(); idle_connections_.emplace(conn.id, &conn); - // Todo: handle result - const auto result = conn.conn.open(); - if (!result) { + if (const auto result = conn.conn.open(); !result) { throw std::runtime_error("Failed to open connection"); } --count; diff --git a/source/orm/sql/field.cpp b/source/orm/sql/field.cpp index d561cef..da1349e 100644 --- a/source/orm/sql/field.cpp +++ b/source/orm/sql/field.cpp @@ -35,6 +35,17 @@ field &field::operator=(field &&x) noexcept { return *this; } +bool field::operator==(const field &rhs) const { + return name_ == rhs.name_ && + type_ == rhs.type_ && + index_ == rhs.index_ && + value_ == rhs.value_; +} + +bool field::operator!=(const field &rhs) const { + return !operator==(rhs); +} + const std::string &field::name() const { return name_; } diff --git a/source/orm/sql/internal/identifier_reader.cpp b/source/orm/sql/internal/identifier_reader.cpp new file mode 100644 index 0000000..015bf79 --- /dev/null +++ b/source/orm/sql/internal/identifier_reader.cpp @@ -0,0 +1,6 @@ +#include "matador/sql/internal/identifier_reader.hpp" + +namespace matador::sql::internal { +identifier_reader::identifier_reader(query_result_reader &reader) +: reader_(reader) {} +} diff --git a/source/orm/sql/internal/identifier_statement_binder.cpp b/source/orm/sql/internal/identifier_statement_binder.cpp new file mode 100644 index 0000000..8ac49b1 --- /dev/null +++ b/source/orm/sql/internal/identifier_statement_binder.cpp @@ -0,0 +1,56 @@ +#include "matador/sql/internal/identifier_statement_binder.hpp" + +#include "matador/sql/statement.hpp" + +namespace matador::sql { +identifier_statement_binder::identifier_statement_binder(statement &stmt, const size_t index) +: stmt_(stmt) +, index_(index) {} + +void identifier_statement_binder::bind(const utils::identifier &id) { + id.serialize(*this); +} + +void identifier_statement_binder::serialize(int8_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(int16_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(int32_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(int64_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(uint8_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(uint16_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(uint32_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(uint64_t &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(const char *value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(std::string &value, const utils::field_attributes &) { + stmt_.bind(index_, value); +} + +void identifier_statement_binder::serialize(utils::null_type_t &/*value*/, const utils::field_attributes &) { +} +} diff --git a/source/orm/sql/internal/resolver_producer.cpp b/source/orm/sql/internal/resolver_producer.cpp new file mode 100644 index 0000000..dd51d3a --- /dev/null +++ b/source/orm/sql/internal/resolver_producer.cpp @@ -0,0 +1,10 @@ +#include "matador/sql/internal/resolver_producer.hpp" + +namespace matador::sql { +const std::type_index & resolver_producer::type() const { + return type_; +} + +resolver_producer::resolver_producer(const std::type_index &type) +: type_(type) {} +} diff --git a/source/orm/sql/producer_resolver_factory.cpp b/source/orm/sql/producer_resolver_factory.cpp new file mode 100644 index 0000000..a1fd68c --- /dev/null +++ b/source/orm/sql/producer_resolver_factory.cpp @@ -0,0 +1,14 @@ +#include "matador/sql/producer_resolver_factory.hpp" + +namespace matador::sql { +std::shared_ptr producer_resolver_factory::acquire_resolver(const std::type_index &type) { + if (const auto it = resolvers_.find(type); it != resolvers_.end()) { + return it->second; + } + return nullptr; +} + +void producer_resolver_factory::register_resolver(std::shared_ptr &&resolver) { + resolvers_[resolver->type()] = std::move(resolver); +} +} diff --git a/source/orm/sql/query_result.cpp b/source/orm/sql/query_result.cpp index 8b8839f..ae04ff8 100644 --- a/source/orm/sql/query_result.cpp +++ b/source/orm/sql/query_result.cpp @@ -5,12 +5,11 @@ namespace matador::sql::detail { -template<> -record *create_prototype(const std::vector &prototype) { - auto result = std::make_unique(); +record create_prototype(const std::vector &prototype) { + record rec; int index{0}; for (const auto &col: prototype) { - result->append({ + rec.append({ col.name(), col.type(), col.attributes().options(), @@ -18,7 +17,7 @@ record *create_prototype(const std::vector &prototype index++ }); } - return result.release(); + return rec; } diff --git a/source/orm/sql/record.cpp b/source/orm/sql/record.cpp index 78c5aaf..1bc2d89 100644 --- a/source/orm/sql/record.cpp +++ b/source/orm/sql/record.cpp @@ -70,9 +70,25 @@ record::const_iterator record::find(const std::string &column_name) const { return it != fields_by_name_.end() ? fields_.begin() + it->second.second : fields_.end(); } +bool record::operator==(const record &rhs) const { + if (size() != rhs.size()) { + return false; + } + for(size_t i = 0; i < size(); ++i) { + if (at(i) != rhs.at(i)) { + return false; + } + } + return true; +} + +bool record::operator!=(const record &rhs) const { + return !operator==(rhs); +} + void record::append(const field &col) { - const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()}); - fields_.push_back(std::ref(it.first->second.first)); + const auto [fst, snd] = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()}); + fields_.push_back(std::ref(fst->second.first)); } record::iterator record::begin() diff --git a/source/orm/sql/statement.cpp b/source/orm/sql/statement.cpp index 0b13c95..e2e9dc1 100644 --- a/source/orm/sql/statement.cpp +++ b/source/orm/sql/statement.cpp @@ -1,6 +1,7 @@ #include "matador/sql/statement.hpp" #include "matador/sql/record.hpp" #include "matador/sql/field.hpp" +#include "matador/sql/query_record_result.hpp" #include #include @@ -64,9 +65,7 @@ utils::result, utils::error> statement::fetch() const { } // logger_.info(statement_->query_.sql); const auto prototype = result.value()->prototype(); - return utils::ok(query_result(std::move(*result), [prototype] { - return detail::create_prototype(prototype); - })); + return utils::ok(query_result(std::move(*result), prototype)); } utils::result, utils::error> statement::fetch_one() const { @@ -77,15 +76,13 @@ utils::result, utils::error> statement::fetch_one() const } const auto prototype = result.value()->prototype(); - query_result records(std::move(*result), [prototype] { - return sql::detail::create_prototype(prototype); - }); + query_result records(std::move(*result), prototype); auto first = records.begin(); if (first == records.end()) { return utils::ok(std::optional{std::nullopt}); } - return utils::ok(std::optional{*first.get()}); + return utils::ok(std::optional{first.release()}); } void statement::reset() const { diff --git a/test/backends/QueryBasicTest.cpp b/test/backends/QueryBasicTest.cpp index 9bdb73d..fc806d9 100644 --- a/test/backends/QueryBasicTest.cpp +++ b/test/backends/QueryBasicTest.cpp @@ -79,7 +79,7 @@ TEST_CASE_METHOD(QueryFixture, "Insert and select basic datatypes", "[query][dat .from("types") .fetch_one(db); REQUIRE(result.is_ok()); - REQUIRE(*result != nullptr); + REQUIRE(result->get() != nullptr); REQUIRE((*result)->id_ == 1); REQUIRE((*result)->char_ == cval); @@ -356,7 +356,7 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key", "[query][primary key]") { .from("pk") .fetch_one(db); REQUIRE(row.is_ok()); - REQUIRE(*row != nullptr); + REQUIRE(row->get() != nullptr); REQUIRE(row.value()->id > 0); } @@ -386,7 +386,7 @@ TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key auto row = stmt->fetch_one(); REQUIRE(row.is_ok()); - REQUIRE(*row != nullptr); + REQUIRE(*row); REQUIRE(row.value()->id > 0); REQUIRE(row.value()->name == "george"); } diff --git a/test/backends/QueryStatementTests.cpp b/test/backends/QueryStatementTests.cpp index b0667b4..313569d 100644 --- a/test/backends/QueryStatementTests.cpp +++ b/test/backends/QueryStatementTests.cpp @@ -70,7 +70,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][inse .fetch_one(db); REQUIRE(row.is_ok()); - REQUIRE(*row != nullptr); + REQUIRE(*row); REQUIRE((*row)->id == 1); REQUIRE((*row)->name == "george"); REQUIRE((*row)->age == 45); @@ -101,7 +101,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda .fetch_one(db); REQUIRE(row.is_ok()); - REQUIRE(*row != nullptr); + REQUIRE(*row); REQUIRE((*row)->id == 1); REQUIRE((*row)->name == "george"); REQUIRE((*row)->age == 45); @@ -126,7 +126,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][upda .fetch_one(db); REQUIRE(row.is_ok()); - REQUIRE(*row != nullptr); + REQUIRE(*row); REQUIRE((*row)->id == 1); REQUIRE((*row)->name == "george"); REQUIRE((*row)->age == 36); @@ -170,12 +170,12 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele .fetch(); REQUIRE(rows.is_ok()); - for (const auto &r : *rows) { + for (const auto r : *rows) { constexpr size_t index = 1; - REQUIRE(r.id == peoples[index].id); - REQUIRE(r.name == peoples[index].name); - REQUIRE(r.age == peoples[index].age); - REQUIRE(r.image == peoples[index].image); + REQUIRE(r->id == peoples[index].id); + REQUIRE(r->name == peoples[index].name); + REQUIRE(r->age == peoples[index].age); + REQUIRE(r->image == peoples[index].image); } stmt = query::remove() @@ -192,9 +192,7 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele select_stmt->reset(); auto row = select_stmt->bind(0, "jane") .fetch_one(); - REQUIRE(row.is_ok()); - - REQUIRE(*row == nullptr); + REQUIRE(!row); stmt->reset(); res = stmt->bind(0, "merlin") @@ -205,9 +203,7 @@ TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][dele select_stmt->reset(); row = select_stmt->bind(0, "merlin") .fetch_one(); - REQUIRE(row.is_ok()); - - REQUIRE(*row == nullptr); + REQUIRE(!row); } TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][statement][reuse]") { @@ -247,10 +243,10 @@ TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][stateme size_t index = 0; for (const auto &r : *rows) { - REQUIRE(r.id == peoples[index].id); - REQUIRE(r.name == peoples[index].name); - REQUIRE(r.age == peoples[index].age); - REQUIRE(r.image == peoples[index].image); + REQUIRE(r->id == peoples[index].id); + REQUIRE(r->name == peoples[index].name); + REQUIRE(r->age == peoples[index].age); + REQUIRE(r->image == peoples[index].image); ++index; } @@ -261,10 +257,10 @@ TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][stateme index = 0; for (const auto &r : *rows) { - REQUIRE(r.id == peoples[index].id); - REQUIRE(r.name == peoples[index].name); - REQUIRE(r.age == peoples[index].age); - REQUIRE(r.image == peoples[index].image); + REQUIRE(r->id == peoples[index].id); + REQUIRE(r->name == peoples[index].name); + REQUIRE(r->age == peoples[index].age); + REQUIRE(r->image == peoples[index].image); ++index; } } \ No newline at end of file diff --git a/test/backends/QueryTest.cpp b/test/backends/QueryTest.cpp index 78cd201..4057c79 100644 --- a/test/backends/QueryTest.cpp +++ b/test/backends/QueryTest.cpp @@ -27,7 +27,7 @@ using namespace matador::test; using namespace matador::query::meta; TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") { - auto result = repo.attach("airplane") + const auto result = repo.attach("airplane") .and_then([this] { return repo.attach("flight");}) .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); @@ -81,9 +81,9 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[q REQUIRE(result_person.is_ok()); for (const auto &i: *result_person) { - REQUIRE(i.id == 7); - REQUIRE(i.name == "george"); - REQUIRE(i.age == 45); + REQUIRE(i->id == 7); + REQUIRE(i->name == "george"); + REQUIRE(i->age == 45); } } @@ -143,9 +143,9 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][for REQUIRE(db.exists(FLIGHT.table_name())); std::vector planes{ - object_ptr(new airplane{1, "Airbus", "A380"}), - object_ptr(new airplane{2, "Boeing", "707"}), - object_ptr(new airplane{3, "Boeing", "747"}) + object_ptr(std::make_shared(1, "Airbus", "A380")), + object_ptr(std::make_shared(2, "Boeing", "707")), + object_ptr(std::make_shared(3, "Boeing", "747")) }; for (const auto &plane: planes) { @@ -163,6 +163,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][for REQUIRE(count.is_ok()); REQUIRE(*count == 3); + flight f4711{4, planes.at(1), "hans"}; auto res = query::insert() @@ -192,9 +193,9 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" REQUIRE(db.exists(FLIGHT.table_name())); std::vector planes{ - object_ptr(new airplane{1, "Airbus", "A380"}), - object_ptr(new airplane{2, "Boeing", "707"}), - object_ptr(new airplane{3, "Boeing", "747"}) + object_ptr(std::make_shared(1, "Airbus", "A380")), + object_ptr(std::make_shared(2, "Boeing", "707")), + object_ptr(std::make_shared(3, "Boeing", "747")) }; for (const auto &plane: planes) { @@ -212,10 +213,10 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left" REQUIRE(count == 3); std::vector flights{ - object_ptr(new flight{4, planes.at(0), "hans"}), - object_ptr(new flight{5, planes.at(0), "otto"}), - object_ptr(new flight{6, planes.at(1), "george"}), - object_ptr(new flight{7, planes.at(2), "paul"}) + object_ptr(std::make_shared(4, planes.at(0), "hans")), + object_ptr(std::make_shared(5, planes.at(0), "otto")), + object_ptr(std::make_shared(6, planes.at(1), "george")), + object_ptr(std::make_shared(7, planes.at(2), "paul")) }; for (const auto &f: flights) { @@ -267,13 +268,15 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); + repo.initialize_executor(db); + REQUIRE(db.exists(AIRPLANE.table_name())); REQUIRE(db.exists(FLIGHT.table_name())); std::vector planes{ - object_ptr(new airplane{1, "Airbus", "A380"}), - object_ptr(new airplane{2, "Boeing", "707"}), - object_ptr(new airplane{3, "Boeing", "747"}) + object_ptr(std::make_shared(1, "Airbus", "A380")), + object_ptr(std::make_shared(2, "Boeing", "707")), + object_ptr(std::make_shared(3, "Boeing", "747")) }; for (const auto &plane: planes) { @@ -293,10 +296,10 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single REQUIRE(count->value() == 3); std::vector flights{ - object_ptr(new flight{4, planes.at(0), "hans"}), - object_ptr(new flight{5, planes.at(0), "otto"}), - object_ptr(new flight{6, planes.at(1), "george"}), - object_ptr(new flight{7, planes.at(2), "paul"}) + object_ptr(std::make_shared(4, planes.at(0), "hans")), + object_ptr(std::make_shared(5, planes.at(0), "otto")), + object_ptr(std::make_shared(6, planes.at(1), "george")), + object_ptr(std::make_shared(7, planes.at(2), "paul")) }; for (const auto &f: flights) { @@ -495,8 +498,8 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", REQUIRE(result.is_ok()); const std::vector shipments { - object_ptr{new shipment{1, "4711"}}, - object_ptr{new shipment{2, "0815"}} + object_ptr{std::make_shared(1, "4711")}, + object_ptr{std::make_shared(2, "0815")} }; for (const auto &sh: shipments) { @@ -515,11 +518,11 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", REQUIRE(*count == 2); std::vector packages { - object_ptr{new package{3, 15.4, shipments.at(0)}}, - object_ptr{new package{4, 1.3, shipments.at(0)}}, - object_ptr{new package{5, 30.9, shipments.at(1)}}, - object_ptr{new package{6, 22.8, shipments.at(1)}}, - object_ptr{new package{7, 17.2, shipments.at(1)}} + object_ptr{std::make_shared(3, 15.4, shipments.at(0))}, + object_ptr{std::make_shared(4, 1.3, shipments.at(0))}, + object_ptr{std::make_shared(5, 30.9, shipments.at(1))}, + object_ptr{std::make_shared(6, 22.8, shipments.at(1))}, + object_ptr{std::make_shared(7, 17.2, shipments.at(1))} }; for (const auto &pkg: packages) { @@ -576,9 +579,9 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", std::vector packages_sizes{2, 3}; std::cout << "\n"; for (const auto &s : *shipment_result) { - REQUIRE(s.id == shipments.at(index)->id); - REQUIRE(s.tracking_number == shipments.at(index)->tracking_number); - REQUIRE(s.packages.size() == packages_sizes.at(index++)); + REQUIRE(s->id == shipments.at(index)->id); + REQUIRE(s->tracking_number == shipments.at(index)->tracking_number); + REQUIRE(s->packages.size() == packages_sizes.at(index++)); } } @@ -588,17 +591,19 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy belongs to relation", .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); + repo.initialize_executor(db); + const std::vector deps { - object_ptr{new department{1, "Human Resources"}}, - object_ptr{new department{2, "Invoices"}} + object_ptr{std::make_shared(1, "Human Resources")}, + object_ptr{std::make_shared(2, "Invoices")} }; const std::vector emps { - object_ptr{new employee{3, "Hans", "Wurst", deps[0]}}, - object_ptr{new employee{4, "Steven", "Spielberg", deps[0]}}, - object_ptr{new employee{5, "Julia", "Roberts", deps[0]}}, - object_ptr{new employee{6, "Otto", "Walkes", deps[1]}}, - object_ptr{new employee{7, "Miss", "Marple", deps[1]}}, + object_ptr{std::make_shared(3, "Hans", "Wurst", deps[0])}, + object_ptr{std::make_shared(4, "Steven", "Spielberg", deps[0])}, + object_ptr{std::make_shared(5, "Julia", "Roberts", deps[0])}, + object_ptr{std::make_shared(6, "Otto", "Walkes", deps[1])}, + object_ptr{std::make_shared(7, "Miss", "Marple", deps[1])}, }; for (const auto &dep: deps) { @@ -631,21 +636,26 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy belongs to relation", REQUIRE(count.is_ok()); REQUIRE(*count == 5); - auto emps_result = query::select({EMPLOYEE.id, EMPLOYEE.first_name, EMPLOYEE.last_name, DEPARTMENT.id, DEPARTMENT.name}) + // auto emps_result = query::select({EMPLOYEE.id, EMPLOYEE.first_name, EMPLOYEE.last_name, DEPARTMENT.id, DEPARTMENT.name}) + // .from(EMPLOYEE) + // .join_left(DEPARTMENT) + // .on(EMPLOYEE.dep_id == DEPARTMENT.id) + // .order_by(EMPLOYEE.id).asc() + // .fetch_all(db); + + auto emps_result = query::select({EMPLOYEE.id, EMPLOYEE.first_name, EMPLOYEE.last_name, EMPLOYEE.dep_id}) .from(EMPLOYEE) - .join_left(DEPARTMENT) - .on(EMPLOYEE.dep_id == DEPARTMENT.id) .order_by(EMPLOYEE.id).asc() .fetch_all(db); REQUIRE(emps_result.is_ok()); size_t index{0}; for (const auto &e : *emps_result) { - REQUIRE(e.id == emps.at(index)->id); - REQUIRE(e.first_name == emps.at(index)->first_name); - REQUIRE(e.last_name == emps.at(index)->last_name); - REQUIRE(e.dep->id == emps.at(index)->dep->id); - REQUIRE(e.dep->name == emps.at(index)->dep->name); + REQUIRE(e->id == emps.at(index)->id); + REQUIRE(e->first_name == emps.at(index)->first_name); + REQUIRE(e->last_name == emps.at(index)->last_name); + REQUIRE(e->dep->id == emps.at(index)->dep->id); + REQUIRE(e->dep->name == emps.at(index)->dep->name); ++index; } } @@ -658,21 +668,21 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager belongs to relation" } ); const std::vector authors { - object_ptr{new author{1, "Michael", "Crichton", "23.10.1942", 1975, true, {}}}, - object_ptr{new author{ 2, "Steven", "King", "21.9.1947", 1956, false, {}}} + object_ptr{std::make_shared(1, "Michael", "Crichton", "23.10.1942", 1975, true)}, + object_ptr{std::make_shared( 2, "Steven", "King", "21.9.1947", 1956, false)} }; const std::vector books { - object_ptr{new book{3, "Jurassic Park", authors[0], 1990}}, - object_ptr{new book{4, "Timeline", authors[0], 1999}}, - object_ptr{new book{5, "The Andromeda Strain", authors[0], 1969}}, - object_ptr{new book{6, "Congo", authors[0], 1980}}, - object_ptr{new book{7, "Prey", authors[0], 2002}}, - object_ptr{new book{8, "Carrie", authors[1], 1974}}, - object_ptr{new book{9, "The Shining", authors[1], 1977}}, - object_ptr{new book{10, "It", authors[1], 1986}}, - object_ptr{new book{11, "Misery", authors[1], 1987}}, - object_ptr{new book{12, "The Dark Tower: The Gunslinger", authors[1], 1982}}, + object_ptr{std::make_shared(3, "Jurassic Park", authors[0], 1990)}, + object_ptr{std::make_shared(4, "Timeline", authors[0], 1999)}, + object_ptr{std::make_shared(5, "The Andromeda Strain", authors[0], 1969)}, + object_ptr{std::make_shared(6, "Congo", authors[0], 1980)}, + object_ptr{std::make_shared(7, "Prey", authors[0], 2002)}, + object_ptr{std::make_shared(8, "Carrie", authors[1], 1974)}, + object_ptr{std::make_shared(9, "The Shining", authors[1], 1977)}, + object_ptr{std::make_shared(10, "It", authors[1], 1986)}, + object_ptr{std::make_shared(11, "Misery", authors[1], 1987)}, + object_ptr{std::make_shared(12, "The Dark Tower: The Gunslinger", authors[1], 1982)}, }; for (const auto &a: authors) { @@ -715,15 +725,15 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager belongs to relation" REQUIRE(books_result.is_ok()); size_t index{0}; for (const auto &b : *books_result) { - REQUIRE(b.id == books.at(index)->id); - REQUIRE(b.title == books.at(index)->title); - REQUIRE(b.book_author->id == books.at(index)->book_author->id); - REQUIRE(b.book_author->first_name == books.at(index)->book_author->first_name); - REQUIRE(b.book_author->last_name == books.at(index)->book_author->last_name); - REQUIRE(b.book_author->date_of_birth == books.at(index)->book_author->date_of_birth); - REQUIRE(b.book_author->year_of_birth == books.at(index)->book_author->year_of_birth); - REQUIRE(b.book_author->distinguished == books.at(index)->book_author->distinguished); - REQUIRE(b.published_in == books.at(index)->published_in); + REQUIRE(b->id == books.at(index)->id); + REQUIRE(b->title == books.at(index)->title); + REQUIRE(b->book_author->id == books.at(index)->book_author->id); + REQUIRE(b->book_author->first_name == books.at(index)->book_author->first_name); + REQUIRE(b->book_author->last_name == books.at(index)->book_author->last_name); + REQUIRE(b->book_author->date_of_birth == books.at(index)->book_author->date_of_birth); + REQUIRE(b->book_author->year_of_birth == books.at(index)->book_author->year_of_birth); + REQUIRE(b->book_author->distinguished == books.at(index)->book_author->distinguished); + REQUIRE(b->published_in == books.at(index)->published_in); ++index; } } \ No newline at end of file diff --git a/test/backends/SessionFixture.cpp b/test/backends/SessionFixture.cpp index 8ac004f..4e1f0fa 100644 --- a/test/backends/SessionFixture.cpp +++ b/test/backends/SessionFixture.cpp @@ -7,13 +7,15 @@ namespace matador::test { SessionFixture::SessionFixture() -: pool(connection::dns, 4) -, ses({bus, pool}, schema) {} +: ses({bus, connection::dns, 4}, schema) +, db(connection::dns) { + REQUIRE(db.open()); +} SessionFixture::~SessionFixture() { - const auto conn = pool.acquire(); - const auto result = schema.drop(*conn); + const auto result = schema.drop(db); REQUIRE(result.is_ok()); + REQUIRE(db.close()); } void SessionFixture::drop_table_if_exists(const std::string &table_name) const { diff --git a/test/backends/SessionFixture.hpp b/test/backends/SessionFixture.hpp index b1d0261..c642689 100644 --- a/test/backends/SessionFixture.hpp +++ b/test/backends/SessionFixture.hpp @@ -15,9 +15,9 @@ public: ~SessionFixture(); protected: - sql::connection_pool pool; orm::session ses; utils::message_bus bus; + sql::connection db; query::schema schema; diff --git a/test/backends/SessionTest.cpp b/test/backends/SessionTest.cpp index 45d786d..a713200 100644 --- a/test/backends/SessionTest.cpp +++ b/test/backends/SessionTest.cpp @@ -18,8 +18,7 @@ using namespace matador::test; TEST_CASE_METHOD(SessionFixture, "Session insert test", "[session][insert]") { const auto result = schema.attach("airplanes") .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); + return schema.create(db); } ); REQUIRE(result.is_ok()); @@ -37,8 +36,7 @@ TEST_CASE_METHOD(SessionFixture, "Session insert test", "[session][insert]") { TEST_CASE_METHOD(SessionFixture, "Session update test", "[session][update]") { const auto res = schema.attach("airplanes") .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); + return schema.create(db); } ); REQUIRE(res.is_ok()); @@ -69,12 +67,11 @@ TEST_CASE_METHOD(SessionFixture, "Session update test", "[session][update]") { TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") { const auto res = schema.attach("airplanes") - .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); - } ); + .and_then([this] { return schema.create(db); } ); REQUIRE(res.is_ok()); + schema.initialize_executor(ses); + auto result = ses.insert(1, "Boeing", "747"); REQUIRE(result.is_ok()); @@ -94,10 +91,7 @@ TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") { TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { const auto result = schema.attach("airplanes") .and_then([this] { return schema.attach("flights"); } ) - .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); - } ); + .and_then([this] { return schema.create(db); } ); REQUIRE(result.is_ok()); auto plane = ses.insert(1, "Boeing", "A380"); @@ -118,10 +112,7 @@ TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") { const auto result = schema.attach("airplanes") - .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); - } ); + .and_then([this] { return schema.create(db); } ); REQUIRE(result.is_ok()); auto a380 = ses.insert(1, "Boeing", "A380"); @@ -141,8 +132,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") { const auto result = schema.attach("airplanes") .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); + return schema.create(db); } ); REQUIRE(result.is_ok()); @@ -167,9 +157,9 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][f auto all_planes = find_result.release(); size_t index {0}; for (const auto &i: all_planes) { - REQUIRE(i.id == std::get<0>(expected_result[index])); - REQUIRE(i.brand == std::get<1>(expected_result[index])); - REQUIRE(i.model == std::get<2>(expected_result[index])); + REQUIRE(i->id == std::get<0>(expected_result[index])); + REQUIRE(i->brand == std::get<1>(expected_result[index])); + REQUIRE(i->model == std::get<2>(expected_result[index])); ++index; } } @@ -177,17 +167,16 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][f TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-many lazy relation", "[session][find][one-to-many][eager]") { auto result = schema.attach("authors") .and_then( [this] { return schema.attach("books"); } ) - .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); - } ); + .and_then([this] { return schema.create(db); } ); - std::vector> authors; - authors.emplace_back(new author{1, "Michael", "Crichton", "23.10.1942", 1975, true, {}}); - authors.emplace_back(new author{ 2, "Steven", "King", "21.9.1947", 1956, false, {}}); + schema.initialize_executor(ses); + std::vector authors { + object_ptr{std::make_shared(1, "Michael", "Crichton", "23.10.1942", 1975, true)}, + object_ptr{std::make_shared( 2, "Steven", "King", "21.9.1947", 1956, false)} + }; - for (auto &&a: authors) { - auto res = ses.insert(a.release()); + for (const auto &a: authors) { + auto res = ses.insert(a); REQUIRE(res.is_ok()); } @@ -197,7 +186,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma std::vector> author_repo; for (auto it = all_authors.begin(); it != all_authors.end(); ++it) { std::cout << "author: " << it->first_name << " (books: " << it->books.size() << ")\n"; - author_repo.emplace_back(it.release()); + author_repo.emplace_back(it.optr()); } REQUIRE(author_repo.size() == 2); @@ -231,13 +220,12 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma auto result = schema.attach("departments") .and_then( [this] { return schema.attach("employees"); } ) .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); + return schema.create(db); } ); std::vector> departments; - departments.emplace_back(new department{1, "Insurance", {}}); - departments.emplace_back(new department{2, "Invoice", {}}); + departments.emplace_back(new department{1, "Insurance"}); + departments.emplace_back(new department{2, "Invoice"}); for (auto &&a: departments) { auto res = ses.insert(a.release()); @@ -250,7 +238,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma std::vector> departments_repo; for (auto it = all_departments.begin(); it != all_departments.end(); ++it) { std::cout << "department: " << it->name << " (employees: " << it->employees.size() << ")\n"; - departments_repo.emplace_back(it.release()); + departments_repo.emplace_back(it.optr()); } REQUIRE(departments_repo.size() == 2); @@ -287,8 +275,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-m auto result = schema.attach("recipes") .and_then( [this] { return schema.attach("ingredients"); } ) .and_then([this] { - const auto conn = pool.acquire(); - return schema.create(*conn); + return schema.create(db); } ); std::vector> ingredients; diff --git a/test/backends/StatementTest.cpp b/test/backends/StatementTest.cpp index a0c4fcc..b8b8370 100644 --- a/test/backends/StatementTest.cpp +++ b/test/backends/StatementTest.cpp @@ -21,24 +21,18 @@ using namespace matador::query; using namespace matador::test; using namespace matador::query::meta; -namespace matador::test::detail { -template -[[maybe_unused]] object_ptr make_object_ptr(Args&&... args) { - return object_ptr(new Type(std::forward(args)...)); -} -} - class StatementTestFixture : public QueryFixture { public: StatementTestFixture() { REQUIRE(repo.attach("airplanes")); + repo.initialize_executor(db); } protected: - std::vector> planes{ - matador::test::detail::make_object_ptr(1, "Airbus", "A380"), - matador::test::detail::make_object_ptr(2, "Boeing", "707"), - matador::test::detail::make_object_ptr(3, "Boeing", "747") + std::vector planes { + {1, "Airbus", "A380"}, + {2, "Boeing", "707"}, + {3, "Boeing", "747"} }; }; @@ -53,7 +47,7 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement] REQUIRE(stmt.is_ok()); for (const auto &plane: planes) { - auto res = stmt->bind(*plane).execute(); + auto res = stmt->bind(plane).execute(); REQUIRE(res.is_ok()); REQUIRE(*res == 1); stmt->reset(); @@ -66,9 +60,9 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement] REQUIRE(result.is_ok()); size_t index{0}; for (const auto &i: *result) { - REQUIRE(i.id == planes[index]->id); - REQUIRE(i.brand == planes[index]->brand); - REQUIRE(i.model == planes[index++]->model); + REQUIRE(i->id == planes[index].id); + REQUIRE(i->brand == planes[index].brand); + REQUIRE(i->model == planes[index++].model); } } @@ -76,13 +70,13 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement] for (const auto &plane: planes) { auto res = query::insert() .into(AIRPLANE, {AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model}) - .values(*plane) + .values(plane) .execute(db); REQUIRE(res.is_ok()); REQUIRE(*res == 1); } - auto stmt = query::select(generator::columns(repo)) + auto stmt = query::select({AIRPLANE.id, AIRPLANE.brand, AIRPLANE.model}) .from(AIRPLANE) .where(AIRPLANE.brand == _) .prepare(db); @@ -93,9 +87,9 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement] REQUIRE(result.is_ok()); for (const auto &i: *result) { - REQUIRE(i.id == planes[0]->id); - REQUIRE(i.brand == planes[0]->brand); - REQUIRE(i.model == planes[0]->model); + REQUIRE(i->id == planes[0].id); + REQUIRE(i->brand == planes[0].brand); + REQUIRE(i->model == planes[0].model); } stmt->reset(); @@ -106,9 +100,9 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement] size_t index{1}; REQUIRE(result.is_ok()); for (const auto &i: *result) { - REQUIRE(i.id == planes[index]->id); - REQUIRE(i.brand == planes[index]->brand); - REQUIRE(i.model == planes[index++]->model); + REQUIRE(i->id == planes[index].id); + REQUIRE(i->brand == planes[index].brand); + REQUIRE(i->model == planes[index++].model); } } } \ No newline at end of file diff --git a/test/models/author.hpp b/test/models/author.hpp index cb2ab35..c6a4b54 100644 --- a/test/models/author.hpp +++ b/test/models/author.hpp @@ -12,6 +12,13 @@ namespace matador::test { struct book; struct author { + author() = default; + author(const unsigned int id, std::string first_name, std::string last_name, std::string date_of_birth, const unsigned short year_of_birth, const bool distinguished) + : id(id), first_name(std::move(first_name)) + , last_name(std::move(last_name)) + , date_of_birth(std::move(date_of_birth)) + , year_of_birth(year_of_birth) + , distinguished(distinguished) {} unsigned int id{}; std::string first_name; std::string last_name; diff --git a/test/models/book.hpp b/test/models/book.hpp index da61dc1..d09c1d0 100644 --- a/test/models/book.hpp +++ b/test/models/book.hpp @@ -12,6 +12,9 @@ namespace matador::test { struct author; struct book { + book() = default; + book(const unsigned int id, std::string title, object::object_ptr author, unsigned short published_in) + : id(id), title(std::move(title)), book_author(std::move(author)), published_in(published_in) {} unsigned int id{}; std::string title; object::object_ptr book_author; diff --git a/test/models/department.hpp b/test/models/department.hpp index 90a1d61..e3aa821 100644 --- a/test/models/department.hpp +++ b/test/models/department.hpp @@ -11,6 +11,9 @@ namespace matador::test { struct employee; struct department { + department() = default; + department(const unsigned int id, std::string name) + : id(id), name(std::move(name)) {} unsigned int id{}; std::string name; std::vector> employees{}; @@ -27,6 +30,9 @@ struct department { }; struct employee { + employee() = default; + employee(const unsigned int id, std::string first, std::string last, object::object_ptr dep) + : id(id), first_name(std::move(first)), last_name(std::move(last)), dep(std::move(dep)) {} unsigned int id{}; std::string first_name; std::string last_name; diff --git a/test/models/product.hpp b/test/models/product.hpp index 95bf231..79685ed 100644 --- a/test/models/product.hpp +++ b/test/models/product.hpp @@ -5,7 +5,6 @@ #include "supplier.hpp" #include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" #include "matador/object/object_ptr.hpp" @@ -14,8 +13,8 @@ namespace matador::test { struct product { std::string product_name; - object::object_ptr supplier; - object::object_ptr category; + object::object_ptr seller; + object::object_ptr type; std::string quantity_per_unit; unsigned int unit_price; unsigned int units_in_stock; @@ -28,8 +27,8 @@ struct product { namespace field = matador::access; using namespace matador::utils; field::primary_key(op, "product_name", product_name, 255); - field::belongs_to(op, "supplier_id", supplier, CascadeAllFetchLazy); - field::belongs_to(op, "category_id", category, CascadeAllFetchLazy); + field::belongs_to(op, "supplier_id", seller, CascadeAllFetchLazy); + field::belongs_to(op, "category_id", type, CascadeAllFetchLazy); field::attribute(op, "quantity_per_unit", quantity_per_unit, 255); field::attribute(op, "unit_price", unit_price); field::attribute(op, "units_in_stock", units_in_stock); diff --git a/test/models/shipment.hpp b/test/models/shipment.hpp index 5044571..8e8b79f 100644 --- a/test/models/shipment.hpp +++ b/test/models/shipment.hpp @@ -13,6 +13,10 @@ namespace matador::test { struct package; struct shipment { + shipment() = default; + shipment(const long id, std::string tracking_number) + : id(id), tracking_number(std::move(tracking_number)) {} + long id{}; std::string tracking_number; object::collection > packages{}; @@ -27,6 +31,10 @@ struct shipment { }; struct package { + package() = default; + package(const long id, double weight, object::object_ptr del) + : id(id), weight(weight), delivery(std::move(del)){} + long id{}; double weight{}; object::object_ptr delivery; diff --git a/test/orm/backend/test_connection.cpp b/test/orm/backend/test_connection.cpp index 636a693..db07042 100644 --- a/test/orm/backend/test_connection.cpp +++ b/test/orm/backend/test_connection.cpp @@ -49,7 +49,10 @@ utils::result test_connection::execute(const std::string & } utils::result, utils::error> test_connection::fetch(const sql::query_context &context) { - return utils::ok(std::make_unique(std::make_unique(), context.prototype, context.prototype.size())); + return utils::ok(std::make_unique(std::make_unique(), + context.prototype, + context.resolver_factory, + context.prototype.size())); } utils::result, utils::error> test_connection::prepare(const sql::query_context &context) { @@ -61,12 +64,12 @@ utils::result, utils::error> test_connection::des return utils::ok(std::vector{}); } -utils::result test_connection::exists(const std::string &/*schema_name*/, const std::string &/*table_name*/) { +utils::result test_connection::exists(const std::string &/*schema_name*/, + const std::string &/*table_name*/) { return utils::ok(false); } -std::string test_connection::to_escaped_string(const utils::blob_type_t& value) const { +std::string test_connection::to_escaped_string(const utils::blob_type_t &value) const { return utils::to_string(value); } - -} \ No newline at end of file +} diff --git a/test/orm/backend/test_statement.cpp b/test/orm/backend/test_statement.cpp index d7f1d7e..c664f0e 100644 --- a/test/orm/backend/test_statement.cpp +++ b/test/orm/backend/test_statement.cpp @@ -19,7 +19,10 @@ utils::result test_statement::execute(const sql::parameter } utils::result, utils::error> test_statement::fetch(const sql::parameter_binder &/*bindings*/) { - return utils::ok(std::make_unique(std::make_unique(), query_.prototype, query_.prototype.size())); + return utils::ok(std::make_unique(std::make_unique(), + query_.prototype, + query_.resolver_factory, + query_.prototype.size())); } void test_statement::reset() {} diff --git a/test/orm/orm/SessionQueryBuilderTest.cpp b/test/orm/orm/SessionQueryBuilderTest.cpp index 4785a3e..09b1ff0 100644 --- a/test/orm/orm/SessionQueryBuilderTest.cpp +++ b/test/orm/orm/SessionQueryBuilderTest.cpp @@ -12,11 +12,8 @@ #include "matador/query/table.hpp" #include "matador/query/schema.hpp" -#include "matador/utils/macro_map.hpp" +#include "matador/query/query_builder.hpp" -#include "matador/orm/session_query_builder.hpp" - -#include "../backend/test_connection.hpp" #include "../backend/test_backend_service.hpp" #include "../../models/airplane.hpp" @@ -29,7 +26,6 @@ #include "../../models/student.hpp" using namespace matador::object; -using namespace matador::orm; using namespace matador::query; using namespace matador::utils; using namespace matador::sql; @@ -46,7 +42,7 @@ TEST_CASE("Create sql query data for entity with eager has one", "[query][entity .and_then( [&scm] { return scm.attach("flights"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); const auto it = scm.find(typeid(flight)); REQUIRE(it != scm.end()); @@ -100,7 +96,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent .and_then( [&scm] { return scm.attach("books"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); const auto it = scm.find(typeid(book)); REQUIRE(it != scm.end()); @@ -172,7 +168,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q .and_then( [&scm] { return scm.attach("orders"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); const auto it = scm.find(typeid(order)); REQUIRE(it != scm.end()); @@ -236,7 +232,7 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e .and_then( [&scm] { return scm.attach("ingredients"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); const auto it = scm.find(typeid(ingredient)); REQUIRE(it != scm.end()); @@ -290,7 +286,7 @@ TEST_CASE("Create sql query data for entity with eager many to many (inverse par .and_then( [&scm] { return scm.attach("courses"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); const auto it = scm.find(typeid(course)); REQUIRE(it != scm.end()); @@ -344,7 +340,7 @@ TEST_CASE("Test eager relationship", "[session][eager]") { .and_then( [&scm] { return scm.attach("employees"); } ); REQUIRE(result); - session_query_builder eqb(scm, db); + query_builder eqb(scm, db); auto data = eqb.build(); REQUIRE(data.is_ok());