collection_resolver progress

This commit is contained in:
Sascha Kühl 2026-01-29 17:23:16 +01:00
parent 602b77e67b
commit bcec740d60
17 changed files with 100 additions and 59 deletions

View File

@ -111,7 +111,8 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
std::move(prototype), std::move(prototype),
context.resolver)); context.resolver,
context.result_type));
} }
std::string postgres_connection::generate_statement_name(const sql::query_context &query) { std::string postgres_connection::generate_statement_name(const sql::query_context &query) {

View File

@ -60,7 +60,10 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_st
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql)); return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
} }
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), query_.prototype, query_.resolver)); return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
query_.prototype,
query_.resolver,
query_.result_type));
} }
std::unique_ptr<utils::attribute_writer> postgres_statement::create_binder() const { std::unique_ptr<utils::attribute_writer> postgres_statement::create_binder() const {

View File

@ -1,4 +1,7 @@
#include "matador/object/repository.hpp" #include "matador/object/repository.hpp"
#include "matador/object/collection.hpp"
#include "matador/sql/resolver_service.hpp"
#include "matador/logger/log_manager.hpp" #include "matador/logger/log_manager.hpp"
@ -131,6 +134,12 @@ int main() {
// logger::default_min_log_level(logger::log_level::LVL_DEBUG); // logger::default_min_log_level(logger::log_level::LVL_DEBUG);
// logger::add_log_sink(logger::create_stdout_sink()); // logger::add_log_sink(logger::create_stdout_sink());
sql::resolver_service rs;
std::weak_ptr cr = rs.collection_resolver<object::object_ptr<names>>( typeid(person), "names" );
utils::identifier id{1};
object::collection_proxy cp(cr, id);
{ {
// has_many with builtin-type // has_many with builtin-type
object::repository repo; object::repository repo;

View File

@ -1,8 +1,6 @@
#ifndef MATADOR_NET_OS_HPP #ifndef MATADOR_NET_OS_HPP
#define MATADOR_NET_OS_HPP #define MATADOR_NET_OS_HPP
#include "matador/net/export.hpp"
#include <cstdio> #include <cstdio>
#if _WIN32 #if _WIN32

View File

@ -1,12 +1,13 @@
#ifndef MATADOR_COLLECTION_PROXY_HPP #ifndef MATADOR_COLLECTION_PROXY_HPP
#define MATADOR_COLLECTION_PROXY_HPP #define MATADOR_COLLECTION_PROXY_HPP
#include <atomic>
#include "matador/object/collection_resolver.hpp" #include "matador/object/collection_resolver.hpp"
#include "matador/utils/identifier.hpp" #include "matador/utils/identifier.hpp"
#include <atomic>
#include <mutex>
#include <vector> #include <vector>
namespace matador::object { namespace matador::object {
@ -33,13 +34,15 @@ public:
return owner_id_; return owner_id_;
} }
const std::vector<Type>& items() const { const std::vector<Type>& items() const {
resolve();
return items_; return items_;
} }
std::vector<Type>& items() { std::vector<Type>& items() {
resolve();
return items_; return items_;
} }
private: private:
void resolve() const { void resolve() {
if (loaded_) { if (loaded_) {
return; return;
} }
@ -53,6 +56,7 @@ private:
} }
items_ = resolver->resolve(owner_id_); items_ = resolver->resolve(owner_id_);
loaded_ = true;
} }
private: private:

View File

@ -16,7 +16,7 @@ class collection_resolver_factory : public abstract_collection_resolver_factory
public: public:
template<class Type> template<class Type>
[[nodiscard]] std::shared_ptr<collection_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const { [[nodiscard]] std::shared_ptr<collection_resolver<Type>> 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) { if (!res) {
return std::dynamic_pointer_cast<collection_resolver<Type>>(res); return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
} }

View File

@ -26,7 +26,7 @@ protected:
public: public:
template < class Type > template < class Type >
utils::result<sql::query_result<Type>, utils::error> fetch_all(sql::executor &exec) { utils::result<sql::query_result<Type>, utils::error> fetch_all(sql::executor &exec) {
auto result = fetch(exec); auto result = fetch(exec, typeid(Type));
if (!result.is_ok()) { if (!result.is_ok()) {
return utils::failure(result.err()); return utils::failure(result.err());
} }
@ -40,9 +40,8 @@ public:
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::executor &exec) const; [[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::executor &exec) const;
template < class Type > template < class Type >
utils::result<object::object_ptr<Type>, utils::error> fetch_one(sql::executor &exec) utils::result<object::object_ptr<Type>, utils::error> fetch_one(sql::executor &exec) {
{ auto result = fetch(exec, typeid(Type));
auto result = fetch(exec);
if (!result.is_ok()) { if (!result.is_ok()) {
return utils::failure(result.err()); return utils::failure(result.err());
} }
@ -83,7 +82,7 @@ public:
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const; [[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
private: private:
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::executor &exec) const; [[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::executor &exec, const std::type_index& index) const;
}; };
} }

View File

@ -6,9 +6,6 @@
#include "matador/sql/internal/identifier_statement_binder.hpp" #include "matador/sql/internal/identifier_statement_binder.hpp"
#include "matador/sql/statement.hpp" #include "matador/sql/statement.hpp"
#include "matador/query/table.hpp"
#include "matador/query/select_query_builder.hpp"
namespace matador::sql { namespace matador::sql {
class executor; class executor;
} }
@ -17,47 +14,33 @@ namespace matador::query {
template<typename Type> template<typename Type>
class query_collection_resolver : public object::collection_resolver<Type> { class query_collection_resolver : public object::collection_resolver<Type> {
public: 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<Type>(root_type, join_column) : object::collection_resolver<Type>(root_type, join_column)
, executor_(exec) , stmt_(std::move(stmt))
, schema_(repo)
, table_(tab)
, pk_name_(std::move(pk_name))
, join_column_(std::move(join_column))
{} {}
std::vector<Type> resolve(const utils::identifier &id) override; std::vector<Type> resolve(const utils::identifier &id) override;
protected: protected:
sql::executor& executor_; sql::statement stmt_;
const basic_schema &schema_;
const table &table_;
std::string pk_name_;
std::string join_column_;
std::type_index index{typeid(Type)}; std::type_index index{typeid(Type)};
}; };
template<typename Type> template<typename Type>
std::vector<Type> query_collection_resolver<Type>::resolve(const utils::identifier &id) { std::vector<Type> query_collection_resolver<Type>::resolve(const utils::identifier &id) {
const auto *pk_column = table_[pk_name_]; sql::identifier_statement_binder binder(stmt_);
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);
binder.bind(id); binder.bind(id);
auto result = stmt->template fetch_one_raw<Type>(); auto result = stmt_.fetch();
if (!result) { if (!result) {
return {}; return {};
} }
return {}; std::vector<Type> out;
for (const auto &i: *result) {
// Todo: convert the first value of record into an utils::identifier
// then create a object_proxy<Type>(resolver, identifier)
// out.emplace_back(resolver, identifier);
}
return out;
} }
} }
#endif //MATADOR_QUERY_CONTAINER_RESOLVER_HPP #endif //MATADOR_QUERY_CONTAINER_RESOLVER_HPP

View File

@ -51,7 +51,19 @@ public:
{} {}
std::shared_ptr<object::abstract_collection_resolver> produce(sql::executor &exec) override { std::shared_ptr<object::abstract_collection_resolver> produce(sql::executor &exec) override {
return std::make_shared<query_collection_resolver<Type>>(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<query_collection_resolver<Type>>(stmt.release(), root_type(), collection_name());
} }
private: private:
@ -201,13 +213,13 @@ public:
throw query_builder_exception{query_build_error::MissingPrimaryKey}; throw query_builder_exception{query_build_error::MissingPrimaryKey};
} }
auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type::value_type>>( auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type>>(
schema_, schema_,
it->second.table(), it->second.table(),
it->second.node().info().primary_key_attribute()->name(), it->second.node().info().primary_key_attribute()->name(),
root_type_, root_type_,
join_column); 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); schema_.collection_resolver_producers_[key] = std::move(producer);
} }

