added delete_query_builder (progress)
This commit is contained in:
parent
141d798a41
commit
57f57956de
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue