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),
std::move(prototype),
context.resolver));
context.resolver,
context.result_type));
}
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::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 {

View File

@ -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<object::object_ptr<names>>( typeid(person), "names" );
utils::identifier id{1};
object::collection_proxy cp(cr, id);
{
// has_many with builtin-type
object::repository repo;

View File

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

View File

@ -1,12 +1,13 @@
#ifndef MATADOR_COLLECTION_PROXY_HPP
#define MATADOR_COLLECTION_PROXY_HPP
#include <atomic>
#include "matador/object/collection_resolver.hpp"
#include "matador/utils/identifier.hpp"
#include <atomic>
#include <mutex>
#include <vector>
namespace matador::object {
@ -33,13 +34,15 @@ public:
return owner_id_;
}
const std::vector<Type>& items() const {
resolve();
return items_;
}
std::vector<Type>& 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:

View File

@ -16,7 +16,7 @@ class collection_resolver_factory : public abstract_collection_resolver_factory
public:
template<class Type>
[[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) {
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
}

View File

@ -26,7 +26,7 @@ protected:
public:
template < class Type >
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()) {
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;
template < class Type >
utils::result<object::object_ptr<Type>, utils::error> fetch_one(sql::executor &exec)
{
auto result = fetch(exec);
utils::result<object::object_ptr<Type>, 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<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/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<typename Type>
class query_collection_resolver : public object::collection_resolver<Type> {
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)
, executor_(exec)
, schema_(repo)
, table_(tab)
, pk_name_(std::move(pk_name))
, join_column_(std::move(join_column))
, stmt_(std::move(stmt))
{}
std::vector<Type> 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<typename Type>
std::vector<Type> query_collection_resolver<Type>::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<Type>();
auto result = stmt_.fetch();
if (!result) {
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

View File

@ -51,7 +51,19 @@ public:
{}
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:
@ -201,13 +213,13 @@ public:
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_,
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);
}

View File

@ -78,6 +78,7 @@ public:
query_result_impl(std::unique_ptr<query_result_reader> &&reader,
std::vector<object::attribute> prototype,
const std::shared_ptr<resolver_service>& resolver,
const std::type_index& result_type,
size_t column_index = 0);
template<typename ValueType>
@ -127,13 +128,12 @@ public:
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) {
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>();
std::vector<typename CollectionType::value_type> objects;
if (attr.fetch() == utils::fetch_type::Lazy) {
// pk_reader_.read(*id, column_index_++);
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, current_pk_));
} else {
const auto ti = std::type_index(typeid(value_type));
auto obj = std::make_shared<typename CollectionType::value_type::value_type>();
@ -227,7 +227,8 @@ protected:
size_t column_index_ = 0;
std::vector<object::attribute> prototype_;
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_;
detail::pk_reader pk_reader_;
std::stack<std::type_index> type_stack_;

View File

@ -41,7 +41,9 @@ struct query_context {
std::vector<object::attribute> prototype{};
std::vector<std::string> bind_vars{};
std::vector<utils::database_type> bind_types{};
// Data for resolving query result
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/result.hpp"
#include "matador/utils/os.hpp"
#include <array>
#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) {
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>
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::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);
/**
* 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
}

View File

@ -1,10 +1,12 @@
#include "matador/utils/os.hpp"
#include <sys/stat.h>
#include <cstring>
#include <vector>
#include <stdexcept>
#include <ctime>
#include <algorithm>
#include <cstring>
#include <stdexcept>
#include <vector>
#ifdef _WIN32
#include <io.h>
@ -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<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 {
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<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());
ctx.resolver = exec.resolver();
ctx.result_type = index;
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,
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)
: column_index_(column_index)
, prototype_(std::move(prototype))
, reader_(std::move(reader))
, resolver_(resolver)
, result_type_(result_type)
, id_reader_(*reader_)
, pk_reader_(*reader_) {
}

View File

@ -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`)