fixed session tests

This commit is contained in:
Sascha Kühl 2025-10-08 16:03:00 +02:00
parent cb0f6322f4
commit 463647ba59
9 changed files with 144 additions and 91 deletions

View File

@ -100,61 +100,25 @@ public:
utils::result<void, utils::error> create_schema() const;
/**
* Insert the given object into the session.
*
* @tparam Type Type of object to insert
* @param obj Object to insert
* @return Inserted object
*/
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
template< class Type, typename... Args >
utils::result<object::object_ptr<Type>, utils::error> insert(Args&&... args) {
return insert(new Type(std::forward<Args>(args)...));
}
template<typename Type, typename PrimaryKeyType>
utils::result<object::object_ptr<Type>, utils::error> find(const PrimaryKeyType &pk) {
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_, *this);
auto data = eqb.build<Type>(pk);
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
auto res = build_select_query(data.release()).prepare(*this);
// auto obj = build_select_query(data.release()).template fetch_one<Type>(*this);
if (!res) {
return utils::failure(res.err());
}
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType&>(pk)).template fetch_one<Type>();
if (!stmt_result) {
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + "."));
}
return utils::ok(object::object_ptr<Type>{ stmt_result->release() });
}
utils::result<object::object_ptr<Type>, utils::error> insert(Args&&... args);
template<typename Type>
utils::result<sql::query_result<Type>, utils::error> find() {
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
session_query_builder eqb(*schema_, *this);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
const auto ctx = build_select_query(data.release()).compile(dialect_, query::query_mode::Prepared);
auto result = fetch(ctx);
if (!result.is_ok()) {
return utils::failure(result.err());
}
return utils::ok(sql::query_result<Type>(result.release(), []{ return new Type();}));
}
template<typename Type, typename PrimaryKeyType>
utils::result<object::object_ptr<Type>, utils::error> find(const PrimaryKeyType &pk);
template<typename Type>
utils::result<sql::query_result<Type>, utils::error> find();
template<typename Type>
utils::result<void, utils::error> drop_table();
@ -207,14 +171,89 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(Type *obj)
auto res = query::query::insert()
.into(info->get().name(), sql::column_generator::generate<Type>(*schema_, true))
.values(*obj)
.execute(*this);
.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(object::object_ptr{obj});
}
template<class Type, typename ... Args>
utils::result<object::object_ptr<Type>, utils::error> session::insert( Args&&... args ) {
return insert(new Type(std::forward<Args>(args)...));
}
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> session::update( const object::object_ptr<Type>& obj ) {
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(info.err());
}
auto res = query::query::update(info->get().name())
.set(*obj)
// .where(sql::column(info->prototype().primary_key()->name()) == _)
.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(object::object_ptr{obj});
}
template<typename Type, typename PrimaryKeyType>
utils::result<object::object_ptr<Type>, utils::error> session::find( const PrimaryKeyType& pk ) {
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_, *this);
auto data = eqb.build<Type>(pk);
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
auto res = build_select_query(data.release()).prepare(*this);
if (!res) {
return utils::failure(res.err());
}
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType&>(pk)).template fetch_one<Type>();
if (stmt_result && !stmt_result.value()) {
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + "."));
}
return utils::ok(object::object_ptr<Type>{ stmt_result->release() });
}
template<typename Type>
utils::result<sql::query_result<Type>, utils::error> session::find() {
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_, *this);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
auto result = build_select_query(data.release()).prepare(*this);
if (!result.is_ok()) {
return utils::failure(result.err());
}
return result->template fetch<Type>();
}
template<typename Type>
utils::result<void, utils::error> session::drop_table() {
auto info = schema_->info<Type>();

View File

@ -261,6 +261,11 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
const auto foreign_table = std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index));
if (attr.fetch() == utils::fetch_type::EAGER) {
@ -274,10 +279,6 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
access::process(*this, obj);
table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join(
sql::column{table_info_stack_.top().table, id},
sql::column{next->second, pk->name()}
@ -287,9 +288,9 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
using namespace matador::utils;
using namespace matador::query;
// create select query
auto result = matador::query::query::select<typename Pointer::value_type>(schema_)
auto result = matador::query::query::select(sql::column_generator::generate<typename Pointer::value_type>(schema_, true))
.from(*foreign_table)
.where(sql::column(foreign_table, id, "") == _)
.where(sql::column(foreign_table, pk->name(), "") == _)
.prepare(executor_);
if (!result) {
throw query_builder_exception(query_build_error::QueryError, result.release_error());

View File

@ -31,7 +31,10 @@ public:
return utils::failure(result.err());
}
return utils::ok(sql::query_result<Type>(result.release()));
const auto prototype = result.value()->prototype();
return utils::ok(sql::query_result<Type>(result.release(), [prototype] {
return sql::detail::create_prototype<Type>(prototype);
}));
}
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::executor &exec) const;
@ -43,7 +46,10 @@ public:
return utils::failure(result.err());
}
auto objects = sql::query_result<Type>(result.release());
const auto prototype = result.value()->prototype();
auto objects = sql::query_result<Type>(result.release(), [prototype] {
return sql::detail::create_prototype<Type>(prototype);
});
auto first = objects.begin();
if (first == objects.end()) {
return utils::ok(std::unique_ptr<Type>{nullptr});

View File

@ -40,8 +40,7 @@ public:
, result_(x.result_)
{}
query_result_iterator& operator=(query_result_iterator&& x) noexcept
{
query_result_iterator& operator=(query_result_iterator&& x) noexcept {
result_ = x.result_;
obj_ = std::move(x.obj_);
return *this;
@ -49,18 +48,15 @@ public:
~query_result_iterator() = default;
bool operator==(const query_result_iterator& rhs)
{
bool operator==(const query_result_iterator& rhs) {
return obj_ == rhs.obj_;
}
bool operator!=(const query_result_iterator& rhs)
{
bool operator!=(const query_result_iterator& rhs) {
return obj_ != rhs.obj_;
}
self& operator++()
{
self& operator++() {
obj_.reset(result_->create());
result_->bind(*obj_);
if (!result_->fetch(*obj_)) {
@ -70,8 +66,7 @@ public:
return *this;
}
self operator++(int)
{
self operator++(int) {
const self tmp(result_, obj_);
obj_.reset(result_->create());
@ -83,23 +78,19 @@ public:
return tmp;
}
pointer operator->()
{
pointer operator->() {
return obj_.get();
}
reference operator*()
{
reference operator*() {
return *obj_;
}
pointer get()
{
pointer get() {
return obj_.get();
}
pointer release()
{
pointer release() {
return obj_.release();
}
@ -111,8 +102,7 @@ private:
namespace detail {
template < typename Type >
Type* create_prototype(const std::vector<object::attribute_definition> &/*prototype*/)
{
Type* create_prototype(const std::vector<object::attribute_definition> &/*prototype*/) {
return new Type{};
}
@ -128,11 +118,11 @@ public:
using creator_func = std::function<Type*()>;
public:
explicit query_result(std::unique_ptr<query_result_impl> &&impl)
: impl_(std::move(impl))
, creator_([this] {
return detail::create_prototype<Type>(impl_->prototype());
}) {}
// explicit query_result(std::unique_ptr<query_result_impl> &&impl)
// : impl_(std::move(impl))
// , creator_([this] {
// return detail::create_prototype<Type>(impl_->prototype());
// }) {}
query_result(std::unique_ptr<query_result_impl> &&impl, creator_func&& creator)
: impl_(std::move(impl))

View File

@ -150,7 +150,10 @@ statement &statement::bind(const Type &obj) {
template<class Type>
utils::result<query_result<Type>, utils::error> statement::fetch() {
return statement_proxy_->fetch(*bindings_).and_then([](std::unique_ptr<query_result_impl> &&value) {
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value)));
const auto prototype = value->prototype();
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), [prototype] {
return detail::create_prototype<Type>(prototype);
}));
});
}
@ -160,7 +163,10 @@ utils::result<std::unique_ptr<Type>, utils::error> statement::fetch_one() {
if (!result.is_ok()) {
return utils::failure(result.err());
}
auto records = query_result<Type>(result.release());
const auto prototype = result.value()->prototype();
auto records = query_result<Type>(result.release(), [prototype] {
return detail::create_prototype<Type>(prototype);
});
auto first = records.begin();
if (first == records.end()) {
return utils::ok(std::unique_ptr<Type>{nullptr});

View File

@ -108,7 +108,10 @@ utils::result<sql::query_result<sql::record>, utils::error> session::fetch_all(c
if (!res) {
return utils::failure(res.err());
}
return utils::ok(sql::query_result<sql::record>{std::move(*res)});
const auto prototype = res.value()->prototype();
return utils::ok(sql::query_result<sql::record>{std::move(*res), [prototype] {
return sql::detail::create_prototype<sql::record>(prototype);
}});
}
utils::result<size_t, utils::error> session::execute(const std::string &sql) const {

View File

@ -53,7 +53,10 @@ utils::result<std::optional<sql::record>, utils::error> fetchable_query::fetch_o
return utils::failure(result.err());
}
sql::query_result<sql::record> records(std::move(*result));
const auto prototype = result.value()->prototype();
sql::query_result<sql::record> records(std::move(*result), [prototype] {
return detail::create_prototype(prototype);
});
auto first = records.begin();
if (first == records.end()) {
return utils::ok(std::optional<sql::record>{std::nullopt});

View File

@ -61,7 +61,10 @@ utils::result<query_result<record>, utils::error> statement::fetch() const {
return utils::failure(result.err());
}
// logger_.info(statement_->query_.sql);
return utils::ok(query_result<record>{std::move(*result)});
const auto prototype = result.value()->prototype();
return utils::ok(query_result<record>(std::move(*result), [prototype] {
return detail::create_prototype<record>(prototype);
}));
}
utils::result<std::optional<record>, utils::error> statement::fetch_one() const {
@ -71,7 +74,10 @@ utils::result<std::optional<record>, utils::error> statement::fetch_one() const
return utils::failure(result.err());
}
query_result<record> records(std::move(*result));
const auto prototype = result.value()->prototype();
query_result<record> records(std::move(*result), [prototype] {
return sql::detail::create_prototype<record>(prototype);
});
auto first = records.begin();
if (first == records.end()) {
return utils::ok(std::optional<record>{std::nullopt});

View File

@ -208,7 +208,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-ma
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-many eager relation", "[session][find][many-to-many][eager]") {
auto result = ses.attach<recipe>("recipes")
.and_then( [this] { return ses.attach<ingredient>("ingredients"); } )
.and_then( [this] { return ses.attach<recipe_ingredient>("recipe_ingredients"); } )
// .and_then( [this] { return ses.attach<recipe_ingredient>("recipe_ingredients"); } )
.and_then( [this] { return ses.create_schema(); } );
tables_to_drop.emplace("recipes");
@ -217,7 +217,6 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-m
std::vector<std::unique_ptr<ingredient>> ingredients;
ingredients.push_back(std::make_unique<ingredient>(1, "Apple"));
ingredients.push_back(std::make_unique<ingredient>(1, "Apple"));
ingredients.push_back(std::make_unique<ingredient>(2, "Strawberry"));
ingredients.push_back(std::make_unique<ingredient>(3, "Pineapple"));
ingredients.push_back(std::make_unique<ingredient>(4, "Sugar"));