v2.0.2-rc (#27)
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
## v2.0.2-rc ### BREAKING CHANGES * Refactored `config.json` - will need to verify configuration settings prior to mounting ### Issues * \#12 \[Unit Test\] Complete all providers unit tests * \#14 \[Unit Test\] SQLite mini-ORM unit tests and cleanup * \#16 Add support for bucket name in Sia provider * \#17 Update to common c++ build system * A single 64-bit Linux Jenkins server is used to build all Linux and Windows versions * All dependency sources are now included * MSVC is no longer supported * MSYS2 is required for building Windows binaries on Windows * OS X support is temporarily disabled * \#19 \[bug\] Rename file is broken for files that are existing * \#23 \[bug\] Incorrect file size displayed while upload is pending * \#24 RocksDB implementations should be transactional * \#25 Writes should block when maximum cache size is reached * \#26 Complete ring buffer and direct download support ### Changes from v2.0.1-rc * Ability to choose between RocksDB and SQLite databases * Added direct reads and implemented download fallback * Corrected file times on S3 and Sia providers * Corrected handling of `chown()` and `chmod()` * Fixed erroneous download of chunks after resize Reviewed-on: #27
This commit is contained in:
250
support/src/utils/db/sqlite/db_common.cpp
Normal file
250
support/src/utils/db/sqlite/db_common.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_common.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace repertory::utils::db::sqlite {
|
||||
void sqlite3_deleter::operator()(sqlite3 *db3) const {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
if (db3 == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string err_msg;
|
||||
if (not execute_sql(*db3, "VACUUM;", err_msg)) {
|
||||
utils::error::handle_error(function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to vacuum database",
|
||||
err_msg,
|
||||
}));
|
||||
}
|
||||
|
||||
if (not utils::retry_action([&db3]() -> bool {
|
||||
auto res = sqlite3_close_v2(db3);
|
||||
if (res == SQLITE_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto &&err_str = sqlite3_errstr(res);
|
||||
utils::error::handle_error(
|
||||
function_name,
|
||||
utils::error::create_error_message({
|
||||
"failed to close database",
|
||||
(err_str == nullptr ? std::to_string(res) : err_str),
|
||||
}));
|
||||
return false;
|
||||
})) {
|
||||
repertory::utils::error::handle_error(function_name,
|
||||
"failed to close database");
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
auto db_result::db_column::get_value_as_json() const -> nlohmann::json {
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[this](std::int64_t value) -> auto {
|
||||
return nlohmann::json({{name_, value}});
|
||||
},
|
||||
[](auto &&value) -> auto { return nlohmann::json::parse(value); },
|
||||
},
|
||||
value_);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
db_result::db_row::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};
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
}
|
||||
}
|
||||
|
||||
auto db_result::get_error_str() const -> std::string {
|
||||
auto &&err_msg = sqlite3_errstr(res_);
|
||||
return err_msg == nullptr ? std::to_string(res_) : err_msg;
|
||||
}
|
||||
|
||||
auto db_result::get_row(std::optional<row> &opt_row) const -> bool {
|
||||
opt_row.reset();
|
||||
|
||||
if (not has_row()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
opt_row = db_row{ctx_};
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto db_result::has_row() const -> bool { return res_ == SQLITE_ROW; }
|
||||
|
||||
void db_result::next_row() const {
|
||||
if (not has_row()) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_res(sqlite3_step(ctx_->stmt.get()));
|
||||
}
|
||||
|
||||
auto db_result::ok() const -> bool {
|
||||
return res_ == SQLITE_DONE || res_ == SQLITE_ROW;
|
||||
}
|
||||
|
||||
auto create_db(std::string db_path,
|
||||
const std::map<std::string, std::string> &sql_create_tables)
|
||||
-> db3_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
sqlite3 *db_ptr{nullptr};
|
||||
auto db_res =
|
||||
sqlite3_open_v2(db_path.c_str(), &db_ptr,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
|
||||
if (db_res != SQLITE_OK) {
|
||||
const auto *msg = sqlite3_errstr(db_res);
|
||||
throw utils::error::create_exception(
|
||||
function_name, {
|
||||
"failed to open db",
|
||||
db_path,
|
||||
(msg == nullptr ? std::to_string(db_res) : msg),
|
||||
});
|
||||
}
|
||||
|
||||
auto db3 = db3_t{
|
||||
db_ptr,
|
||||
sqlite3_deleter(),
|
||||
};
|
||||
|
||||
for (auto &&create_item : sql_create_tables) {
|
||||
std::string err_msg;
|
||||
if (not sqlite::execute_sql(*db3, create_item.second, err_msg)) {
|
||||
db3.reset();
|
||||
throw utils::error::create_exception(function_name, {
|
||||
err_msg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
set_journal_mode(*db3);
|
||||
|
||||
return db3;
|
||||
}
|
||||
|
||||
auto execute_sql(sqlite3 &db3, const std::string &sql, std::string &err)
|
||||
-> bool {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
char *err_msg{nullptr};
|
||||
auto res = sqlite3_exec(&db3, sql.c_str(), nullptr, nullptr, &err_msg);
|
||||
if (err_msg != nullptr) {
|
||||
err = err_msg;
|
||||
sqlite3_free(err_msg);
|
||||
err_msg = nullptr;
|
||||
}
|
||||
|
||||
if (res == SQLITE_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = utils::error::create_error_message({
|
||||
function_name,
|
||||
"failed to execute sql",
|
||||
err,
|
||||
sql,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_journal_mode(sqlite3 &db3) {
|
||||
sqlite3_exec(&db3,
|
||||
"PRAGMA journal_mode = WAL;PRAGMA synchronous = NORMAL;PRAGMA "
|
||||
"auto_vacuum = NONE;",
|
||||
nullptr, nullptr, nullptr);
|
||||
}
|
||||
} // namespace repertory::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
115
support/src/utils/db/sqlite/db_delete.cpp
Normal file
115
support/src/utils/db/sqlite/db_delete.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_delete.hpp"
|
||||
|
||||
namespace repertory::utils::db::sqlite {
|
||||
auto db_delete::context::db_delete_op_t::dump() const -> std::string {
|
||||
return db_delete{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_delete::context::db_delete_op_t::go() const -> db_result {
|
||||
return db_delete{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_delete::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "DELETE FROM \"" << ctx_->table_name << "\"";
|
||||
|
||||
if (ctx_->where_data) {
|
||||
std::int32_t idx{};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_delete::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
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) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&stmt, &idx](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&stmt, &idx](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_delete::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_delete::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace repertory::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
99
support/src/utils/db/sqlite/db_insert.cpp
Normal file
99
support/src/utils/db/sqlite/db_insert.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_insert.hpp"
|
||||
|
||||
namespace repertory::utils::db::sqlite {
|
||||
auto db_insert::column_value(std::string column_name, db_types_t value)
|
||||
-> db_insert {
|
||||
ctx_->values[column_name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_insert::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "INSERT ";
|
||||
if (ctx_->or_replace) {
|
||||
query << "OR REPLACE ";
|
||||
}
|
||||
query << "INTO \"" << ctx_->table_name << "\" (";
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << '"' << std::next(ctx_->values.begin(), idx)->first << '"';
|
||||
}
|
||||
|
||||
query << ") VALUES (";
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << "?" << (idx + 1);
|
||||
}
|
||||
query << ");";
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_insert::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
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) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->values.size()); idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
std::next(ctx_->values.begin(), idx)->second);
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
} // namespace repertory::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
221
support/src/utils/db/sqlite/db_select.cpp
Normal file
221
support/src/utils/db/sqlite/db_select.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_select.hpp"
|
||||
|
||||
namespace repertory::utils::db::sqlite {
|
||||
auto db_select::context::db_select_op_t::dump() const -> std::string {
|
||||
return db_select{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::go() const -> db_result {
|
||||
return db_select{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::group_by(std::string column_name)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.group_by(column_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::limit(std::int32_t value)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.limit(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::offset(std::int32_t value)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.offset(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::context::db_select_op_t::order_by(std::string column_name,
|
||||
bool ascending)
|
||||
-> db_select::context::db_select_op_t {
|
||||
db_select{ctx}.order_by(column_name, ascending);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::column(std::string column_name) -> db_select {
|
||||
ctx_->columns.push_back(column_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::count(std::string column_name, std::string as_column_name)
|
||||
-> db_select {
|
||||
ctx_->count_columns[column_name] = as_column_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "SELECT ";
|
||||
bool has_column{false};
|
||||
if (ctx_->columns.empty()) {
|
||||
if (ctx_->count_columns.empty()) {
|
||||
query << "*";
|
||||
has_column = true;
|
||||
}
|
||||
} else {
|
||||
has_column = not ctx_->columns.empty();
|
||||
for (std::size_t idx = 0U; idx < ctx_->columns.size(); idx++) {
|
||||
if (idx > 0U) {
|
||||
query << ", ";
|
||||
}
|
||||
query << ctx_->columns.at(idx);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0U;
|
||||
idx < static_cast<std::int32_t>(ctx_->count_columns.size()); idx++) {
|
||||
if (has_column || idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
query << "COUNT(\"";
|
||||
auto &count_column = *std::next(ctx_->count_columns.begin(), idx);
|
||||
query << count_column.first << "\") AS \"" << count_column.second << '"';
|
||||
}
|
||||
query << " FROM \"" << ctx_->table_name << "\"";
|
||||
|
||||
if (ctx_->where_data) {
|
||||
std::int32_t idx{};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
if (not ctx_->group_by.empty()) {
|
||||
query << " GROUP BY ";
|
||||
for (std::size_t idx = 0U; idx < ctx_->group_by.size(); idx++) {
|
||||
if (idx > 0U) {
|
||||
query << ", ";
|
||||
}
|
||||
|
||||
query << "\"" << ctx_->group_by.at(idx) << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx_->order_by.has_value()) {
|
||||
query << " ORDER BY \"" << ctx_->order_by.value().first << "\" ";
|
||||
query << (ctx_->order_by.value().second ? "ASC" : "DESC");
|
||||
}
|
||||
|
||||
if (ctx_->limit.has_value()) {
|
||||
query << " LIMIT " << ctx_->limit.value();
|
||||
}
|
||||
|
||||
if (ctx_->offset.has_value()) {
|
||||
query << " OFFSET " << ctx_->offset.value();
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_select::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
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) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_select::group_by(std::string column_name) -> db_select {
|
||||
ctx_->group_by.emplace_back(std::move(column_name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::limit(std::int32_t value) -> db_select {
|
||||
ctx_->limit = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::offset(std::int32_t value) -> db_select {
|
||||
ctx_->offset = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::order_by(std::string column_name, bool ascending) -> db_select {
|
||||
ctx_->order_by = {column_name, ascending};
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_select::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace repertory::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
189
support/src/utils/db/sqlite/db_update.cpp
Normal file
189
support/src/utils/db/sqlite/db_update.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "utils/db/sqlite/db_update.hpp"
|
||||
|
||||
namespace repertory::utils::db::sqlite {
|
||||
auto db_update::context::db_update_op_t::dump() const -> std::string {
|
||||
return db_update{ctx}.dump();
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::go() const -> db_result {
|
||||
return db_update{ctx}.go();
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::limit(std::int32_t value)
|
||||
-> db_update::context::db_update_op_t {
|
||||
db_update{ctx}.limit(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::context::db_update_op_t::order_by(std::string column_name,
|
||||
bool ascending)
|
||||
-> db_update::context::db_update_op_t {
|
||||
db_update{ctx}.order_by(column_name, ascending);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::column_value(std::string column_name, db_types_t value)
|
||||
-> db_update {
|
||||
ctx_->column_values[column_name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::dump() const -> std::string {
|
||||
std::stringstream query;
|
||||
query << "UPDATE \"" << ctx_->table_name << "\" SET ";
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) {
|
||||
if (idx > 0) {
|
||||
query << ", ";
|
||||
}
|
||||
|
||||
auto column = std::next(ctx_->column_values.begin(), idx);
|
||||
query << '"' << column->first << "\"=?" + std::to_string(idx + 1);
|
||||
}
|
||||
|
||||
if (ctx_->where_data) {
|
||||
auto idx{static_cast<std::int32_t>(ctx_->column_values.size())};
|
||||
query << " WHERE " << ctx_->where_data->base.dump(idx);
|
||||
}
|
||||
|
||||
if (ctx_->order_by.has_value()) {
|
||||
query << " ORDER BY \"" << ctx_->order_by.value().first << "\" ";
|
||||
query << (ctx_->order_by.value().second ? "ASC" : "DESC");
|
||||
}
|
||||
|
||||
if (ctx_->limit.has_value()) {
|
||||
query << " LIMIT " << ctx_->limit.value();
|
||||
}
|
||||
|
||||
query << ';';
|
||||
|
||||
return query.str();
|
||||
}
|
||||
|
||||
auto db_update::go() const -> db_result {
|
||||
sqlite3_stmt *stmt_ptr{nullptr};
|
||||
|
||||
auto query_str = dump();
|
||||
auto res =
|
||||
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) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->column_values.size()); idx++) {
|
||||
res =
|
||||
std::visit(overloaded{
|
||||
[&idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(stmt.get(), idx + 1, data);
|
||||
},
|
||||
[&idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(stmt.get(), idx + 1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
std::next(ctx_->column_values.begin(), idx)->second);
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
if (not ctx_->where_data) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
for (std::int32_t idx = 0;
|
||||
idx < static_cast<std::int32_t>(ctx_->where_data->values.size());
|
||||
idx++) {
|
||||
res = std::visit(
|
||||
overloaded{
|
||||
[this, &idx, &stmt](std::int64_t data) -> std::int32_t {
|
||||
return sqlite3_bind_int64(
|
||||
stmt.get(),
|
||||
idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
|
||||
1,
|
||||
data);
|
||||
},
|
||||
[this, &idx, &stmt](const std::string &data) -> std::int32_t {
|
||||
return sqlite3_bind_text(
|
||||
stmt.get(),
|
||||
idx + static_cast<std::int32_t>(ctx_->column_values.size()) +
|
||||
1,
|
||||
data.c_str(), -1, nullptr);
|
||||
},
|
||||
},
|
||||
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
|
||||
if (res != SQLITE_OK) {
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(stmt), res};
|
||||
}
|
||||
|
||||
auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.group(std::move(func));
|
||||
}
|
||||
|
||||
auto db_update::limit(std::int32_t value) -> db_update {
|
||||
ctx_->limit = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::order_by(std::string column_name, bool ascending) -> db_update {
|
||||
ctx_->order_by = {column_name, ascending};
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto db_update::where(std::string column_name) const -> context::w_t::cn_t {
|
||||
if (not ctx_->where_data) {
|
||||
ctx_->where_data = std::make_unique<context::wd_t>(context::wd_t{
|
||||
context::w_t{0U, ctx_},
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
|
||||
return ctx_->where_data->base.where(column_name);
|
||||
}
|
||||
} // namespace repertory::utils::db::sqlite
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
Reference in New Issue
Block a user