Files
msime-windows/engine/local_modes/local_database.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

103 lines
2.8 KiB
C++

#include "../contracts/assets/assets.h"
#include "local_database.h"
#include "../core/data_path.h"
#include <algorithm>
#include <mutex>
#include <string>
#include <vector>
namespace metasequoia::local_modes
{
namespace
{
std::shared_ptr<sqlite3> open_read_only(const std::filesystem::path &path)
{
sqlite3 *raw = nullptr;
// FULLMUTEX: a shared connection is used from the IME worker and from the
// emoji/kaomoji lookup threads.
if (sqlite3_open_v2(path_to_utf8(path).c_str(), &raw, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) !=
SQLITE_OK)
{
if (raw != nullptr)
{
sqlite3_close(raw);
}
return {};
}
sqlite3_busy_timeout(raw, 1000);
return std::shared_ptr<sqlite3>(raw, [](sqlite3 *database) { sqlite3_close(database); });
}
// Only the shipped dictionaries are shared: msime.db is held open by the engine
// for the whole session anyway, and others.db is replaced only by the installer
// while the Server is stopped. Keeping anything else open could block a tool or
// a test that deletes its database afterwards, which Windows refuses while a
// handle is open.
bool is_shared_dictionary(const std::filesystem::path &path)
{
return path == data_file_path(metasequoia::assets::main_dictionary) ||
path == data_file_path(metasequoia::assets::other_dictionary);
}
struct CachedConnection
{
std::filesystem::path path;
std::shared_ptr<sqlite3> connection;
};
std::mutex &cache_mutex()
{
static std::mutex mutex;
return mutex;
}
std::vector<CachedConnection> &cache()
{
static std::vector<CachedConnection> connections;
return connections;
}
} // namespace
std::shared_ptr<sqlite3> open_local_database(const std::filesystem::path &path)
{
if (path.empty())
{
return {};
}
if (!is_shared_dictionary(path))
{
return open_read_only(path);
}
const std::lock_guard<std::mutex> guard(cache_mutex());
auto &connections = cache();
for (const CachedConnection &cached : connections)
{
if (cached.path == path)
{
return cached.connection;
}
}
auto connection = open_read_only(path);
if (connection)
{
// At most one entry per shipped dictionary: a connection cached for a
// data directory that is no longer active is released here.
connections.erase(
std::remove_if(connections.begin(), connections.end(),
[&](const CachedConnection &cached) { return cached.path.filename() == path.filename(); }),
connections.end());
connections.push_back({path, connection});
}
return connection;
}
void close_cached_local_databases()
{
const std::lock_guard<std::mutex> guard(cache_mutex());
cache().clear();
}
} // namespace metasequoia::local_modes