View File

@ -78,6 +78,7 @@ public:
query_result_impl(std::unique_ptr<query_result_reader> &&reader, query_result_impl(std::unique_ptr<query_result_reader> &&reader,
std::vector<object::attribute> prototype, std::vector<object::attribute> prototype,
const std::shared_ptr<resolver_service>& resolver, const std::shared_ptr<resolver_service>& resolver,
const std::type_index& result_type,
size_t column_index = 0); size_t column_index = 0);
template<typename ValueType> template<typename ValueType>
@ -127,13 +128,12 @@ public:
template<class CollectionType> template<class CollectionType>
void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) { void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
using value_type = typename CollectionType::value_type::value_type; using value_type = typename CollectionType::value_type::value_type;
auto resolver = resolver_->collection_resolver<value_type>(type_stack_.top(), join_column); auto resolver = resolver_->collection_resolver<typename CollectionType::value_type>(result_type_, join_column);
auto object_resolver = resolver_->object_resolver<value_type>(); auto object_resolver = resolver_->object_resolver<value_type>();
std::vector<typename CollectionType::value_type> objects; std::vector<typename CollectionType::value_type> objects;
if (attr.fetch() == utils::fetch_type::Lazy) { if (attr.fetch() == utils::fetch_type::Lazy) {
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, current_pk_));
// pk_reader_.read(*id, column_index_++);
} else { } else {
const auto ti = std::type_index(typeid(value_type)); const auto ti = std::type_index(typeid(value_type));
auto obj = std::make_shared<typename CollectionType::value_type::value_type>(); auto obj = std::make_shared<typename CollectionType::value_type::value_type>();
@ -227,7 +227,8 @@ protected:
size_t column_index_ = 0; size_t column_index_ = 0;
std::vector<object::attribute> prototype_; std::vector<object::attribute> prototype_;
std::unique_ptr<query_result_reader> reader_; std::unique_ptr<query_result_reader> reader_;
std::shared_ptr<sql::resolver_service> resolver_; std::shared_ptr<resolver_service> resolver_;
const std::type_index result_type_;
internal::identifier_reader id_reader_; internal::identifier_reader id_reader_;
detail::pk_reader pk_reader_; detail::pk_reader pk_reader_;
std::stack<std::type_index> type_stack_; std::stack<std::type_index> type_stack_;

View File

@ -41,7 +41,9 @@ struct query_context {
std::vector<object::attribute> prototype{}; std::vector<object::attribute> prototype{};
std::vector<std::string> bind_vars{}; std::vector<std::string> bind_vars{};
std::vector<utils::database_type> bind_types{}; std::vector<utils::database_type> bind_types{};
// Data for resolving query result
std::shared_ptr<resolver_service> resolver{}; std::shared_ptr<resolver_service> resolver{};
std::type_index result_type = typeid(void);
}; };
} }

