insert_query_builder progress
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "matador/query/criteria.hpp"
|
||||
#include "matador/query/insert_query_builder.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/query_contexts.hpp"
|
||||
#include "matador/query/generator.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
|
||||
@@ -89,6 +90,7 @@ private:
|
||||
const query::basic_schema &schema_;
|
||||
mutable std::unordered_map<std::string, std::vector<object::attribute> > prototypes_;
|
||||
std::shared_ptr<sql::resolver_service> resolver_service_;
|
||||
std::unordered_map<std::type_index, query::query_contexts> contexts_by_type_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
@@ -103,63 +105,72 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
|
||||
}
|
||||
|
||||
// Build dependency-ordered insert steps (deps first, root last)
|
||||
query::insert_query_builder iqb(schema_);
|
||||
query::insert_query_builder iqb(schema_, contexts_by_type_);
|
||||
auto steps = iqb.build(obj);
|
||||
if (!steps.is_ok()) {
|
||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build insert dependency queries."));
|
||||
}
|
||||
|
||||
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
|
||||
// for (auto &step : *steps) {
|
||||
// if (step.pk_is_unset && step.set_pk) {
|
||||
// if (step.pk_generator == utils::generator_type::Manual) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// return utils::failure(make_error(error_code::NoPrimaryKey, "Manual primary key is required but unset."));
|
||||
// }
|
||||
// } else if (step.pk_generator == utils::generator_type::Sequence) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// // hard-coded naming as you specified earlier
|
||||
// // <table_name>_seq (table name known in schema meta; if you prefer, store it in step too)
|
||||
// // For now, we derive from the schema type used for the root insert: simplistic but works if table name == entity name.
|
||||
// const std::string seq_name = it->second.name() + "_seq";
|
||||
//
|
||||
// auto id_res = query::select().nextval(seq_name).fetch_value<std::uint64_t>(*this);
|
||||
// if (!id_res.is_ok() || !id_res.value().has_value()) {
|
||||
// return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to obtain next sequence value."));
|
||||
// }
|
||||
// step.set_pk(*id_res.value());
|
||||
// }
|
||||
// } else if (step.pk_generator == utils::generator_type::Table) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// // TODO: implement table id generation; same idea as above:
|
||||
// // UPDATE <table>_tbl_seq ... RETURNING ...
|
||||
// return utils::failure(make_error(error_code::Failed, "Table primary key generator not implemented yet."));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // --- Execute step ---
|
||||
// if (std::holds_alternative<query::executable_query>(step.query)) {
|
||||
// const auto &q = std::get<query::executable_query>(step.query);
|
||||
// const auto exec_res = q.execute(*this);
|
||||
// if (!exec_res.is_ok()) {
|
||||
// return utils::failure(exec_res.err());
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const auto &q = std::get<query::fetchable_query>(step.query);
|
||||
// auto rec_res = q.fetch_one(*this);
|
||||
// if (!rec_res.is_ok()) {
|
||||
// return utils::failure(rec_res.err());
|
||||
// }
|
||||
// if (!rec_res.value().has_value()) {
|
||||
// return utils::failure(make_error(error_code::FailedToFindObject, "INSERT ... RETURNING did not return a row."));
|
||||
// }
|
||||
// if (step.apply_returning) {
|
||||
// step.apply_returning(*rec_res.value());
|
||||
// }
|
||||
// }
|
||||
for (auto &step : *steps) {
|
||||
// if (step.pk_is_unset && step.set_pk) {
|
||||
// if (step.pk_generator == utils::generator_type::Manual) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// return utils::failure(make_error(error_code::NoPrimaryKey, "Manual primary key is required but unset."));
|
||||
// }
|
||||
// } else if (step.pk_generator == utils::generator_type::Sequence) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// // hard-coded naming as you specified earlier
|
||||
// // <table_name>_seq (table name known in schema meta; if you prefer, store it in step too)
|
||||
// // For now, we derive from the schema type used for the root insert: simplistic but works if table name == entity name.
|
||||
// const std::string seq_name = it->second.name() + "_seq";
|
||||
//
|
||||
// auto id_res = query::select().nextval(seq_name).fetch_value<std::uint64_t>(*this);
|
||||
// if (!id_res.is_ok() || !id_res.value().has_value()) {
|
||||
// return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to obtain next sequence value."));
|
||||
// }
|
||||
// step.set_pk(*id_res.value());
|
||||
// }
|
||||
// } else if (step.pk_generator == utils::generator_type::Table) {
|
||||
// if (step.pk_is_unset()) {
|
||||
// // TODO: implement table id generation; same idea as above:
|
||||
// // UPDATE <table>_tbl_seq ... RETURNING ...
|
||||
// return utils::failure(make_error(error_code::Failed, "Table primary key generator not implemented yet."));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
auto result = step.acquire_and_bind(cache_);
|
||||
if (!result.is_ok()) {
|
||||
return utils::failure(result.err());
|
||||
}
|
||||
|
||||
if (const auto exec_result = result->execute(); !exec_result.is_ok()) {
|
||||
return utils::failure(exec_result.err());
|
||||
}
|
||||
|
||||
// --- Execute step ---
|
||||
// if (std::holds_alternative<query::executable_query>(step.query)) {
|
||||
// const auto &q = std::get<query::executable_query>(step.query);
|
||||
// const auto exec_res = q.execute(*this);
|
||||
// if (!exec_res.is_ok()) {
|
||||
// return utils::failure(exec_res.err());
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const auto &q = std::get<query::fetchable_query>(step.query);
|
||||
// auto rec_res = q.fetch_one(*this);
|
||||
// if (!rec_res.is_ok()) {
|
||||
// return utils::failure(rec_res.err());
|
||||
// }
|
||||
// if (!rec_res.value().has_value()) {
|
||||
// return utils::failure(make_error(error_code::FailedToFindObject, "INSERT ... RETURNING did not return a row."));
|
||||
// }
|
||||
// if (step.apply_returning) {
|
||||
// step.apply_returning(*rec_res.value());
|
||||
// }
|
||||
}
|
||||
|
||||
obj.change_state(object::object_state::Persistent);
|
||||
return utils::ok(obj);
|
||||
|
||||
@@ -280,6 +280,8 @@ std::vector<utils::placeholder> placeholders(const Type &obj) {
|
||||
return generator.generate(obj);
|
||||
}
|
||||
|
||||
std::vector<utils::placeholder> placeholders(size_t num);
|
||||
|
||||
template<typename Type>
|
||||
std::vector<internal::column_value_pair> column_value_pairs(const Type &obj, const column_value_generator_options options = column_value_generator_options::None) {
|
||||
column_value_generator generator(options);
|
||||
|
||||
@@ -1,20 +1,65 @@
|
||||
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "matador/query/basic_schema.hpp"
|
||||
#include "matador/query/intermediates/executable_query.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/query_contexts.hpp"
|
||||
#include "matador/query/query_builder_exception.hpp"
|
||||
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/utils/primary_key_accessor.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class statement_cache;
|
||||
}
|
||||
|
||||
namespace matador::query {
|
||||
template < class ObjectType >
|
||||
class has_many_linker {
|
||||
public:
|
||||
has_many_linker(const object::object_ptr<ObjectType> &ptr, std::string join_column)
|
||||
: ptr_(ptr), join_column_(std::move(join_column)) {}
|
||||
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>
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_belongs_to(const char *id, object::object_ptr<ObjectType> &obj, const utils::foreign_attributes &/*attr*/) {
|
||||
if (id != join_column_) {
|
||||
return;
|
||||
}
|
||||
|
||||
obj = ptr_;
|
||||
}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class CollectionType>
|
||||
static void on_has_many(const char * /*id*/, CollectionType &/*con*/, const char *, const utils::foreign_attributes &/*attr*/, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {}
|
||||
template<class Collection>
|
||||
static void on_has_many_to_many(const char * /*id*/, Collection &/*container*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
||||
template<class Collection>
|
||||
static void on_has_many_to_many(const char * /*id*/, Collection &/*container*/, const utils::foreign_attributes & ) {}
|
||||
|
||||
private:
|
||||
object::object_ptr<ObjectType> ptr_;
|
||||
std::string join_column_;
|
||||
};
|
||||
|
||||
|
||||
class insert_query_builder {
|
||||
public:
|
||||
using step_query_t = std::variant<executable_query, fetchable_query>;
|
||||
|
||||
struct insert_step {
|
||||
step_query_t query;
|
||||
sql::query_context ctx;
|
||||
|
||||
utils::result<sql::statement, utils::error> acquire_and_bind(sql::statement_cache &cache) const;
|
||||
|
||||
// Session uses these to handle manual/sequence/table pre-insert PKs
|
||||
utils::generator_type pk_generator{utils::generator_type::Manual};
|
||||
@@ -22,10 +67,11 @@ public:
|
||||
|
||||
// Identity post-insert
|
||||
std::function<void(const sql::record &)> apply_returning{};
|
||||
std::function<void(sql::statement &)> bind_object{};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit insert_query_builder(const basic_schema &schema);
|
||||
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type);
|
||||
|
||||
template<class EntityType>
|
||||
utils::result<std::vector<insert_step>, query_build_error> build(const object::object_ptr<EntityType> &ptr) {
|
||||
@@ -47,10 +93,9 @@ public:
|
||||
return utils::ok(steps_);
|
||||
}
|
||||
|
||||
template < class V >
|
||||
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/) {}
|
||||
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*/) {}
|
||||
|
||||
@@ -165,10 +210,16 @@ private:
|
||||
step.pk_generator = it->second.pk_generator().type();
|
||||
}
|
||||
|
||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
||||
if (cit == contexts_by_type_.end()) {
|
||||
throw query_builder_exception(query_build_error::UnknownType);
|
||||
}
|
||||
step.ctx = cit->second.insert;
|
||||
step.bind_object = [ptr](sql::statement &stmt) { stmt.bind(*ptr); };
|
||||
if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) {
|
||||
const auto pk_name = info.primary_key_attribute()->name();
|
||||
const table_column pk_col(&it->second.table(), pk_name);
|
||||
step.query = fetchable_query{insert().into(it->second.table()).values(*ptr).returning(pk_col)};
|
||||
// step.query = fetchable_query{insert().into(it->second.table()).values(*ptr).returning(pk_col)};
|
||||
step.apply_returning = [ptr, &step, pk_name = pk_name](const sql::record &rec) {
|
||||
const auto& f = rec.at(pk_name);
|
||||
utils::identifier id;
|
||||
@@ -176,7 +227,7 @@ private:
|
||||
step.pk_accessor.set(*ptr, id);
|
||||
};
|
||||
} else {
|
||||
step.query = executable_query{insert().into(it->second.table()).values(*ptr)};
|
||||
// step.query = executable_query{insert().into(it->second.table()).values(*ptr)};
|
||||
}
|
||||
steps_.push_back(std::move(step));
|
||||
}
|
||||
@@ -197,6 +248,7 @@ private:
|
||||
}
|
||||
private:
|
||||
const basic_schema &schema_;
|
||||
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||
|
||||
std::vector<insert_step> steps_;
|
||||
std::vector<insert_step> relation_steps_;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../../utils/result.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class dialect;
|
||||
class executor;
|
||||
class statement;
|
||||
struct execute_result;
|
||||
@@ -21,7 +22,7 @@ public:
|
||||
|
||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::executor &exec) const;
|
||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
||||
[[nodiscard]] sql::query_context compile(const sql::executor &exec) const;
|
||||
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
||||
[[nodiscard]] std::string str(const sql::executor &exec) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef MATADOR_QUERY_CONTEXTS_HPP
|
||||
#define MATADOR_QUERY_CONTEXTS_HPP
|
||||
|
||||
#include "matador/sql/query_context.hpp"
|
||||
|
||||
namespace matador::query {
|
||||
struct query_contexts {
|
||||
sql::query_context insert;
|
||||
sql::query_context update_one;
|
||||
sql::query_context delete_one;
|
||||
sql::query_context select_one;
|
||||
sql::query_context select_all;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_QUERY_CONTEXTS_HPP
|
||||
@@ -256,8 +256,8 @@ public:
|
||||
void dump(std::ostream &os) const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||
[[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||
[[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::dialect &d) ;
|
||||
[[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::dialect &d) ;
|
||||
|
||||
iterator insert_table(const std::type_index& ti, const object::repository_node &node, utils::generator_type generator_type);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef QUERY_TABLE_HPP
|
||||
#define QUERY_TABLE_HPP
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "matador/query/table_column.hpp"
|
||||
|
||||
#include <string>
|
||||
@@ -41,11 +43,12 @@ public:
|
||||
[[nodiscard]] bool is_relation_table() const;
|
||||
[[nodiscard]] bool has_primary_key() const;
|
||||
|
||||
[[nodiscard]] std::optional<table_column*> primary_key_column() const;
|
||||
[[nodiscard]] const std::string& join_column_name() const;
|
||||
[[nodiscard]] const std::string& inverse_join_column_name() const;
|
||||
|
||||
protected:
|
||||
static const table_column& create_column(class table& tab, const std::string& name);
|
||||
// static const table_column& create_column(class table& tab, const std::string& name);
|
||||
|
||||
table(std::string name, std::string alias, const std::vector<table_column>& columns);
|
||||
|
||||
@@ -58,7 +61,7 @@ private:
|
||||
std::string schema_name_;
|
||||
std::vector<table_column> columns_;
|
||||
|
||||
std::string pk_column_name_;
|
||||
std::optional<table_column*> pk_column_{std::nullopt};
|
||||
std::string join_column_name_;
|
||||
std::string inverse_join_column_name_;
|
||||
};
|
||||
|
||||
@@ -91,6 +91,11 @@ public:
|
||||
|
||||
[[nodiscard]] bool is_function() const;
|
||||
[[nodiscard]] bool is_nullable() const;
|
||||
[[nodiscard]] bool is_primary_key() const;
|
||||
[[nodiscard]] bool is_foreign_key() const;
|
||||
[[nodiscard]] bool is_unique() const;
|
||||
[[nodiscard]] bool is_identity() const;
|
||||
|
||||
[[nodiscard]] sql::sql_function_t function() const;
|
||||
[[nodiscard]] bool has_alias() const;
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
*/
|
||||
template<class Type>
|
||||
statement &bind(const Type &obj);
|
||||
template<class Type>
|
||||
statement &bind(const object::object_ptr<Type> &ptr);
|
||||
template<typename Type>
|
||||
statement &bind(size_t pos, Type &value);
|
||||
|
||||
@@ -161,6 +163,11 @@ statement &statement::bind(const Type &obj) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
statement &statement::bind(const object::object_ptr<Type> &ptr) {
|
||||
return bind(*ptr);
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
utils::result<query_result<Type>, utils::error> statement::fetch() {
|
||||
std::cout << statement_proxy_->sql() << std::endl;
|
||||
|
||||
@@ -18,8 +18,7 @@ template < typename ValueType, typename ErrorType >
|
||||
struct is_result<result<ValueType, ErrorType>> : std::true_type {};
|
||||
|
||||
template < typename ValueType >
|
||||
class ok
|
||||
{
|
||||
class ok {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
explicit constexpr ok(const ValueType &value) : value_(value) {}
|
||||
@@ -34,16 +33,14 @@ private:
|
||||
};
|
||||
|
||||
template <>
|
||||
class ok<void>
|
||||
{
|
||||
class ok<void> {
|
||||
public:
|
||||
using value_type = void;
|
||||
explicit constexpr ok() = default;
|
||||
};
|
||||
|
||||
template < typename ErrorType >
|
||||
class failure
|
||||
{
|
||||
class failure {
|
||||
public:
|
||||
using value_type = ErrorType;
|
||||
|
||||
@@ -59,8 +56,7 @@ private:
|
||||
};
|
||||
|
||||
template < typename ValueType, typename ErrorType >
|
||||
class result
|
||||
{
|
||||
class result {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using error_type = ErrorType;
|
||||
|
||||
Reference in New Issue
Block a user