From bcec740d601f4dac62d0e2cf6f15f06b4961e5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 29 Jan 2026 17:23:16 +0100 Subject: [PATCH] collection_resolver progress --- backends/postgres/src/postgres_connection.cpp | 3 +- backends/postgres/src/postgres_statement.cpp | 5 ++- demo/sandbox.cpp | 9 ++++ include/matador/net/os.hpp | 2 - include/matador/object/collection_proxy.hpp | 8 +++- .../object/collection_resolver_factory.hpp | 2 +- .../query/intermediates/fetchable_query.hpp | 9 ++-- .../query/query_collection_resolver.hpp | 41 ++++++------------- include/matador/query/schema.hpp | 18 ++++++-- .../sql/internal/query_result_impl.hpp | 9 ++-- include/matador/sql/query_context.hpp | 2 + include/matador/utils/convert.hpp | 11 +++-- include/matador/utils/os.hpp | 8 ++++ source/core/utils/os.cpp | 20 +++++++-- .../query/intermediates/fetchable_query.cpp | 6 ++- .../orm/query/internal/query_result_impl.cpp | 4 +- todo.md | 2 +- 17 files changed, 100 insertions(+), 59 deletions(-) diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index b8c00e8..55bb4f2 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -111,7 +111,8 @@ utils::result, utils::error> postgres_co return utils::ok(std::make_unique(std::make_unique(res), std::move(prototype), - context.resolver)); + context.resolver, + context.result_type)); } 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 38b84b4..9159859 100644 --- a/backends/postgres/src/postgres_statement.cpp +++ b/backends/postgres/src/postgres_statement.cpp @@ -60,7 +60,10 @@ 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, query_.resolver)); + return utils::ok(std::make_unique(std::make_unique(res), + query_.prototype, + query_.resolver, + query_.result_type)); } std::unique_ptr postgres_statement::create_binder() const { diff --git a/demo/sandbox.cpp b/demo/sandbox.cpp index 7b88ecc..4f41883 100644 --- a/demo/sandbox.cpp +++ b/demo/sandbox.cpp @@ -1,4 +1,7 @@ #include "matador/object/repository.hpp" +#include "matador/object/collection.hpp" + +#include "matador/sql/resolver_service.hpp" #include "matador/logger/log_manager.hpp" @@ -131,6 +134,12 @@ int main() { // logger::default_min_log_level(logger::log_level::LVL_DEBUG); // logger::add_log_sink(logger::create_stdout_sink()); + sql::resolver_service rs; + + std::weak_ptr cr = rs.collection_resolver>( typeid(person), "names" ); + utils::identifier id{1}; + + object::collection_proxy cp(cr, id); { // has_many with builtin-type object::repository repo; diff --git a/include/matador/net/os.hpp b/include/matador/net/os.hpp index d7a6acd..b7f19e2 100644 --- a/include/matador/net/os.hpp +++ b/include/matador/net/os.hpp @@ -1,8 +1,6 @@ #ifndef MATADOR_NET_OS_HPP #define MATADOR_NET_OS_HPP -#include "matador/net/export.hpp" - #include #if _WIN32 diff --git a/include/matador/object/collection_proxy.hpp b/include/matador/object/collection_proxy.hpp index 16f2f97..99f4615 100644 --- a/include/matador/object/collection_proxy.hpp +++ b/include/matador/object/collection_proxy.hpp @@ -1,12 +1,13 @@ #ifndef MATADOR_COLLECTION_PROXY_HPP #define MATADOR_COLLECTION_PROXY_HPP -#include #include "matador/object/collection_resolver.hpp" #include "matador/utils/identifier.hpp" +#include +#include #include namespace matador::object { @@ -33,13 +34,15 @@ public: return owner_id_; } const std::vector& items() const { + resolve(); return items_; } std::vector& items() { + resolve(); return items_; } private: - void resolve() const { + void resolve() { if (loaded_) { return; } @@ -53,6 +56,7 @@ private: } items_ = resolver->resolve(owner_id_); + loaded_ = true; } private: diff --git a/include/matador/object/collection_resolver_factory.hpp b/include/matador/object/collection_resolver_factory.hpp index b01d06a..679323d 100644 --- a/include/matador/object/collection_resolver_factory.hpp +++ b/include/matador/object/collection_resolver_factory.hpp @@ -16,7 +16,7 @@ class collection_resolver_factory : public abstract_collection_resolver_factory public: template [[nodiscard]] std::shared_ptr> resolver(const std::type_index &root_type, const std::string &collection_name) const { - const auto res = acquire_collection_resolver(typeid(Type), root_type, collection_name); + const auto res = acquire_collection_resolver(root_type, typeid(Type), collection_name); if (!res) { return std::dynamic_pointer_cast>(res); } diff --git a/include/matador/query/intermediates/fetchable_query.hpp b/include/matador/query/intermediates/fetchable_query.hpp index 6c8a144..95f23bd 100644 --- a/include/matador/query/intermediates/fetchable_query.hpp +++ b/include/matador/query/intermediates/fetchable_query.hpp @@ -26,7 +26,7 @@ protected: public: template < class Type > utils::result, utils::error> fetch_all(sql::executor &exec) { - auto result = fetch(exec); + auto result = fetch(exec, typeid(Type)); if (!result.is_ok()) { return utils::failure(result.err()); } @@ -40,9 +40,8 @@ public: [[nodiscard]] utils::result, utils::error> fetch_all(const sql::executor &exec) const; template < class Type > - utils::result, utils::error> fetch_one(sql::executor &exec) - { - auto result = fetch(exec); + utils::result, utils::error> fetch_one(sql::executor &exec) { + auto result = fetch(exec, typeid(Type)); if (!result.is_ok()) { return utils::failure(result.err()); } @@ -83,7 +82,7 @@ public: [[nodiscard]] sql::query_context compile(const sql::dialect &d) const; private: - [[nodiscard]] utils::result, utils::error> fetch(const sql::executor &exec) const; + [[nodiscard]] utils::result, utils::error> fetch(const sql::executor &exec, const std::type_index& index) const; }; } diff --git a/include/matador/query/query_collection_resolver.hpp b/include/matador/query/query_collection_resolver.hpp index d89f2b2..d879933 100644 --- a/include/matador/query/query_collection_resolver.hpp +++ b/include/matador/query/query_collection_resolver.hpp @@ -6,9 +6,6 @@ #include "matador/sql/internal/identifier_statement_binder.hpp" #include "matador/sql/statement.hpp" -#include "matador/query/table.hpp" -#include "matador/query/select_query_builder.hpp" - namespace matador::sql { class executor; } @@ -17,47 +14,33 @@ namespace matador::query { template class query_collection_resolver : public object::collection_resolver { public: - explicit query_collection_resolver(const basic_schema &repo, sql::executor& exec, const table &tab, std::string pk_name, const std::type_index& root_type, std::string join_column) + explicit query_collection_resolver(sql::statement &&stmt, const std::type_index& root_type, std::string join_column) : object::collection_resolver(root_type, join_column) - , executor_(exec) - , schema_(repo) - , table_(tab) - , pk_name_(std::move(pk_name)) - , join_column_(std::move(join_column)) + , stmt_(std::move(stmt)) {} std::vector resolve(const utils::identifier &id) override; protected: - sql::executor& executor_; - const basic_schema &schema_; - const table &table_; - std::string pk_name_; - std::string join_column_; + sql::statement stmt_; std::type_index index{typeid(Type)}; }; template std::vector query_collection_resolver::resolve(const utils::identifier &id) { - const auto *pk_column = table_[pk_name_]; - const auto *join_column = table_[join_column_]; - - auto stmt = query::select({*pk_column}) - .from(table_) - .where(*join_column == id) - .prepare(executor_); - - if (!stmt) { - return {}; - } - - sql::identifier_statement_binder binder(*stmt); + sql::identifier_statement_binder binder(stmt_); binder.bind(id); - auto result = stmt->template fetch_one_raw(); + auto result = stmt_.fetch(); if (!result) { return {}; } - return {}; + std::vector out; + for (const auto &i: *result) { + // Todo: convert the first value of record into an utils::identifier + // then create a object_proxy(resolver, identifier) + // out.emplace_back(resolver, identifier); + } + return out; } } #endif //MATADOR_QUERY_CONTAINER_RESOLVER_HPP \ No newline at end of file diff --git a/include/matador/query/schema.hpp b/include/matador/query/schema.hpp index 2493bfd..172b955 100644 --- a/include/matador/query/schema.hpp +++ b/include/matador/query/schema.hpp @@ -51,7 +51,19 @@ public: {} std::shared_ptr produce(sql::executor &exec) override { - return std::make_shared>(repo_, exec, table_, std::move(pk_name_), root_type(), collection_name()); + const auto *pk_column = table_[pk_name_]; + const auto *join_column = table_[collection_name()]; + + auto stmt = query::select({*pk_column}) + .from(table_) + .where(*join_column == utils::_) + .prepare(exec); + + if (!stmt) { + return {}; + } + + return std::make_shared>(stmt.release(), root_type(), collection_name()); } private: @@ -201,13 +213,13 @@ public: throw query_builder_exception{query_build_error::MissingPrimaryKey}; } - auto producer = std::make_unique>( + auto producer = std::make_unique>( schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name(), root_type_, join_column); - const sql::composite_key key{root_type_, typeid(typename CollectionType::value_type::value_type), join_column}; + const sql::composite_key key{root_type_, typeid(typename CollectionType::value_type), join_column}; schema_.collection_resolver_producers_[key] = std::move(producer); } diff --git a/include/matador/sql/internal/query_result_impl.hpp b/include/matador/sql/internal/query_result_impl.hpp index 0fdcc6f..a4190ab 100644 --- a/include/matador/sql/internal/query_result_impl.hpp +++ b/include/matador/sql/internal/query_result_impl.hpp @@ -78,6 +78,7 @@ public: query_result_impl(std::unique_ptr &&reader, std::vector prototype, const std::shared_ptr& resolver, + const std::type_index& result_type, size_t column_index = 0); template @@ -127,13 +128,12 @@ public: template void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t::value> * = nullptr) { using value_type = typename CollectionType::value_type::value_type; - auto resolver = resolver_->collection_resolver(type_stack_.top(), join_column); + auto resolver = resolver_->collection_resolver(result_type_, join_column); auto object_resolver = resolver_->object_resolver(); std::vector objects; if (attr.fetch() == utils::fetch_type::Lazy) { - - // pk_reader_.read(*id, column_index_++); + cont.reset(std::make_shared>(resolver, current_pk_)); } else { const auto ti = std::type_index(typeid(value_type)); auto obj = std::make_shared(); @@ -227,7 +227,8 @@ protected: size_t column_index_ = 0; std::vector prototype_; std::unique_ptr reader_; - std::shared_ptr resolver_; + std::shared_ptr resolver_; + const std::type_index result_type_; internal::identifier_reader id_reader_; detail::pk_reader pk_reader_; std::stack type_stack_; diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index 55a1c17..47f4554 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -41,7 +41,9 @@ struct query_context { std::vector prototype{}; std::vector bind_vars{}; std::vector bind_types{}; + // Data for resolving query result std::shared_ptr resolver{}; + std::type_index result_type = typeid(void); }; } diff --git a/include/matador/utils/convert.hpp b/include/matador/utils/convert.hpp index 0bacd9f..eca0e67 100644 --- a/include/matador/utils/convert.hpp +++ b/include/matador/utils/convert.hpp @@ -3,6 +3,7 @@ #include "matador/utils/types.hpp" #include "matador/utils/result.hpp" +#include "matador/utils/os.hpp" #include #include @@ -467,18 +468,20 @@ template < typename DestType> result to(const timestamp_type_t &source, std::enable_if_t>* = nullptr) { const std::time_t tt = std::chrono::system_clock::to_time_t(source); - const std::tm* result = std::localtime(&tt); + std::tm result; + matador::os::localtime(tt, result); - return ok(time_type_t{static_cast(result->tm_hour), static_cast(result->tm_min), static_cast(result->tm_sec)}); + return ok(time_type_t{static_cast(result.tm_hour), static_cast(result.tm_min), static_cast(result.tm_sec)}); } template < typename DestType> result to(const timestamp_type_t &source, std::enable_if_t>* = nullptr) { const std::time_t tt = std::chrono::system_clock::to_time_t(source); - const std::tm* result = std::localtime(&tt); + std::tm result; + matador::os::localtime(tt, result); - return ok(date_type_t{result->tm_year + 1900, static_cast(result->tm_mon + 1), static_cast(result->tm_mday)}); + return ok(date_type_t{result.tm_year + 1900, static_cast(result.tm_mon + 1), static_cast(result.tm_mday)}); } } diff --git a/include/matador/utils/os.hpp b/include/matador/utils/os.hpp index b3e0365..d78993c 100644 --- a/include/matador/utils/os.hpp +++ b/include/matador/utils/os.hpp @@ -98,6 +98,14 @@ int sprintf(char* str, size_t s, const char* format, ARGS const&... args) MATADOR_UTILS_API char* strerror(int err, char* errbuf, size_t bufsize); +/** + * Multi platform version of localtime + * + * @param in time_t value to be converted + * @param out converted value + */ +MATADOR_UTILS_API void localtime(const time_t &in, struct tm &out); + /// @endcond } diff --git a/source/core/utils/os.cpp b/source/core/utils/os.cpp index 1e8e8fc..7718974 100644 --- a/source/core/utils/os.cpp +++ b/source/core/utils/os.cpp @@ -1,10 +1,12 @@ #include "matador/utils/os.hpp" #include -#include -#include -#include +#include + #include +#include +#include +#include #ifdef _WIN32 #include @@ -357,4 +359,16 @@ char* strerror(int err, char* errbuf, size_t bufsize) #endif } +void localtime(const time_t &in, struct tm &out) { +#if defined(__unix__) + localtime_r(&in, &out); +#elif defined(_MSC_VER) + errno_t err = localtime_s(&out, &in); +#else + static std::mutex mtx; + std::lock_guard lock(mtx); + out = *std::localtime(&in); +#endif +} + } diff --git a/source/orm/query/intermediates/fetchable_query.cpp b/source/orm/query/intermediates/fetchable_query.cpp index 04e1e1f..b9f57c3 100644 --- a/source/orm/query/intermediates/fetchable_query.cpp +++ b/source/orm/query/intermediates/fetchable_query.cpp @@ -28,7 +28,8 @@ sql::record *create_prototype(const std::vector &prototype) { } utils::result, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const { query_builder compiler; - const auto ctx = compiler.compile(*context_, exec.dialect(), std::nullopt); + auto ctx = compiler.compile(*context_, exec.dialect(), std::nullopt); + ctx.resolver = exec.resolver(); return exec.fetch(ctx) .and_then([](auto &&res) { const auto prototype = res->prototype(); @@ -67,9 +68,10 @@ sql::query_context fetchable_query::compile(const sql::dialect &d) const { return compiler.compile(*context_, d, std::nullopt); } -utils::result, utils::error> fetchable_query::fetch(const sql::executor &exec) const { +utils::result, utils::error> fetchable_query::fetch(const sql::executor &exec, const std::type_index& index) const { auto ctx = compile(exec.dialect()); ctx.resolver = exec.resolver(); + ctx.result_type = index; return exec.fetch(ctx); } diff --git a/source/orm/query/internal/query_result_impl.cpp b/source/orm/query/internal/query_result_impl.cpp index 8d4e290..531e26a 100644 --- a/source/orm/query/internal/query_result_impl.cpp +++ b/source/orm/query/internal/query_result_impl.cpp @@ -9,12 +9,14 @@ detail::pk_reader::pk_reader(query_result_reader &reader) query_result_impl::query_result_impl(std::unique_ptr &&reader, std::vector prototype, - const std::shared_ptr& resolver, + const std::shared_ptr& resolver, + const std::type_index& result_type, const size_t column_index) : column_index_(column_index) , prototype_(std::move(prototype)) , reader_(std::move(reader)) , resolver_(resolver) +, result_type_(result_type) , id_reader_(*reader_) , pk_reader_(*reader_) { } diff --git a/todo.md b/todo.md index 7fbc34d..c22d10a 100644 --- a/todo.md +++ b/todo.md @@ -2,7 +2,7 @@ - move `prepare_*` methods from `dialect` to `query_compiler` - add `session_insert_builder` and `session_update_builder` (returning multiple statements) -- finish fetch eager has-many/belongs-to relations +- finish fetch eager many-to-many relations - implement lazy loading - implement polymorphic class hierarchies - finish `schema_repository` classes (move add/drop from `session` to `schema`)