summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp777
1 files changed, 777 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp
new file mode 100644
index 0000000..61f5cb2
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp
@@ -0,0 +1,777 @@
+#include "dbobjectorganizer.h"
+#include "db/db.h"
+#include "common/utils_sql.h"
+#include "datatype.h"
+#include "services/notifymanager.h"
+#include "db/attachguard.h"
+#include "dbversionconverter.h"
+#include <QDebug>
+#include <QThreadPool>
+
+DbObjectOrganizer::DbObjectOrganizer()
+{
+ // Default organizaer denies any referenced objects
+ confirmFunction = [](const QStringList&) -> bool {return false;};
+ nameConflictResolveFunction = [](QString&) -> bool {return false;};
+ conversionConfimFunction = [](const QList<QPair<QString,QString>>&) -> bool {return false;};
+ conversionErrorsConfimFunction = [](const QHash<QString,QSet<QString>>&) -> bool {return false;};
+ init();
+}
+
+DbObjectOrganizer::DbObjectOrganizer(DbObjectOrganizer::ReferencedTablesConfimFunction confirmFunction, NameConflictResolveFunction nameConflictResolveFunction,
+ ConversionConfimFunction conversionConfimFunction, ConversionErrorsConfimFunction conversionErrorsConfimFunction) :
+ confirmFunction(confirmFunction), nameConflictResolveFunction(nameConflictResolveFunction), conversionConfimFunction(conversionConfimFunction),
+ conversionErrorsConfimFunction(conversionErrorsConfimFunction)
+{
+ init();
+}
+
+DbObjectOrganizer::~DbObjectOrganizer()
+{
+ safe_delete(srcResolver);
+ safe_delete(dstResolver);
+ safe_delete(versionConverter);
+}
+
+void DbObjectOrganizer::init()
+{
+ versionConverter = new DbVersionConverter();
+ connect(this, SIGNAL(preparetionFinished()), this, SLOT(processPreparationFinished()));
+}
+
+void DbObjectOrganizer::copyObjectsToDb(Db* srcDb, const QStringList& objNames, Db* dstDb, bool includeData, bool includeIndexes, bool includeTriggers)
+{
+ copyOrMoveObjectsToDb(srcDb, objNames.toSet(), dstDb, includeData, includeIndexes, includeTriggers, false);
+}
+
+void DbObjectOrganizer::moveObjectsToDb(Db* srcDb, const QStringList& objNames, Db* dstDb, bool includeData, bool includeIndexes, bool includeTriggers)
+{
+ copyOrMoveObjectsToDb(srcDb, objNames.toSet(), dstDb, includeData, includeIndexes, includeTriggers, true);
+}
+
+void DbObjectOrganizer::interrupt()
+{
+ QMutexLocker locker(&interruptMutex);
+ interrupted = true;
+ srcDb->interrupt();
+ dstDb->interrupt();
+}
+
+bool DbObjectOrganizer::isExecuting()
+{
+ QMutexLocker lock(&executingMutex);
+ return executing;
+}
+
+void DbObjectOrganizer::run()
+{
+ switch (mode)
+ {
+ case Mode::PREPARE_TO_COPY_OBJECTS:
+ case Mode::PREPARE_TO_MOVE_OBJECTS:
+ processPreparation();
+ break;
+ case Mode::COPY_OBJECTS:
+ case Mode::MOVE_OBJECTS:
+ emitFinished(processAll());
+ break;
+ case Mode::unknown:
+ qCritical() << "DbObjectOrganizer::run() called with unknown mode.";
+ emitFinished(false);
+ return;
+ }
+}
+
+void DbObjectOrganizer::reset()
+{
+ attachName = QString();
+ mode = Mode::COPY_OBJECTS;
+ srcDb = nullptr;
+ dstDb = nullptr;
+ srcNames.clear();
+ srcTables.clear();
+ srcIndexes.clear();
+ srcTriggers.clear();
+ srcViews.clear();
+ renamed.clear();
+ srcTable = QString::null;
+ includeData = false;
+ includeIndexes = false;
+ includeTriggers = false;
+ deleteSourceObjects = false;
+ referencedTables.clear();
+ diffListToConfirm.clear();
+ errorsToConfirm.clear();
+ binaryColumns.clear();
+ safe_delete(srcResolver);
+ safe_delete(dstResolver);
+ interrupted = false;
+ setExecuting(false);
+}
+
+void DbObjectOrganizer::copyOrMoveObjectsToDb(Db* srcDb, const QSet<QString>& objNames, Db* dstDb, bool includeData, bool includeIndexes, bool includeTriggers, bool move)
+{
+ if (isExecuting())
+ {
+ notifyError("Schema modification is currently in progress. Please try again in a moment.");
+ qWarning() << "Tried to call DbObjectOrganizer::copyOrMoveObjectsToDb() while other execution was in progress.";
+ return;
+ }
+
+ reset();
+ setExecuting(true);
+ if (move)
+ {
+ mode = Mode::PREPARE_TO_MOVE_OBJECTS;
+ deleteSourceObjects = true;
+ }
+ else
+ {
+ mode = Mode::PREPARE_TO_COPY_OBJECTS;
+ }
+
+ this->srcNames = objNames;
+ this->includeData = includeData;
+ this->includeIndexes = includeIndexes;
+ this->includeTriggers = includeTriggers;
+ setSrcAndDstDb(srcDb, dstDb);
+
+ QThreadPool::globalInstance()->start(this);
+}
+
+void DbObjectOrganizer::processPreparation()
+{
+ StrHash<SqliteQueryPtr> allParsedObjects = srcResolver->getAllParsedObjects();
+ StrHash<SchemaResolver::ObjectDetails> details = srcResolver->getAllObjectDetails();
+ for (const QString& srcName : srcNames)
+ {
+ if (!details.contains(srcName))
+ {
+ qDebug() << "Object" << srcName << "not found in source database, skipping.";
+ continue;
+ }
+
+ switch (details[srcName].type)
+ {
+ case SchemaResolver::TABLE:
+ srcTables << srcName;
+ findBinaryColumns(srcName, allParsedObjects);
+ collectReferencedTables(srcName, allParsedObjects);
+ collectReferencedIndexes(srcName);
+ collectReferencedTriggersForTable(srcName);
+ break;
+ case SchemaResolver::INDEX:
+ break;
+ case SchemaResolver::TRIGGER:
+ break;
+ case SchemaResolver::VIEW:
+ srcViews << srcName;
+ collectReferencedTriggersForView(srcName);
+ break;
+ case SchemaResolver::ANY:
+ qCritical() << "Unhandled type in DbObjectOrganizer::processPreparation():" << SchemaResolver::objectTypeToString(details[srcName].type);
+ break;
+ }
+ }
+
+ if (referencedTables.size() > 0 && !confirmFunction(referencedTables.toList()))
+ referencedTables.clear();
+
+ for (const QString& srcTable : referencedTables)
+ {
+ collectReferencedIndexes(srcTable);
+ collectReferencedTriggersForTable(srcTable);
+ }
+
+ collectDiffs(details);
+
+ emit preparetionFinished();
+}
+
+bool DbObjectOrganizer::processAll()
+{
+ if (!srcDb->isOpen())
+ {
+ //notifyError(tr("Cannot copy or move objects from closed database. Open it first.")); // TODO this is in another thread - handle it
+ return false;
+ }
+
+ if (!dstDb->isOpen())
+ {
+ //notifyError(tr("Cannot copy or move objects to closed database. Open it first.")); // TODO this is in another thread - handle it
+ return false;
+ }
+
+ // Attaching target db if needed
+ AttachGuard attach;
+ if (!(referencedTables + srcTables).isEmpty())
+ {
+ attach = srcDb->guardedAttach(dstDb, true);
+ attachName = attach->getName();
+ }
+
+ if (!srcDb->begin())
+ {
+ // TODO message
+ return false;
+ }
+
+ if (!dstDb->begin())
+ {
+ // TODO message
+ srcDb->rollback();
+ return false;
+ }
+
+ if (!setFkEnabled(false))
+ {
+ srcDb->rollback();
+ dstDb->rollback();
+ return false;
+ }
+
+ bool res = false;
+ switch (mode)
+ {
+ case Mode::COPY_OBJECTS:
+ case Mode::MOVE_OBJECTS:
+ {
+ res = processDbObjects();
+ break;
+ }
+ case Mode::PREPARE_TO_COPY_OBJECTS:
+ case Mode::PREPARE_TO_MOVE_OBJECTS:
+ {
+ qCritical() << "DbObjectOrganizer::processAll() called with PREAPRE mode.";
+ return false; // this method should not be called with this mode
+ }
+ case Mode::unknown:
+ {
+ qWarning() << "Unhandled unknown mode in DbObjectOrganizer.";
+ return false;
+ }
+ }
+
+ if (!res)
+ {
+ srcDb->rollback();
+ dstDb->rollback();
+ setFkEnabled(true);
+ return false;
+ }
+
+ if (!setFkEnabled(true))
+ {
+ srcDb->rollback();
+ dstDb->rollback();
+ return false;
+ }
+
+ if (!dstDb->commit())
+ {
+ // notifyError(tr("Could not commit transaction in database '%1'.").arg(dstDb->getName())); // TODO this is in another thread, cannot use notifyError
+ dstDb->rollback();
+ srcDb->rollback();
+ return false;
+ }
+
+ if (!srcDb->commit())
+ {
+ // TODO message - this can happen also for attached db operations, so also for creating objects in dstDb, so this affects not only srcDb, but also dstDb
+ srcDb->rollback();
+ return false;
+ }
+
+ return true;
+}
+
+bool DbObjectOrganizer::processDbObjects()
+{
+ for (const QString& table : (referencedTables + srcTables))
+ {
+ if (!copyTableToDb(table) || isInterrupted())
+ return false;
+ }
+
+ for (const QString& view : srcViews)
+ {
+ if (!copyViewToDb(view) || isInterrupted())
+ return false;
+ }
+
+ if (includeIndexes)
+ {
+ for (const QString& idx : srcIndexes)
+ {
+ if (!copyIndexToDb(idx) || isInterrupted())
+ return false;
+ }
+ }
+
+ if (includeTriggers)
+ {
+ for (const QString& trig : srcTriggers)
+ {
+ if (!copyTriggerToDb(trig) || isInterrupted())
+ return false;
+ }
+ }
+
+ if (deleteSourceObjects)
+ {
+ for (const QString& table : (referencedTables + srcTables))
+ dropTable(table);
+
+ for (const QString& view : srcViews)
+ dropView(view);
+ }
+
+ return true;
+}
+
+bool DbObjectOrganizer::resolveNameConflicts()
+{
+ QSet<QString> names;
+ QStringList namesInDst;
+ switch (mode)
+ {
+ case Mode::PREPARE_TO_COPY_OBJECTS:
+ case Mode::PREPARE_TO_MOVE_OBJECTS:
+ case Mode::COPY_OBJECTS:
+ case Mode::MOVE_OBJECTS:
+ {
+ names = referencedTables + srcTables + srcViews + srcIndexes + srcTriggers;
+ namesInDst = dstResolver->getAllObjects();
+ break;
+ }
+ case Mode::unknown:
+ {
+ qWarning() << "Unhandled unknown mode in DbObjectOrganizer::resolveNameConflicts().";
+ return false;
+ }
+ }
+
+ QString finalName;
+ for (const QString& srcName : names)
+ {
+ finalName = srcName;
+ while (namesInDst.contains(finalName, Qt::CaseInsensitive))
+ {
+ if (!nameConflictResolveFunction(finalName))
+ return false;
+ }
+ if (finalName != srcName)
+ renamed[srcName] = finalName;
+ }
+ return true;
+}
+
+bool DbObjectOrganizer::copyTableToDb(const QString& table)
+{
+ QString ddl;
+ QString targetTable = table;
+// AttachGuard attach = srcDb->guardedAttach(dstDb, true);
+// QString attachName = attach->getName();
+ if (renamed.contains(table) || !attachName.isNull())
+ {
+ SqliteQueryPtr parsedObject = srcResolver->getParsedObject(table, SchemaResolver::TABLE);
+ SqliteCreateTablePtr createTable = parsedObject.dynamicCast<SqliteCreateTable>();
+ if (!createTable)
+ {
+ qCritical() << "Could not parse table while copying:" << table << ", ddl:" << srcResolver->getObjectDdl(table, SchemaResolver::TABLE);
+ notifyError(tr("Error while creating table in target database: %1").arg(tr("Could not parse table.")));
+ return false;
+ }
+
+ if (renamed.contains(table))
+ targetTable = renamed[table];
+
+ createTable->table = targetTable;
+ if (!attachName.isNull())
+ createTable->database = attachName;
+
+ createTable->rebuildTokens();
+ ddl = createTable->detokenize();
+ }
+ else
+ {
+ ddl = srcResolver->getObjectDdl(table, SchemaResolver::TABLE);
+ }
+
+ ddl = convertDdlToDstVersion(ddl);
+ if (ddl.trimmed() == ";") // empty query, result of ignored errors in UI
+ return true;
+
+ SqlQueryPtr result;
+
+ if (attachName.isNull())
+ result = dstDb->exec(ddl);
+ else
+ result = srcDb->exec(ddl); // uses attachName to create object in attached db
+
+ if (result->isError())
+ {
+ notifyError(tr("Error while creating table in target database: %1").arg(result->getErrorText()));
+ return false;
+ }
+
+ if (!includeData)
+ return true;
+
+ if (isInterrupted())
+ return false;
+
+ srcTable = table;
+ bool res;
+ if (attachName.isNull())
+ {
+ notifyInfo(tr("Database %1 could not be attached to database %2, so the data of table %3 will be copied "
+ "with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient.")
+ .arg(dstDb->getName(), srcDb->getName(), srcTable));
+
+ res = copyDataAsMiddleware(targetTable);
+ }
+ else
+ {
+ res = copyDataUsingAttach(targetTable);
+ }
+ return res;
+}
+
+bool DbObjectOrganizer::copyDataAsMiddleware(const QString& table)
+{
+ QStringList srcColumns = srcResolver->getTableColumns(srcTable);
+ QString wrappedSrcTable = wrapObjIfNeeded(srcTable, srcDb->getDialect());
+ SqlQueryPtr results = srcDb->prepare("SELECT * FROM " + wrappedSrcTable);
+ setupSqlite2Helper(results, table, srcColumns);
+ if (!results->execute())
+ {
+ notifyError(tr("Error while copying data for table %1: %2").arg(table).arg(results->getErrorText()));
+ return false;
+ }
+
+ QStringList argPlaceholderList;
+ for (int i = 0, total = srcColumns.size(); i < total; ++i)
+ argPlaceholderList << "?";
+
+ QString wrappedDstTable = wrapObjIfNeeded(table, dstDb->getDialect());
+ QString sql = "INSERT INTO " + wrappedDstTable + " VALUES (" + argPlaceholderList.join(", ") + ")";
+ SqlQueryPtr insertQuery = dstDb->prepare(sql);
+
+ SqlResultsRowPtr row;
+ int i = 0;
+ while (results->hasNext())
+ {
+ row = results->next();
+ if (!row)
+ {
+ notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(results->getErrorText()));
+ return false;
+ }
+
+ insertQuery->setArgs(row->valueList());
+ if (!insertQuery->execute())
+ {
+ notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(insertQuery->getErrorText()));
+ return false;
+ }
+
+ if ((i % 1000) == 0 && isInterrupted())
+ return false;
+
+ i++;
+ }
+
+ if (isInterrupted())
+ return false;
+
+ return true;
+}
+
+bool DbObjectOrganizer::copyDataUsingAttach(const QString& table)
+{
+ QString wrappedSrcTable = wrapObjIfNeeded(srcTable, srcDb->getDialect());
+ QString wrappedDstTable = wrapObjIfNeeded(table, srcDb->getDialect());
+ SqlQueryPtr results = srcDb->exec("INSERT INTO " + attachName + "." + wrappedDstTable + " SELECT * FROM " + wrappedSrcTable);
+ if (results->isError())
+ {
+ notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(results->getErrorText()));
+ return false;
+ }
+ return true;
+}
+
+void DbObjectOrganizer::setupSqlite2Helper(SqlQueryPtr query, const QString& table, const QStringList& colNames)
+{
+ Sqlite2ColumnDataTypeHelper* sqlite2Helper = dynamic_cast<Sqlite2ColumnDataTypeHelper*>(query.data());
+ if (sqlite2Helper && binaryColumns.contains(table))
+ {
+ int i = 0;
+ QStringList binCols = binaryColumns[table];
+ for (const QString& colName : colNames)
+ {
+ if (binCols.contains(colName))
+ sqlite2Helper->setBinaryType(i);
+
+ i++;
+ }
+ }
+}
+
+void DbObjectOrganizer::dropTable(const QString& table)
+{
+ dropObject(table, "TABLE");
+}
+
+void DbObjectOrganizer::dropView(const QString& view)
+{
+ dropObject(view, "VIEW");
+}
+
+void DbObjectOrganizer::dropObject(const QString& name, const QString& type)
+{
+ QString wrappedSrcObj = wrapObjIfNeeded(name, srcDb->getDialect());
+ SqlQueryPtr results = srcDb->exec("DROP " + type + " " + wrappedSrcObj);
+ if (results->isError())
+ {
+ notifyWarn(tr("Error while dropping source view %1: %2\nTables, indexes, triggers and views copied to database %3 will remain.")
+ .arg(name).arg(results->getErrorText()).arg(dstDb->getName()));
+ }
+}
+
+bool DbObjectOrganizer::copyViewToDb(const QString& view)
+{
+ return copySimpleObjectToDb(view, tr("Error while creating view in target database: %1"));
+}
+
+bool DbObjectOrganizer::copyIndexToDb(const QString& index)
+{
+ return copySimpleObjectToDb(index, tr("Error while creating index in target database: %1"));
+}
+
+bool DbObjectOrganizer::copyTriggerToDb(const QString& trigger)
+{
+ return copySimpleObjectToDb(trigger, tr("Error while creating trigger in target database: %1"));
+}
+
+bool DbObjectOrganizer::copySimpleObjectToDb(const QString& name, const QString& errorMessage)
+{
+ QString ddl = srcResolver->getObjectDdl(name, SchemaResolver::ANY);
+ QString convertedDdl = convertDdlToDstVersion(ddl);
+ if (convertedDdl.trimmed() == ";") // empty query, result of ignored errors in UI
+ return true;
+
+ SqlQueryPtr result = dstDb->exec(convertedDdl);
+ if (result->isError())
+ {
+ notifyError(errorMessage.arg(result->getErrorText()));
+ qDebug() << "DDL that caused error in DbObjectOrganizer::copySimpleObjectToDb():" << ddl << "\nAfter converting:" << convertedDdl;
+ return false;
+ }
+
+ return true;
+}
+
+QSet<QString> DbObjectOrganizer::resolveReferencedTables(const QString& table, const QList<SqliteCreateTablePtr>& parsedTables)
+{
+ QSet<QString> tables = SchemaResolver::getFkReferencingTables(table, parsedTables).toSet();
+ for (const QString& fkTable : tables)
+ tables += SchemaResolver::getFkReferencingTables(fkTable, parsedTables).toSet();
+
+ tables.remove(table); // if it appeared somewhere in the references - we still don't need it here, it's the table we asked by in the first place
+ return tables;
+}
+
+void DbObjectOrganizer::collectDiffs(const StrHash<SchemaResolver::ObjectDetails>& details)
+{
+ if (srcDb->getVersion() == dstDb->getVersion())
+ return;
+
+
+ int dstVersion = dstDb->getVersion();
+ QSet<QString> names = srcTables + srcViews + referencedTables + srcIndexes + srcTriggers;
+ for (const QString& name : names)
+ {
+ if (!details.contains(name))
+ {
+ qCritical() << "Object named" << name << "not found in details when trying to prepare Diff for copying or moving object.";
+ continue;
+ }
+
+ versionConverter->reset();
+ if (dstVersion == 3)
+ versionConverter->convert2To3(details[name].ddl);
+ else
+ versionConverter->convert3To2(details[name].ddl);
+
+ diffListToConfirm += versionConverter->getDiffList();
+ if (!versionConverter->getErrors().isEmpty())
+ errorsToConfirm[name] = versionConverter->getErrors();
+ }
+}
+
+QString DbObjectOrganizer::convertDdlToDstVersion(const QString& ddl)
+{
+ if (srcDb->getVersion() == dstDb->getVersion())
+ return ddl;
+
+ if (dstDb->getVersion() == 3)
+ return versionConverter->convert2To3(ddl);
+ else
+ return versionConverter->convert3To2(ddl);
+}
+
+void DbObjectOrganizer::collectReferencedTables(const QString& table, const StrHash<SqliteQueryPtr>& allParsedObjects)
+{
+ QList<SqliteCreateTablePtr> parsedTables;
+ SqliteCreateTablePtr parsedTable;
+ for (SqliteQueryPtr query : allParsedObjects.values())
+ {
+ parsedTable = query.dynamicCast<SqliteCreateTable>();
+ if (parsedTable)
+ parsedTables << parsedTable;
+ }
+
+ QSet<QString> tables = resolveReferencedTables(table, parsedTables);
+ for (const QString& refTable : tables)
+ {
+ if (!referencedTables.contains(refTable) && !srcTables.contains(refTable))
+ referencedTables << refTable;
+ }
+}
+
+void DbObjectOrganizer::collectReferencedIndexes(const QString& table)
+{
+ srcIndexes += srcResolver->getIndexesForTable(table).toSet();
+}
+
+void DbObjectOrganizer::collectReferencedTriggersForTable(const QString& table)
+{
+ srcTriggers += srcResolver->getTriggersForTable(table).toSet();
+}
+
+void DbObjectOrganizer::collectReferencedTriggersForView(const QString& view)
+{
+ srcTriggers += srcResolver->getTriggersForView(view).toSet();
+}
+
+void DbObjectOrganizer::findBinaryColumns(const QString& table, const StrHash<SqliteQueryPtr>& allParsedObjects)
+{
+ if (!allParsedObjects.contains(table))
+ {
+ qWarning() << "Parsed objects don't have table" << table << "in DbObjectOrganizer::findBinaryColumns()";
+ return;
+ }
+
+ SqliteQueryPtr query = allParsedObjects[table];
+ SqliteCreateTablePtr createTable = query.dynamicCast<SqliteCreateTable>();
+ if (!createTable)
+ {
+ qWarning() << "Not CreateTable in DbObjectOrganizer::findBinaryColumns()";
+ return;
+ }
+
+ for (SqliteCreateTable::Column* column : createTable->columns)
+ {
+ if (!column->type)
+ continue;
+
+ if (DataType::isBinary(column->type->name))
+ binaryColumns[table] << column->name;
+ }
+}
+
+bool DbObjectOrganizer::setFkEnabled(bool enabled)
+{
+ if (dstDb->getVersion() == 2)
+ return true;
+
+ SqlQueryPtr result = dstDb->exec(QString("PRAGMA foreign_keys = %1").arg(enabled ? "on" : "off"));
+ if (result->isError())
+ {
+ // notifyError(tr("Error while executing PRAGMA on target database: %1").arg(result->getErrorText())); // TODO this is in another thread, cannot use notifyError
+ return false;
+ }
+ return true;
+}
+
+bool DbObjectOrganizer::isInterrupted()
+{
+ QMutexLocker locker(&interruptMutex);
+ return interrupted;
+}
+
+void DbObjectOrganizer::setExecuting(bool executing)
+{
+ QMutexLocker lock(&executingMutex);
+ this->executing = executing;
+}
+
+void DbObjectOrganizer::setSrcAndDstDb(Db* srcDb, Db* dstDb)
+{
+ safe_delete(srcResolver);
+ safe_delete(dstResolver);
+ this->srcDb = srcDb;
+ this->dstDb = dstDb;
+ srcResolver = new SchemaResolver(srcDb);
+ dstResolver = new SchemaResolver(dstDb);
+ srcResolver->setIgnoreSystemObjects(true);
+ dstResolver->setIgnoreSystemObjects(true);
+}
+
+void DbObjectOrganizer::emitFinished(bool success)
+{
+ switch (mode)
+ {
+ case Mode::COPY_OBJECTS:
+ case Mode::PREPARE_TO_COPY_OBJECTS:
+ emit finishedDbObjectsCopy(success, srcDb, dstDb);
+ break;
+ case Mode::PREPARE_TO_MOVE_OBJECTS:
+ case Mode::MOVE_OBJECTS:
+ emit finishedDbObjectsMove(success, srcDb, dstDb);
+ break;
+ case Mode::unknown:
+ break;
+ }
+ setExecuting(false);
+}
+
+void DbObjectOrganizer::processPreparationFinished()
+{
+ if (errorsToConfirm.size() > 0 && !conversionErrorsConfimFunction(errorsToConfirm))
+ {
+ emitFinished(false);
+ return;
+ }
+
+ if (diffListToConfirm.size() > 0 && !conversionConfimFunction(diffListToConfirm))
+ {
+ emitFinished(false);
+ return;
+ }
+
+ if (!resolveNameConflicts())
+ {
+ emitFinished(false);
+ return;
+ }
+
+ switch (mode)
+ {
+ case Mode::PREPARE_TO_COPY_OBJECTS:
+ mode = Mode::COPY_OBJECTS;
+ break;
+ case Mode::PREPARE_TO_MOVE_OBJECTS:
+ mode = Mode::MOVE_OBJECTS;
+ break;
+ case Mode::COPY_OBJECTS:
+ case Mode::MOVE_OBJECTS:
+ case Mode::unknown:
+ qCritical() << "DbObjectOrganizer::processPreparationFinished() called with a not PREPARE mode.";
+ emitFinished(false);
+ return;
+ }
+
+ QThreadPool::globalInstance()->start(this);
+}