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

View File

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

View File

@ -219,6 +219,30 @@ bool migrateFunc(SqliteDb *db, int version, bool isNewDb, void *ctx)
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)
{
SqliteStmt stmt;
@ -512,24 +536,7 @@ bool ConfManager::setupDb()
return false;
}
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" }
},
},
};
SqliteDb::MigrateOptions opt = migrateOptions();
if (!sqliteDb()->migrate(opt)) {
qCCritical(LC) << "Migration error" << sqliteDb()->filePath();
@ -773,13 +780,11 @@ bool ConfManager::importBackup(const QString &path)
return false;
settings->clearCache();
emit iniUserChanged(iniUser(), /*onlyFlags=*/false);
}
// Import DB: Close DB from UI side
{
sqliteDb()->close();
}
// Import DB
return importMasterBackup(inPath);
}
@ -798,17 +803,20 @@ bool ConfManager::importMasterBackup(const QString &path)
// Import Db
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;
}
IoC<FortManager>()->processRestartRequired("Backup Imported");
return ok;
}