aboutsummaryrefslogtreecommitdiffstats
path: root/Plugins/ConfigMigration/configmigrationwizard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/ConfigMigration/configmigrationwizard.cpp')
-rw-r--r--Plugins/ConfigMigration/configmigrationwizard.cpp407
1 files changed, 407 insertions, 0 deletions
diff --git a/Plugins/ConfigMigration/configmigrationwizard.cpp b/Plugins/ConfigMigration/configmigrationwizard.cpp
new file mode 100644
index 0000000..afdf705
--- /dev/null
+++ b/Plugins/ConfigMigration/configmigrationwizard.cpp
@@ -0,0 +1,407 @@
+#include "configmigrationwizard.h"
+#include "ui_configmigrationwizard.h"
+#include "configmigration.h"
+#include "configmigrationitem.h"
+#include "iconmanager.h"
+#include "uiutils.h"
+#include "dbtree/dbtree.h"
+#include "dbtree/dbtreemodel.h"
+#include "services/config.h"
+#include "sqlitestudio.h"
+#include "db/dbsqlite3.h"
+#include "services/notifymanager.h"
+#include "services/dbmanager.h"
+
+ConfigMigrationWizard::ConfigMigrationWizard(QWidget *parent, ConfigMigration* cfgMigration) :
+ QWizard(parent),
+ ui(new Ui::ConfigMigrationWizard),
+ cfgMigration(cfgMigration)
+{
+ init();
+}
+
+ConfigMigrationWizard::~ConfigMigrationWizard()
+{
+ clearFunctions();
+ delete ui;
+}
+
+bool ConfigMigrationWizard::didMigrate()
+{
+ return migrated;
+}
+
+void ConfigMigrationWizard::accept()
+{
+ migrate();
+ QWizard::accept();
+}
+
+void ConfigMigrationWizard::init()
+{
+ ui->setupUi(this);
+
+#ifdef Q_OS_MACX
+ resize(width() + 150, height());
+ setPixmap(QWizard::BackgroundPixmap, addOpacity(ICONMANAGER->getIcon("config_migration")->pixmap(180, 180), 0.4));
+#endif
+
+ ui->optionsPage->setValidator([=]() -> bool
+ {
+ QString grpName = ui->groupNameEdit->text();
+
+ bool grpOk = true;
+ QString grpErrorMsg;
+ if (ui->dbGroup->isEnabled() && ui->dbGroup->isChecked())
+ {
+ if (grpName.isEmpty())
+ {
+ grpOk = false;
+ grpErrorMsg = tr("Enter a non-empty name.");
+ }
+ else
+ {
+ DbTreeItem* item = DBTREE->getModel()->findItem(DbTreeItem::Type::DIR, grpName);
+ if (item && !item->parentDbTreeItem())
+ {
+ grpOk = false;
+ grpErrorMsg = tr("Top level group named '%1' already exists. Enter a group name that does not exist yet.").arg(grpName);
+ }
+ }
+ }
+
+ setValidState(ui->groupNameEdit, grpOk, grpErrorMsg);
+
+ return grpOk;
+ });
+
+
+ QTreeWidgetItem* treeItem = nullptr;
+ for (ConfigMigrationItem* cfgItem : cfgMigration->getItemsToMigrate())
+ {
+ treeItem = new QTreeWidgetItem({cfgItem->label});
+ treeItem->setData(0, Qt::UserRole, static_cast<int>(cfgItem->type));
+ treeItem->setFlags(treeItem->flags() | Qt::ItemIsUserCheckable);
+ treeItem->setCheckState(0, Qt::Checked);
+ ui->itemsTree->addTopLevelItem(treeItem);
+ }
+
+ connect(ui->dbGroup, SIGNAL(clicked()), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(ui->groupNameEdit, SIGNAL(textChanged(QString)), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(this, SIGNAL(updateOptionsValidation()), ui->optionsPage, SIGNAL(completeChanged()));
+ connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(updateOptions()));
+
+ emit updateOptionsValidation();
+}
+
+void ConfigMigrationWizard::migrate()
+{
+ Db* oldCfgDb = cfgMigration->getOldCfgDb();
+ if (!oldCfgDb->open())
+ {
+ notifyError(tr("Could not open old configuration file in order to migrate settings from it."));
+ return;
+ }
+
+ QString cfgFilePath = SQLITESTUDIO->getConfig()->getConfigFilePath();
+ Db* newCfgDb = new DbSqlite3("Config migration connection", cfgFilePath, {{DB_PURE_INIT, true}});
+ if (!newCfgDb->open())
+ {
+ notifyError(tr("Could not open current configuration file in order to migrate settings from old configuration file."));
+ delete newCfgDb;
+ return;
+ }
+
+ newCfgDb->begin();
+ bool migrated = migrateSelected(oldCfgDb, newCfgDb);
+ if (migrated && !newCfgDb->commit())
+ {
+ notifyError(tr("Could not commit migrated data into new configuration file: %1").arg(newCfgDb->getErrorText()));
+ newCfgDb->rollback();
+ }
+ else if (!migrated)
+ {
+ newCfgDb->rollback();
+ }
+ else
+ {
+ finalize();
+ }
+ oldCfgDb->close();
+ newCfgDb->close();
+ delete newCfgDb;
+ clearFunctions();
+}
+
+bool ConfigMigrationWizard::migrateSelected(Db* oldCfgDb, Db* newCfgDb)
+{
+ if (checkedTypes.contains(ConfigMigrationItem::Type::BUG_REPORTS) && !migrateBugReports(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::DATABASES) && !migrateDatabases(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::FUNCTION_LIST) && !migrateFunction(oldCfgDb, newCfgDb))
+ return false;
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::SQL_HISTORY) && !migrateSqlHistory(oldCfgDb, newCfgDb))
+ return false;
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateBugReports(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(oldBugsQuery, "SELECT created_on, brief, url, type FROM bugs");
+ static_qstring(newBugsInsert, "INSERT INTO reports_history (timestamp, feature_request, title, url) VALUES (?, ?, ?, ?)");
+
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldBugsQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read bug reports history from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ bool feature;
+ QString url;
+ while (results->hasNext())
+ {
+ row = results->next();
+ feature = (row->value("type").toString().toUpper() == "FEATURE");
+ url = row->value("url").toString().trimmed();
+ if (url.startsWith("http://") && url.contains("sqlitestudio.one.pl"))
+ url.replace("sqlitestudio.one.pl", "sqlitestudio.pl").replace("report_bug.rvt", "report_bug3.rvt");
+
+ insertResults = newCfgDb->exec(newBugsInsert, {row->value("created_on"), feature, row->value("brief"), url});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert a bug reports history entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateDatabases(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(oldDbListQuery, "SELECT name, path FROM dblist");
+ static_qstring(newDbListInsert, "INSERT INTO dblist (name, path) VALUES (?, ?)");
+ static_qstring(groupOrderQuery, "SELECT max([order]) + 1 FROM groups WHERE parent %1");
+ static_qstring(groupInsert, "INSERT INTO groups (name, [order], parent, open, dbname) VALUES (?, ?, ?, ?, ?)");
+
+ SqlQueryPtr groupResults;
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldDbListQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read database list from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ // Creating containing group
+ bool putInGroup = ui->dbGroup->isEnabled() && ui->dbGroup->isChecked();
+ qint64 groupId = -1;
+ int order;
+ if (putInGroup)
+ {
+ // Query order
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("IS NULL"));
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could query for available order for containing group in new configuration file in order to migrate the database list: %1")
+ .arg(groupResults->getErrorText()));
+ return false;
+ }
+
+ order = groupResults->getSingleCell().toInt();
+
+ // Insert group
+ groupResults = newCfgDb->exec(groupInsert, {ui->groupNameEdit->text(), order, QVariant(), 1, QVariant()});
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could not create containing group in new configuration file in order to migrate the database list: %1").arg(groupResults->getErrorText()));
+ return false;
+ }
+ groupId = groupResults->getRegularInsertRowId();
+ }
+
+ // Migrating the list
+ QString name;
+ QString path;
+ while (results->hasNext())
+ {
+ row = results->next();
+ name = row->value("name").toString();
+ path = row->value("path").toString();
+
+ if (DBLIST->getByName(name) || DBLIST->getByPath(path)) // already on the new list
+ continue;
+
+ insertResults = newCfgDb->exec(newDbListInsert, {name, path});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert a database entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+
+ // Query order
+ if (putInGroup)
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("= ?"), {groupId});
+ else
+ groupResults = newCfgDb->exec(groupOrderQuery.arg("IS NULL"));
+
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could query for available order for next database in new configuration file in order to migrate the database list: %1")
+ .arg(groupResults->getErrorText()));
+ return false;
+ }
+
+ order = groupResults->getSingleCell().toInt();
+
+ // Insert group
+ groupResults = newCfgDb->exec(groupInsert, {QVariant(), order, putInGroup ? QVariant(groupId) : QVariant(), 0, name});
+ if (groupResults->isError())
+ {
+ notifyError(tr("Could not create group referencing the database in new configuration file: %1").arg(groupResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateFunction(Db* oldCfgDb, Db* newCfgDb)
+{
+ UNUSED(newCfgDb);
+
+ static_qstring(oldFunctionsQuery, "SELECT name, type, code FROM functions");
+
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldFunctionsQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read function list from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ clearFunctions();
+ for (FunctionManager::ScriptFunction* fn : FUNCTIONS->getAllScriptFunctions())
+ fnList << new FunctionManager::ScriptFunction(*fn);
+
+ FunctionManager::ScriptFunction* fn = nullptr;
+ while (results->hasNext())
+ {
+ row = results->next();
+
+ fn = new FunctionManager::ScriptFunction();
+ fn->type = FunctionManager::ScriptFunction::SCALAR;
+ fn->lang = row->value("type").toString();
+ fn->name = row->value("name").toString();
+ fn->code = row->value("code").toString();
+ fnList << fn;
+ }
+
+ return true;
+}
+
+bool ConfigMigrationWizard::migrateSqlHistory(Db* oldCfgDb, Db* newCfgDb)
+{
+ static_qstring(historyIdQuery, "SELECT CASE WHEN max(id) IS NULL THEN 0 ELSE max(id) + 1 END FROM sqleditor_history");
+ static_qstring(oldHistoryQuery, "SELECT dbname, date, time, rows, sql FROM history");
+ static_qstring(newHistoryInsert, "INSERT INTO sqleditor_history (id, dbname, date, time_spent, rows, sql) VALUES (?, ?, ?, ?, ?, ?)");
+
+ SqlQueryPtr insertResults;
+ SqlResultsRowPtr row;
+ SqlQueryPtr results = oldCfgDb->exec(oldHistoryQuery);
+ if (results->isError())
+ {
+ notifyError(tr("Could not read SQL queries history from old configuration file in order to migrate it: %1").arg(results->getErrorText()));
+ return false;
+ }
+
+ SqlQueryPtr idResults = newCfgDb->exec(historyIdQuery);
+ if (idResults->isError())
+ {
+ notifyError(tr("Could not read next ID for SQL queries history in new configuration file: %1").arg(idResults->getErrorText()));
+ return false;
+ }
+ qint64 nextId = idResults->getSingleCell().toLongLong();
+
+ int timeSpent;
+ int date;
+ while (results->hasNext())
+ {
+ row = results->next();
+ timeSpent = qRound(row->value("time").toDouble() * 1000);
+ date = QDateTime::fromString(row->value("date").toString(), "yyyy-MM-dd HH:mm").toTime_t();
+
+ insertResults = newCfgDb->exec(newHistoryInsert, {nextId++, row->value("dbname"), date, timeSpent, row->value("rows"), row->value("sql")});
+ if (insertResults->isError())
+ {
+ notifyError(tr("Could not insert SQL history entry into new configuration file: %1").arg(insertResults->getErrorText()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ConfigMigrationWizard::finalize()
+{
+ if (checkedTypes.contains(ConfigMigrationItem::Type::FUNCTION_LIST))
+ {
+ FUNCTIONS->setScriptFunctions(fnList);
+ fnList.clear();
+ }
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::SQL_HISTORY))
+ CFG->refreshSqlHistory();
+
+ if (checkedTypes.contains(ConfigMigrationItem::Type::DATABASES))
+ {
+ bool ignore = DBTREE->getModel()->getIgnoreDbLoadedSignal();
+ DBTREE->getModel()->setIgnoreDbLoadedSignal(true);
+ DBLIST->scanForNewDatabasesInConfig();
+ DBTREE->getModel()->setIgnoreDbLoadedSignal(ignore);
+ DBTREE->getModel()->loadDbList();
+ }
+
+ migrated = true;
+}
+
+void ConfigMigrationWizard::collectCheckedTypes()
+{
+ checkedTypes.clear();
+
+ QTreeWidgetItem* item = nullptr;
+ for (int i = 0, total = ui->itemsTree->topLevelItemCount(); i < total; ++i)
+ {
+ item = ui->itemsTree->topLevelItem(i);
+ if (item->checkState(0) != Qt::Checked)
+ continue;
+
+ checkedTypes << static_cast<ConfigMigrationItem::Type>(item->data(0, Qt::UserRole).toInt());
+ }
+}
+
+void ConfigMigrationWizard::clearFunctions()
+{
+ for (FunctionManager::ScriptFunction* fn : fnList)
+ delete fn;
+
+ fnList.clear();
+}
+
+void ConfigMigrationWizard::updateOptions()
+{
+ if (currentPage() == ui->optionsPage)
+ {
+ collectCheckedTypes();
+ ui->dbGroup->setEnabled(checkedTypes.contains(ConfigMigrationItem::Type::DATABASES));
+ }
+}