This commit is contained in:
Scott E. Graves 2024-10-23 11:13:56 -05:00
parent 18c53fad9a
commit 2a6ecf61b2
9 changed files with 233 additions and 223 deletions

View File

@ -68,19 +68,24 @@ struct db_context_t {
std::string table_name; std::string table_name;
}; };
struct db_result final {
struct context final {
db3_stmt_t stmt;
};
class db_column final { class db_column final {
public: public:
db_column(std::int32_t index, std::string name, db_types_t value) noexcept;
db_column() noexcept = default; db_column() noexcept = default;
db_column(const db_column &) = default; db_column(const db_column &) = default;
db_column(db_column &&column) noexcept = default; db_column(db_column &&column) noexcept = default;
~db_column() = default; ~db_column() = default;
auto operator=(const db_column &) -> db_column & = default; auto operator=(const db_column &) -> db_column & = default;
auto operator=(db_column &&) -> db_column & = default; auto operator=(db_column &&) -> db_column & = default;
db_column(std::int32_t index, std::string name, db_types_t value) noexcept
: index_(index), name_(std::move(name)), value_(std::move(value)) {}
private: private:
std::int32_t index_{}; std::int32_t index_{};
std::string name_; std::string name_;
@ -113,77 +118,22 @@ public:
#endif // defined(PROJECT_ENABLE_JSON) #endif // defined(PROJECT_ENABLE_JSON)
}; };
template <typename ctx_t> class db_row final { class db_row final {
public: public:
db_row(std::shared_ptr<ctx_t> ctx) { db_row(std::shared_ptr<context> ctx);
REPERTORY_USES_FUNCTION_NAME();
auto column_count = sqlite3_column_count(ctx->stmt.get());
for (std::int32_t col = 0; col < column_count; col++) {
std::string name{sqlite3_column_name(ctx->stmt.get(), col)};
auto column_type = sqlite3_column_type(ctx->stmt.get(), col);
db_types_t value;
switch (column_type) {
case SQLITE_INTEGER: {
value = sqlite3_column_int64(ctx->stmt.get(), col);
} break;
case SQLITE_TEXT: {
const auto *text = reinterpret_cast<const char *>(
sqlite3_column_text(ctx->stmt.get(), col));
value = std::string(text == nullptr ? "" : text);
} break;
default:
throw utils::error::create_exception(function_name,
{
"column type not implemented",
std::to_string(column_type),
});
}
columns_[name] = db_column{col, name, value};
}
}
private: private:
std::map<std::string, db_column> columns_; std::map<std::string, db_column> columns_;
public: public:
[[nodiscard]] auto get_columns() const -> std::vector<db_column> { [[nodiscard]] auto get_columns() const -> std::vector<db_column>;
std::vector<db_column> ret;
for (const auto &item : columns_) {
ret.push_back(item.second);
}
return ret;
}
[[nodiscard]] auto get_column(std::int32_t index) const -> db_column { [[nodiscard]] auto get_column(std::int32_t index) const -> db_column;
auto iter = std::find_if(columns_.begin(), columns_.end(),
[&index](auto &&col) -> bool {
return col.second.get_index() == index;
});
if (iter == columns_.end()) {
throw std::out_of_range("");
}
return iter->second; [[nodiscard]] auto get_column(std::string name) const -> db_column;
}
[[nodiscard]] auto get_column(std::string name) const -> db_column {
return columns_.at(name);
}
}; };
struct db_result final { db_result(db3_stmt_t stmt, std::int32_t res);
struct context final {
db3_stmt_t stmt;
};
using row = db_row<context>;
db_result(sqlite3_stmt *stmt, std::int32_t res);
db_result() = default; db_result() = default;
db_result(const db_result &) = default; db_result(const db_result &) = default;
@ -192,6 +142,8 @@ struct db_result final {
auto operator=(const db_result &) -> db_result & = default; auto operator=(const db_result &) -> db_result & = default;
auto operator=(db_result &&) -> db_result & = default; auto operator=(db_result &&) -> db_result & = default;
using row = db_row;
private: private:
std::shared_ptr<context> ctx_; std::shared_ptr<context> ctx_;
mutable std::int32_t res_{}; mutable std::int32_t res_{};

View File

@ -36,8 +36,6 @@ public:
std::map<std::string, db_types_t> values; std::map<std::string, db_types_t> values;
}; };
using row = db_row<context>;
public: public:
db_insert(sqlite3 &db3, std::string table_name) db_insert(sqlite3 &db3, std::string table_name)
: ctx_(std::make_shared<context>(&db3, table_name)) {} : ctx_(std::make_shared<context>(&db3, table_name)) {}
@ -48,15 +46,13 @@ private:
std::shared_ptr<context> ctx_; std::shared_ptr<context> ctx_;
public: public:
[[nodiscard]] auto or_replace() -> db_insert & { [[nodiscard]] auto or_replace() -> db_insert {
ctx_->or_replace = true; ctx_->or_replace = true;
return *this; return *this;
} }
[[nodiscard]] auto column_value(std::string column_name, db_types_t value) [[nodiscard]] auto column_value(std::string column_name, db_types_t value)
-> db_insert;
-> db_insert &;
[[nodiscard]] auto dump() const -> std::string; [[nodiscard]] auto dump() const -> std::string;

View File

@ -45,8 +45,6 @@ public:
[[nodiscard]] auto offset(std::int32_t value) -> db_select_op_t; [[nodiscard]] auto offset(std::int32_t value) -> db_select_op_t;
[[nodiscard]] auto order_by(std::string column_name, bool ascending) [[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_select_op_t; -> db_select_op_t;
}; };
@ -77,32 +75,26 @@ private:
std::shared_ptr<context> ctx_; std::shared_ptr<context> ctx_;
public: public:
[[nodiscard]] auto column(std::string column_name) -> db_select &; [[nodiscard]] auto column(std::string column_name) -> db_select;
[[nodiscard]] auto count(std::string column_name, std::string as_column_name) [[nodiscard]] auto count(std::string column_name, std::string as_column_name)
-> db_select;
-> db_select &;
[[nodiscard]] auto dump() const -> std::string; [[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result; [[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto group_by(std::string column_name) -> db_select &; [[nodiscard]] auto group_by(std::string column_name) -> db_select;
[[nodiscard]] auto group(context::w_t::group_func_t func) [[nodiscard]] auto group(context::w_t::group_func_t func)
-> context::w_t::wn_t; -> context::w_t::wn_t;
[[nodiscard]] auto limit(std::int32_t value) -> db_select &; [[nodiscard]] auto limit(std::int32_t value) -> db_select;
[[nodiscard]] auto offset(std::int32_t value) -> db_select &; [[nodiscard]] auto offset(std::int32_t value) -> db_select;
[[nodiscard]] auto order_by(std::string column_name, bool ascending) [[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_select;
-> db_select &;
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t; [[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
}; };

View File

@ -69,22 +69,19 @@ private:
public: public:
[[nodiscard]] auto column_value(std::string column_name, db_types_t value) [[nodiscard]] auto column_value(std::string column_name, db_types_t value)
-> db_update;
-> db_update &;
[[nodiscard]] auto dump() const -> std::string; [[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result; [[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto group(context::w_t::group_func_t func) [[nodiscard]] auto group(context::w_t::group_func_t func)
-> context::w_t::wn_t; -> context::w_t::wn_t;
[[nodiscard]] auto limit(std::int32_t value) -> db_update &; [[nodiscard]] auto limit(std::int32_t value) -> db_update;
[[nodiscard]] auto order_by(std::string column_name, bool ascending) [[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_update;
-> db_update &;
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t; [[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
}; };

View File

@ -63,8 +63,12 @@ void sqlite3_deleter::operator()(sqlite3 *db3) const {
} }
} }
db_result::db_column::db_column(std::int32_t index, std::string name,
db_types_t value) noexcept
: index_(index), name_(std::move(name)), value_(std::move(value)) {}
#if defined(PROJECT_ENABLE_JSON) #if defined(PROJECT_ENABLE_JSON)
auto db_column::get_value_as_json() const -> nlohmann::json { auto db_result::db_column::get_value_as_json() const -> nlohmann::json {
return std::visit( return std::visit(
overloaded{ overloaded{
[this](std::int64_t value) -> auto { [this](std::int64_t value) -> auto {
@ -76,13 +80,64 @@ auto db_column::get_value_as_json() const -> nlohmann::json {
} }
#endif // defined(PROJECT_ENABLE_JSON) #endif // defined(PROJECT_ENABLE_JSON)
db_result::db_result(sqlite3_stmt *stmt, std::int32_t res) db_result::db_row::db_row(std::shared_ptr<context> ctx) {
: ctx_(std::make_shared<context>()), res_(res) { REPERTORY_USES_FUNCTION_NAME();
ctx_->stmt = db3_stmt_t{
stmt,
sqlite3_statement_deleter(),
};
auto column_count = sqlite3_column_count(ctx->stmt.get());
for (std::int32_t col = 0; col < column_count; col++) {
std::string name{sqlite3_column_name(ctx->stmt.get(), col)};
auto column_type = sqlite3_column_type(ctx->stmt.get(), col);
db_types_t value;
switch (column_type) {
case SQLITE_INTEGER: {
value = sqlite3_column_int64(ctx->stmt.get(), col);
} break;
case SQLITE_TEXT: {
const auto *text = reinterpret_cast<const char *>(
sqlite3_column_text(ctx->stmt.get(), col));
value = std::string(text == nullptr ? "" : text);
} break;
default:
throw utils::error::create_exception(function_name,
{
"column type not implemented",
std::to_string(column_type),
});
}
columns_[name] = db_column{col, name, value};
}
}
auto db_result::db_row::get_columns() const -> std::vector<db_column> {
std::vector<db_column> ret;
for (const auto &item : columns_) {
ret.push_back(item.second);
}
return ret;
}
auto db_result::db_row::get_column(std::int32_t index) const -> db_column {
auto iter = std::find_if(
columns_.begin(), columns_.end(),
[&index](auto &&col) -> bool { return col.second.get_index() == index; });
if (iter == columns_.end()) {
throw std::out_of_range("");
}
return iter->second;
}
auto db_result::db_row::get_column(std::string name) const -> db_column {
return columns_.at(name);
}
db_result::db_result(db3_stmt_t stmt, std::int32_t res)
: ctx_(std::make_shared<context>()), res_(res) {
ctx_->stmt = std::move(stmt);
if (res == SQLITE_OK) { if (res == SQLITE_OK) {
set_res(sqlite3_step(ctx_->stmt.get())); set_res(sqlite3_step(ctx_->stmt.get()));
} }
@ -100,7 +155,7 @@ auto db_result::get_row(std::optional<row> &opt_row) const -> bool {
return false; return false;
} }
opt_row = db_row<context>{ctx_}; opt_row = db_row{ctx_};
set_res(sqlite3_step(ctx_->stmt.get())); set_res(sqlite3_step(ctx_->stmt.get()));
return true; return true;
} }

View File

@ -52,34 +52,39 @@ auto db_delete::go() const -> db_result {
auto res = auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr); sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
auto stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
if (not ctx_->where_data) { if (not ctx_->where_data) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
for (std::int32_t idx = 0; for (std::int32_t idx = 0;
idx < static_cast<std::int32_t>(ctx_->where_data->values.size()); idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
idx++) { idx++) {
res = std::visit( res =
overloaded{ std::visit(overloaded{
[&stmt_ptr, &idx](std::int64_t data) -> std::int32_t { [&stmt, &idx](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(stmt_ptr, idx + 1, data); return sqlite3_bind_int64(stmt.get(), idx + 1, data);
}, },
[&stmt_ptr, &idx](const std::string &data) -> std::int32_t { [&stmt, &idx](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(stmt_ptr, idx + 1, data.c_str(), -1, return sqlite3_bind_text(stmt.get(), idx + 1,
nullptr); data.c_str(), -1, nullptr);
}, },
}, },
ctx_->where_data->values.at(static_cast<std::size_t>(idx))); ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} }
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
auto db_delete::group(context::w_t::group_func_t func) -> context::w_t::wn_t { auto db_delete::group(context::w_t::group_func_t func) -> context::w_t::wn_t {

View File

@ -24,8 +24,8 @@
#include "utils/db/sqlite/db_insert.hpp" #include "utils/db/sqlite/db_insert.hpp"
namespace repertory::utils::db::sqlite { namespace repertory::utils::db::sqlite {
auto db_insert::column_value(std::string column_name, auto db_insert::column_value(std::string column_name, db_types_t value)
db_types_t value) -> db_insert & { -> db_insert {
ctx_->values[column_name] = value; ctx_->values[column_name] = value;
return *this; return *this;
} }
@ -65,29 +65,34 @@ auto db_insert::go() const -> db_result {
auto res = auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr); sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
auto stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
for (std::int32_t idx = 0; for (std::int32_t idx = 0;
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) { idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
res = std::visit( res =
overloaded{ std::visit(overloaded{
[&idx, &stmt_ptr](std::int64_t data) -> std::int32_t { [&idx, &stmt](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(stmt_ptr, idx + 1, data); return sqlite3_bind_int64(stmt.get(), idx + 1, data);
}, },
[&idx, &stmt_ptr](const std::string &data) -> std::int32_t { [&idx, &stmt](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(stmt_ptr, idx + 1, data.c_str(), -1, return sqlite3_bind_text(stmt.get(), idx + 1,
nullptr); data.c_str(), -1, nullptr);
}, },
}, },
std::next(ctx_->values.begin(), idx)->second); std::next(ctx_->values.begin(), idx)->second);
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} }
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} // namespace repertory::utils::db::sqlite } // namespace repertory::utils::db::sqlite

View File

@ -57,13 +57,13 @@ auto db_select::context::db_select_op_t::order_by(std::string column_name,
return *this; return *this;
} }
auto db_select::column(std::string column_name) -> db_select & { auto db_select::column(std::string column_name) -> db_select {
ctx_->columns.push_back(column_name); ctx_->columns.push_back(column_name);
return *this; return *this;
} }
auto db_select::count(std::string column_name, auto db_select::count(std::string column_name, std::string as_column_name)
std::string as_column_name) -> db_select & { -> db_select {
ctx_->count_columns[column_name] = as_column_name; ctx_->count_columns[column_name] = as_column_name;
return *this; return *this;
} }
@ -138,34 +138,39 @@ auto db_select::go() const -> db_result {
auto res = auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr); sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
auto stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
if (not ctx_->where_data) { if (not ctx_->where_data) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
for (std::int32_t idx = 0; for (std::int32_t idx = 0;
idx < static_cast<std::int32_t>(ctx_->where_data->values.size()); idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
idx++) { idx++) {
res = std::visit( res =
overloaded{ std::visit(overloaded{
[&idx, &stmt_ptr](std::int64_t data) -> std::int32_t { [&idx, &stmt](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(stmt_ptr, idx + 1, data); return sqlite3_bind_int64(stmt.get(), idx + 1, data);
}, },
[&idx, &stmt_ptr](const std::string &data) -> std::int32_t { [&idx, &stmt](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(stmt_ptr, idx + 1, data.c_str(), -1, return sqlite3_bind_text(stmt.get(), idx + 1,
nullptr); data.c_str(), -1, nullptr);
}, },
}, },
ctx_->where_data->values.at(static_cast<std::size_t>(idx))); ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} }
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t { auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
@ -180,23 +185,22 @@ auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
return ctx_->where_data->base.group(std::move(func)); return ctx_->where_data->base.group(std::move(func));
} }
auto db_select::group_by(std::string column_name) -> db_select & { auto db_select::group_by(std::string column_name) -> db_select {
ctx_->group_by.emplace_back(std::move(column_name)); ctx_->group_by.emplace_back(std::move(column_name));
return *this; return *this;
} }
auto db_select::limit(std::int32_t value) -> db_select & { auto db_select::limit(std::int32_t value) -> db_select {
ctx_->limit = value; ctx_->limit = value;
return *this; return *this;
} }
auto db_select::offset(std::int32_t value) -> db_select & { auto db_select::offset(std::int32_t value) -> db_select {
ctx_->offset = value; ctx_->offset = value;
return *this; return *this;
} }
auto db_select::order_by(std::string column_name, auto db_select::order_by(std::string column_name, bool ascending) -> db_select {
bool ascending) -> db_select & {
ctx_->order_by = {column_name, ascending}; ctx_->order_by = {column_name, ascending};
return *this; return *this;
} }

View File

@ -45,8 +45,8 @@ auto db_update::context::db_update_op_t::order_by(std::string column_name,
return *this; return *this;
} }
auto db_update::column_value(std::string column_name, auto db_update::column_value(std::string column_name, db_types_t value)
db_types_t value) -> db_update & { -> db_update {
ctx_->column_values[column_name] = value; ctx_->column_values[column_name] = value;
return *this; return *this;
} }
@ -91,30 +91,35 @@ auto db_update::go() const -> db_result {
auto res = auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr); sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
auto stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
for (std::int32_t idx = 0; for (std::int32_t idx = 0;
idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) { idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) {
res = std::visit( res =
overloaded{ std::visit(overloaded{
[&stmt_ptr, &idx](std::int64_t data) -> std::int32_t { [&idx, &stmt](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(stmt_ptr, idx + 1, data); return sqlite3_bind_int64(stmt.get(), idx + 1, data);
}, },
[&stmt_ptr, &idx](const std::string &data) -> std::int32_t { [&idx, &stmt](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(stmt_ptr, idx + 1, data.c_str(), -1, return sqlite3_bind_text(stmt.get(), idx + 1,
nullptr); data.c_str(), -1, nullptr);
}, },
}, },
std::next(ctx_->column_values.begin(), idx)->second); std::next(ctx_->column_values.begin(), idx)->second);
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} }
if (not ctx_->where_data) { if (not ctx_->where_data) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
for (std::int32_t idx = 0; for (std::int32_t idx = 0;
@ -122,16 +127,16 @@ auto db_update::go() const -> db_result {
idx++) { idx++) {
res = std::visit( res = std::visit(
overloaded{ overloaded{
[this, &idx, &stmt_ptr](std::int64_t data) -> std::int32_t { [this, &idx, &stmt](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64( return sqlite3_bind_int64(
stmt_ptr, stmt.get(),
idx + static_cast<std::int32_t>(ctx_->column_values.size()) + idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
1, 1,
data); data);
}, },
[this, &idx, &stmt_ptr](const std::string &data) -> std::int32_t { [this, &idx, &stmt](const std::string &data) -> std::int32_t {
return sqlite3_bind_text( return sqlite3_bind_text(
stmt_ptr, stmt.get(),
idx + static_cast<std::int32_t>(ctx_->column_values.size()) + idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
1, 1,
data.c_str(), -1, nullptr); data.c_str(), -1, nullptr);
@ -139,11 +144,11 @@ auto db_update::go() const -> db_result {
}, },
ctx_->where_data->values.at(static_cast<std::size_t>(idx))); ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
} }
return {stmt_ptr, res}; return {std::move(stmt), res};
} }
auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t { auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
@ -158,13 +163,12 @@ auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
return ctx_->where_data->base.group(std::move(func)); return ctx_->where_data->base.group(std::move(func));
} }
auto db_update::limit(std::int32_t value) -> db_update & { auto db_update::limit(std::int32_t value) -> db_update {
ctx_->limit = value; ctx_->limit = value;
return *this; return *this;
} }
auto db_update::order_by(std::string column_name, auto db_update::order_by(std::string column_name, bool ascending) -> db_update {
bool ascending) -> db_update & {
ctx_->order_by = {column_name, ascending}; ctx_->order_by = {column_name, ascending};
return *this; return *this;
} }