Files
msime-windows/engine/local_modes/quick_phrase_query.cpp
T
fanlusky 2c3546f823 perf(engine): 简拼、快捷短语、表情、颜文字查询复用词库连接
这四种模式每个键都新开一次 SQLite 连接,第一次 prepare 还要解析整个 schema
——msime.db 有两百多张分表。新增 local_modes/local_database,四个查询统一经它
打开只读连接:路径正是当前数据目录下的 msime.db 或 others.db 时共享一条连接,
其余路径(测试、工具)照旧每次独立打开、用完即关。

只共享这两个随安装包发布的库:msime.db 本来就被引擎整个会话持有,others.db 只在
Server 停止时由安装程序替换;保持其它文件常开会让删除它们的工具或测试在 Windows
上失败。close_default_user_database() 同时释放这些连接,调用方原本就用它在删除
或替换数据目录前放开文件。

引擎测试(本地单独构建,CI 不跑)23/23 通过;不加释放钩子时 input_session、
mixed_expressive_input_session、jianpin_input_session 会因目录删不掉而失败。
2026-09-27 16:10:01 +08:00

100 lines
3.2 KiB
C++

#include "../contracts/assets/assets.h"
#include "quick_phrase_query.h"
#include "local_database.h"
#include "../core/data_path.h"
#include <sqlite3.h>
#include <algorithm>
#include <memory>
namespace metasequoia::local_modes
{
namespace
{
struct StatementCloser
{
void operator()(sqlite3_stmt *statement) const
{
if (statement != nullptr)
{
sqlite3_finalize(statement);
}
}
};
using Statement = std::unique_ptr<sqlite3_stmt, StatementCloser>;
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.");
}
const std::shared_ptr<sqlite3> database = open_local_database(database_path);
if (!database)
{
return query_failure("Quick phrase database is unavailable.");
}
constexpr const char *kSql = "SELECT key,value,weight FROM quick_parases WHERE key>=?1 AND key<?2 "
"ORDER BY weight DESC,key,value LIMIT ?3";
sqlite3_stmt *raw_statement = nullptr;
if (sqlite3_prepare_v2(database.get(), kSql, -1, &raw_statement, nullptr) != SQLITE_OK)
{
return query_failure("Quick phrase database could not be queried.");
}
Statement statement(raw_statement);
std::string upper_bound = prefix;
upper_bound.push_back(static_cast<char>(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<const char *>(sqlite3_column_text(statement.get(), 0));
const auto *value = reinterpret_cast<const char *>(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