added delete_query_builder (progress)

This commit is contained in:
Sascha Kühl 2026-05-26 07:01:53 +02:00
parent 141d798a41
commit 57f57956de
10 changed files with 484 additions and 84 deletions

View File

@ -46,6 +46,7 @@ set(TEST_SOURCES
../../../test/models/user.hpp
../../../test/utils/RecordPrinter.cpp
../../../test/utils/RecordPrinter.hpp
../../../test/backends/SessionDeleteHasMany.cpp
)
set(LIBRARY_TEST_TARGET PostgresTests)

View File

@ -0,0 +1,267 @@
#ifndef MATADOR_DELETE_QUERY_BUILDER_HPP
#define MATADOR_DELETE_QUERY_BUILDER_HPP
#include "matador/object/collection.hpp"
#include "matador/object/object_cache.hpp"
#include "matador/object/object_ptr.hpp"
#include "matador/query/basic_schema.hpp"
#include "matador/query/error_code.hpp"
#include "matador/query/execute_step.hpp"
#include "matador/query/query_contexts.hpp"
#include "matador/query/query_builder_exception.hpp"
#include "matador/query/query_builder_utils.hpp"
#include "matador/sql/execute_result.hpp"
#include "matador/sql/statement.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/identifier.hpp"
#include "matador/utils/primary_key_accessor.hpp"
#include "matador/utils/result.hpp"
namespace matador::query {
template<typename ObjectType>
class delete_step_object final : public execute_step {
public:
delete_step_object(sql::query_context ctx, const object::object_ptr<ObjectType> &ptr)
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &/*conn*/) override {
id_ = ptr_.primary_key();
return utils::ok<void>();
}
utils::result<void, utils::error> execute(sql::statement &stmt) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
if (const auto result = stmt.bind(0, id_).execute(); !result.is_ok()) {
return utils::failure(result.err());
}
return utils::ok<void>();
}
utils::result<void, utils::error> finalize(object::object_cache &cache, const resolver_service_ptr& /*resolver_service*/) override {
if (!ptr_) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
cache.erase<ObjectType>(id_);
ptr_.change_state(object::object_state::Transient);
return utils::ok<void>();
}
private:
object::object_ptr<ObjectType> ptr_;
};
struct delete_context {
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
std::vector<std::unique_ptr<execute_step>> steps_{};
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
};
template<typename ObjectType>
class delete_step_processor {
public:
explicit delete_step_processor(delete_context &ctx)
: ctx_(ctx) {}
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
if (!ptr) {
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
}
ptr_ = ptr;
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
return utils::ok<void>();
}
ctx_.visited_.insert(key);
const auto it = ctx_.schema_.find(typeid(ObjectType));
if (it == ctx_.schema_.end()) {
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
}
const auto &info = it->second.node().info();
if (!info.has_primary_key()) {
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
}
try {
access::process(*this, *ptr_);
} catch (const query_builder_exception &ex) {
return utils::failure(ex.error());
}
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
if (cit == ctx_.contexts_by_type_.end()) {
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
}
if (as_relation_step) {
ctx_.relation_steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
} else {
ctx_.steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
}
ptr_.reset();
return utils::ok<void>();
}
template<class PrimaryKeyType>
static void on_primary_key(const char * /*id*/, PrimaryKeyType &, const utils::primary_key_attribute & /*attr*/) {}
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
template<typename Type>
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes & /*attr*/) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
on_foreign_object(obj, attr);
}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
on_foreign_object(obj, attr);
}
template<class CollectionType>
void on_has_many(const char * /*id*/,
object::collection<object::object_ptr<CollectionType>> &objects,
const char *join_column,
const utils::foreign_attributes &attr) {
if (join_column == nullptr) {
return;
}
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
return;
}
delete_step_processor<CollectionType> processor{ctx_};
for (auto &obj : objects) {
if (!obj) {
continue;
}
auto result = processor.build(obj, true);
if (!result) {
throw query_builder_exception(result.release_error());
}
}
}
template<class CollectionType>
static void on_has_many(const char * /*id*/,
object::collection<CollectionType> & /*objects*/,
const char * /*join_column*/,
const utils::foreign_attributes & /*attr*/) {
// Value-Collections bzw. Relationstabellen werden hier nicht direkt gelöscht.
// Dafür wird das Delete-Statement der jeweiligen Entity verwendet.
}
template<class ForeignType>
void on_has_many_to_many(const char * /*id*/,
object::collection<object::object_ptr<ForeignType>> &objects,
const char * /*join_column*/,
const char * /*inverse_join_column*/,
const utils::foreign_attributes &attr) {
on_many_to_many_objects(objects, attr);
}
template<class ForeignType>
void on_has_many_to_many(const char * /*id*/,
object::collection<object::object_ptr<ForeignType>> &objects,
const utils::foreign_attributes &attr) {
on_many_to_many_objects(objects, attr);
}
private:
template<class PointerType>
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove) || !obj) {
return;
}
delete_step_processor<PointerType> processor{ctx_};
auto result = processor.build(obj);
if (!result) {
throw query_builder_exception(result.release_error());
}
}
template<class ForeignType>
void on_many_to_many_objects(object::collection<object::object_ptr<ForeignType>> &objects,
const utils::foreign_attributes &attr) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
return;
}
delete_step_processor<ForeignType> processor{ctx_};
for (auto &obj : objects) {
if (!obj) {
continue;
}
auto result = processor.build(obj, true);
if (!result) {
throw query_builder_exception(result.release_error());
}
}
}
private:
delete_context &ctx_;
object::object_ptr<ObjectType> ptr_;
};
template<class ObjectType>
class delete_query_builder {
public:
explicit delete_query_builder(const basic_schema &schema,
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
: schema_(schema)
, contexts_by_type_(contexts_by_type) {}
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"});
}
delete_context ctx{schema_, contexts_by_type_};
delete_step_processor<ObjectType> processor{ctx};
const auto result = processor.build(ptr);
if (!result) {
return utils::failure(result.err());
}
// relation inserts must run after all entity inserts were collected
for (auto &s : ctx.steps_) {
ctx.relation_steps_.push_back(std::move(s));
}
ctx.steps_.clear();
return utils::ok(std::move(ctx.relation_steps_));
}
private:
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
};
} // namespace matador::query
#endif //MATADOR_DELETE_QUERY_BUILDER_HPP

