insert_query_builder progress
This commit is contained in:
parent
0876535b44
commit
e5072741f0
|
|
@ -33,7 +33,8 @@ public:
|
||||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
||||||
: obj_(obj)
|
: obj_(obj)
|
||||||
, resolver_(resolver)
|
, resolver_(resolver)
|
||||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {
|
, pk_(primary_key_resolver::resolve_object(*obj).pk)
|
||||||
|
, state_(object_state::Persistent){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transient
|
// Transient
|
||||||
|
|
@ -60,8 +61,9 @@ public:
|
||||||
bool is_transient() const { return state_ == object_state::Transient; }
|
bool is_transient() const { return state_ == object_state::Transient; }
|
||||||
bool is_detached() const { return state_ == object_state::Detached; }
|
bool is_detached() const { return state_ == object_state::Detached; }
|
||||||
bool is_removed() const { return state_ == object_state::Removed; }
|
bool is_removed() const { return state_ == object_state::Removed; }
|
||||||
|
|
||||||
void change_state(const object_state state) {
|
void change_state(const object_state state) {
|
||||||
state_ = state;
|
state_.store(state, std::memory_order_release);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Type* resolve() const {
|
Type* resolve() const {
|
||||||
|
|
@ -86,7 +88,7 @@ private:
|
||||||
std::shared_ptr<Type> obj_{};
|
std::shared_ptr<Type> obj_{};
|
||||||
std::weak_ptr<object_resolver<Type>> resolver_{};
|
std::weak_ptr<object_resolver<Type>> resolver_{};
|
||||||
utils::identifier pk_{};
|
utils::identifier pk_{};
|
||||||
object_state state_{object_state::Transient};
|
std::atomic<object_state> state_{object_state::Transient};
|
||||||
mutable std::mutex mutex_{};
|
mutable std::mutex mutex_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ public:
|
||||||
|
|
||||||
void reset() { proxy_.reset(); }
|
void reset() { proxy_.reset(); }
|
||||||
|
|
||||||
operator bool() { return valid(); }
|
operator bool() const { return valid(); }
|
||||||
bool valid() { return proxy_ != nullptr; }
|
[[nodiscard]] bool valid() const { return proxy_ != nullptr; }
|
||||||
|
|
||||||
[[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(); }
|
||||||
|
|
@ -53,6 +53,11 @@ public:
|
||||||
[[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) {
|
||||||
|
if (proxy_) {
|
||||||
|
proxy_->change_state(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<object_proxy<Type> > proxy_{};
|
std::shared_ptr<object_proxy<Type> > proxy_{};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -97,52 +97,6 @@ private:
|
||||||
const std::unordered_map<std::string, sql::statement> &statements_per_column_;
|
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 {
|
class session final : public sql::executor {
|
||||||
public:
|
public:
|
||||||
session(session_context &&ctx, const query::schema &scm);
|
session(session_context &&ctx, const query::schema &scm);
|
||||||
|
|
@ -202,6 +156,10 @@ private:
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
|
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));
|
const auto it = schema_.find(typeid(Type));
|
||||||
if (it == schema_.end()) {
|
if (it == schema_.end()) {
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
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
|
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
|
||||||
for (auto &step : *steps) {
|
for (auto &step : *steps) {
|
||||||
if (!step.has_returning) {
|
if (step.pk_is_unset && step.set_pk) {
|
||||||
const auto exec_res = step.query.execute(*this);
|
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()) {
|
if (!exec_res.is_ok()) {
|
||||||
return utils::failure(exec_res.err());
|
return utils::failure(exec_res.err());
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stmt_res = step.query.prepare(*this);
|
const auto &q = std::get<query::fetchable_query>(step.query);
|
||||||
if (!stmt_res.is_ok()) {
|
auto rec_res = q.fetch_one(*this);
|
||||||
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()) {
|
if (!rec_res.is_ok()) {
|
||||||
return utils::failure(rec_res.err());
|
return utils::failure(rec_res.err());
|
||||||
}
|
}
|
||||||
if (!rec_res.value().has_value()) {
|
if (!rec_res.value().has_value()) {
|
||||||
return utils::failure(make_error(error_code::FailedToFindObject, "INSERT ... RETURNING did not return a row."));
|
return utils::failure(make_error(error_code::FailedToFindObject, "INSERT ... RETURNING did not return a row."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step.apply_returning) {
|
if (step.apply_returning) {
|
||||||
step.apply_returning(*rec_res.value());
|
step.apply_returning(*rec_res.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj.change_state(object::object_state::Persistent);
|
||||||
return utils::ok(obj);
|
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."));
|
||||||
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>())
|
||||||
auto res = query::query::insert()
|
// .prepare(*this);
|
||||||
.into(it->second.name(), query::generator::columns<Type>(schema_))
|
// if (!res) {
|
||||||
.values(query::generator::placeholders<Type>())
|
// return utils::failure(res.err());
|
||||||
.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());
|
||||||
|
// }
|
||||||
if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) {
|
// return utils::ok(obj);
|
||||||
return utils::failure(insert_result.err());
|
|
||||||
}
|
|
||||||
return utils::ok(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class pk_object_binder final {
|
class pk_object_binder final {
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,47 @@ struct pk_setter {
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pk_unset_checker {
|
||||||
|
const std::string &name;
|
||||||
|
bool unset{true};
|
||||||
|
|
||||||
|
template<class V>
|
||||||
|
void on_primary_key(const char *id, V &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
||||||
|
if (id != nullptr && name == id) {
|
||||||
|
// Your convention: 0 means unset for integer PKs
|
||||||
|
unset = (static_cast<std::uint64_t>(pk) == 0ULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
||||||
|
template<typename T>
|
||||||
|
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {}
|
||||||
|
template<class P>
|
||||||
|
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
||||||
|
template<class P>
|
||||||
|
static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
||||||
|
};
|
||||||
|
|
||||||
class insert_query_builder {
|
class insert_query_builder {
|
||||||
public:
|
public:
|
||||||
|
using step_query_t = std::variant<executable_query, fetchable_query>;
|
||||||
|
|
||||||
struct insert_step {
|
struct insert_step {
|
||||||
executable_query query;
|
step_query_t query;
|
||||||
bool has_returning{false};
|
|
||||||
|
// Session uses these to handle manual/sequence/table pre-insert PKs
|
||||||
|
utils::generator_type pk_generator{utils::generator_type::Manually};
|
||||||
|
std::string pk_name;
|
||||||
|
|
||||||
|
std::function<bool()> pk_is_unset{};
|
||||||
|
std::function<void(std::uint64_t)> set_pk{};
|
||||||
|
|
||||||
|
// Identity post-insert
|
||||||
std::function<void(const sql::record &)> apply_returning{};
|
std::function<void(const sql::record &)> apply_returning{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -68,13 +104,19 @@ public:
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
|
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_object(id, obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
|
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_object(id, obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
||||||
|
template<class C>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<class EntityType>
|
template<class EntityType>
|
||||||
|
|
@ -112,35 +154,36 @@ private:
|
||||||
access::process(*this, *ptr);
|
access::process(*this, *ptr);
|
||||||
|
|
||||||
// 2) Build INSERT for this object
|
// 2) Build INSERT for this object
|
||||||
// For Identity PK strategy: append RETURNING(pk_column)
|
|
||||||
const auto &info = it->second.node().info();
|
const auto &info = it->second.node().info();
|
||||||
if (info.has_primary_key() && it->second.generator_type() == utils::generator_type::Identity) {
|
insert_step step{};
|
||||||
const std::string pk_name = info.primary_key_attribute()->name();
|
if (info.has_primary_key()) {
|
||||||
const table_column pk_col(&it->second.table(), pk_name);
|
step.pk_name = info.primary_key_attribute()->name();
|
||||||
insert_step step {
|
step.pk_generator = it->second.generator_type();
|
||||||
query::query::insert()
|
|
||||||
.into(it->second.table())
|
step.pk_is_unset = [ptr, name = step.pk_name]() {
|
||||||
.values(*ptr)
|
pk_unset_checker chk{name};
|
||||||
.returning(pk_col),
|
access::process(chk, *ptr);
|
||||||
true,
|
return chk.unset;
|
||||||
[ptr, pk_name](const sql::record &rec) {
|
};
|
||||||
// record.at<T>(name) returns optional<T>
|
|
||||||
// We assume integer-like PKs for now (as in your examples).
|
step.set_pk = [ptr, name = step.pk_name](std::uint64_t v) {
|
||||||
if (auto v = rec.at<std::uint64_t>(pk_name); v.has_value()) {
|
pk_setter setter{name, v};
|
||||||
pk_setter setter{pk_name, *v};
|
access::process(setter, *ptr);
|
||||||
access::process(setter, *ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
steps_.push_back(std::move(step));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_step step{
|
if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) {
|
||||||
query::query::insert().into(it->second.table()).values(*ptr),
|
const table_column pk_col(&it->second.table(), step.pk_name);
|
||||||
false,
|
step.query = fetchable_query{query::query::insert().into(it->second.table()).values(*ptr).returning(pk_col)};
|
||||||
{}
|
step.apply_returning = [ptr, pk_name = step.pk_name](const sql::record &rec) {
|
||||||
};
|
if (auto v = rec.at<std::uint64_t>(pk_name); v.has_value()) {
|
||||||
|
pk_setter setter{pk_name, *v};
|
||||||
|
access::process(setter, *ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
step.query = executable_query{query::query::insert().into(it->second.table()).values(*ptr)};
|
||||||
|
}
|
||||||
steps_.push_back(std::move(step));
|
steps_.push_back(std::move(step));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ utils::result<void, utils::error> schema::create(const sql::connection &conn) co
|
||||||
.columns(node.info().attributes())
|
.columns(node.info().attributes())
|
||||||
.compile(conn);
|
.compile(conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +38,6 @@ utils::result<void, utils::error> schema::create(const sql::connection &conn) co
|
||||||
}
|
}
|
||||||
auto ctx = build_add_constraint_context(node, cons, conn);
|
auto ctx = build_add_constraint_context(node, cons, conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +51,6 @@ utils::result<void, utils::error> schema::create(const sql::connection &conn) co
|
||||||
}
|
}
|
||||||
auto ctx = build_add_constraint_context(node, cons, conn);
|
auto ctx = build_add_constraint_context(node, cons, conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +71,6 @@ utils::result<void, utils::error> schema::drop(const sql::connection &conn) cons
|
||||||
.drop_constraint(cons)
|
.drop_constraint(cons)
|
||||||
.compile(conn);
|
.compile(conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +88,6 @@ utils::result<void, utils::error> schema::drop(const sql::connection &conn) cons
|
||||||
.drop_constraint(cons)
|
.drop_constraint(cons)
|
||||||
.compile(conn);
|
.compile(conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +100,6 @@ utils::result<void, utils::error> schema::drop(const sql::connection &conn) cons
|
||||||
.table(node.name())
|
.table(node.name())
|
||||||
.compile(conn);
|
.compile(conn);
|
||||||
|
|
||||||
std::cout << ctx.sql << std::endl;
|
|
||||||
if (auto result = conn.execute(ctx); !result) {
|
if (auto result = conn.execute(ctx); !result) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue