From 34070bba890f75881abde01c0fc78b79ac7d049e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 29 Sep 2024 09:49:28 -0500 Subject: [PATCH] [bug] Rename file is broken for files that are existing #19 --- .../include/database/db_update.hpp | 102 ++++++++++++++ .../include/file_manager/file_manager.hpp | 3 +- .../librepertory/src/database/db_update.cpp | 124 ++++++++++++++++++ .../src/file_manager/file_manager.cpp | 19 ++- 4 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 repertory/librepertory/include/database/db_update.hpp create mode 100644 repertory/librepertory/src/database/db_update.cpp diff --git a/repertory/librepertory/include/database/db_update.hpp b/repertory/librepertory/include/database/db_update.hpp new file mode 100644 index 00000000..444b4710 --- /dev/null +++ b/repertory/librepertory/include/database/db_update.hpp @@ -0,0 +1,102 @@ +/* + 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_UPDATE_HPP_ +#define INCLUDE_DATABASE_DB_UPDATE_HPP_ + +#include "database/db_common.hpp" + +namespace repertory::db { +class db_update 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{}; + std::map values{}; + db3_stmt_t stmt{nullptr}; + }; + + using row = db_row; + +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_update{context_}.dump(); + } + + [[nodiscard]] auto go() const -> db_result { + return db_update{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_}; + } + }; + +public: + db_update(sqlite3 &db3, std::string table_name) + : context_(std::make_shared(db3, table_name)) {} + + db_update(std::shared_ptr ctx) : context_(std::move(ctx)) {} + +private: + std::shared_ptr context_; + +public: + [[nodiscard]] auto column_value(std::string column_name, + db_types_t value) -> db_update &; + + [[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_UPDATE_HPP_ diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 38f23994..0f265f23 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -481,7 +481,8 @@ private: void remove_upload(const std::string &api_path, bool no_lock); - void swap_renamed_items(std::string from_api_path, std::string to_api_path); + void swap_renamed_items(std::string from_api_path, std::string to_api_path, + bool directory); void upload_completed(const file_upload_completed &evt); diff --git a/repertory/librepertory/src/database/db_update.cpp b/repertory/librepertory/src/database/db_update.cpp new file mode 100644 index 00000000..d5116716 --- /dev/null +++ b/repertory/librepertory/src/database/db_update.cpp @@ -0,0 +1,124 @@ +/* + 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_update.hpp" + +namespace repertory::db { +auto db_update::dump() const -> std::string { + std::stringstream query; + query << "UPDATE \"" << context_->table_name << "\" SET "; + + for (std::int32_t idx = 0; + idx < static_cast(context_->values.size()); idx++) { + if (idx > 0) { + query << ", "; + } + + auto column = std::next(context_->values.begin(), idx); + query << column->first << " = ?"; + } + + 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_update::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_->values.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); + }, + }, + std::next(context_->values.begin(), idx)->second); + if (res != SQLITE_OK) { + utils::error::raise_error(function_name, "failed to bind|" + + std::to_string(res) + '|' + + sqlite3_errstr(res)); + return {context_, res}; + } + } + + 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_update::where(std::string column_name) const -> db_where { + return db_where{context_, column_name}; +} +} // namespace repertory::db diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 2154050a..82619d82 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -25,6 +25,7 @@ #include "database/db_common.hpp" #include "database/db_insert.hpp" #include "database/db_select.hpp" +#include "database/db_update.hpp" #include "file_manager/events.hpp" #include "providers/i_provider.hpp" #include "types/repertory.hpp" @@ -415,7 +416,7 @@ auto file_manager::handle_file_rename(const std::string &from_api_path, return ret; } - swap_renamed_items(from_api_path, to_api_path); + swap_renamed_items(from_api_path, to_api_path, false); ret = source_path.empty() ? api_error::success @@ -709,7 +710,7 @@ auto file_manager::rename_directory(const std::string &from_api_path, return res; } - swap_renamed_items(from_api_path, to_api_path); + swap_renamed_items(from_api_path, to_api_path, true); return api_error::success; } @@ -965,14 +966,24 @@ void file_manager::store_resume(const i_open_file &file) { } void file_manager::swap_renamed_items(std::string from_api_path, - std::string to_api_path) { + std::string to_api_path, bool directory) { auto file_iter = open_file_lookup_.find(from_api_path); if (file_iter != open_file_lookup_.end()) { auto ptr = std::move(open_file_lookup_[from_api_path]); open_file_lookup_.erase(from_api_path); ptr->set_api_path(to_api_path); - open_file_lookup_[to_api_path] = ptr; + open_file_lookup_[to_api_path] = std::move(ptr); } + + if (directory) { + return; + } + + auto result = db::db_update{*db_.get(), resume_table} + .column_value("api_path", to_api_path) + .where("api_path") + .equals(from_api_path) + .go(); } void file_manager::upload_completed(const file_upload_completed &evt) {