refactored insert_query_builder into insert_step_processor and added insert_context
This commit is contained in:
parent
aa8da1f76f
commit
7216f07c9f
|
|
@ -4,6 +4,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "matador/object/collection.hpp"
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/query/basic_schema.hpp"
|
#include "matador/query/basic_schema.hpp"
|
||||||
#include "matador/query/intermediates/executable_query.hpp"
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
|
|
@ -56,38 +57,117 @@ private:
|
||||||
std::string join_column_;
|
std::string join_column_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ObjectType>
|
template<class EntityType>
|
||||||
class insert_query_builder {
|
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 {
|
||||||
|
// combine hashes (simple + sufficient here)
|
||||||
|
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)};
|
||||||
|
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<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::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:
|
public:
|
||||||
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
explicit insert_step_processor(insert_context &ctx)
|
||||||
: schema_(schema)
|
: ctx_{ctx}
|
||||||
, 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<void, utils::error> build(object::object_ptr<ObjectType> ptr) {
|
||||||
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
if (!ptr) {
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
}
|
}
|
||||||
|
|
||||||
relation_steps_.clear();
|
|
||||||
processing_many_to_many_relations_.clear();
|
|
||||||
steps_.clear();
|
|
||||||
visited_.clear();
|
|
||||||
|
|
||||||
ptr_ = ptr;
|
ptr_ = ptr;
|
||||||
const auto result = build_insert_steps(ptr, steps_);
|
|
||||||
|
const auto key = make_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"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Traverse relations first => dependencies will be inserted before this object
|
||||||
|
try {
|
||||||
|
access::process(*this, *ptr_);
|
||||||
|
|
||||||
|
// relation inserts must run after all entity inserts were collected
|
||||||
|
for (auto &s : ctx_.relation_steps_) {
|
||||||
|
ctx_.steps_.push_back(std::move(s));
|
||||||
|
}
|
||||||
|
ctx_.relation_steps_.clear();
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Build INSERT for this object
|
||||||
|
const auto &info = it->second.node().info();
|
||||||
|
if (!info.has_primary_key() || it->second.pk_generator().type() == utils::generator_type::None) {
|
||||||
|
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||||
|
}
|
||||||
|
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 (it->second.pk_generator().type() == utils::generator_type::Manual) {
|
||||||
|
ctx_.steps_.push_back(std::make_unique<insert_step_pk_manual<ObjectType>>(cit->second.insert, ptr_));
|
||||||
|
} else if (it->second.pk_generator().type() == utils::generator_type::Identity) {
|
||||||
|
ctx_.steps_.push_back(std::make_unique<insert_step_pk_identity<ObjectType>>(cit->second.insert, ptr_, info.primary_key_attribute()->name()));
|
||||||
|
} else {
|
||||||
|
ctx_.steps_.push_back(std::make_unique<insert_step_pk_generated<ObjectType>>(cit->second.insert, ptr_, it->second.pk_generator()));
|
||||||
|
}
|
||||||
|
|
||||||
ptr_.reset();
|
ptr_.reset();
|
||||||
if (!result) {
|
|
||||||
return utils::failure(result.err());
|
|
||||||
}
|
|
||||||
|
|
||||||
// relation inserts must run after all entity inserts were collected
|
return utils::ok<void>();
|
||||||
for (auto &s : relation_steps_) {
|
|
||||||
steps_.push_back(std::move(s));
|
|
||||||
}
|
|
||||||
relation_steps_.clear();
|
|
||||||
|
|
||||||
return utils::ok(std::move(steps_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < class PrimaryKeyType >
|
template < class PrimaryKeyType >
|
||||||
|
|
@ -105,6 +185,7 @@ public:
|
||||||
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_object(obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/,
|
void on_has_many(const char * /*id*/,
|
||||||
object::collection<object::object_ptr<CollectionType>> &objects,
|
object::collection<object::object_ptr<CollectionType>> &objects,
|
||||||
|
|
@ -118,12 +199,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
has_many_linker<ObjectType> linker(ptr_, join_column);
|
has_many_linker<ObjectType> linker(ptr_, join_column);
|
||||||
|
insert_context ctx{ctx_.schema_, ctx_.contexts_by_type_};
|
||||||
|
insert_step_processor<CollectionType> processor{ctx};
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (obj.is_transient()) {
|
if (!obj.is_transient()) {
|
||||||
const auto result = build_insert_steps(obj, relation_steps_);
|
continue;
|
||||||
if (!result) {
|
}
|
||||||
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
|
||||||
}
|
const auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
||||||
|
// return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
access::process(linker, *obj);
|
access::process(linker, *obj);
|
||||||
|
|
@ -142,8 +228,8 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto it = schema_.find(std::string{id});
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
if (it == schema_.end()) {
|
if (it == ctx_.schema_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type " + std::string{id});
|
throw query_builder_exception(error_code::UnknownType, "Unknown type " + std::string{id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,29 +238,40 @@ public:
|
||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
if (cit == contexts_by_type_.end()) {
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type" + std::string{id});
|
throw query_builder_exception(error_code::UnknownType, "Unknown type" + std::string{id});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
auto rel = object::make_object<relation_value_type>(join_column, "value", ptr_, obj);
|
auto rel = object::make_object<relation_value_type>(join_column, "value", ptr_, obj);
|
||||||
|
|
||||||
relation_steps_.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
ctx_.relation_steps_.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ForeignType>
|
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) {
|
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) {
|
||||||
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
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)};
|
||||||
insert_many_to_many_relations<relation_value_type>(
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
id,
|
id,
|
||||||
objects,
|
objects,
|
||||||
attr,
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
std::cout << "Processing many-to-many relation: " << local_type.name() << ":" << foreign_type.name() << ":" << relation_name << std::endl;
|
||||||
|
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) {
|
[this, join_column, inverse_join_column](const auto &obj) {
|
||||||
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
||||||
});
|
});
|
||||||
|
|
@ -193,90 +290,56 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
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)};
|
||||||
insert_many_to_many_relations<relation_value_type>(
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
id,
|
id,
|
||||||
objects,
|
objects,
|
||||||
attr,
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
std::cout << "Processing many-to-many relation: " << foreign_type.name() << ":" << local_type.name() << ":" << relation_name << std::endl;
|
||||||
|
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) {
|
[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_);
|
return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<class EntityType>
|
template<class PointerType>
|
||||||
static std::pair<std::type_index, const void *> make_visit_key(const EntityType &ptr) {
|
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
|
||||||
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&ptr)};
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert) || !obj || obj.is_transient()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_context ctx{ctx_.schema_, ctx_.contexts_by_type_};
|
||||||
|
insert_step_processor<PointerType> processor{ctx};
|
||||||
|
|
||||||
|
const auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
||||||
|
// return utils::failure(result.err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct visit_key_hash {
|
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
|
||||||
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
|
void insert_many_to_many_relations(const char *id,
|
||||||
// combine hashes (simple + sufficient here)
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
const size_t h1 = p.first.hash_code();
|
const utils::foreign_attributes &attr,
|
||||||
const size_t h2 = std::hash<const void *>{}(p.second);
|
RelationKeyFactory make_relation_key,
|
||||||
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
RelationFactory make_relation) {
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class EntityType>
|
|
||||||
utils::result<void, utils::error> build_insert_steps(const object::object_ptr<EntityType> &ptr, std::vector<std::unique_ptr<insert_step>> &steps) {
|
|
||||||
if (!ptr) {
|
|
||||||
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto key = make_visit_key<EntityType>(*ptr);
|
|
||||||
if (visited_.find(key) != visited_.end()) {
|
|
||||||
return utils::ok<void>();
|
|
||||||
}
|
|
||||||
visited_.insert(key);
|
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(EntityType));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) Traverse relations first => dependencies will be inserted before this object
|
|
||||||
try {
|
|
||||||
access::process(*this, *ptr);
|
|
||||||
} catch (const query_builder_exception &ex) {
|
|
||||||
return utils::failure(ex.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Build INSERT for this object
|
|
||||||
const auto &info = it->second.node().info();
|
|
||||||
if (!info.has_primary_key() || it->second.pk_generator().type() == utils::generator_type::None) {
|
|
||||||
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
|
||||||
}
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
|
||||||
if (cit == contexts_by_type_.end()) {
|
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it->second.pk_generator().type() == utils::generator_type::Manual) {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_manual<EntityType>>(cit->second.insert, ptr));
|
|
||||||
} else if (it->second.pk_generator().type() == utils::generator_type::Identity) {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_identity<EntityType>>(cit->second.insert, ptr, info.primary_key_attribute()->name()));
|
|
||||||
} else {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_generated<EntityType>>(cit->second.insert, ptr, it->second.pk_generator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils::ok<void>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class LocalType, class ForeignType, class RelationFactory>
|
|
||||||
void insert_many_to_many_relations(const char *id,
|
|
||||||
object::collection<object::object_ptr<ForeignType>> &objects,
|
|
||||||
const utils::foreign_attributes &attr,
|
|
||||||
RelationFactory make_relation) {
|
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) {
|
const auto key = make_relation_key(id);
|
||||||
|
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto it = schema_.find(std::string{id});
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
if (it == schema_.end()) {
|
if (it == ctx_.schema_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,24 +347,23 @@ private:
|
||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
if (cit == contexts_by_type_.end()) {
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ignore = processing_many_to_many_relations_.insert(id);
|
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<insert_step>> insert_relation_steps;
|
||||||
|
insert_step_processor<ForeignType> processor(ctx_);
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (!obj) {
|
if (!obj || !obj.is_transient()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure target exists as dependency (deps first)
|
const auto result = processor.build(obj);
|
||||||
if (obj.is_transient()) {
|
if (!result) {
|
||||||
const auto result = build_insert_steps(obj, relation_steps_);
|
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
||||||
if (!result) {
|
// return utils::failure(result.err());
|
||||||
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rel = make_relation(obj);
|
auto rel = make_relation(obj);
|
||||||
|
|
@ -310,43 +372,43 @@ private:
|
||||||
|
|
||||||
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
|
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
|
||||||
}
|
}
|
||||||
relation_steps_.insert(
|
ctx_.relation_steps_.insert(
|
||||||
relation_steps_.end(),
|
ctx_.relation_steps_.end(),
|
||||||
std::make_move_iterator(insert_relation_steps.begin()),
|
std::make_move_iterator(insert_relation_steps.begin()),
|
||||||
std::make_move_iterator(insert_relation_steps.end()));
|
std::make_move_iterator(insert_relation_steps.end()));
|
||||||
processing_many_to_many_relations_.erase(id);
|
ctx_.processing_many_to_many_relations_.erase(key);
|
||||||
}
|
|
||||||
template<class Pointer>
|
|
||||||
void on_foreign_object(Pointer &obj, const utils::foreign_attributes &attr) {
|
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.is_persistent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using dep_t = std::remove_reference_t<decltype(*obj)>;
|
|
||||||
const auto result = build_insert_steps<dep_t>(obj, steps_);
|
|
||||||
if (!result) {
|
|
||||||
throw query_builder_exception(error_code::InvalidObject, "Invalid object");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const basic_schema &schema_;
|
insert_context& ctx_;
|
||||||
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
|
||||||
|
|
||||||
object::object_ptr<ObjectType> ptr_;
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::unique_ptr<insert_step>> steps_;
|
template<class ObjectType>
|
||||||
std::vector<std::unique_ptr<insert_step>> relation_steps_;
|
class insert_query_builder {
|
||||||
std::unordered_set<std::pair<std::type_index, const void *>, visit_key_hash> visited_;
|
public:
|
||||||
std::unordered_set<std::string> processing_many_to_many_relations_;
|
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
||||||
|
: context_{schema, contexts_by_type}
|
||||||
|
{}
|
||||||
|
|
||||||
|
utils::result<std::vector<std::unique_ptr<insert_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
||||||
|
if (const auto it = context_.schema_.find(typeid(ObjectType)); it == context_.schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_context ctx{context_.schema_, context_.contexts_by_type_};
|
||||||
|
insert_step_processor<ObjectType> processor{ctx};
|
||||||
|
|
||||||
|
const auto result = processor.build(ptr);
|
||||||
|
if (!result) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok(std::move(ctx.steps_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
insert_context context_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
Loading…
Reference in New Issue