diff --git a/repertory/librepertory/include/database/db_column.hpp b/repertory/librepertory/include/database/db_column.hpp deleted file mode 100644 index 19a45a8b..00000000 --- a/repertory/librepertory/include/database/db_column.hpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - 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_COMMON_HPP_ -#define INCLUDE_DATABASE_DB_COMMON_HPP_ - -#include "utils/error_utils.hpp" - -namespace repertory::db { -using db_types_t = std::variant; - -struct sqlite3_deleter { - void operator()(sqlite3 *db3) { - if (db3 != nullptr) { - sqlite3_close_v2(db3); - } - } -}; - -using db3_t = std::unique_ptr; - -struct sqlite3_statement_deleter { - void operator()(sqlite3_stmt *stmt) { - if (stmt != nullptr) { - sqlite3_finalize(stmt); - } - } -}; - -using db3_stmt_t = std::unique_ptr; - -struct comp_data_t final { - std::string column_name; - std::string op_type; - db_types_t value; -}; - -class db_column final { -public: - db_column() noexcept = default; - db_column(const db_column &) = default; - db_column(db_column &&column) noexcept = default; - ~db_column() = default; - - auto operator=(const 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: - std::int32_t index_{}; - std::string name_; - db_types_t value_; - -public: - [[nodiscard]] auto get_index() const -> std::int32_t { return index_; } - - [[nodiscard]] auto get_name() const -> std::string { return name_; } - - template - [[nodiscard]] auto get_value() const -> data_type { - return std::visit( - overloaded{ - [](const data_type &value) -> data_type { return value; }, - [](auto &&) -> data_type { - throw std::runtime_error("data type not supported"); - }, - }, - value_); - } - - [[nodiscard]] auto 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_); - } -}; - -template class db_row final { -public: - db_row(std::shared_ptr context) { - auto column_count = sqlite3_column_count(context->stmt.get()); - for (std::int32_t col = 0; col < column_count; col++) { - std::string name{sqlite3_column_name(context->stmt.get(), col)}; - auto column_type = sqlite3_column_type(context->stmt.get(), col); - - db_types_t value; - switch (column_type) { - case SQLITE_INTEGER: { - value = sqlite3_column_int64(context->stmt.get(), col); - } break; - - case SQLITE_TEXT: { - const auto *text = reinterpret_cast( - sqlite3_column_text(context->stmt.get(), col)); - value = std::string(text == nullptr ? "" : text); - } break; - - default: - throw std::runtime_error("column type not implemented|" + name + '|' + - std::to_string(column_type)); - } - - columns_[name] = db_column{col, name, value}; - } - } - -private: - std::map columns_; - -public: - [[nodiscard]] auto get_columns() const -> std::vector { - std::vector ret; - for (const auto &item : columns_) { - ret.push_back(item.second); - } - return ret; - } - - [[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 { - return columns_.at(name); - } -}; - -template struct db_result final { - db_result(std::shared_ptr context, std::int32_t res) - : context_(std::move(context)), res_(res) { - constexpr const auto *function_name = - static_cast(__FUNCTION__); - - if (res == SQLITE_OK) { - set_res(sqlite3_step(context_->stmt.get()), function_name); - } - } - -private: - std::shared_ptr context_; - mutable std::int32_t res_; - -private: - void set_res(std::int32_t res, std::string_view function) const { - if (res != SQLITE_OK && res != SQLITE_DONE && res != SQLITE_ROW) { - utils::error::raise_error(function, "failed to step|" + - std::to_string(res) + '|' + - sqlite3_errstr(res)); - } - res_ = res; - } - -public: - [[nodiscard]] auto ok() const -> bool { - return res_ == SQLITE_DONE || res_ == SQLITE_ROW; - } - - [[nodiscard]] auto get_error() const -> std::int32_t { return res_; } - - [[nodiscard]] auto get_error_str() const -> std::string { - return sqlite3_errstr(res_); - } - - [[nodiscard]] auto - get_row(std::optional> &row) const -> bool { - constexpr const auto *function_name = - static_cast(__FUNCTION__); - - row.reset(); - if (has_row()) { - row = db_row{context_}; - set_res(sqlite3_step(context_->stmt.get()), function_name); - return true; - } - return false; - } - - [[nodiscard]] auto has_row() const -> bool { return res_ == SQLITE_ROW; } - - void next_row() const { - constexpr const auto *function_name = - static_cast(__FUNCTION__); - - if (has_row()) { - set_res(sqlite3_step(context_->stmt.get()), function_name); - } - } -}; - -inline void set_journal_mode(sqlite3 &db3) { - sqlite3_exec(&db3, "PRAGMA journal_mode = WAL;PRAGMA synchronous = NORMAL;", - nullptr, nullptr, nullptr); -} - -[[nodiscard]] inline auto execute_sql(sqlite3 &db3, const std::string &sql, - std::string &err) -> bool { - 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) { - err = "failed to execute sql|" + sql + "|" + std::to_string(res) + '|' + - (err.empty() ? sqlite3_errstr(res) : err); - return false; - } - - return true; -} - -template struct db_where_t final { - db_where_t(std::shared_ptr ctx, std::string column_name) - : context_(std::move(ctx)), column_name_(std::move(column_name)) {} - -private: - std::shared_ptr context_; - std::string column_name_; - -public: - [[nodiscard]] auto equals(db_types_t value) const -> next_t { - context_->ands.emplace_back(comp_data_t{column_name_, "=", value}); - return next_t{context_}; - } -}; - -template -struct db_where_next_t final { - db_where_next_t(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_t { - return db_where_t{context_, column_name}; - } - - [[nodiscard]] auto dump() const -> std::string { - return operation_t{context_}.dump(); - } - - [[nodiscard]] auto go() const -> db_result { - return operation_t{context_}.go(); - } -}; - -template struct db_where_with_limit_next_t 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_t { - return db_where_t{context_, column_name}; - } - - [[nodiscard]] auto dump() const -> std::string { - return operation_t{context_}.dump(); - } - - [[nodiscard]] auto go() const -> db_result { - return operation_t{context_}.go(); - } - - [[nodiscard]] auto limit(std::int32_t value) const -> operation_t { - return operation_t{context_}.limit(value); - } - - [[nodiscard]] auto order_by(std::string column_name, - bool ascending) const -> operation_t { - return operation_t{context_}.order_by(column_name, ascending); - } -}; -} // namespace repertory::db - -#endif // INCLUDE_DATABASE_DB_COMMON_HPP_ diff --git a/repertory/librepertory/include/database/db_common.hpp b/repertory/librepertory/include/database/db_common.hpp index 585fd11c..8d0a1fdb 100644 --- a/repertory/librepertory/include/database/db_common.hpp +++ b/repertory/librepertory/include/database/db_common.hpp @@ -23,6 +23,7 @@ #define INCLUDE_DATABASE_DB_COMMON_HPP_ #include "utils/error_utils.hpp" +#include namespace repertory::db { using db_types_t = std::variant; @@ -225,77 +226,333 @@ public: } }; -// .where([](db_group &grp) { -// grp.where("column1").equals("test0").and_where("column1").equals( -// "test1"); -// }); -template struct db_where_t final { - db_where_t(std::shared_ptr ctx, std::string column_name) - : context_(std::move(ctx)), column_name_(std::move(column_name)) {} +template +struct db_next_t final { + std::string action; + w_t &parent; + w_t next{parent.ctx, &parent}; -private: - std::shared_ptr context_; - std::string column_name_; + using group_func_t = std::function; -public: - [[nodiscard]] auto equals(db_types_t value) const -> next_t { - context_->ands.emplace_back(comp_data_t{column_name_, "=", value}); - return next_t{context_}; + [[nodiscard]] auto column(std::string column_name) -> cn_t { + return next.column(column_name); + } + + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return parent.dump(idx); + } + + [[nodiscard]] auto go() const -> auto { return op_t{parent.ctx}.go(); } + + [[nodiscard]] auto group(group_func_t func) -> wn_t { + return next.group(std::move(func)); } }; -template +template +struct db_next_limit_t final { + std::string action; + w_t &parent; + w_t next{parent.ctx, &parent}; + + using group_func_t = std::function; + + [[nodiscard]] auto column(std::string column_name) -> cn_t { + return next.column(column_name); + } + + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return parent.dump(idx); + } + + [[nodiscard]] auto go() const -> auto { return op_t{parent.ctx}.go(); } + + [[nodiscard]] auto group(group_func_t func) -> wn_t { + return next.group(std::move(func)); + } + + [[nodiscard]] auto limit(std::int32_t value) -> op_t { + return op_t{parent.ctx}.limit(value); + } + + [[nodiscard]] auto order_by(std::string column_name, bool ascending) -> op_t { + return op_t{parent.ctx}.order_by(column_name, ascending); + } +}; + +template struct db_where_next_t final { - db_where_next_t(std::shared_ptr ctx) : context_(std::move(ctx)) {} + w_t &owner; + w_t &parent; -private: - std::shared_ptr context_; + using n_t = db_next_t; -public: - [[nodiscard]] auto and_where(std::string column_name) const - -> db_where_t { - return db_where_t{context_, column_name}; + [[nodiscard]] auto and_() -> n_t & { + owner.actions.emplace_back(n_t{ + "AND", + parent, + }); + + return std::get(*std::prev(owner.actions.end())); } - [[nodiscard]] auto dump() const -> std::string { - return operation_t{context_}.dump(); + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return parent.dump(idx); } - [[nodiscard]] auto go() const -> db_result { - return operation_t{context_}.go(); + [[nodiscard]] auto go() const -> auto { return op_t{parent.ctx}.go(); } + + [[nodiscard]] auto or_() -> n_t & { + owner.actions.emplace_back(n_t{ + "OR", + parent, + }); + + return std::get(*std::prev(owner.actions.end())); + } +}; + +template +struct db_where_next_limit_t final { + w_t &owner; + w_t &parent; + + using n_t = db_next_t; + + [[nodiscard]] auto and_() -> n_t & { + owner.actions.emplace_back(n_t{ + "AND", + parent, + }); + + return std::get(*std::prev(owner.actions.end())); + } + + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return parent.dump(idx); + } + + [[nodiscard]] auto go() const -> auto { return op_t{parent.ctx}.go(); } + + [[nodiscard]] auto limit(std::int32_t value) -> op_t { + return op_t{parent.ctx}.limit(value); + } + + [[nodiscard]] auto order_by(std::string column_name, bool ascending) -> op_t { + return op_t{parent.ctx}.order_by(column_name, ascending); + } + + [[nodiscard]] auto or_() -> n_t & { + owner.actions.emplace_back(n_t{ + "OR", + parent, + }); + + return std::get(*std::prev(owner.actions.end())); + } +}; + +template struct db_comp_next_t final { + w_t &owner; + w_t &parent; + std::string column_name; + + using wn_t = db_where_next_t; + + [[nodiscard]] auto create(std::string operation, db::db_types_t value) { + owner.actions.emplace_back(comp_data_t{ + column_name, + operation, + value, + }); + + return wn_t{ + owner, + parent, + }; + } + + auto equals(db::db_types_t value) -> wn_t { return create("=", value); }; + + auto gt(db::db_types_t value) -> wn_t { return create(">", value); } + + auto gte(db::db_types_t value) -> wn_t { return create(">=", value); } + + auto like(db::db_types_t value) -> wn_t { return create("LIKE", value); } + + auto lt(db::db_types_t value) -> wn_t { return create("<", value); } + + auto lte(db::db_types_t value) -> wn_t { return create("<=", value); } + + auto not_equals(db::db_types_t value) -> wn_t { return create("!=", value); }; +}; + +template struct db_comp_next_limit_t final { + w_t &owner; + w_t &parent; + std::string column_name; + + using wn_t = db_where_next_limit_t; + + [[nodiscard]] auto create(std::string operation, db::db_types_t value) { + owner.actions.emplace_back(comp_data_t{ + column_name, + operation, + value, + }); + + return wn_t{ + owner, + parent, + }; + } + + auto equals(db::db_types_t value) -> wn_t { return create("=", value); }; + + auto gt(db::db_types_t value) -> wn_t { return create(">", value); } + + auto gte(db::db_types_t value) -> wn_t { return create(">=", value); } + + auto like(db::db_types_t value) -> wn_t { return create("LIKE", value); } + + auto lt(db::db_types_t value) -> wn_t { return create("<", value); } + + auto lte(db::db_types_t value) -> wn_t { return create("<=", value); } + + auto not_equals(db::db_types_t value) -> wn_t { return create("!=", value); }; +}; + +template struct db_where_t final { + std::shared_ptr ctx; + db_where_t *parent{this}; + + using cn_t = db_comp_next_t; + using wn_t = db_where_next_t; + using n_t = db_next_t; + + using group_func_t = std::function; + + using action_t = std::variant; + + std::vector actions{}; + + [[nodiscard]] static auto dump(std::int64_t &idx, + auto &&data) -> std::string { + std::stringstream stream; + + for (auto &&action : data.actions) { + std::visit(overloaded{ + [&idx, &stream](const comp_data_t &comp) { + stream << comp.column_name << comp.op_type + << '?' + std::to_string(++idx); + }, + [&idx, &stream](const n_t &next) { + stream << ' ' << next.action << ' ' + << dump(idx, next.next); + }, + [&idx, &stream](const db_where_t &where) { + stream << '(' << dump(idx, where) << ')'; + }, + }, + action); + } + + return stream.str(); + } + + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return dump(idx, *this); + } + + [[nodiscard]] auto group(group_func_t func) -> wn_t { + db_where_t where{ctx, parent}; + func(where); + + actions.emplace_back(std::move(where)); + return wn_t{ + *this, + *parent, + }; + } + + [[nodiscard]] auto where(std::string column_name) -> cn_t { + return cn_t{ + *this, + *parent, + column_name, + }; } }; template -struct db_where_with_limit_next_t final { - db_where_with_limit_next_t(std::shared_ptr ctx) - : context_(std::move(ctx)) {} +struct db_where_with_limit_t final { + std::shared_ptr ctx; + db_where_with_limit_t *parent{this}; -private: - std::shared_ptr context_; + using cn_t = db_comp_next_limit_t; + using wn_t = db_where_next_limit_t; + using n_t = db_next_limit_t; -public: - [[nodiscard]] auto and_where(std::string column_name) const - -> db_where_t { - return db_where_t{context_, - column_name}; + using group_func_t = std::function; + + using action_t = std::variant; + + std::vector actions{}; + + [[nodiscard]] static auto dump(std::int64_t &idx, + auto &&data) -> std::string { + std::stringstream stream; + + for (auto &&action : data.actions) { + std::visit(overloaded{ + [&idx, &stream](const comp_data_t &comp) { + stream << comp.column_name << comp.op_type + << '?' + std::to_string(++idx); + }, + [&idx, &stream](const n_t &next) { + stream << ' ' << next.action << ' ' + << dump(idx, next.next); + }, + [&idx, &stream](const db_where_with_limit_t &where) { + stream << '(' << dump(idx, where) << ')'; + }, + }, + action); + } + + return stream.str(); } - [[nodiscard]] auto dump() const -> std::string { - return operation_t{context_}.dump(); + [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { + return dump(idx, *this); } - [[nodiscard]] auto go() const -> db_result { - return operation_t{context_}.go(); + [[nodiscard]] auto group(group_func_t func) -> wn_t { + db_where_with_limit_t where{ctx, parent}; + func(where); + + actions.emplace_back(std::move(where)); + return wn_t{ + *this, + *parent, + }; } - [[nodiscard]] auto limit(std::int32_t value) const -> operation_t { - return operation_t{context_}.limit(value); + [[nodiscard]] auto limit(std::int32_t value) -> operation_t { + return operation_t{ctx}.limit(value); } [[nodiscard]] auto order_by(std::string column_name, - bool ascending) const -> operation_t { - return operation_t{context_}.order_by(column_name, ascending); + bool ascending) -> operation_t { + return operation_t{ctx}.order_by(column_name, ascending); + } + + [[nodiscard]] auto where(std::string column_name) -> cn_t { + return cn_t{ + *this, + *parent, + column_name, + }; } }; } // namespace repertory::db diff --git a/repertory/librepertory/include/database/db_delete.hpp b/repertory/librepertory/include/database/db_delete.hpp index e3bbbfb8..f4ce0cf9 100644 --- a/repertory/librepertory/include/database/db_delete.hpp +++ b/repertory/librepertory/include/database/db_delete.hpp @@ -35,7 +35,7 @@ public: sqlite3 &db3; std::string table_name; - std::vector ands{}; + std::optional> where; db3_stmt_t stmt{nullptr}; }; @@ -55,8 +55,11 @@ public: [[nodiscard]] auto go() const -> db_result; - [[nodiscard]] auto where(std::string column_name) const - -> db_where_t>; + [[nodiscard]] auto group(db_where_t::group_func_t func) + -> db_where_t::wn_t; + + [[nodiscard]] auto + where(std::string column_name) const -> db_where_t::cn_t; }; } // namespace repertory::db diff --git a/repertory/librepertory/include/database/db_select.hpp b/repertory/librepertory/include/database/db_select.hpp index 526d6360..3c2daf84 100644 --- a/repertory/librepertory/include/database/db_select.hpp +++ b/repertory/librepertory/include/database/db_select.hpp @@ -36,11 +36,11 @@ public: sqlite3 &db3; std::string table_name; - std::vector ands{}; std::vector columns{}; std::map count_columns{}; std::optional limit; std::optional> order_by; + std::optional> where; db3_stmt_t stmt{nullptr}; }; @@ -65,13 +65,17 @@ public: [[nodiscard]] auto go() const -> db_result; + [[nodiscard]] auto + group(db_where_with_limit_t::group_func_t func) + -> db_where_with_limit_t::wn_t; + [[nodiscard]] auto limit(std::int32_t value) -> db_select &; [[nodiscard]] auto order_by(std::string column_name, bool ascending) -> db_select &; [[nodiscard]] auto where(std::string column_name) const - -> db_where_t>; + -> db_where_with_limit_t::cn_t; }; } // namespace repertory::db diff --git a/repertory/librepertory/include/database/db_update.hpp b/repertory/librepertory/include/database/db_update.hpp index e2ab90f3..de6002e3 100644 --- a/repertory/librepertory/include/database/db_update.hpp +++ b/repertory/librepertory/include/database/db_update.hpp @@ -34,7 +34,7 @@ public: sqlite3 &db3; std::string table_name; - std::vector ands{}; + std::optional> where; std::map values{}; std::optional limit; std::optional> order_by; @@ -60,13 +60,17 @@ public: [[nodiscard]] auto go() const -> db_result; + [[nodiscard]] auto + group(db_where_with_limit_t::group_func_t func) + -> db_where_with_limit_t::wn_t; + [[nodiscard]] auto limit(std::int32_t value) -> db_update &; [[nodiscard]] auto order_by(std::string column_name, bool ascending) -> db_update &; [[nodiscard]] auto where(std::string column_name) const - -> db_where_t>; + -> db_where_with_limit_t::cn_t; }; } // namespace repertory::db diff --git a/repertory/librepertory/src/database/db_delete.cpp b/repertory/librepertory/src/database/db_delete.cpp index 4f0be276..9e766fb0 100644 --- a/repertory/librepertory/src/database/db_delete.cpp +++ b/repertory/librepertory/src/database/db_delete.cpp @@ -26,19 +26,9 @@ 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 << ")"; + if (context_->where.has_value()) { + std::int64_t idx{}; + query << " WHERE " << context_->where->dump(idx); } query << ';'; @@ -63,33 +53,46 @@ auto db_delete::go() const -> db_result { } 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}; - } - } + // 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::group(db_where_t::group_func_t func) + -> db_where_t::wn_t { + if (not context_->where.has_value()) { + context_->where = db_where_t{context_}; + } + + return context_->where->group(std::move(func)); +} + auto db_delete::where(std::string column_name) const - -> db_where_t> { - return db_where_t>{context_, - column_name}; + -> db_where_t::cn_t { + if (not context_->where.has_value()) { + context_->where = db_where_t{context_}; + } + + return context_->where->where(column_name); } } // namespace repertory::db diff --git a/repertory/librepertory/src/database/db_select.cpp b/repertory/librepertory/src/database/db_select.cpp index 2ac78270..5fb53e36 100644 --- a/repertory/librepertory/src/database/db_select.cpp +++ b/repertory/librepertory/src/database/db_select.cpp @@ -63,19 +63,9 @@ auto db_select::dump() const -> std::string { } query << " 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 << ")"; + if (context_->where.has_value()) { + std::int64_t idx{}; + query << " WHERE " << context_->where->dump(idx); } if (context_->order_by.has_value()) { @@ -109,30 +99,41 @@ auto db_select::go() const -> db_result { } 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}; - } - } + // 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_select::group( + db_where_with_limit_t::group_func_t func) + -> db_where_with_limit_t::wn_t { + if (not context_->where.has_value()) { + context_->where = db_where_with_limit_t{context_}; + } + + return context_->where->group(std::move(func)); +} + auto db_select::limit(std::int32_t value) -> db_select & { context_->limit = value; return *this; @@ -145,8 +146,11 @@ auto db_select::order_by(std::string column_name, } auto db_select::where(std::string column_name) const - -> db_where_t> { - return db_where_t>{ - context_, column_name}; + -> db_where_with_limit_t::cn_t { + if (not context_->where.has_value()) { + context_->where = db_where_with_limit_t{context_}; + } + + return context_->where->where(column_name); } } // namespace repertory::db diff --git a/repertory/librepertory/src/database/db_update.cpp b/repertory/librepertory/src/database/db_update.cpp index 1441b330..31b2ab53 100644 --- a/repertory/librepertory/src/database/db_update.cpp +++ b/repertory/librepertory/src/database/db_update.cpp @@ -42,19 +42,9 @@ auto db_update::dump() const -> std::string { query << '"' << column->first << "\"=?" + std::to_string(idx + 1); } - 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 + static_cast(context_->values.size()) + 1); - } - query << ")"; + if (context_->where.has_value()) { + auto idx{static_cast(context_->values.size())}; + query << " WHERE " << context_->where->dump(idx); } if (context_->order_by.has_value()) { @@ -109,35 +99,46 @@ auto db_update::go() const -> db_result { } } - 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 + static_cast(context_->values.size()) + 1, - data); - }, - [this, &idx](const std::string &data) -> std::int32_t { - return sqlite3_bind_text( - context_->stmt.get(), - idx + static_cast(context_->values.size()) + 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}; - } - } + // 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 + static_cast(context_->values.size()) + + // 1, data); + // }, + // [this, &idx](const std::string &data) -> std::int32_t { + // return sqlite3_bind_text( + // context_->stmt.get(), + // idx + static_cast(context_->values.size()) + + // 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::group( + db_where_with_limit_t::group_func_t func) + -> db_where_with_limit_t::wn_t { + if (not context_->where.has_value()) { + context_->where = db_where_with_limit_t{context_}; + } + + return context_->where->group(std::move(func)); +} + auto db_update::limit(std::int32_t value) -> db_update & { context_->limit = value; return *this; @@ -150,8 +151,11 @@ auto db_update::order_by(std::string column_name, } auto db_update::where(std::string column_name) const - -> db_where_t> { - return db_where_t>{ - context_, column_name}; + -> db_where_with_limit_t::cn_t { + if (not context_->where.has_value()) { + context_->where = db_where_with_limit_t{context_}; + } + + return context_->where->where(column_name); } } // namespace repertory::db diff --git a/repertory/repertory_test/src/database_test.cpp b/repertory/repertory_test/src/database_test.cpp index e526f823..4a1f426b 100644 --- a/repertory/repertory_test/src/database_test.cpp +++ b/repertory/repertory_test/src/database_test.cpp @@ -27,7 +27,6 @@ #include "database/db_select.hpp" #include "database/db_update.hpp" #include "utils/path.hpp" -#include namespace repertory { class database_test : public ::testing::Test { @@ -139,9 +138,11 @@ TEST_F(database_test, db_delete_where_query) { auto query = db::db_delete{*db3.get(), "table"} .where("column1") .equals("test1") - .and_where("column2") + .and_() + .column("column2") .equals("test2"); - auto query_str = query.dump(); + std::int64_t idx{}; + auto query_str = query.dump(idx); std::cout << query_str << std::endl; EXPECT_STREQ(R"(DELETE FROM "table" WHERE ("column1"=?1 AND "column2"=?2);)", query_str.c_str()); @@ -180,9 +181,11 @@ TEST_F(database_test, db_select_where_query) { auto query = db::db_select{*db3.get(), "table"} .where("column1") .equals("test1") - .and_where("column2") + .and_() + .column("column2") .equals("test2"); - auto query_str = query.dump(); + std::int64_t idx{}; + auto query_str = query.dump(idx); std::cout << query_str << std::endl; EXPECT_STREQ( R"(SELECT * FROM "table" WHERE ("column1"=?1 AND "column2"=?2);)", @@ -195,9 +198,11 @@ TEST_F(database_test, db_select_columns_query) { .column("column2") .where("column1") .equals("test1") - .and_where("column2") + .and_() + .column("column2") .equals("test2"); - auto query_str = query.dump(); + std::int64_t idx{}; + auto query_str = query.dump(idx); std::cout << query_str << std::endl; EXPECT_STREQ( R"(SELECT column1, column2 FROM "table" WHERE ("column1"=?1 AND "column2"=?2);)", @@ -209,9 +214,11 @@ TEST_F(database_test, db_update_query) { .column_value("column1", "moose") .where("column1") .equals("test1") - .and_where("column2") + .and_() + .column("column2") .equals("test2"); - auto query_str = query.dump(); + std::int64_t idx{}; + auto query_str = query.dump(idx); std::cout << query_str << std::endl; EXPECT_STREQ( R"(UPDATE "table" SET "column1"=?1 WHERE ("column1"=?2 AND "column2"=?3);)", @@ -234,7 +241,8 @@ TEST_F(database_test, insert_update_delete) { .column_value("column1", "moose") .where("column1") .equals("test0"); - std::cout << query.dump() << std::endl; + std::int64_t idx{}; + std::cout << query.dump(idx) << std::endl; auto res = query.go(); EXPECT_TRUE(res.ok()); } @@ -262,176 +270,25 @@ TEST_F(database_test, insert_or_replace_and_delete) { common_delete(*db3.get()); } -struct comp_t final { - std::string column_name; - std::string comparison; - db::db_types_t data; -}; - -template struct next_t final { - std::string action; - w_t &parent; - w_t next{&parent}; - - using group_func_t = std::function; - - [[nodiscard]] auto column(std::string column_name) -> cn_t { - return next.column(column_name); - } - - [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { - return parent.dump(idx); - } - - [[nodiscard]] auto group(group_func_t func) -> wn_t { - return next.group(std::move(func)); - } -}; - -template struct where_next_t final { - w_t &owner; - w_t &parent; - - using n_t = next_t; - - [[nodiscard]] auto and_() -> n_t & { - owner.actions.emplace_back(n_t{ - "AND", - parent, - }); - - return std::get(*std::prev(owner.actions.end())); - } - - [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { - return parent.dump(idx); - } - - [[nodiscard]] auto or_() -> n_t & { - owner.actions.emplace_back(n_t{ - "OR", - parent, - }); - - return std::get(*std::prev(owner.actions.end())); - } -}; - -template struct comp_next_t final { - w_t &owner; - w_t &parent; - std::string column_name; - - using wn_t = where_next_t; - - [[nodiscard]] auto create(std::string operation, db::db_types_t value) { - owner.actions.emplace_back(comp_t{ - column_name, - operation, - value, - }); - - return wn_t{ - owner, - parent, - }; - } - - auto equals(db::db_types_t value) -> wn_t { return create("=", value); }; - - auto gt(db::db_types_t value) -> wn_t { return create(">", value); } - - auto gte(db::db_types_t value) -> wn_t { return create(">=", value); } - - auto like(db::db_types_t value) -> wn_t { return create("LIKE", value); } - - auto lt(db::db_types_t value) -> wn_t { return create("<", value); } - - auto lte(db::db_types_t value) -> wn_t { return create("<=", value); } - - auto not_equals(db::db_types_t value) -> wn_t { return create("!=", value); }; -}; - -struct where_t final { - where_t *parent{this}; - - using cn_t = comp_next_t; - using wn_t = where_next_t; - using n_t = next_t; - - using group_func_t = std::function; - - using action_t = std::variant; - - std::vector actions{}; - - [[nodiscard]] auto column(std::string column_name) -> cn_t { - return comp_next_t{ - *this, - *parent, - column_name, - }; - } - - [[nodiscard]] static auto dump(std::int64_t &idx, - auto &&data) -> std::string { - std::stringstream stream; - - for (auto &&action : data.actions) { - std::visit(overloaded{ - [&idx, &stream](const comp_t &comp) { - stream << comp.column_name << comp.comparison - << '?' + std::to_string(++idx); - }, - [&idx, &stream](const n_t &next) { - stream << ' ' << next.action << ' ' - << dump(idx, next.next); - }, - [&idx, &stream](const where_t &where) { - stream << '(' << dump(idx, where) << ')'; - }, - }, - action); - } - - return stream.str(); - } - - [[nodiscard]] auto dump(std::int64_t &idx) const -> std::string { - return dump(idx, *this); - } - - [[nodiscard]] auto group(group_func_t func) -> wn_t { - where_t where{parent}; - func(where); - - actions.emplace_back(std::move(where)); - return wn_t{ - *this, - *parent, - }; - } -}; - TEST(database, groups) { - // "s=a and r=t" - std::int64_t idx{0U}; - auto str = - where_t().column("s").equals("a").and_().column("r").equals("t").dump( - idx); - std::cout << str << std::endl; - - // "(s=a and r=t) OR (s=c or s=x)" - idx = 0U; - str = where_t() - .group([](where_t &where) { - where.column("s").equals("a").and_().column("r").equals("t"); - }) - .or_() - .group([](where_t &where) { - where.column("s").equals("c").or_().column("s").equals("x"); - }) - .dump(idx); - std::cout << str << std::endl; + // // "s=a and r=t" + // std::int64_t idx{0U}; + // auto str = + // db_where_t().column("s").equals("a").and_().column("r").equals("t").dump( + // idx); + // std::cout << str << std::endl; + // + // // "(s=a and r=t) OR (s=c or s=x)" + // idx = 0U; + // str = db_where_t() + // .group([](db_where_t &where) { + // where.column("s").equals("a").and_().column("r").equals("t"); + // }) + // .or_() + // .group([](db_where_t &where) { + // where.column("s").equals("c").or_().column("s").equals("x"); + // }) + // .dump(idx); + // std::cout << str << std::endl; } } // namespace repertory