#include "../contracts/assets/assets.h" #include "quick_phrase_query.h" #include "../core/data_path.h" #include #include #include namespace metasequoia::local_modes { namespace { struct DatabaseCloser { void operator()(sqlite3 *database) const { if (database != nullptr) { sqlite3_close(database); } } }; struct StatementCloser { void operator()(sqlite3_stmt *statement) const { if (statement != nullptr) { sqlite3_finalize(statement); } } }; using Database = std::unique_ptr; using Statement = std::unique_ptr; bool valid_prefix(const std::string &prefix) { return !prefix.empty() && std::all_of(prefix.begin(), prefix.end(), [](unsigned char character) { return character >= 'a' && character <= 'z'; }); } QuickPhraseQueryResult query_failure(const char *diagnostic) { return {{}, std::string(diagnostic)}; } } // namespace QuickPhraseQueryResult query_quick_phrases(const std::string &prefix, int limit) { return query_quick_phrases(prefix, data_file_path(metasequoia::assets::main_dictionary), limit); } QuickPhraseQueryResult query_quick_phrases(const std::string &prefix, const std::filesystem::path &database_path, int limit) { if (!valid_prefix(prefix) || limit <= 0) { return {}; } if (database_path.empty()) { return query_failure("Quick phrase database is unavailable."); } sqlite3 *raw_database = nullptr; if (sqlite3_open_v2(path_to_utf8(database_path).c_str(), &raw_database, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) { if (raw_database != nullptr) { sqlite3_close(raw_database); } return query_failure("Quick phrase database is unavailable."); } Database database(raw_database); sqlite3_busy_timeout(database.get(), 1000); constexpr const char *kSql = "SELECT key,value,weight FROM quick_parases WHERE key>=?1 AND key(0x7f)); if (sqlite3_bind_text(statement.get(), 1, prefix.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK || sqlite3_bind_text(statement.get(), 2, upper_bound.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK || sqlite3_bind_int(statement.get(), 3, limit) != SQLITE_OK) { return query_failure("Quick phrase database could not be queried."); } QuickPhraseQueryResult result; int step_result = SQLITE_ROW; while ((step_result = sqlite3_step(statement.get())) == SQLITE_ROW) { const auto *key = reinterpret_cast(sqlite3_column_text(statement.get(), 0)); const auto *value = reinterpret_cast(sqlite3_column_text(statement.get(), 1)); if (key != nullptr && value != nullptr) { result.candidates.emplace_back(key, value, sqlite3_column_int64(statement.get(), 2), CandidateSource::QuickPhrase); } } if (step_result != SQLITE_DONE) { return query_failure("Quick phrase database could not be queried."); } return result; } } // namespace metasequoia::local_modes