insert_query_builder progress

This commit is contained in:
2026-02-18 15:32:27 +01:00
parent 0876535b44
commit e5072741f0
5 changed files with 142 additions and 119 deletions
+58 -79
View File
@@ -97,52 +97,6 @@ private:
const std::unordered_map<std::string, sql::statement> &statements_per_column_;
};
/*
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
const auto it = schema_.find(typeid(Type));
if (it == schema_.end()) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
// Build dependency-ordered insert steps (deps first, root last)
query::insert_query_builder iqb(schema_);
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.has_returning) {
const auto exec_res = step.query.execute(*this);
if (!exec_res.is_ok()) {
return utils::failure(exec_res.err());
}
continue;
}
auto stmt_res = step.query.prepare(*this);
if (!stmt_res.is_ok()) {
return utils::failure(stmt_res.err());
}
// RETURNING produces a result set; fetch the first row (single-row insert)
auto rec_res = stmt_res->fetch_one(); // const overload => std::optional<sql::record>
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());
}
}
return utils::ok(obj);
} */
class session final : public sql::executor {
public:
session(session_context &&ctx, const query::schema &scm);
@@ -202,6 +156,10 @@ private:
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
if (obj.is_persistent()) {
return utils::ok(obj);
}
const auto it = schema_.find(typeid(Type));
if (it == schema_.end()) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
@@ -216,57 +174,78 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
for (auto &step : *steps) {
if (!step.has_returning) {
const auto exec_res = step.query.execute(*this);
if (step.pk_is_unset && step.set_pk) {
if (step.pk_generator == utils::generator_type::Manually) {
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::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;
}
auto stmt_res = step.query.prepare(*this);
if (!stmt_res.is_ok()) {
return utils::failure(stmt_res.err());
}
// RETURNING produces a result set; fetch the first row (single-row insert)
auto rec_res = stmt_res->fetch_one(); // const overload => std::optional<sql::record>
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);
const auto it = schema_.find(typeid(Type));
if (it == schema_.end()) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
auto res = query::query::insert()
.into(it->second.name(), query::generator::columns<Type>(schema_))
.values(query::generator::placeholders<Type>())
.prepare(*this);
if (!res) {
return utils::failure(res.err());
}
if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) {
return utils::failure(insert_result.err());
}
return utils::ok(obj);
//
//
//
// const auto it = schema_.find(typeid(Type));
// if (it == schema_.end()) {
// return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
// }
//
// auto res = query::query::insert()
// .into(it->second.name(), query::generator::columns<Type>(schema_))
// .values(query::generator::placeholders<Type>())
// .prepare(*this);
// if (!res) {
// return utils::failure(res.err());
// }
//
// if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) {
// return utils::failure(insert_result.err());
// }
// return utils::ok(obj);
}
class pk_object_binder final {