View File

@ -3,6 +3,7 @@
#include "matador/utils/types.hpp" #include "matador/utils/types.hpp"
#include "matador/utils/result.hpp" #include "matador/utils/result.hpp"
#include "matador/utils/os.hpp"
#include <array> #include <array>
#include <charconv> #include <charconv>
@ -467,18 +468,20 @@ template < typename DestType>
result<DestType, conversion_error> to(const timestamp_type_t &source, std::enable_if_t<std::is_same_v<time_type_t, DestType>>* = nullptr) { result<DestType, conversion_error> to(const timestamp_type_t &source, std::enable_if_t<std::is_same_v<time_type_t, DestType>>* = nullptr) {
const std::time_t tt = std::chrono::system_clock::to_time_t(source); 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<uint8_t>(result->tm_hour), static_cast<uint8_t>(result->tm_min), static_cast<uint8_t>(result->tm_sec)}); return ok(time_type_t{static_cast<uint8_t>(result.tm_hour), static_cast<uint8_t>(result.tm_min), static_cast<uint8_t>(result.tm_sec)});
} }
template < typename DestType> template < typename DestType>
result<DestType, conversion_error> to(const timestamp_type_t &source, std::enable_if_t<std::is_same_v<date_type_t, DestType>>* = nullptr) { result<DestType, conversion_error> to(const timestamp_type_t &source, std::enable_if_t<std::is_same_v<date_type_t, DestType>>* = nullptr) {
const std::time_t tt = std::chrono::system_clock::to_time_t(source); 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<uint8_t>(result->tm_mon + 1), static_cast<uint8_t>(result->tm_mday)}); return ok(date_type_t{result.tm_year + 1900, static_cast<uint8_t>(result.tm_mon + 1), static_cast<uint8_t>(result.tm_mday)});
} }
} }

View File

@ -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); 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 /// @endcond
} }

View File

@ -1,10 +1,12 @@
#include "matador/utils/os.hpp" #include "matador/utils/os.hpp"
#include <sys/stat.h> #include <sys/stat.h>
#include <cstring> #include <ctime>
#include <vector>
#include <stdexcept>
#include <algorithm> #include <algorithm>
#include <cstring>
#include <stdexcept>
#include <vector>
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
@ -357,4 +359,16 @@ char* strerror(int err, char* errbuf, size_t bufsize)
#endif #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<std::mutex> lock(mtx);
out = *std::localtime(&in);
#endif
}
} }

View File

@ -28,7 +28,8 @@ sql::record *create_prototype(const std::vector<object::attribute> &prototype) {
} }
utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const { utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fetch_all(const sql::executor &exec) const {
query_builder compiler; 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) return exec.fetch(ctx)
.and_then([](auto &&res) { .and_then([](auto &&res) {
const auto prototype = res->prototype(); 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); return compiler.compile(*context_, d, std::nullopt);
} }
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetchable_query::fetch(const sql::executor &exec) const { utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetchable_query::fetch(const sql::executor &exec, const std::type_index& index) const {
auto ctx = compile(exec.dialect()); auto ctx = compile(exec.dialect());
ctx.resolver = exec.resolver(); ctx.resolver = exec.resolver();
ctx.result_type = index;
return exec.fetch(ctx); return exec.fetch(ctx);
} }

View File

@ -9,12 +9,14 @@ detail::pk_reader::pk_reader(query_result_reader &reader)
query_result_impl::query_result_impl(std::unique_ptr<query_result_reader> &&reader, query_result_impl::query_result_impl(std::unique_ptr<query_result_reader> &&reader,
std::vector<object::attribute> prototype, std::vector<object::attribute> prototype,
const std::shared_ptr<sql::resolver_service>& resolver, const std::shared_ptr<resolver_service>& resolver,
const std::type_index& result_type,
const size_t column_index) const size_t column_index)
: column_index_(column_index) : column_index_(column_index)
, prototype_(std::move(prototype)) , prototype_(std::move(prototype))
, reader_(std::move(reader)) , reader_(std::move(reader))
, resolver_(resolver) , resolver_(resolver)
, result_type_(result_type)
, id_reader_(*reader_) , id_reader_(*reader_)
, pk_reader_(*reader_) { , pk_reader_(*reader_) {
} }

View File

@ -2,7 +2,7 @@
- move `prepare_*` methods from `dialect` to `query_compiler` - move `prepare_*` methods from `dialect` to `query_compiler`
- add `session_insert_builder` and `session_update_builder` (returning multiple statements) - 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 lazy loading
- implement polymorphic class hierarchies - implement polymorphic class hierarchies
- finish `schema_repository` classes (move add/drop from `session` to `schema`) - finish `schema_repository` classes (move add/drop from `session` to `schema`)