delete has many to many progress
This commit is contained in:
@@ -7,13 +7,11 @@
|
||||
|
||||
#include "matador/query/basic_schema.hpp"
|
||||
#include "matador/query/error_code.hpp"
|
||||
#include "matador/query/execute_step.hpp"
|
||||
#include "matador/query/delete_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/internal/identifier_statement_binder.hpp"
|
||||
#include "matador/sql/execute_result.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/utils/error.hpp"
|
||||
@@ -22,61 +20,10 @@
|
||||
#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"});
|
||||
}
|
||||
|
||||
sql::identifier_statement_binder binder(stmt, 0);
|
||||
binder.bind(id_);
|
||||
|
||||
if (const auto result = stmt.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)
|
||||
explicit delete_step_processor(query_builder_context &ctx)
|
||||
: ctx_(ctx) {}
|
||||
|
||||
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
|
||||
@@ -97,8 +44,7 @@ public:
|
||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||
}
|
||||
|
||||
const auto &info = it->second.node().info();
|
||||
if (!info.has_primary_key()) {
|
||||
if (const auto &info = it->second.node().info(); !info.has_primary_key()) {
|
||||
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||
}
|
||||
|
||||
@@ -177,19 +123,50 @@ public:
|
||||
}
|
||||
|
||||
template<class ForeignType>
|
||||
void on_has_many_to_many(const char * /*id*/,
|
||||
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 char *join_column,
|
||||
const char *inverse_join_column,
|
||||
const utils::foreign_attributes &attr) {
|
||||
on_many_to_many_objects(objects, attr);
|
||||
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
||||
const std::type_index foreign_type{typeid(ForeignType)};
|
||||
const std::type_index local_type{typeid(ObjectType)};
|
||||
on_many_to_many_objects<relation_value_type>(
|
||||
id,
|
||||
objects,
|
||||
attr,
|
||||
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||
return {std::string{relation_name}, local_type, foreign_type};
|
||||
});
|
||||
}
|
||||
|
||||
template<class ForeignType>
|
||||
void on_has_many_to_many(const char * /*id*/,
|
||||
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);
|
||||
if (id == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
object::join_columns_collector collector;
|
||||
if (auto join_columns = collector.collect<ForeignType>(); join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
||||
const std::type_index foreign_type{typeid(ForeignType)};
|
||||
const std::type_index local_type{typeid(ObjectType)};
|
||||
on_many_to_many_objects<relation_value_type>(
|
||||
id,
|
||||
objects,
|
||||
attr,
|
||||
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||
return {std::string{relation_name}, foreign_type, local_type};
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -207,28 +184,52 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
template<class ForeignType>
|
||||
void on_many_to_many_objects(object::collection<object::object_ptr<ForeignType>> &objects,
|
||||
const utils::foreign_attributes &attr) {
|
||||
template<class LocalType, class ForeignType, class RelationKeyFactory>
|
||||
void on_many_to_many_objects(const char *id,
|
||||
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||
const utils::foreign_attributes &attr,
|
||||
RelationKeyFactory make_relation_key) {
|
||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto key = make_relation_key(id);
|
||||
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto it = ctx_.schema_.find(std::string{id});
|
||||
if (it == ctx_.schema_.end()) {
|
||||
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
|
||||
}
|
||||
|
||||
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
|
||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
|
||||
}
|
||||
|
||||
if (const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index()); cit == ctx_.contexts_by_type_.end()) {
|
||||
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
|
||||
}
|
||||
|
||||
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
||||
std::vector<std::unique_ptr<execute_step>> delete_relation_steps;
|
||||
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());
|
||||
if (obj.is_persistent()) {
|
||||
auto result = processor.build(obj, true);
|
||||
if (!result) {
|
||||
throw query_builder_exception(result.release_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
delete_context &ctx_;
|
||||
query_builder_context &ctx_;
|
||||
object::object_ptr<ObjectType> ptr_;
|
||||
};
|
||||
|
||||
@@ -245,7 +246,7 @@ public:
|
||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"});
|
||||
}
|
||||
|
||||
delete_context ctx{schema_, contexts_by_type_};
|
||||
query_builder_context ctx{schema_, contexts_by_type_};
|
||||
delete_step_processor<ObjectType> processor{ctx};
|
||||
|
||||
const auto result = processor.build(ptr);
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef MATADOR_DELETE_STEP_HPP
|
||||
#define MATADOR_DELETE_STEP_HPP
|
||||
|
||||
#include "matador/query/error_code.hpp"
|
||||
#include "matador/query/execute_step.hpp"
|
||||
|
||||
#include "matador/sql/internal/identifier_statement_binder.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.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"});
|
||||
}
|
||||
|
||||
sql::identifier_statement_binder binder(stmt, 0);
|
||||
binder.bind(id_);
|
||||
|
||||
if (const auto result = stmt.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_;
|
||||
};
|
||||
|
||||
template <typename ObjectType>
|
||||
class delete_step_relation : public execute_step {
|
||||
public:
|
||||
delete_step_relation(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 &) override {
|
||||
return utils::ok<void>();
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
return utils::ok<void>();
|
||||
}
|
||||
|
||||
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
|
||||
return utils::ok<void>();
|
||||
}
|
||||
|
||||
private:
|
||||
object::object_ptr<ObjectType> ptr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_DELETE_STEP_HPP
|
||||
@@ -56,49 +56,10 @@ private:
|
||||
std::string join_column_;
|
||||
};
|
||||
|
||||
struct processing_many_to_many_key {
|
||||
std::string id;
|
||||
std::type_index local_type{typeid(void)};
|
||||
std::type_index foreign_type{typeid(void)};
|
||||
bool operator==(processing_many_to_many_key const &other) const {
|
||||
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
template<class LocalType, typename ForeignType>
|
||||
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
|
||||
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
|
||||
}
|
||||
|
||||
struct processing_many_to_many_key_hash {
|
||||
size_t operator()(const processing_many_to_many_key &p) const noexcept {
|
||||
size_t seed = std::hash<std::type_index>{}(p.local_type);
|
||||
|
||||
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
|
||||
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||
|
||||
const size_t id_hash = std::hash<std::string>{}(p.id);
|
||||
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
struct insert_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_{};
|
||||
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
|
||||
|
||||
};
|
||||
|
||||
template < typename ObjectType >
|
||||
class insert_step_processor {
|
||||
public:
|
||||
explicit insert_step_processor(insert_context &ctx)
|
||||
explicit insert_step_processor(query_builder_context &ctx)
|
||||
: ctx_{ctx}
|
||||
{}
|
||||
|
||||
@@ -247,7 +208,6 @@ public:
|
||||
attr,
|
||||
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||
return {std::string{relation_name}, local_type, foreign_type};
|
||||
// return make_processing_many_to_many_key<ObjectType, ForeignType>(relation_name);
|
||||
},
|
||||
[this, join_column, inverse_join_column](const auto &obj) {
|
||||
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
||||
@@ -275,7 +235,6 @@ public:
|
||||
attr,
|
||||
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||
return {std::string{relation_name}, foreign_type, local_type};
|
||||
return make_processing_many_to_many_key<ForeignType, ObjectType>(relation_name);
|
||||
},
|
||||
[this, join_columns = std::move(join_columns)](const auto &obj) {
|
||||
return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
|
||||
@@ -297,12 +256,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
|
||||
void insert_many_to_many_relations(const char *id,
|
||||
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||
const utils::foreign_attributes &attr,
|
||||
RelationKeyFactory make_relation_key,
|
||||
RelationFactory make_relation) {
|
||||
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
|
||||
void insert_many_to_many_relations(const char *id,
|
||||
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||
const utils::foreign_attributes &attr,
|
||||
RelationKeyFactory make_relation_key,
|
||||
RelationFactory make_relation) {
|
||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||
return;
|
||||
}
|
||||
@@ -343,7 +302,7 @@ private:
|
||||
|
||||
auto rel = make_relation(obj);
|
||||
|
||||
access::process(*this, *rel);
|
||||
// access::process(*this, *rel);
|
||||
|
||||
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
|
||||
}
|
||||
@@ -365,7 +324,7 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
insert_context& ctx_;
|
||||
query_builder_context& ctx_;
|
||||
object::object_ptr<ObjectType> ptr_;
|
||||
};
|
||||
|
||||
@@ -382,7 +341,7 @@ public:
|
||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
||||
}
|
||||
|
||||
insert_context ctx{schema_, contexts_by_type_};
|
||||
query_builder_context ctx{schema_, contexts_by_type_};
|
||||
insert_step_processor<ObjectType> processor{ctx};
|
||||
|
||||
const auto result = processor.build(ptr);
|
||||
|
||||
@@ -18,5 +18,43 @@ struct entity_visit_key_hash {
|
||||
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
||||
}
|
||||
};
|
||||
|
||||
struct processing_many_to_many_key {
|
||||
std::string id;
|
||||
std::type_index local_type{typeid(void)};
|
||||
std::type_index foreign_type{typeid(void)};
|
||||
bool operator==(processing_many_to_many_key const &other) const {
|
||||
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
template<class LocalType, typename ForeignType>
|
||||
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
|
||||
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
|
||||
}
|
||||
|
||||
struct processing_many_to_many_key_hash {
|
||||
size_t operator()(const processing_many_to_many_key &p) const noexcept {
|
||||
size_t seed = std::hash<std::type_index>{}(p.local_type);
|
||||
|
||||
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
|
||||
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||
|
||||
const size_t id_hash = std::hash<std::string>{}(p.id);
|
||||
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
struct query_builder_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_{};
|
||||
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||
|
||||
@@ -352,10 +352,6 @@ utils::result<sql::query_context, utils::error> query_object_resolver_producer<T
|
||||
|
||||
template<typename Type>
|
||||
utils::result<sql::query_context, utils::error> query_joined_object_resolver_producer<Type>::build_query(const sql::dialect &d) {
|
||||
// producer_creator pc(repo_, typeid(Type));
|
||||
// Type obj;
|
||||
// access::process(pc, obj);
|
||||
|
||||
select_query_builder qb(repo_);
|
||||
const auto *join_column = table_[collection_name()];
|
||||
const auto result = qb.build<Type>(*join_column == utils::_);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef MATADOR_SCHEMA_UTILS_HPP
|
||||
#define MATADOR_SCHEMA_UTILS_HPP
|
||||
|
||||
#include "matador/query/query_contexts.hpp"
|
||||
#include "matador/query/basic_schema.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class dialect;
|
||||
}
|
||||
namespace matador::query {
|
||||
query_contexts to_query_contexts(const schema_node &node, const sql::dialect &d);
|
||||
}
|
||||
#endif //MATADOR_SCHEMA_UTILS_HPP
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
#include "matador/sql/abstract_sql_logger.hpp"
|
||||
#include "matador/sql/error_code.hpp"
|
||||
#include "matador/sql/execute_result.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/interface/statement_proxy.hpp"
|
||||
|
||||
#include "matador/object/basic_repository.hpp"
|
||||
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user