You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/Module/GeoSigmaDraw/StorageFactory.cpp

621 lines
16 KiB
C++

1 month ago
#include "stdafx.h"
#include "StorageFactory.h"
#include <chrono>
#include <filesystem>
#include <optional>
#include <mutex>
#include "sqlite3.h"
#include "Util.h"
struct BaseRecord
{
int64_t id;
string filePath;
string dbPath;
std::string createAt;
};
class SQLiteStorage : public IActionStorage
{
public:
SQLiteStorage(const std::filesystem::path& backupDir, const std::string& dbName)
: m_backupDir(backupDir),
m_dbName(dbName),
m_baseStorage(MakeBaseStorage())
{
std::vector<BaseRecord> records = GetAllBaseRecords();
for (const auto& record : records)
{
m_cacheMap[record.filePath] = record.dbPath;
}
}
~SQLiteStorage()
{
for (auto& pair : m_cacheStorage)
{
sqlite3_free(pair.second);
}
}
bool InsertFileData(const std::string& filePath, FileData& fileData) override
{
if (!ExistFilePath(filePath))
{
CreateFileMapInfo(filePath);
}
sqlite3* storage = GetStorage(m_cacheMap[filePath]);
return InsertFileData(storage, fileData);
}
std::optional<FileData> RetrieveFileData(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return std::nullopt;
}
sqlite3* storage = GetStorage(it->second);
auto fileDatas = GetAllFileData(storage);
if (fileDatas.empty())
{
return std::nullopt;
}
return fileDatas[0];
}
bool ExistsFileData(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return false;
}
sqlite3* storage = GetStorage(it->second);
return CountFileData(storage) > 0;
}
void RemoveFileData(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it != m_cacheMap.end())
{
std::string dbName = it->second;
std::error_code ec;
// ɾ<><C9BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ݵ<EFBFBD><DDB5><EFBFBD><EFBFBD>ݿ<EFBFBD>
std::filesystem::remove(dbName, ec);
}
// ɾ<><C9BE>ӳ<EFBFBD><D3B3><EFBFBD><EFBFBD>Ϣ
RemoveBaseRecord(filePath);
m_cacheMap.erase(filePath);
}
bool AddActionRecord(const std::string& filePath, const ActionRecord& record) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return false;
}
sqlite3* storage = GetStorage(it->second);
InsertActionRecord(storage, record);
return true;
}
bool ExistsActionRecord(const std::string& filePath, const std::string& uuid) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return false;
}
sqlite3* storage = GetStorage(it->second);
return CountActionRecords(storage, uuid) > 0;
}
int ActionRecordCount(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return false;
}
sqlite3* storage = GetStorage(it->second);
return CountActionRecords(storage);
}
std::vector<ActionRecord> RetrieveAllActionRecords(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return {};
}
sqlite3* storage = GetStorage(it->second);
return GetAllActionRecords(storage);
}
void ClearBackup(const std::string& filePath) override
{
auto it = m_cacheMap.find(filePath);
if (it == m_cacheMap.end())
{
return;
}
ClearStorage(it->second);
RemoveBaseRecord(it->first);
m_cacheMap.erase(filePath);
}
private:
sqlite3* MakeBaseStorage() const
{
std::filesystem::path fullPath = m_backupDir / m_dbName;
std::error_code ec;
if (!std::filesystem::exists(m_backupDir))
{
if (!std::filesystem::create_directory(m_backupDir, ec))
{
throw std::runtime_error("Create backup directory failed");
}
}
sqlite3* db = nullptr;
if (sqlite3_open(fullPath.string().c_str(), &db) != SQLITE_OK)
{
throw std::runtime_error("Failed to open database");
}
const char* createTableSQL = R"(
CREATE TABLE IF NOT EXISTS map (
id INTEGER PRIMARY KEY AUTOINCREMENT,
file_path TEXT NOT NULL,
db_path TEXT NOT NULL,
create_at TEXT NOT NULL
);
)";
char* errMsg = nullptr;
if (sqlite3_exec(db, createTableSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
{
std::string err = errMsg ? errMsg : "Unknown error";
sqlite3_free(errMsg);
sqlite3_close(db);
throw std::runtime_error("Failed to create table: " + err);
}
return db;
}
void CreateFileMapInfo(const std::string& filePath)
{
// <20><>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD>ݿ<EFBFBD><DDBF>ļ<EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD> {<7B><><EFBFBD><EFBFBD>Ŀ¼}/{<7B>ļ<EFBFBD><C4BC><EFBFBD>}.{UUID}.sqlite
// <20><>ȡ<EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>׺<EFBFBD><D7BA>
std::string fileNameStem = std::filesystem::path(filePath).stem().string();
// <20><><EFBFBD>ɴ<EFBFBD> UUID <20><><EFBFBD>º<EFBFBD>׺<EFBFBD><D7BA><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
std::string newFileName = fileNameStem + "." + GenerateUUID() + ".sqlite";
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݿ<EFBFBD><DDBF>ļ<EFBFBD>·<EFBFBD><C2B7>
std::filesystem::path dbPath = m_backupDir / newFileName;
std::string dbNameStr = dbPath.string();
auto createAt = FormatTime(std::chrono::system_clock::now());
const char* insertSQL = R"(
INSERT INTO map (file_path, db_path, create_at) VALUES (?, ?, ?);
)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(m_baseStorage, insertSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare insert statement");
}
sqlite3_bind_text(stmt, 1, filePath.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, dbNameStr.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, createAt.c_str(), -1, SQLITE_TRANSIENT);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
sqlite3_finalize(stmt);
throw std::runtime_error("Failed to execute insert statement");
}
sqlite3_finalize(stmt);
m_cacheMap[filePath] = dbNameStr;
}
bool ExistFilePath(const std::string& filePath)
{
return m_cacheMap.find(filePath) != m_cacheMap.end();
}
sqlite3* GetStorage(const std::string& dbPath)
{
if (m_cacheStorage.find(dbPath) == m_cacheStorage.end())
{
sqlite3* db = nullptr;
if (sqlite3_open(dbPath.c_str(), &db) != SQLITE_OK)
{
throw std::runtime_error("Failed to open database");
}
const char* createFileDataTableSQL = R"(
CREATE TABLE IF NOT EXISTS filedata (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filepath TEXT NOT NULL,
data BLOB NOT NULL,
create_at TEXT NOT NULL
);
)";
const char* createActionTableSQL = R"(
CREATE TABLE IF NOT EXISTS actions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL,
class_type TEXT NOT NULL,
type TEXT NOT NULL,
data BLOB NOT NULL,
timestamp TEXT NOT NULL,
create_at TEXT NOT NULL
);
)";
char* errMsg = nullptr;
if (sqlite3_exec(db, createFileDataTableSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
{
std::string err = errMsg ? errMsg : "Unknown error";
sqlite3_free(errMsg);
sqlite3_close(db);
throw std::runtime_error("Failed to create filedata table: " + err);
}
if (sqlite3_exec(db, createActionTableSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
{
std::string err = errMsg ? errMsg : "Unknown error";
sqlite3_free(errMsg);
sqlite3_close(db);
throw std::runtime_error("Failed to create actions table: " + err);
}
m_cacheStorage.insert({ dbPath, db });
}
return m_cacheStorage.at(dbPath);
}
void ClearStorage(const std::string& dbPath)
{
auto it = m_cacheStorage.find(dbPath);
if (it != m_cacheStorage.end())
{
sqlite3_close(it->second);
m_cacheStorage.erase(it);
}
std::error_code ec;
CString ansiDbpath = Utf8StringToCString(dbPath);
std::filesystem::remove(ansiDbpath.GetBuffer(), ec);
}
std::vector<BaseRecord> GetAllBaseRecords()
{
std::vector<BaseRecord> records;
const char* selectSQL = "SELECT id, file_path, db_path, create_at FROM map";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(m_baseStorage, selectSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare select statement");
}
while (sqlite3_step(stmt) == SQLITE_ROW)
{
BaseRecord record;
record.id = sqlite3_column_int64(stmt, 0);
record.filePath = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
record.dbPath = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
record.createAt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
records.push_back(record);
}
sqlite3_finalize(stmt);
return records;
}
std::vector<FileData> GetAllFileData(sqlite3* db)
{
std::vector<FileData> fileDatas;
const char* selectSQL = "SELECT id, filepath, data, create_at FROM filedata";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, selectSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare select statement");
}
while (sqlite3_step(stmt) == SQLITE_ROW)
{
FileData fileData;
fileData.id = sqlite3_column_int64(stmt, 0);
fileData.filepath = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const void* data = sqlite3_column_blob(stmt, 2);
int dataSize = sqlite3_column_bytes(stmt, 2);
fileData.data.assign(static_cast<const char*>(data), static_cast<const char*>(data) + dataSize);
fileData.createAt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
fileDatas.push_back(fileData);
}
sqlite3_finalize(stmt);
return fileDatas;
}
bool InsertFileData(sqlite3* db, FileData& fileData)
{
const char* deleteSQL = "DELETE FROM filedata";
char* errMsg = nullptr;
if (sqlite3_exec(db, deleteSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
{
std::string err = errMsg ? errMsg : "Unknown error";
sqlite3_free(errMsg);
throw std::runtime_error("Failed to delete existing filedata: " + err);
}
const char* insertSQL = "INSERT INTO filedata (filepath, data, create_at) VALUES (?, ?, ?)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, insertSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare insert statement");
}
sqlite3_bind_text(stmt, 1, fileData.filepath.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_blob(stmt, 2, fileData.data.data(), static_cast<int>(fileData.data.size()), SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, fileData.createAt.c_str(), -1, SQLITE_TRANSIENT);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
sqlite3_finalize(stmt);
throw std::runtime_error("Failed to execute insert statement");
}
sqlite3_finalize(stmt);
return true;
}
int CountFileData(sqlite3* db)
{
const char* countSQL = "SELECT COUNT(*) FROM filedata";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, countSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare count statement");
}
int count = 0;
if (sqlite3_step(stmt) == SQLITE_ROW)
{
count = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
return count;
}
void RemoveBaseRecord(const std::string& filePath)
{
const char* deleteSQL = "DELETE FROM map WHERE file_path = ?";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(m_baseStorage, deleteSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare delete statement");
}
sqlite3_bind_text(stmt, 1, filePath.c_str(), -1, SQLITE_TRANSIENT);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
sqlite3_finalize(stmt);
throw std::runtime_error("Failed to execute delete statement");
}
sqlite3_finalize(stmt);
}
void InsertActionRecord(sqlite3* db, const ActionRecord& record)
{
const char* insertSQL = R"(
INSERT INTO actions (uuid, class_type, type, data, timestamp, create_at)
VALUES (?, ?, ?, ?, ?, ?)
)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, insertSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare insert statement");
}
sqlite3_bind_text(stmt, 1, record.uuid.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, record.classType.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, record.type.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_blob(stmt, 4, record.data.data(), static_cast<int>(record.data.size()), SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, record.timestamp.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, record.createAt.c_str(), -1, SQLITE_TRANSIENT);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
sqlite3_finalize(stmt);
throw std::runtime_error("Failed to execute insert statement");
}
sqlite3_finalize(stmt);
}
int CountActionRecords(sqlite3* db)
{
const char* countSQL = "SELECT COUNT(*) FROM actions";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, countSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare count statement");
}
int count = 0;
if (sqlite3_step(stmt) == SQLITE_ROW)
{
count = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
return count;
}
int CountActionRecords(sqlite3* db, const std::string& uuid)
{
const char* countSQL = "SELECT COUNT(*) FROM actions WHERE uuid = ?";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, countSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare count statement");
}
sqlite3_bind_text(stmt, 1, uuid.c_str(), -1, SQLITE_TRANSIENT);
int count = 0;
if (sqlite3_step(stmt) == SQLITE_ROW)
{
count = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
return count;
}
std::vector<ActionRecord> GetAllActionRecords(sqlite3* db)
{
std::vector<ActionRecord> records;
const char* selectSQL = "SELECT id, uuid, class_type, type, data, timestamp, create_at FROM actions";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, selectSQL, -1, &stmt, nullptr) != SQLITE_OK)
{
throw std::runtime_error("Failed to prepare select statement");
}
while (sqlite3_step(stmt) == SQLITE_ROW)
{
ActionRecord record;
record.id = sqlite3_column_int64(stmt, 0);
record.uuid = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
record.classType = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
record.type = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
const void* data = sqlite3_column_blob(stmt, 4);
int dataSize = sqlite3_column_bytes(stmt, 4);
record.data.assign(static_cast<const char*>(data), static_cast<const char*>(data) + dataSize);
record.timestamp = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 5));
record.createAt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 6));
records.push_back(record);
}
sqlite3_finalize(stmt);
return records;
}
private:
std::filesystem::path m_backupDir;
std::string m_dbName;
sqlite3* m_baseStorage;
std::unordered_map<std::string, std::string> m_cacheMap;
std::unordered_map<std::string, sqlite3*> m_cacheStorage;
};
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD>洢ʵ<E6B4A2><CAB5>
StorageFactory& StorageFactory::GetInstance()
{
static StorageFactory factory;
return factory;
}
/**
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ŵ<EFBFBD> %appdata% Ŀ¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ź<EFBFBD><EFBFBD><EFBFBD>Ŀ¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼<EFBFBD>DZ<EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD> exe <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>п<EFBFBD><EFBFBD><EFBFBD>ûȨ<EFBFBD>ޣ<EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> app <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݵ<EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡʧ<EFBFBD><EFBFBD>(<EFBFBD><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>)<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ŵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼<EFBFBD><EFBFBD>
*
* \return
*/
static std::filesystem::path GetBackupPath()
{
PWSTR RoamingPath = nullptr;
HRESULT result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &RoamingPath);
FinalAction finalAction([RoamingPath]() { CoTaskMemFree(RoamingPath); });
if (result == S_OK)
{
std::filesystem::path path = RoamingPath;
path /= "KEVisualization";
path /= "backup";
if (!std::filesystem::exists(path))
{
std::filesystem::create_directories(path);
}
return path;
}
else
{
return "backup";
}
}
std::shared_ptr<IActionStorage> StorageFactory::CreateDefaultStorage()
{
std::filesystem::path backup = GetBackupPath();
return StorageFactory::GetInstance().CreateStorage(backup, "base.sqlite");
}
std::shared_ptr<IActionStorage> StorageFactory::CreateStorage(const std::filesystem::path& backupDir, const std::string& dbName)
{
try
{
std::filesystem::path dbPath = (backupDir / dbName).string();
auto it = m_map.find(dbPath.string());
if (it != m_map.end())
{
return it->second;
}
auto storage = std::make_shared<SQLiteStorage>(backupDir, dbName);
m_map[dbPath.string()] = storage;
return storage;
}
catch (std::runtime_error &e)
{
TRACE("Create storage failed: %s\n", e.what());
return nullptr;
}
}