View File

@ -0,0 +1,37 @@
#ifndef MATADOR_EXECUTE_STEP_HPP
#define MATADOR_EXECUTE_STEP_HPP
#include "matador/utils/identifier.hpp"
#include "matador/utils/primary_key_accessor.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include "matador/object/object_cache.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/sql/executor.hpp"
#include "matador/query/abstract_pk_generator.hpp"
namespace matador::query {
using resolver_service_ptr = std::shared_ptr<sql::resolver_service>;
class execute_step {
public:
explicit execute_step(sql::query_context ctx)
: ctx_(std::move(ctx)) {}
virtual ~execute_step() = default;
virtual utils::result<void, utils::error> prepare(sql::executor &conn) = 0;
virtual utils::result<void, utils::error> execute(sql::statement &stmt) = 0;
virtual utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) = 0;
[[nodiscard]] const sql::query_context& ctx() const { return ctx_; }
protected:
utils::identifier id_;
utils::primary_key_accessor pk_accessor_;
sql::query_context ctx_;
};
}
#endif //MATADOR_EXECUTE_STEP_HPP

View File

@ -12,14 +12,12 @@ class foreign_attributes;
namespace matador::query::detail {
class fk_value_extractor
{
class fk_value_extractor {
public:
fk_value_extractor() = default;
template<class Type>
utils::database_type extract(Type &x)
{
utils::database_type extract(Type &x) {
access::process(*this, x);
return value_;
}

View File

@ -10,6 +10,7 @@
#include "matador/query/query.hpp"
#include "matador/query/query_contexts.hpp"
#include "matador/query/query_builder_exception.hpp"
#include "matador/query/query_builder_utils.hpp"
#include "matador/sql/statement.hpp"
@ -55,19 +56,6 @@ private:
std::string join_column_;
};
template<class EntityType>
static std::pair<std::type_index, const void *> make_visit_key(const EntityType &ptr) {
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&ptr)};
}
struct visit_key_hash {
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
const size_t h1 = p.first.hash_code();
const size_t h2 = std::hash<const void *>{}(p.second);
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
}
};
struct processing_many_to_many_key {
std::string id;
std::type_index local_type{typeid(void)};
@ -100,9 +88,9 @@ struct insert_context {
const basic_schema &schema_;
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
std::vector<std::unique_ptr<insert_step>> steps_{};
std::vector<std::unique_ptr<insert_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, visit_key_hash> visited_{};
std::vector<std::unique_ptr<execute_step>> steps_{};
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
};
@ -120,7 +108,7 @@ public:
}
ptr_ = ptr;
const auto key = make_visit_key<ObjectType>(*ptr_);
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
return utils::ok<void>();
}
@ -339,7 +327,7 @@ private:
}
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
std::vector<std::unique_ptr<insert_step>> insert_relation_steps;
std::vector<std::unique_ptr<execute_step>> insert_relation_steps;
insert_step_processor<ForeignType> processor(ctx_);
for (auto &obj : objects) {
if (!obj) {
@ -366,7 +354,7 @@ private:
ctx_.processing_many_to_many_relations_.erase(key);
}
std::unique_ptr<insert_step> create_insert_step(const sql::query_context& query_ctx, const schema_node& node) {
std::unique_ptr<execute_step> create_insert_step(const sql::query_context& query_ctx, const schema_node& node) {
if (node.pk_generator().type() == utils::generator_type::Manual) {
return std::make_unique<insert_step_pk_manual<ObjectType>>(query_ctx, ptr_);
}
@ -389,7 +377,7 @@ public:
, contexts_by_type_(contexts_by_type)
{}
utils::result<std::vector<std::unique_ptr<insert_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
}

View File

@ -1,44 +1,18 @@
#ifndef MATADOR_INSERT_STEP_HPP
#define MATADOR_INSERT_STEP_HPP
#include <utility>
#include "matador/utils/identifier.hpp"
#include "matador/utils/primary_key_accessor.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include "matador/query/abstract_pk_generator.hpp"
#include "matador/query/execute_step.hpp"
#include "matador/query/error_code.hpp"
#include "matador/object/object_cache.hpp"
#include "matador/object/object_ptr.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/sql/execute_result.hpp"
#include "matador/sql/executor.hpp"
#include "matador/sql/resolver_service.hpp"
#include "matador/sql/statement.hpp"
#include "matador/query/error_code.hpp"
#include "matador/query/abstract_pk_generator.hpp"
namespace matador::query {
using resolver_service_ptr = std::shared_ptr<sql::resolver_service>;
class insert_step {
public:
explicit insert_step(sql::query_context ctx)
: ctx_(std::move(ctx)) {}
virtual ~insert_step() = default;
virtual utils::result<void, utils::error> prepare(sql::executor &conn) = 0;
virtual utils::result<void, utils::error> insert(sql::statement &stmt) = 0;
virtual utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) = 0;
[[nodiscard]] const sql::query_context& ctx() const { return ctx_; }
protected:
utils::identifier id_;
utils::primary_key_accessor pk_accessor_;
sql::query_context ctx_;
};
template<typename ObjectType>
utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<ObjectType> &ptr,
const utils::identifier& pk,
@ -66,10 +40,10 @@ utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<Ob
}
template <typename ObjectType>
class insert_step_pk_generated : public insert_step {
class insert_step_pk_generated : public execute_step {
public:
insert_step_pk_generated(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, abstract_pk_generator& pk_generator)
: insert_step(std::move(ctx))
: execute_step(std::move(ctx))
, ptr_(ptr)
, pk_generator_(pk_generator){}
@ -84,7 +58,7 @@ public:
return utils::ok<void>();
}
utils::result<void, utils::error> insert(sql::statement& stmt) override {
utils::result<void, utils::error> execute(sql::statement& stmt) override {
stmt.bind(*ptr_);
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
@ -105,10 +79,10 @@ private:
};
template <typename ObjectType>
class insert_step_pk_identity : public insert_step {
class insert_step_pk_identity : public execute_step {
public:
insert_step_pk_identity(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, std::string pk_column_name)
: insert_step(std::move(ctx))
: execute_step(std::move(ctx))
, ptr_(ptr)
, pk_column_name_(std::move(pk_column_name)){}
@ -116,7 +90,7 @@ public:
return utils::ok<void>();
}
utils::result<void, utils::error> insert(sql::statement& stmt) override {
utils::result<void, utils::error> execute(sql::statement& stmt) override {
stmt.bind(*ptr_);
auto result = stmt.fetch_one();
@ -148,16 +122,16 @@ private:
};
template <typename ObjectType>
class insert_step_pk_manual : public insert_step {
class insert_step_pk_manual : public execute_step {
public:
insert_step_pk_manual(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
: insert_step(std::move(ctx))
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &) override {
return utils::ok<void>();
}
utils::result<void, utils::error> insert(sql::statement &stmt) override {
utils::result<void, utils::error> execute(sql::statement &stmt) override {
stmt.bind(*ptr_);
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
return utils::failure(exec_result.err());
@ -177,16 +151,16 @@ private:
};
template <typename ObjectType>
class insert_step_relation : public insert_step {
class insert_step_relation : public execute_step {
public:
insert_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
: insert_step(std::move(ctx))
: execute_step(std::move(ctx))
, ptr_(ptr) {}
utils::result<void, utils::error> prepare(sql::executor &) override {
return utils::ok<void>();
}
utils::result<void, utils::error> insert(sql::statement &stmt) override {
utils::result<void, utils::error> execute(sql::statement &stmt) override {
stmt.bind(*ptr_);
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
return utils::failure(exec_result.err());

View File

@ -0,0 +1,22 @@
#ifndef MATADOR_QUERY_BUILDER_UTILS_HPP
#define MATADOR_QUERY_BUILDER_UTILS_HPP
#include <functional>
#include <map>
#include <typeindex>
namespace matador::query {
template<class EntityType>
static std::pair<std::type_index, const void *> make_entity_visit_key(const EntityType &ptr) {
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&ptr)};
}
struct entity_visit_key_hash {
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
const size_t h1 = p.first.hash_code();
const size_t h2 = std::hash<const void *>{}(p.second);
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
}
};
}
#endif //MATADOR_QUERY_BUILDER_UTILS_HPP

View File

@ -2,6 +2,7 @@
#define QUERY_SESSION_HPP
#include "matador/query/error_code.hpp"
#include "matador/query/delete_query_builder.hpp"
#include "matador/query/select_query_builder.hpp"
#include "matador/query/criteria.hpp"
#include "matador/query/insert_query_builder.hpp"
@ -52,7 +53,6 @@ public:
*/
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
template<typename Type>
@ -112,7 +112,7 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
return utils::failure(stmt.err());
}
if (const auto result = step->insert(*stmt); !result.is_ok()) {
if (const auto result = step->execute(*stmt); !result.is_ok()) {
return utils::failure(result.err());
}
}
@ -207,28 +207,67 @@ utils::result<object::object_ptr<Type>, utils::error> session::update(const obje
template<typename Type>
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
const auto it = schema_.find(typeid(Type));
if (it == schema_.end()) {
if (!obj.is_persistent()) {
return utils::ok<void>();
}
if (const auto it = schema_.find(typeid(Type)); it == schema_.end()) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
using namespace matador::utils;
using namespace matador::query;
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
auto stmt = cache_.acquire(cit->second.delete_one);
if (!stmt.is_ok()) {
return failure(stmt.err());
delete_query_builder<Type> dqb(schema_, contexts_by_type_);
auto steps = dqb.build(obj);
if (!steps.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build delete dependency queries."));
}
pk_object_binder binder(*stmt, stmt->bind_pos());
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
return utils::failure(update_result.err());
for (auto &step : *steps) {
const auto conn = pool_.acquire();
if (!conn.valid()) {
return utils::failure(make_error(error_code::FailedToAcquirePool, "Failed to acquire connection pool for primary key generation."));
}
if (const auto result = step->prepare(*conn); !result.is_ok()) {
return utils::failure(result.err());
}
conn.release();
auto stmt = cache_.acquire(step->ctx());
if (!stmt.is_ok()) {
return utils::failure(stmt.err());
}
if (const auto result = step->execute(*stmt); !result.is_ok()) {
return utils::failure(result.err());
}
}
// After successfully executed all deletes, add them to the object cache
for (auto &step : *steps) {
if (const auto result = step->finalize(object_cache_, resolver_service_); !result.is_ok()) {
return utils::failure(result.err());
}
}
return utils::ok<void>();
// using namespace matador::utils;
// using namespace matador::query;
//
// const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
// const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
// if (cit == contexts_by_type_.end()) {
// return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
// }
// auto stmt = cache_.acquire(cit->second.delete_one);
// if (!stmt.is_ok()) {
// return failure(stmt.err());
// }
//
// pk_object_binder binder(*stmt, stmt->bind_pos());
// if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
// return utils::failure(update_result.err());
// }
// return utils::ok<void>();
}
template<typename Type, typename PrimaryKeyType>

View File

@ -31,7 +31,7 @@ add_library(matador-orm STATIC
../../include/matador/query/identity_pk_generator.hpp
../../include/matador/query/insert_query_builder.hpp
../../include/matador/query/insert_query_builder.hpp
../../include/matador/query/insert_step.hpp
../../include/matador/query/execute_step.hpp
../../include/matador/query/intermediates/executable_query.hpp
../../include/matador/query/intermediates/fetchable_query.hpp
../../include/matador/query/intermediates/query_alter_intermediate.hpp
@ -221,6 +221,9 @@ add_library(matador-orm STATIC
sql/resolver_service.cpp
sql/statement.cpp
sql/statement_cache.cpp
../../include/matador/query/delete_query_builder.hpp
../../include/matador/query/query_builder_utils.hpp
../../include/matador/query/insert_step.hpp
)
target_include_directories(matador-orm

View File

@ -0,0 +1,71 @@
#include "catch2/catch_test_macros.hpp"
#include "SessionFixture.hpp"
#include "connection.hpp"
#include "matador/query/session.hpp"
#include "models/author.hpp"
#include "models/book.hpp"
using namespace matador::test;
using namespace matador::query;
using namespace matador::object;
namespace matador::test {
template<typename AuthorType>
void validate_author_state(const object_ptr<AuthorType>& ptr, object_state expected_state) {
REQUIRE(ptr.is_state(expected_state));
for (auto &b: ptr->books) {
REQUIRE(b.is_state(expected_state));
}
}
}
namespace matador::utils {
template < typename ValueType >
std::ostream& operator<<(std::ostream& os, const result<ValueType, error>& value) {
if (value) {
return os;
}
return os << "Error: " << value.err();
}
std::ostream& operator<<(std::ostream& os, const result<void, error>& value) {
if (value) {
return os;
}
return os << "Error: " << value.err();
}
}
TEST_CASE_METHOD(SessionFixture, "Test delete object with has many relation", "[session][delete][has_many]") {
const auto result = schema.attach<book>("books")
.and_then( [this] { return schema.attach<author>("authors"); } )
.and_then([this] { return schema.create(db); } );
REQUIRE(result.is_ok());
session ses({bus, connection::dns, 4}, schema);
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
s_king->books.push_back(make_object<book>(2, "Carrie", nullobj, 1974));
s_king->books.push_back(make_object<book>(3, "The Shining", nullobj, 1977));
s_king->books.push_back(make_object<book>(4, "It", nullobj, 1986));
s_king->books.push_back(make_object<book>(5, "Misery", nullobj, 1987));
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", nullobj, 1982));
validate_author_state(s_king, object_state::Transient);
auto res = ses.insert(s_king);
REQUIRE(res);
validate_author_state(s_king, object_state::Persistent);
auto author_result = ses.find<author>(s_king->id);
REQUIRE(author_result);
REQUIRE(author_result->is_persistent());
REQUIRE(author_result.value()->books.size() == 5);
auto del_res = ses.remove(s_king);
REQUIRE(del_res);
}