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; 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> template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj); utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
template< class Type, typename... Args > template< class Type, typename... Args >
utils::result<object::object_ptr<Type>, utils::error> insert(Args&&... 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() });
}
template<typename Type> template<typename Type>
utils::result<sql::query_result<Type>, utils::error> find() { utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
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); template<typename Type, typename PrimaryKeyType>
auto data = eqb.build<Type>(); utils::result<object::object_ptr<Type>, utils::error> find(const PrimaryKeyType &pk);
if (!data.is_ok()) { template<typename Type>
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); utils::result<sql::query_result<Type>, utils::error> find();
}
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> template<typename Type>
utils::result<void, utils::error> drop_table(); 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() auto res = query::query::insert()
.into(info->get().name(), sql::column_generator::generate<Type>(*schema_, true)) .into(info->get().name(), sql::column_generator::generate<Type>(*schema_, true))
.values(*obj) .values(*obj)
.execute(*this); .prepare(*this);
if (!res) { 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{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> template<typename Type>
utils::result<void, utils::error> session::drop_table() { utils::result<void, utils::error> session::drop_table() {
auto info = schema_->info<Type>(); 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) { if (!info) {
throw query_builder_exception{query_build_error::UnknownType}; 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)); const auto foreign_table = std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index));
if (attr.fetch() == utils::fetch_type::EAGER) { 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); access::process(*this, obj);
table_info_stack_.pop(); table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join( append_join(
sql::column{table_info_stack_.top().table, id}, sql::column{table_info_stack_.top().table, id},
sql::column{next->second, pk->name()} 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::utils;
using namespace matador::query; using namespace matador::query;
// create select 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) .from(*foreign_table)
.where(sql::column(foreign_table, id, "") == _) .where(sql::column(foreign_table, pk->name(), "") == _)
.prepare(executor_); .prepare(executor_);
if (!result) { if (!result) {
throw query_builder_exception(query_build_error::QueryError, result.release_error()); 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::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; [[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()); 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(); auto first = objects.begin();
if (first == objects.end()) { if (first == objects.end()) {
return utils::ok(std::unique_ptr<Type>{nullptr}); return utils::ok(std::unique_ptr<Type>{nullptr});

View File

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

View File

@ -150,7 +150,10 @@ statement &statement::bind(const Type &obj) {
template<class Type> template<class Type>
utils::result<query_result<Type>, utils::error> statement::fetch() { utils::result<query_result<Type>, utils::error> statement::fetch() {
return statement_proxy_->fetch(*bindings_).and_then([](std::unique_ptr<query_result_impl> &&value) { 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()) { if (!result.is_ok()) {
return utils::failure(result.err()); 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(); auto first = records.begin();
if (first == records.end()) { if (first == records.end()) {
return utils::ok(std::unique_ptr<Type>{nullptr}); 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) { if (!res) {
return utils::failure(res.err()); 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 { 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()); 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(); auto first = records.begin();
if (first == records.end()) { if (first == records.end()) {
return utils::ok(std::optional<sql::record>{std::nullopt}); 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()); return utils::failure(result.err());
} }
// logger_.info(statement_->query_.sql); // 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 { 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()); 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(); auto first = records.begin();
if (first == records.end()) { if (first == records.end()) {
return utils::ok(std::optional<record>{std::nullopt}); 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]") { 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") auto result = ses.attach<recipe>("recipes")
.and_then( [this] { return ses.attach<ingredient>("ingredients"); } ) .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(); } ); .and_then( [this] { return ses.create_schema(); } );
tables_to_drop.emplace("recipes"); 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; 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>(1, "Apple"));
ingredients.push_back(std::make_unique<ingredient>(2, "Strawberry")); 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>(3, "Pineapple"));
ingredients.push_back(std::make_unique<ingredient>(4, "Sugar")); ingredients.push_back(std::make_unique<ingredient>(4, "Sugar"));