diff --git a/repertory/librepertory/include/database/db_delete.hpp b/repertory/librepertory/include/database/db_delete.hpp new file mode 100644 index 00000000..38640b21 --- /dev/null +++ b/repertory/librepertory/include/database/db_delete.hpp @@ -0,0 +1,99 @@ +/* + Copyright <2018-2024> + + 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. +*/ +#ifndef INCLUDE_DATABASE_DB_DELETE_HPP_ +#define INCLUDE_DATABASE_DB_DELETE_HPP_ + +#include "database/db_common.hpp" +#include "utils/error_utils.hpp" + +namespace repertory::db { +class db_delete final { +public: + struct context final { + context(sqlite3 &db3_, std::string table_name_) + : db3(db3_), table_name(std::move(table_name_)) {} + + sqlite3 &db3; + std::string table_name; + + std::vector ands{}; + db3_stmt_t stmt{nullptr}; + }; + + using row = db_row; + +public: + db_delete(sqlite3 &db3, std::string table_name) + : context_(std::make_shared(db3, table_name)) {} + + db_delete(std::shared_ptr ctx) : context_(std::move(ctx)) {} + +public: + struct db_where final { + db_where(std::shared_ptr ctx, std::string column_name) + : context_(std::move(ctx)), column_name_(std::move(column_name)) {} + + public: + struct db_where_next final { + db_where_next(std::shared_ptr ctx) : context_(std::move(ctx)) {} + + private: + std::shared_ptr context_; + + public: + [[nodiscard]] auto and_where(std::string column_name) const -> db_where { + return db_where{context_, column_name}; + } + + [[nodiscard]] auto dump() const -> std::string { + return db_delete{context_}.dump(); + } + + [[nodiscard]] auto go() const -> db_result { + return db_delete{context_}.go(); + } + }; + + private: + std::shared_ptr context_; + std::string column_name_; + + public: + [[nodiscard]] auto equals(db_types_t value) const -> db_where_next { + context_->ands.emplace_back(comp_data_t{column_name_, "=", value}); + return db_where_next{context_}; + } + }; + +private: + std::shared_ptr context_; + +public: + [[nodiscard]] auto dump() const -> std::string; + + [[nodiscard]] auto go() const -> db_result; + + [[nodiscard]] auto where(std::string column_name) const -> db_where; +}; +} // namespace repertory::db + +#endif // INCLUDE_DATABASE_DB_DELETE_HPP_ diff --git a/repertory/librepertory/include/database/db_select.hpp b/repertory/librepertory/include/database/db_select.hpp index 6a4b9924..0ace9477 100644 --- a/repertory/librepertory/include/database/db_select.hpp +++ b/repertory/librepertory/include/database/db_select.hpp @@ -38,7 +38,6 @@ public: std::vector ands{}; std::vector columns{}; std::map count_columns{}; - bool delete_query{false}; std::optional limit; std::optional> order_by; db3_stmt_t stmt{nullptr}; @@ -81,8 +80,8 @@ public: return db_select{context_}.limit(value); } - [[nodiscard]] auto order_by(std::string column_name, bool ascending) const - -> db_select { + [[nodiscard]] auto order_by(std::string column_name, + bool ascending) const -> db_select { return db_select{context_}.order_by(column_name, ascending); } }; @@ -104,10 +103,8 @@ private: public: [[nodiscard]] auto column(std::string column_name) -> db_select &; - [[nodiscard]] auto count(std::string column_name, std::string as_column_name) - -> db_select &; - - [[nodiscard]] auto delete_query() -> db_select &; + [[nodiscard]] auto count(std::string column_name, + std::string as_column_name) -> db_select &; [[nodiscard]] auto dump() const -> std::string; @@ -115,8 +112,8 @@ public: [[nodiscard]] auto limit(std::int32_t value) -> db_select &; - [[nodiscard]] auto order_by(std::string column_name, bool ascending) - -> db_select &; + [[nodiscard]] auto order_by(std::string column_name, + bool ascending) -> db_select &; [[nodiscard]] auto where(std::string column_name) const -> db_where; }; diff --git a/repertory/librepertory/include/database/db_update.hpp b/repertory/librepertory/include/database/db_update.hpp index 444b4710..8899493e 100644 --- a/repertory/librepertory/include/database/db_update.hpp +++ b/repertory/librepertory/include/database/db_update.hpp @@ -36,6 +36,8 @@ public: std::vector ands{}; std::map values{}; + std::optional limit; + std::optional> order_by; db3_stmt_t stmt{nullptr}; }; @@ -65,6 +67,15 @@ public: [[nodiscard]] auto go() const -> db_result { return db_update{context_}.go(); } + + [[nodiscard]] auto limit(std::int32_t value) const -> db_update { + return db_update{context_}.limit(value); + } + + [[nodiscard]] auto order_by(std::string column_name, + bool ascending) const -> db_update { + return db_update{context_}.order_by(column_name, ascending); + } }; private: diff --git a/repertory/librepertory/src/database/db_delete.cpp b/repertory/librepertory/src/database/db_delete.cpp new file mode 100644 index 00000000..8009a289 --- /dev/null +++ b/repertory/librepertory/src/database/db_delete.cpp @@ -0,0 +1,94 @@ +/* + Copyright <2018-2024> + + 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. +*/ +#include "database/db_delete.hpp" + +namespace repertory::db { + +auto db_delete::dump() const -> std::string { + std::stringstream query; + query << "DELETE FROM \"" << context_->table_name << "\""; + + if (not context_->ands.empty()) { + query << " WHERE ("; + for (std::int32_t idx = 0; + idx < static_cast(context_->ands.size()); idx++) { + if (idx > 0) { + query << " AND "; + } + + auto &item = context_->ands.at(static_cast(idx)); + query << '"' << item.column_name << '"' << item.op_type << "?" + << (idx + 1); + } + query << ")"; + } + + query << ';'; + + return query.str(); +} + +auto db_delete::go() const -> db_result { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + + sqlite3_stmt *stmt_ptr{nullptr}; + auto query_str = dump(); + auto res = sqlite3_prepare_v2(&context_->db3, query_str.c_str(), -1, + &stmt_ptr, nullptr); + if (res != SQLITE_OK) { + utils::error::raise_error(function_name, + "failed to prepare|" + std::to_string(res) + '|' + + sqlite3_errstr(res) + '|' + query_str); + return {context_, res}; + } + context_->stmt.reset(stmt_ptr); + + for (std::int32_t idx = 0; + idx < static_cast(context_->ands.size()); idx++) { + res = std::visit( + overloaded{ + [this, &idx](std::int64_t data) -> std::int32_t { + return sqlite3_bind_int64(context_->stmt.get(), idx + 1, data); + }, + [this, &idx](const std::string &data) -> std::int32_t { + return sqlite3_bind_text(context_->stmt.get(), idx + 1, + data.c_str(), -1, nullptr); + }, + }, + context_->ands.at(static_cast(idx)).value); + if (res != SQLITE_OK) { + utils::error::raise_error(function_name, + "failed to bind|" + std::to_string(res) + '|' + + sqlite3_errstr(res) + '|' + query_str); + return {context_, res}; + } + } + + return {context_, res}; +} + +auto db_delete::where(std::string column_name) const -> db_where { + return db_where{context_, column_name}; +} +} // namespace repertory::db diff --git a/repertory/librepertory/src/database/db_select.cpp b/repertory/librepertory/src/database/db_select.cpp index 787e16a0..d6a54ffc 100644 --- a/repertory/librepertory/src/database/db_select.cpp +++ b/repertory/librepertory/src/database/db_select.cpp @@ -24,10 +24,6 @@ namespace repertory::db { auto db_select::column(std::string column_name) -> db_select & { - if (context_->delete_query) { - throw std::runtime_error("columns may not be specified for delete"); - } - context_->columns.push_back(column_name); return *this; } @@ -38,46 +34,34 @@ auto db_select::count(std::string column_name, return *this; } -auto db_select::delete_query() -> db_select & { - if (not context_->columns.empty()) { - throw std::runtime_error("columns must be empty for delete"); - } - - context_->delete_query = true; - return *this; -} - auto db_select::dump() const -> std::string { std::stringstream query; - query << (context_->delete_query ? "DELETE " : "SELECT "); - if (not context_->delete_query) { - bool has_column{false}; - if (context_->columns.empty()) { - if (context_->count_columns.empty()) { - query << "*"; - has_column = true; - } - } else { - has_column = not context_->columns.empty(); - for (std::size_t idx = 0U; idx < context_->columns.size(); idx++) { - if (idx > 0U) { - query << ", "; - } - query << context_->columns.at(idx); - } + query << "SELECT "; + bool has_column{false}; + if (context_->columns.empty()) { + if (context_->count_columns.empty()) { + query << "*"; + has_column = true; } - - for (std::int32_t idx = 0U; - idx < static_cast(context_->count_columns.size()); - idx++) { - if (has_column || idx > 0) { + } else { + has_column = not context_->columns.empty(); + for (std::size_t idx = 0U; idx < context_->columns.size(); idx++) { + if (idx > 0U) { query << ", "; } - query << "COUNT(\""; - auto &count_column = *std::next(context_->count_columns.begin(), idx); - query << count_column.first << "\") AS \"" << count_column.second << '"'; + query << context_->columns.at(idx); } } + + for (std::int32_t idx = 0U; + idx < static_cast(context_->count_columns.size()); idx++) { + if (has_column || idx > 0) { + query << ", "; + } + query << "COUNT(\""; + auto &count_column = *std::next(context_->count_columns.begin(), idx); + query << count_column.first << "\") AS \"" << count_column.second << '"'; + } query << " FROM \"" << context_->table_name << "\""; if (not context_->ands.empty()) { @@ -95,15 +79,13 @@ auto db_select::dump() const -> std::string { query << ")"; } - if (not context_->delete_query) { - if (context_->order_by.has_value()) { - query << " ORDER BY \"" << context_->order_by.value().first << "\" "; - query << (context_->order_by.value().second ? "ASC" : "DESC"); - } + if (context_->order_by.has_value()) { + query << " ORDER BY \"" << context_->order_by.value().first << "\" "; + query << (context_->order_by.value().second ? "ASC" : "DESC"); + } - if (context_->limit.has_value()) { - query << " LIMIT " << context_->limit.value(); - } + if (context_->limit.has_value()) { + query << " LIMIT " << context_->limit.value(); } query << ';'; @@ -153,20 +135,12 @@ auto db_select::go() const -> db_result { } auto db_select::limit(std::int32_t value) -> db_select & { - if (context_->delete_query) { - throw std::runtime_error("limit may not be specified for delete"); - } - context_->limit = value; return *this; } auto db_select::order_by(std::string column_name, bool ascending) -> db_select & { - if (context_->delete_query) { - throw std::runtime_error("order_by may not be specified for delete"); - } - context_->order_by = {column_name, ascending}; return *this; } diff --git a/repertory/librepertory/src/database/db_update.cpp b/repertory/librepertory/src/database/db_update.cpp index a1f461f8..0176e6b5 100644 --- a/repertory/librepertory/src/database/db_update.cpp +++ b/repertory/librepertory/src/database/db_update.cpp @@ -39,7 +39,7 @@ auto db_update::dump() const -> std::string { } auto column = std::next(context_->values.begin(), idx); - query << column->first << " = ?"; + query << column->first << "=?" + std::to_string(idx + 1U); } if (not context_->ands.empty()) { @@ -52,11 +52,20 @@ auto db_update::dump() const -> std::string { auto &item = context_->ands.at(static_cast(idx)); query << '"' << item.column_name << '"' << item.op_type << "?" - << (idx + 1); + << (idx + context_->values.size() + 1U); } query << ")"; } + if (context_->order_by.has_value()) { + query << " ORDER BY \"" << context_->order_by.value().first << "\" "; + query << (context_->order_by.value().second ? "ASC" : "DESC"); + } + + if (context_->limit.has_value()) { + query << " LIMIT " << context_->limit.value(); + } + query << ';'; return query.str(); diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 82619d82..a0ba0041 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -23,6 +23,7 @@ #include "app_config.hpp" #include "database/db_common.hpp" +#include "database/db_delete.hpp" #include "database/db_insert.hpp" #include "database/db_select.hpp" #include "database/db_update.hpp" @@ -565,8 +566,7 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { remove_upload(api_path); - auto result = db::db_select{*db_.get(), resume_table} - .delete_query() + auto result = db::db_delete{*db_.get(), resume_table} .where("api_path") .equals(api_path) .go(); @@ -587,8 +587,7 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { void file_manager::remove_resume(const std::string &api_path, const std::string &source_path) { - auto result = db::db_select{*db_.get(), resume_table} - .delete_query() + auto result = db::db_delete{*db_.get(), resume_table} .where("api_path") .equals(api_path) .go(); @@ -612,14 +611,12 @@ void file_manager::remove_upload(const std::string &api_path, bool no_lock) { lock = std::make_unique(upload_mtx_); } - auto result = db::db_select{*db_.get(), upload_table} - .delete_query() + auto result = db::db_delete{*db_.get(), upload_table} .where("api_path") .equals(api_path) .go(); - result = db::db_select{*db_.get(), upload_active_table} - .delete_query() + result = db::db_delete{*db_.get(), upload_active_table} .where("api_path") .equals(api_path) .go(); @@ -996,8 +993,7 @@ void file_manager::upload_completed(const file_upload_completed &evt) { if (not utils::string::to_bool(evt.get_cancelled().get())) { auto err = api_error_from_string(evt.get_result().get()); if (err == api_error::success) { - auto result = db::db_select{*db_.get(), upload_active_table} - .delete_query() + auto result = db::db_delete{*db_.get(), upload_active_table} .where("api_path") .equals(evt.get_api_path().get()) .go(); @@ -1071,8 +1067,7 @@ void file_manager::upload_handler() { upload_lookup_[fsi.api_path] = std::make_unique(fsi, provider_); - auto del_res = db::db_select{*db_.get(), upload_table} - .delete_query() + auto del_res = db::db_delete{*db_.get(), upload_table} .where("api_path") .equals(api_path) .go(); diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index 6990b973..e684ad04 100644 --- a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp +++ b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp @@ -22,6 +22,7 @@ #include "providers/encrypt/encrypt_provider.hpp" #include "database/db_common.hpp" +#include "database/db_delete.hpp" #include "database/db_insert.hpp" #include "database/db_select.hpp" #include "events/event_system.hpp" @@ -999,14 +1000,12 @@ void encrypt_provider::remove_deleted_files() { for (auto &&item : removed_list) { if (not item.directory) { - auto del_res = db::db_select{*db_, source_table} - .delete_query() + auto del_res = db::db_delete{*db_, source_table} .where("api_path") .equals(item.api_path) .go(); // TODO handle error - del_res = db::db_select{*db_, file_table} - .delete_query() + del_res = db::db_delete{*db_, file_table} .where("source_path") .equals(item.source_path) .go(); @@ -1018,14 +1017,12 @@ void encrypt_provider::remove_deleted_files() { for (auto &&item : removed_list) { if (item.directory) { - auto del_res = db::db_select{*db_, source_table} - .delete_query() + auto del_res = db::db_delete{*db_, source_table} .where("api_path") .equals(item.api_path) .go(); // TODO handle error - del_res = db::db_select{*db_, directory_table} - .delete_query() + del_res = db::db_delete{*db_, directory_table} .where("source_path") .equals(item.source_path) .go(); diff --git a/repertory/librepertory/src/providers/meta_db.cpp b/repertory/librepertory/src/providers/meta_db.cpp index cfdd22df..4321f2d4 100644 --- a/repertory/librepertory/src/providers/meta_db.cpp +++ b/repertory/librepertory/src/providers/meta_db.cpp @@ -23,6 +23,7 @@ #include "app_config.hpp" #include "database/db_common.hpp" +#include "database/db_delete.hpp" #include "database/db_insert.hpp" #include "database/db_select.hpp" #include "utils/error_utils.hpp" @@ -241,11 +242,8 @@ void meta_db::remove_api_path(const std::string &api_path) { static_cast(__FUNCTION__), }; - auto result = db::db_select{*db_, table_name} - .delete_query() - .where("api_path") - .equals(api_path) - .go(); + auto result = + db::db_delete{*db_, table_name}.where("api_path").equals(api_path).go(); if (not result.ok()) { utils::error::raise_api_path_error( function_name, api_path, result.get_error(), "failed to remove meta"); diff --git a/repertory/repertory_test/src/database_test.cpp b/repertory/repertory_test/src/database_test.cpp index 447f1725..fadea575 100644 --- a/repertory/repertory_test/src/database_test.cpp +++ b/repertory/repertory_test/src/database_test.cpp @@ -22,12 +22,45 @@ #include "test_common.hpp" #include "database/db_common.hpp" +#include "database/db_delete.hpp" #include "database/db_insert.hpp" #include "database/db_select.hpp" #include "utils/path.hpp" namespace repertory { -TEST(database, db_insert) { +TEST(database, db_delete_query) { + console_consumer consumer1; + event_system::instance().start(); + { + db::db3_t db3; + { + sqlite3 *db3_ptr{nullptr}; + auto res = sqlite3_open_v2( + utils::path::combine(test::get_test_input_dir(), {"test.db3"}) + .c_str(), + &db3_ptr, SQLITE_OPEN_READWRITE, nullptr); + ASSERT_EQ(SQLITE_OK, res); + ASSERT_TRUE(db3_ptr != nullptr); + + db3.reset(db3_ptr); + } + + auto query = db::db_delete{*db3.get(), "table"} + .where("column1") + .equals("test1") + .and_where("column2") + .equals("test2"); + auto query_str = query.dump(); + std::cout << query_str << std::endl; + EXPECT_STREQ( + R"(DELETE FROM "table" WHERE ("column1"=?1 AND "column2"=?2);)", + query_str.c_str()); + } + + event_system::instance().stop(); +} + +TEST(database, db_insert_query) { console_consumer consumer1; event_system::instance().start(); { @@ -52,26 +85,43 @@ TEST(database, db_insert) { EXPECT_STREQ( R"(INSERT INTO "table" ("column1", "column2") VALUES (?1, ?2);)", query_str.c_str()); - - query = db::db_insert{*db3.get(), "table"} - .or_replace() - .column_value("column1", "test1") - .column_value("column2", "test2"); - query_str = query.dump(); - std::cout << query_str << std::endl; - EXPECT_STREQ( - R"(INSERT OR REPLACE INTO "table" ("column1", "column2") VALUES (?1, ?2);)", - query_str.c_str()); - - auto res = query.go(); - EXPECT_TRUE(res.ok()); - EXPECT_FALSE(res.has_row()); } event_system::instance().stop(); } -TEST(database, db_select) { +TEST(database, db_insert_or_replace_query) { + console_consumer consumer1; + event_system::instance().start(); + { + db::db3_t db3; + { + sqlite3 *db3_ptr{nullptr}; + auto res = sqlite3_open_v2( + utils::path::combine(test::get_test_input_dir(), {"test.db3"}) + .c_str(), + &db3_ptr, SQLITE_OPEN_READWRITE, nullptr); + ASSERT_EQ(SQLITE_OK, res); + ASSERT_TRUE(db3_ptr != nullptr); + + db3.reset(db3_ptr); + } + + auto query = db::db_insert{*db3.get(), "table"} + .or_replace() + .column_value("column1", "test1") + .column_value("column2", "test2"); + query_str = query.dump(); + std::cout << query_str << std::endl; + EXPECT_STREQ( + R"(INSERT OR REPLACE INTO "table" ("column1", "column2") VALUES (?1, ?2);)", + query_str.c_str()); + } + + event_system::instance().stop(); +} + +TEST(database, db_select_query) { console_consumer consumer1; event_system::instance().start(); { @@ -98,25 +148,75 @@ TEST(database, db_select) { EXPECT_STREQ( R"(SELECT * FROM "table" WHERE ("column1"=?1 AND "column2"=?2);)", query_str.c_str()); - auto res = query.go(); + // auto res = query.go(); + // + // EXPECT_TRUE(res.ok()); + // EXPECT_TRUE(res.has_row()); + // std::size_t row_count{}; + // while (res.has_row()) { + // std::optional row; + // EXPECT_TRUE(res.get_row(row)); + // EXPECT_TRUE(row.has_value()); + // if (row.has_value()) { + // for (const auto &column : row.value().get_columns()) { + // std::cout << column.get_index() << ':'; + // std::cout << column.get_name() << ':'; + // std::cout << column.get_value() << std::endl; + // } + // } + // ++row_count; + // } + // EXPECT_EQ(std::size_t(1U), row_count); + } - EXPECT_TRUE(res.ok()); - EXPECT_TRUE(res.has_row()); - std::size_t row_count{}; - while (res.has_row()) { - std::optional row; - EXPECT_TRUE(res.get_row(row)); - EXPECT_TRUE(row.has_value()); - if (row.has_value()) { - for (const auto &column : row.value().get_columns()) { - std::cout << column.get_index() << ':'; - std::cout << column.get_name() << ':'; - std::cout << column.get_value() << std::endl; - } - } - ++row_count; + event_system::instance().stop(); +} + +TEST(database, db_update_query) { + console_consumer consumer1; + event_system::instance().start(); + { + db::db3_t db3; + { + sqlite3 *db3_ptr{nullptr}; + auto res = sqlite3_open_v2( + utils::path::combine(test::get_test_input_dir(), {"test.db3"}) + .c_str(), + &db3_ptr, SQLITE_OPEN_READWRITE, nullptr); + ASSERT_EQ(SQLITE_OK, res); + ASSERT_TRUE(db3_ptr != nullptr); + + db3.reset(db3_ptr); } - EXPECT_EQ(std::size_t(1U), row_count); + + auto query = db::db_update{*db3.get(), "table"} + .column() + .where("column1") + .equals("test1") + .and_where("column2") + .equals("test2"); + auto query_str = query.dump(); + std::cout << query_str << std::endl; + EXPECT_STREQ(R"(UPDATE "table" SET ;)", query_str.c_str()); + // auto res = query.go(); + // + // EXPECT_TRUE(res.ok()); + // EXPECT_TRUE(res.has_row()); + // std::size_t row_count{}; + // while (res.has_row()) { + // std::optional row; + // EXPECT_TRUE(res.get_row(row)); + // EXPECT_TRUE(row.has_value()); + // if (row.has_value()) { + // for (const auto &column : row.value().get_columns()) { + // std::cout << column.get_index() << ':'; + // std::cout << column.get_name() << ':'; + // std::cout << column.get_value() << std::endl; + // } + // } + // ++row_count; + // } + // EXPECT_EQ(std::size_t(1U), row_count); } event_system::instance().stop(); diff --git a/repertory/repertory_test/test_config/test.db3 b/repertory/repertory_test/test_config/test.db3 new file mode 100644 index 00000000..6dc34781 Binary files /dev/null and b/repertory/repertory_test/test_config/test.db3 differ