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/models/user.hpp
|
||||||
../../../test/utils/RecordPrinter.cpp
|
../../../test/utils/RecordPrinter.cpp
|
||||||
../../../test/utils/RecordPrinter.hpp
|
../../../test/utils/RecordPrinter.hpp
|
||||||
|
../../../test/backends/SessionDeleteHasMany.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBRARY_TEST_TARGET PostgresTests)
|
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 {
|
namespace matador::query::detail {
|
||||||
|
|
||||||
class fk_value_extractor
|
class fk_value_extractor {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
fk_value_extractor() = default;
|
fk_value_extractor() = default;
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
utils::database_type extract(Type &x)
|
utils::database_type extract(Type &x) {
|
||||||
{
|
|
||||||
access::process(*this, x);
|
access::process(*this, x);
|
||||||
return value_;
|
return value_;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "matador/query/query.hpp"
|
#include "matador/query/query.hpp"
|
||||||
#include "matador/query/query_contexts.hpp"
|
#include "matador/query/query_contexts.hpp"
|
||||||
#include "matador/query/query_builder_exception.hpp"
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
#include "matador/query/query_builder_utils.hpp"
|
||||||
|
|
||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
|
@ -55,19 +56,6 @@ private:
|
||||||
std::string join_column_;
|
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 {
|
struct processing_many_to_many_key {
|
||||||
std::string id;
|
std::string id;
|
||||||
std::type_index local_type{typeid(void)};
|
std::type_index local_type{typeid(void)};
|
||||||
|
|
@ -100,9 +88,9 @@ struct insert_context {
|
||||||
const basic_schema &schema_;
|
const basic_schema &schema_;
|
||||||
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
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<execute_step>> steps_{};
|
||||||
std::vector<std::unique_ptr<insert_step>> relation_steps_{};
|
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
|
||||||
std::unordered_set<std::pair<std::type_index, const void *>, visit_key_hash> visited_{};
|
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_{};
|
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;
|
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()) {
|
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +327,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
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_);
|
insert_step_processor<ForeignType> processor(ctx_);
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
|
@ -366,7 +354,7 @@ private:
|
||||||
ctx_.processing_many_to_many_relations_.erase(key);
|
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) {
|
if (node.pk_generator().type() == utils::generator_type::Manual) {
|
||||||
return std::make_unique<insert_step_pk_manual<ObjectType>>(query_ctx, ptr_);
|
return std::make_unique<insert_step_pk_manual<ObjectType>>(query_ctx, ptr_);
|
||||||
}
|
}
|
||||||
|
|
@ -389,7 +377,7 @@ public:
|
||||||
, contexts_by_type_(contexts_by_type)
|
, 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()) {
|
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,18 @@
|
||||||
#ifndef MATADOR_INSERT_STEP_HPP
|
#ifndef MATADOR_INSERT_STEP_HPP
|
||||||
#define MATADOR_INSERT_STEP_HPP
|
#define MATADOR_INSERT_STEP_HPP
|
||||||
|
|
||||||
#include <utility>
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
#include "matador/query/execute_step.hpp"
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/query/error_code.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/object/object_cache.hpp"
|
||||||
#include "matador/object/object_ptr.hpp"
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/sql/query_context.hpp"
|
|
||||||
#include "matador/sql/execute_result.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/sql/statement.hpp"
|
||||||
|
|
||||||
#include "matador/query/error_code.hpp"
|
|
||||||
#include "matador/query/abstract_pk_generator.hpp"
|
|
||||||
|
|
||||||
namespace matador::query {
|
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>
|
template<typename ObjectType>
|
||||||
utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<ObjectType> &ptr,
|
utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<ObjectType> &ptr,
|
||||||
const utils::identifier& pk,
|
const utils::identifier& pk,
|
||||||
|
|
@ -66,10 +40,10 @@ utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<Ob
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_generated : public insert_step {
|
class insert_step_pk_generated : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_generated(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, abstract_pk_generator& pk_generator)
|
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)
|
, ptr_(ptr)
|
||||||
, pk_generator_(pk_generator){}
|
, pk_generator_(pk_generator){}
|
||||||
|
|
||||||
|
|
@ -84,7 +58,7 @@ public:
|
||||||
return utils::ok<void>();
|
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_);
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
|
@ -105,10 +79,10 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_identity : public insert_step {
|
class insert_step_pk_identity : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_identity(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, std::string pk_column_name)
|
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)
|
, ptr_(ptr)
|
||||||
, pk_column_name_(std::move(pk_column_name)){}
|
, pk_column_name_(std::move(pk_column_name)){}
|
||||||
|
|
||||||
|
|
@ -116,7 +90,7 @@ public:
|
||||||
return utils::ok<void>();
|
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_);
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
auto result = stmt.fetch_one();
|
auto result = stmt.fetch_one();
|
||||||
|
|
@ -148,16 +122,16 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_manual : public insert_step {
|
class insert_step_pk_manual : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_manual(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
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) {}
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
utils::result<void, utils::error> prepare(sql::executor &) override {
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
return utils::ok<void>();
|
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_);
|
stmt.bind(*ptr_);
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
return utils::failure(exec_result.err());
|
return utils::failure(exec_result.err());
|
||||||
|
|
@ -177,16 +151,16 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_relation : public insert_step {
|
class insert_step_relation : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
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) {}
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
utils::result<void, utils::error> prepare(sql::executor &) override {
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
return utils::ok<void>();
|
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_);
|
stmt.bind(*ptr_);
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
return utils::failure(exec_result.err());
|
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
|
#define QUERY_SESSION_HPP
|
||||||
|
|
||||||
#include "matador/query/error_code.hpp"
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/delete_query_builder.hpp"
|
||||||
#include "matador/query/select_query_builder.hpp"
|
#include "matador/query/select_query_builder.hpp"
|
||||||
#include "matador/query/criteria.hpp"
|
#include "matador/query/criteria.hpp"
|
||||||
#include "matador/query/insert_query_builder.hpp"
|
#include "matador/query/insert_query_builder.hpp"
|
||||||
|
|
@ -52,7 +53,6 @@ public:
|
||||||
*/
|
*/
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -112,7 +112,7 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
|
||||||
return utils::failure(stmt.err());
|
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());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -207,28 +207,67 @@ utils::result<object::object_ptr<Type>, utils::error> session::update(const obje
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
||||||
const auto it = schema_.find(typeid(Type));
|
if (!obj.is_persistent()) {
|
||||||
if (it == schema_.end()) {
|
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."));
|
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());
|
delete_query_builder<Type> dqb(schema_, contexts_by_type_);
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
auto steps = dqb.build(obj);
|
||||||
if (cit == contexts_by_type_.end()) {
|
if (!steps.is_ok()) {
|
||||||
return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build delete dependency queries."));
|
||||||
}
|
|
||||||
auto stmt = cache_.acquire(cit->second.delete_one);
|
|
||||||
if (!stmt.is_ok()) {
|
|
||||||
return failure(stmt.err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pk_object_binder binder(*stmt, stmt->bind_pos());
|
for (auto &step : *steps) {
|
||||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
const auto conn = pool_.acquire();
|
||||||
return utils::failure(update_result.err());
|
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>();
|
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>
|
template<typename Type, typename PrimaryKeyType>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ add_library(matador-orm STATIC
|
||||||
../../include/matador/query/identity_pk_generator.hpp
|
../../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_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/executable_query.hpp
|
||||||
../../include/matador/query/intermediates/fetchable_query.hpp
|
../../include/matador/query/intermediates/fetchable_query.hpp
|
||||||
../../include/matador/query/intermediates/query_alter_intermediate.hpp
|
../../include/matador/query/intermediates/query_alter_intermediate.hpp
|
||||||
|
|
@ -221,6 +221,9 @@ add_library(matador-orm STATIC
|
||||||
sql/resolver_service.cpp
|
sql/resolver_service.cpp
|
||||||
sql/statement.cpp
|
sql/statement.cpp
|
||||||
sql/statement_cache.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
|
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