UI: ConfManager: Import DB without app restarting

This commit is contained in:
Nodir Temirkhodjaev 2024-09-28 13:52:08 +05:00
parent 1e251de80b
commit 83114e1569
3 changed files with 89 additions and 49 deletions

View File

@ -88,6 +88,8 @@ bool SqliteDb::open()
const bool ok = sqlite3_open_v2(filePathUtf8.data(), &m_db, m_openFlags, nullptr) == SQLITE_OK; const bool ok = sqlite3_open_v2(filePathUtf8.data(), &m_db, m_openFlags, nullptr) == SQLITE_OK;
if (ok) { if (ok) {
setBusyTimeoutMs(DATABASE_BUSY_TIMEOUT);
SqliteDbExt::registerExtensions(this); SqliteDbExt::registerExtensions(this);
} }
@ -300,10 +302,22 @@ QStringList SqliteDb::columnNames(const QString &tableName, const QString &schem
return list; return list;
} }
bool SqliteDb::import(SqliteDb::MigrateOptions &opt)
{
// Get the importing DB version
{
SqliteDb db(opt.backupFilePath, SqliteDb::OpenDefaultReadOnly);
if (!db.open())
return false;
opt.userVersion = db.userVersion();
}
return importDb(opt);
}
bool SqliteDb::migrate(MigrateOptions &opt) bool SqliteDb::migrate(MigrateOptions &opt)
{ {
setBusyTimeoutMs(DATABASE_BUSY_TIMEOUT);
if (!opt.sqlPragmas) { if (!opt.sqlPragmas) {
opt.sqlPragmas = defaultSqlPragmas; opt.sqlPragmas = defaultSqlPragmas;
} }
@ -316,16 +330,14 @@ bool SqliteDb::migrate(MigrateOptions &opt)
opt.userVersion = userVersion; opt.userVersion = userVersion;
// Check migration options
if (!canMigrate(opt))
return false;
// Migrate the DB // Migrate the DB
const bool isNewDb = (userVersion == 0); const bool isNewDb = (userVersion == 0);
if (isNewDb) { if (isNewDb) {
opt.recreate = false; opt.recreate = false;
} }
opt.backupFilePath = backupFilePath();
return migrateDb(opt, userVersion, isNewDb); return migrateDb(opt, userVersion, isNewDb);
} }
@ -347,6 +359,10 @@ bool SqliteDb::canMigrate(const MigrateOptions &opt) const
bool SqliteDb::migrateDb(const MigrateOptions &opt, int userVersion, bool isNewDb) bool SqliteDb::migrateDb(const MigrateOptions &opt, int userVersion, bool isNewDb)
{ {
// Check migration options
if (!canMigrate(opt))
return false;
if (!migrateDbBegin(opt, userVersion, isNewDb)) if (!migrateDbBegin(opt, userVersion, isNewDb))
return false; return false;
@ -422,7 +438,7 @@ bool SqliteDb::migrateDbBegin(const MigrateOptions &opt, int &userVersion, bool
userVersion = 0; userVersion = 0;
isNewDb = true; isNewDb = true;
return clearWithBackup(opt.sqlPragmas); return clearWithBackup(opt);
} }
bool SqliteDb::migrateDbEnd(const MigrateOptions &opt) bool SqliteDb::migrateDbEnd(const MigrateOptions &opt)
@ -506,21 +522,19 @@ bool SqliteDb::createFtsTable(const FtsTable &ftsTable)
return executeStr(sql); return executeStr(sql);
} }
bool SqliteDb::clearWithBackup(const char *sqlPragmas) bool SqliteDb::clearWithBackup(const MigrateOptions &opt)
{ {
const QString oldEncoding = this->encoding(); const QString oldEncoding = this->encoding();
close(); close();
const QString tempFilePath = backupFilePath(); if (!(renameDbFile(m_filePath, opt.backupFilePath) && open())) {
if (!(renameDbFile(m_filePath, tempFilePath) && open())) {
qCWarning(LC) << "Cannot re-create the DB" << m_filePath; qCWarning(LC) << "Cannot re-create the DB" << m_filePath;
renameDbFile(tempFilePath, m_filePath); renameDbFile(opt.backupFilePath, m_filePath);
return false; return false;
} }
execute(sqlPragmas); execute(opt.sqlPragmas);
setEncoding(oldEncoding); setEncoding(oldEncoding);
return true; return true;
@ -530,16 +544,14 @@ bool SqliteDb::importBackup(const MigrateOptions &opt)
{ {
bool success = true; bool success = true;
const QString tempFilePath = backupFilePath();
// Re-import the DB // Re-import the DB
if (opt.importOldData) { if (opt.importOldData) {
success = importDb(opt, tempFilePath); success = importDb(opt);
} }
// Remove the old DB // Remove the old DB
if (success) { if (success) {
removeDbFile(tempFilePath); removeDbFile(opt.backupFilePath);
} }
return success; return success;
@ -550,11 +562,13 @@ QString SqliteDb::backupFilePath() const
return m_filePath + ".temp"; return m_filePath + ".temp";
} }
bool SqliteDb::importDb(const MigrateOptions &opt, const QString &sourceFilePath) bool SqliteDb::importDb(const MigrateOptions &opt)
{ {
const QString srcSchema = migrationOldSchemaName(); const QString srcSchema = migrationOldSchemaName();
const QString dstSchema = migrationNewSchemaName(); const QString dstSchema = migrationNewSchemaName();
const QString &sourceFilePath = opt.backupFilePath;
if (!attach(srcSchema, sourceFilePath)) { if (!attach(srcSchema, sourceFilePath)) {
qCWarning(LC) << "Cannot attach the DB" << sourceFilePath << "Error:" << errorMessage(); qCWarning(LC) << "Cannot attach the DB" << sourceFilePath << "Error:" << errorMessage();
return false; return false;
@ -587,6 +601,8 @@ bool SqliteDb::copyTables(const QString &srcSchema, const QString &dstSchema)
const QStringList srcTableNames = tableNames(srcSchema); const QStringList srcTableNames = tableNames(srcSchema);
for (const QString &tableName : srcTableNames) { for (const QString &tableName : srcTableNames) {
clearTable(dstSchema, tableName);
if (!copyTable(srcSchema, dstSchema, tableName)) if (!copyTable(srcSchema, dstSchema, tableName))
return false; return false;
} }
@ -623,6 +639,13 @@ bool SqliteDb::copyTable(
return executeStr(sql); return executeStr(sql);
} }
bool SqliteDb::clearTable(const QString &dstSchema, const QString &tableName)
{
const auto sql = QString("DELETE FROM %1;").arg(entityName(dstSchema, tableName));
return executeStr(sql);
}
SqliteStmt *SqliteDb::stmt(const char *sql) SqliteStmt *SqliteDb::stmt(const char *sql)
{ {
SqliteStmt *stmt = m_stmts.value(sql); SqliteStmt *stmt = m_stmts.value(sql);

View File

@ -45,15 +45,21 @@ public:
struct MigrateOptions struct MigrateOptions
{ {
const QString sqlDir; const char *sqlDir;
const char *sqlPragmas = nullptr; const char *sqlPragmas = nullptr;
int version = 0; int version = 0;
int userVersion = 0; int userVersion = 0;
bool recreate = true; bool recreate = true;
bool importOldData = true; bool importOldData = true;
bool autoCopyTables = true; bool autoCopyTables = true;
QString backupFilePath;
SQLITEDB_MIGRATE_FUNC migrateFunc = nullptr; SQLITEDB_MIGRATE_FUNC migrateFunc = nullptr;
void *migrateContext = nullptr; void *migrateContext = nullptr;
QVector<FtsTable> ftsTables; QVector<FtsTable> ftsTables;
}; };
@ -115,6 +121,8 @@ public:
QStringList tableNames(const QString &schemaName = {}); QStringList tableNames(const QString &schemaName = {});
QStringList columnNames(const QString &tableName, const QString &schemaName = {}); QStringList columnNames(const QString &tableName, const QString &schemaName = {});
bool import(SqliteDb::MigrateOptions &opt);
bool migrate(SqliteDb::MigrateOptions &opt); bool migrate(SqliteDb::MigrateOptions &opt);
SqliteStmt *stmt(const char *sql); SqliteStmt *stmt(const char *sql);
@ -136,14 +144,15 @@ private:
bool createFtsTables(const MigrateOptions &opt); bool createFtsTables(const MigrateOptions &opt);
bool createFtsTable(const FtsTable &ftsTable); bool createFtsTable(const FtsTable &ftsTable);
bool clearWithBackup(const char *sqlPragmas); bool clearWithBackup(const MigrateOptions &opt);
bool importBackup(const MigrateOptions &opt); bool importBackup(const MigrateOptions &opt);
QString backupFilePath() const; QString backupFilePath() const;
bool importDb(const MigrateOptions &opt, const QString &sourceFilePath); bool importDb(const MigrateOptions &opt);
bool copyTables(const QString &srcSchema, const QString &dstSchema); bool copyTables(const QString &srcSchema, const QString &dstSchema);
bool copyTable(const QString &srcSchema, const QString &dstSchema, const QString &tableName); bool copyTable(const QString &srcSchema, const QString &dstSchema, const QString &tableName);
bool clearTable(const QString &dstSchema, const QString &tableName);
void clearStmts(); void clearStmts();

View File

@ -219,6 +219,30 @@ bool migrateFunc(SqliteDb *db, int version, bool isNewDb, void *ctx)
return true; return true;
} }
SqliteDb::MigrateOptions migrateOptions()
{
SqliteDb::MigrateOptions opt = {
.sqlDir = ":/conf/migrations",
.version = DATABASE_USER_VERSION,
.recreate = true,
.migrateFunc = &migrateFunc,
.ftsTables = {
{
.contentTable = "app",
.contentRowid = "app_id",
.columns = { "path", "name", "notes" }
},
{
.contentTable = "rule",
.contentRowid = "rule_id",
.columns = { "name", "notes" }
},
},
};
return opt;
}
bool loadAddressGroups(SqliteDb *db, const QList<AddressGroup *> &addressGroups, int &index) bool loadAddressGroups(SqliteDb *db, const QList<AddressGroup *> &addressGroups, int &index)
{ {
SqliteStmt stmt; SqliteStmt stmt;
@ -512,24 +536,7 @@ bool ConfManager::setupDb()
return false; return false;
} }
SqliteDb::MigrateOptions opt = { SqliteDb::MigrateOptions opt = migrateOptions();
.sqlDir = ":/conf/migrations",
.version = DATABASE_USER_VERSION,
.recreate = true,
.migrateFunc = &migrateFunc,
.ftsTables = {
{
.contentTable = "app",
.contentRowid = "app_id",
.columns = { "path", "name", "notes" }
},
{
.contentTable = "rule",
.contentRowid = "rule_id",
.columns = { "name", "notes" }
},
},
};
if (!sqliteDb()->migrate(opt)) { if (!sqliteDb()->migrate(opt)) {
qCCritical(LC) << "Migration error" << sqliteDb()->filePath(); qCCritical(LC) << "Migration error" << sqliteDb()->filePath();
@ -773,13 +780,11 @@ bool ConfManager::importBackup(const QString &path)
return false; return false;
settings->clearCache(); settings->clearCache();
emit iniUserChanged(iniUser(), /*onlyFlags=*/false);
} }
// Import DB: Close DB from UI side // Import DB
{
sqliteDb()->close();
}
return importMasterBackup(inPath); return importMasterBackup(inPath);
} }
@ -798,17 +803,20 @@ bool ConfManager::importMasterBackup(const QString &path)
// Import Db // Import Db
if (ok) { if (ok) {
sqliteDb()->close(); SqliteDb::MigrateOptions opt = migrateOptions();
ok = importFile(sqliteDb()->filePath(), path); opt.backupFilePath = path + FileUtil::fileName(sqliteDb()->filePath());
ok = sqliteDb()->import(opt);
} }
if (!ok) { if (ok) {
emit iniChanged(conf()->ini());
emit confChanged(/*onlyFlags=*/false);
} else {
qCWarning(LC) << "Import error:" << path; qCWarning(LC) << "Import error:" << path;
} }
IoC<FortManager>()->processRestartRequired("Backup Imported");
return ok; return ok;
} }