session and insert query builder progress
This commit is contained in:
parent
0a0e3752ea
commit
d734d647ed
|
|
@ -42,16 +42,16 @@ public:
|
||||||
void reset() { proxy_.reset(); }
|
void reset() { proxy_.reset(); }
|
||||||
|
|
||||||
operator bool() const { return valid(); }
|
operator bool() const { return valid(); }
|
||||||
[[nodiscard]] bool valid() const { return proxy_ != nullptr; }
|
[[nodiscard]] bool valid() const { return proxy_ != nullptr && !proxy_->empty(); }
|
||||||
|
|
||||||
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); }
|
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); }
|
||||||
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); }
|
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); }
|
||||||
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); }
|
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); }
|
||||||
|
|
||||||
[[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); }
|
[[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); }
|
||||||
[[nodiscard]] bool is_transient() const { return !proxy_->is_transient(); }
|
[[nodiscard]] bool is_transient() const { return proxy_->is_transient(); }
|
||||||
[[nodiscard]] bool is_detached() const { return !proxy_->is_detached(); }
|
[[nodiscard]] bool is_detached() const { return proxy_->is_detached(); }
|
||||||
[[nodiscard]] bool is_removed() const { return !proxy_->is_removed(); }
|
[[nodiscard]] bool is_removed() const { return proxy_->is_removed(); }
|
||||||
|
|
||||||
void change_state(object_state s) {
|
void change_state(object_state s) {
|
||||||
if (proxy_) {
|
if (proxy_) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ enum class error_code : uint8_t {
|
||||||
NoPrimaryKey,
|
NoPrimaryKey,
|
||||||
FailedToBuildQuery,
|
FailedToBuildQuery,
|
||||||
FailedToFindObject,
|
FailedToFindObject,
|
||||||
|
FailedToInsertObject,
|
||||||
Failed
|
Failed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "matador/sql/connection.hpp"
|
#include "matador/sql/connection.hpp"
|
||||||
#include "matador/sql/connection_pool.hpp"
|
#include "matador/sql/connection_pool.hpp"
|
||||||
#include "matador/sql/executor.hpp"
|
#include "matador/sql/executor.hpp"
|
||||||
|
#include "matador/sql/record.hpp"
|
||||||
#include "matador/sql/resolver_service.hpp"
|
#include "matador/sql/resolver_service.hpp"
|
||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
#include "matador/sql/statement_cache.hpp"
|
#include "matador/sql/statement_cache.hpp"
|
||||||
|
|
@ -105,40 +106,35 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build dependency-ordered insert steps (deps first, root last)
|
// Build dependency-ordered insert steps (deps first, root last)
|
||||||
query::insert_query_builder iqb(schema_, contexts_by_type_);
|
query::insert_query_builder<Type> iqb(schema_, contexts_by_type_);
|
||||||
auto steps = iqb.build(obj);
|
auto steps = iqb.build(obj);
|
||||||
if (!steps.is_ok()) {
|
if (!steps.is_ok()) {
|
||||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build insert dependency queries."));
|
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
|
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
|
||||||
for (auto &step : *steps) {
|
for (query::insert_step &step : *steps) {
|
||||||
// if (step.pk_is_unset && step.set_pk) {
|
auto stmt = cache_.acquire(step.ctx);
|
||||||
// if (step.pk_generator == utils::generator_type::Manual) {
|
if (!stmt.is_ok()) {
|
||||||
// if (step.pk_is_unset()) {
|
return utils::failure(stmt.err());
|
||||||
// return utils::failure(make_error(error_code::NoPrimaryKey, "Manual primary key is required but unset."));
|
}
|
||||||
// }
|
if (step.pk_generator == utils::generator_type::Identity) {
|
||||||
// } else if (step.pk_generator == utils::generator_type::Sequence) {
|
// insert and read RETURNING
|
||||||
// if (step.pk_is_unset()) {
|
auto record = stmt->fetch_one();
|
||||||
// // hard-coded naming as you specified earlier
|
if (!record.is_ok()) {
|
||||||
// // <table_name>_seq (table name known in schema meta; if you prefer, store it in step too)
|
return utils::failure(record.err());
|
||||||
// // 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";
|
if (!record.value().has_value()) {
|
||||||
//
|
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to insert object and retrieve identity."));
|
||||||
// auto id_res = query::select().nextval(seq_name).fetch_value<std::uint64_t>(*this);
|
}
|
||||||
// if (!id_res.is_ok() || !id_res.value().has_value()) {
|
step.apply_returning(*record.value());
|
||||||
// return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to obtain next sequence value."));
|
} else if (step.pk_generator == utils::generator_type::Sequence || step.pk_generator == utils::generator_type::Table) {
|
||||||
// }
|
auto result = it->second.pk_generator().next_id(*this);
|
||||||
// step.set_pk(*id_res.value());
|
if (!result.is_ok()) {
|
||||||
// }
|
return utils::failure(result.err());
|
||||||
// } else if (step.pk_generator == utils::generator_type::Table) {
|
}
|
||||||
// if (step.pk_is_unset()) {
|
step.apply_primary_key(utils::identifier{*result});
|
||||||
// // 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_);
|
auto result = step.acquire_and_bind(cache_);
|
||||||
if (!result.is_ok()) {
|
if (!result.is_ok()) {
|
||||||
|
|
@ -148,28 +144,6 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
|
||||||
if (const auto exec_result = result->execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = result->execute(); !exec_result.is_ok()) {
|
||||||
return utils::failure(exec_result.err());
|
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);
|
obj.change_state(object::object_state::Persistent);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
#include <utility>
|
#include "matador/object/collection.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"
|
||||||
|
|
@ -62,6 +62,7 @@ struct insert_step {
|
||||||
|
|
||||||
// Identity post-insert
|
// Identity post-insert
|
||||||
std::function<void(const sql::record &)> apply_returning{};
|
std::function<void(const sql::record &)> apply_returning{};
|
||||||
|
std::function<void(const utils::identifier &)> apply_primary_key{};
|
||||||
std::function<void(sql::statement &)> bind_object{};
|
std::function<void(sql::statement &)> bind_object{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -110,21 +111,24 @@ public:
|
||||||
on_foreign_object(obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/, CollectionType &objects, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
void on_has_many(const char * /*id*/, object::collection<object::object_ptr<CollectionType>> &objects, const char *join_column, const utils::foreign_attributes &attr) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
has_many_linker<typename CollectionType::value_type> linker(ptr_, join_column);
|
has_many_linker<ObjectType> linker(ptr_, join_column);
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
obj.is_persistent() ? build_for(obj) : on_foreign_object(obj, attr);
|
if (obj.is_transient()) {
|
||||||
|
build_for(obj);
|
||||||
|
}
|
||||||
|
// obj.is_persistent() ? build_for(obj) : on_foreign_object(obj, attr);
|
||||||
|
|
||||||
access::process(linker, obj);
|
access::process(linker, *obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
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) {}
|
static void on_has_many(const char * /*id*/, object::collection<CollectionType> &/*con*/, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class Collection>
|
template<class Collection>
|
||||||
void on_has_many_to_many(const char *id, Collection &container, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes & ) {
|
void on_has_many_to_many(const char *id, Collection &container, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes & ) {
|
||||||
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
|
|
@ -230,8 +234,10 @@ private:
|
||||||
id.assign(f.value());
|
id.assign(f.value());
|
||||||
step.pk_accessor.set(*ptr, id);
|
step.pk_accessor.set(*ptr, id);
|
||||||
};
|
};
|
||||||
} else {
|
} else if (info.has_primary_key() && step.pk_generator == utils::generator_type::Sequence || step.pk_generator == utils::generator_type::Table) {
|
||||||
// step.query = executable_query{insert().into(it->second.table()).values(*ptr)};
|
step.apply_primary_key = [ptr, &step](const utils::identifier &id) {
|
||||||
|
step.pk_accessor.set(*ptr, id);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
steps_.push_back(std::move(step));
|
steps_.push_back(std::move(step));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,16 +198,16 @@ private:
|
||||||
template <typename Target, typename Source>
|
template <typename Target, typename Source>
|
||||||
bool in_range(Source value) {
|
bool in_range(Source value) {
|
||||||
if constexpr (std::is_signed_v<Source> == std::is_signed_v<Target>) {
|
if constexpr (std::is_signed_v<Source> == std::is_signed_v<Target>) {
|
||||||
return value >= static_cast<Target>(std::numeric_limits<Source>::min()) &&
|
return value >= static_cast<Target>((std::numeric_limits<Source>::min)()) &&
|
||||||
value <= static_cast<Target>(std::numeric_limits<Source>::max());
|
value <= static_cast<Target>((std::numeric_limits<Source>::max)());
|
||||||
} else if constexpr (std::is_signed_v<Source> && !std::is_signed_v<Target>) {
|
} else if constexpr (std::is_signed_v<Source> && !std::is_signed_v<Target>) {
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
using UnsignedSource = std::make_unsigned_t<Source>;
|
using UnsignedSource = std::make_unsigned_t<Source>;
|
||||||
return static_cast<UnsignedSource>(value) <= std::numeric_limits<Target>::max();
|
return static_cast<UnsignedSource>(value) <= (std::numeric_limits<Target>::max)();
|
||||||
} else {
|
} else {
|
||||||
return value <= static_cast<std::make_unsigned_t<Target>>(std::numeric_limits<Target>::max());
|
return value <= static_cast<std::make_unsigned_t<Target>>((std::numeric_limits<Target>::max)());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ std::string orm_category_impl::message(const int ev) const {
|
||||||
return "Failed to build query";
|
return "Failed to build query";
|
||||||
case error_code::FailedToFindObject:
|
case error_code::FailedToFindObject:
|
||||||
return "Failed to find object";
|
return "Failed to find object";
|
||||||
|
case error_code::FailedToInsertObject:
|
||||||
|
return "Failed to insert object";
|
||||||
case error_code::Failed:
|
case error_code::Failed:
|
||||||
return "Failed";
|
return "Failed";
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
#include "matador/sql/connection.hpp"
|
#include "matador/sql/connection.hpp"
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
|
#include "matador/query/query.hpp"
|
||||||
#include "matador/query/schema.hpp"
|
#include "matador/query/schema.hpp"
|
||||||
|
|
||||||
#include "matador/query/insert_query_builder.hpp"
|
#include "matador/query/insert_query_builder.hpp"
|
||||||
|
|
@ -22,6 +24,46 @@ using namespace matador::sql;
|
||||||
using namespace matador::query;
|
using namespace matador::query;
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
|
std::unordered_map<std::type_index, query_contexts> to_contexts_by_name(const schema& scm, const dialect& d) {
|
||||||
|
std::unordered_map<std::type_index, query_contexts> contexts_by_type;
|
||||||
|
for (const auto &[type, node] : scm) {
|
||||||
|
query_contexts queries;
|
||||||
|
|
||||||
|
// SELECT all
|
||||||
|
queries.select_all = select(node.table())
|
||||||
|
.from(node.name())
|
||||||
|
.compile(d);
|
||||||
|
if (node.table().has_primary_key()) {
|
||||||
|
// SELECT one
|
||||||
|
queries.select_one = select(node.table())
|
||||||
|
.from(node.name())
|
||||||
|
.where(*node.table().primary_key_column().value() == _)
|
||||||
|
.compile(d);
|
||||||
|
// UPDATE one
|
||||||
|
auto update_set = update(node.table());
|
||||||
|
for (const auto &col: node.table().columns()) {
|
||||||
|
update_set.set(col, _);
|
||||||
|
}
|
||||||
|
queries.update_one = update_set.where(*node.table().primary_key_column().value() == _)
|
||||||
|
.compile(d);
|
||||||
|
// DELETE one
|
||||||
|
queries.delete_one = remove()
|
||||||
|
.from(node.name())
|
||||||
|
.where(*node.table().primary_key_column().value() == _)
|
||||||
|
.compile(d);
|
||||||
|
}
|
||||||
|
// INSERT one
|
||||||
|
queries.insert = insert()
|
||||||
|
.into(node.name(), node.table())
|
||||||
|
.values(generator::placeholders(node.table().columns().size()))
|
||||||
|
.compile(d);
|
||||||
|
|
||||||
|
contexts_by_type[node.node().type_index()] = queries;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contexts_by_type;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
||||||
using namespace matador::test;
|
using namespace matador::test;
|
||||||
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
||||||
|
|
@ -32,7 +74,8 @@ TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
||||||
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
|
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
|
|
||||||
insert_query_builder iqb(scm);
|
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect());
|
||||||
|
insert_query_builder<airplane> iqb(scm, contexts_by_type);
|
||||||
|
|
||||||
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
|
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
|
||||||
auto build_result = iqb.build(a380);
|
auto build_result = iqb.build(a380);
|
||||||
|
|
@ -40,12 +83,6 @@ TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
||||||
|
|
||||||
const auto& stmts = *build_result;
|
const auto& stmts = *build_result;
|
||||||
REQUIRE(stmts.size() == 1);
|
REQUIRE(stmts.size() == 1);
|
||||||
|
|
||||||
const auto step = stmts.front();
|
|
||||||
|
|
||||||
REQUIRE(std::holds_alternative<executable_query>(step.query));
|
|
||||||
const auto sql = std::get<executable_query>(step.query).str(db);
|
|
||||||
REQUIRE(sql == R"(INSERT INTO "airplanes" ("id", "brand", "model") VALUES (1, 'Boeing', 'A380'))");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_many]") {
|
TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_many]") {
|
||||||
|
|
@ -58,19 +95,21 @@ TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_man
|
||||||
.and_then( [&scm] { return scm.attach<author>("authors"); } );
|
.and_then( [&scm] { return scm.attach<author>("authors"); } );
|
||||||
REQUIRE(result.is_ok());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
||||||
//
|
|
||||||
// s_king->books.push_back(make_object<book>(2, "Carrie", object_ptr<author>{}, 1974));
|
s_king->books.push_back(make_object<book>(2, "Carrie", object_ptr<author>{}, 1974));
|
||||||
// s_king->books.push_back(make_object<book>(3, "The Shining", object_ptr<author>{}, 1977));
|
s_king->books.push_back(make_object<book>(3, "The Shining", object_ptr<author>{}, 1977));
|
||||||
// s_king->books.push_back(make_object<book>(4, "It", object_ptr<author>{}, 1986));
|
s_king->books.push_back(make_object<book>(4, "It", object_ptr<author>{}, 1986));
|
||||||
// s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
|
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
|
||||||
// s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
|
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
|
||||||
//
|
|
||||||
// insert_query_builder iqb(scm);
|
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect());
|
||||||
//
|
insert_query_builder<author> iqb(scm, contexts_by_type);
|
||||||
// auto build_result = iqb.build(s_king);
|
|
||||||
// REQUIRE(build_result.is_ok());
|
auto build_result = iqb.build(s_king);
|
||||||
//
|
REQUIRE(build_result.is_ok());
|
||||||
// const auto& stmts = *build_result;
|
|
||||||
// REQUIRE(stmts.size() == 1);
|
const auto& stmts = *build_result;
|
||||||
|
REQUIRE_FALSE(stmts.empty());
|
||||||
|
REQUIRE(stmts.size() == 6);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue