File indexing completed on 2024-05-12 16:40:15

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Adam Pigg <adam@piggz.co.uk>
0003    Copyright (C) 2004-2016 Jarosław Staniek <staniek@kde.org>
0004    Copyright (C) 2005 Martin Ellis <martin.ellis@kdemail.net>
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This program is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this program; see the file COPYING.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "keximigrate.h"
0023 #include <core/kexi.h>
0024 #include <core/kexiproject.h>
0025 
0026 #include <KDbConnectionProxy>
0027 #include <KDbDriverManager>
0028 #include <KDbDriverMetaData>
0029 #include <KDbProperties>
0030 #include <KDbRecordData>
0031 #include <KDbSqlResult>
0032 #include <KDbVersionInfo>
0033 
0034 #include <QInputDialog>
0035 #include <QMutableListIterator>
0036 
0037 using namespace KexiMigration;
0038 
0039 class Q_DECL_HIDDEN KexiMigrate::Private
0040 {
0041 public:
0042     Private()
0043       : metaData(nullptr)
0044       , migrateData(nullptr)
0045       , sourceConnection(nullptr)
0046     {
0047     }
0048 
0049     ~Private()
0050     {
0051         qDeleteAll(kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport);
0052         kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear();
0053         delete migrateData;
0054     }
0055 
0056     QString couldNotCreateDatabaseErrorMessage() const
0057     {
0058         return xi18nc("@info", "Could not create database <resource>%1</resource>.",
0059                       migrateData->destinationProjectData()->databaseName());
0060     }
0061 
0062     //! Info about the driver's plugin
0063     const KexiMigratePluginMetaData *metaData;
0064 
0065     //! @todo Remove this! KexiMigrate should be usable for multiple concurrent migrations!
0066     //! Migrate Options
0067     KexiMigration::Data* migrateData;
0068 
0069     /*! Driver properties dictionary (indexed by name),
0070      useful for presenting properties to the user.
0071      Set available properties here in driver implementation. */
0072     QMap<QByteArray, QVariant> properties;
0073 
0074     /*! i18n'd captions for properties. You do not need
0075      to set predefined properties' caption in driver implementation
0076      -it's done automatically. */
0077     QMap<QByteArray, QString> propertyCaptions;
0078 
0079     //! KDb driver. For instance, it is used for escaping identifiers
0080     QPointer<KDbDriver> kexiDBDriver;
0081 
0082     /* private */
0083 
0084     //! Table schemas from source DB
0085     QList<KDbTableSchema*> tableSchemas;
0086 
0087     QList<KDbTableSchema*> kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport;
0088 
0089     KDbConnectionProxy* sourceConnection;
0090 
0091     //! Size of migration job
0092     quint64 progressTotal = 0;
0093 
0094     //! Amount of migration job complete
0095     quint64 progressDone = 0;
0096 
0097     //! Don't recalculate progress done until this value is reached.
0098     quint64 progressNextReport = 0;
0099 
0100 };
0101 
0102 KexiMigrate::KexiMigrate(QObject *parent, const QVariantList&)
0103         : QObject(parent)
0104         , KDbResultable()
0105         , d(new Private)
0106 {
0107 }
0108 
0109 //! Used for computing progress:
0110 //! let's assume that each table creation costs the same as inserting 20 rows
0111 #define NUM_OF_ROWS_PER_CREATE_TABLE 20
0112 
0113 
0114 //=============================================================================
0115 // Migration parameters
0116 KexiMigration::Data* KexiMigrate::data()
0117 {
0118     return d->migrateData;
0119 }
0120 
0121 void KexiMigrate::setData(KexiMigration::Data* migrateData)
0122 {
0123     if (d->migrateData && d->migrateData != migrateData) {
0124         delete d->migrateData;
0125     }
0126     d->migrateData = migrateData;
0127 }
0128 
0129 //=============================================================================
0130 // Destructor
0131 KexiMigrate::~KexiMigrate()
0132 {
0133     disconnectInternal();
0134     delete d;
0135 }
0136 
0137 const KexiMigratePluginMetaData* KexiMigrate::metaData() const
0138 {
0139     return d->metaData;
0140 }
0141 
0142 void KexiMigrate::setMetaData(const KexiMigratePluginMetaData *metaData)
0143 {
0144     d->metaData = metaData;
0145     //! @todo KEXI3 d->initInternalProperties();
0146 }
0147 
0148 KDbConnectionProxy* KexiMigrate::sourceConnection()
0149 {
0150     return d->sourceConnection;
0151 }
0152 
0153 bool KexiMigrate::checkIfDestinationDatabaseOverwritingNeedsAccepting(Kexi::ObjectStatus* result,
0154         bool *acceptingNeeded)
0155 {
0156     Q_ASSERT(acceptingNeeded);
0157     *acceptingNeeded = false;
0158     if (result)
0159         result->clearStatus();
0160 
0161     KDbDriverManager drvManager;
0162     KDbDriver *destDriver = drvManager.driver(
0163                                 d->migrateData->destinationProjectData()->connectionData()->driverId());
0164     if (!destDriver) {
0165         if (result) {
0166             result->setStatus(drvManager.resultable(), d->couldNotCreateDatabaseErrorMessage());
0167         }
0168         return false;
0169     }
0170 
0171     // For file-based dest. projects, we've already asked about overwriting
0172     // existing project but for server-based projects we need to ask now.
0173     if (destDriver->metaData()->isFileBased()) {
0174         return true; //nothing to check
0175     }
0176     QScopedPointer<KDbConnection> tmpConn(
0177         destDriver->createConnection(*d->migrateData->destinationProjectData()->connectionData()));
0178     if (!tmpConn || destDriver->result().isError() || !tmpConn->connect()) {
0179         m_result = destDriver->result();
0180         return true;
0181     }
0182     if (tmpConn->databaseExists(d->migrateData->destinationProjectData()->databaseName())) {
0183         *acceptingNeeded = true;
0184     }
0185     tmpConn->disconnect();
0186     return true;
0187 }
0188 
0189 bool KexiMigrate::isSourceAndDestinationDataSourceTheSame() const
0190 {
0191     KDbConnectionData* sourcedata = d->migrateData->source;
0192     KDbConnectionData* destinationdata = d->migrateData->destinationProjectData()->connectionData();
0193     return
0194         sourcedata && destinationdata &&
0195         d->migrateData->sourceName == d->migrateData->destinationProjectData()->databaseName() && // same database name
0196         sourcedata->driverId() == destinationdata->driverId()&& // same driver
0197         sourcedata->hostName() == destinationdata->hostName() && // same host
0198         sourcedata->databaseName() == destinationdata->databaseName(); // same database name/filename
0199 }
0200 
0201 bool KexiMigrate::connectInternal(Kexi::ObjectStatus* result)
0202 {
0203     Q_ASSERT(!d->sourceConnection);
0204     KDbConnection* conn = drv_createConnection();
0205     bool ok = !this->result().isError();
0206     if (ok) { // note: conn == nullptr does not mean failure
0207         if (conn) {
0208             d->sourceConnection = new KDbConnectionProxy(conn);
0209         }
0210         ok = drv_connect();
0211     }
0212     if (ok) {
0213         return true;
0214     }
0215     delete d->sourceConnection; // should not exist but do it for sanity
0216     d->sourceConnection = nullptr;
0217     QString message(xi18n("Could not connect to database %1.",
0218                           d->migrateData->sourceDatabaseInfoString()));
0219     qWarning() << message;
0220     if (result) {
0221         result->setStatus(this, message);
0222     }
0223     return false;
0224 }
0225 
0226 bool KexiMigrate::drv_connect()
0227 {
0228     if (!d->sourceConnection) {
0229         return false;
0230     }
0231     if (!d->sourceConnection->drv_connect()
0232         || !d->sourceConnection->drv_useDatabase(data()->sourceName))
0233     {
0234         m_result = d->sourceConnection->result();
0235         return false;
0236     }
0237     return true;
0238 }
0239 
0240 bool KexiMigrate::disconnectInternal()
0241 {
0242     const bool ok = drv_disconnect();
0243     if (!ok) {
0244         if (!m_result.isError()) {
0245             if (d->sourceConnection) {
0246                 m_result = d->sourceConnection->result();
0247             }
0248         }
0249     }
0250     delete d->sourceConnection;
0251     d->sourceConnection = 0;
0252     return ok;
0253 }
0254 
0255 bool KexiMigrate::drv_disconnect()
0256 {
0257     if (d->sourceConnection) {
0258         return d->sourceConnection->disconnect();
0259     }
0260     return false;
0261 }
0262 
0263 bool KexiMigrate::importTable(const QString& tableName, KDbConnectionProxy *destConn)
0264 {
0265     QScopedPointer<KDbTableSchema> t(new KDbTableSchema());
0266     KDbEscapedString sqlStatement = KDbEscapedString(
0267         "SELECT o_id, o_type, o_name, o_caption, o_desc "
0268         "FROM kexi__objects WHERE o_name=%1 AND o_type=%2")
0269             .arg(d->sourceConnection->escapeString(tableName))
0270             .arg(int(KDb::TableObjectType));
0271     QScopedPointer<KDbRecordData> record;
0272     {
0273         QSharedPointer<KDbSqlResult> result = d->sourceConnection->prepareSql(sqlStatement);
0274         if (!result) {
0275             m_result = d->sourceConnection->result();
0276             return false;
0277         }
0278         record.reset(result->fetchRecordData());
0279         if (!record) {
0280             return !result->lastResult().isError();
0281         }
0282         if (!destConn->setupObjectData(*record, t.data())) {
0283             m_result = d->sourceConnection->result();
0284             return false;
0285         }
0286     }
0287     sqlStatement
0288         = KDbEscapedString("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
0289                            "f_options, f_default, f_order, f_caption, f_help"
0290                            " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->id());
0291     QVector<QList<QVariant>> fieldRecords;
0292     {
0293         QSharedPointer<KDbSqlResult> fieldsResult = d->sourceConnection->prepareSql(sqlStatement);
0294         if (!fieldsResult) {
0295             m_result = d->sourceConnection->result();
0296             return false;
0297         }
0298         Q_FOREVER {
0299             QScopedPointer<KDbRecordData> fieldsRecord(fieldsResult->fetchRecordData());
0300             if (!fieldsRecord) {
0301                 if (!fieldsResult->lastResult().isError()) {
0302                     break;
0303                 }
0304                 m_result = fieldsResult->lastResult();
0305                 return false;
0306             }
0307             QScopedPointer<KDbField> f(destConn->setupField(*fieldsRecord));
0308             if (!f) {
0309                 return false;
0310             }
0311             QString testName(f->name());
0312             int i = 1;
0313             Q_FOREVER { // try to find unique name
0314                 if (!t->field(testName)) {
0315                     break;
0316                 }
0317                 ++i;
0318                 testName = f->name() + QString::number(i);
0319             }
0320             if (testName != f->name()) {
0321                 f->setName(testName);
0322                 if (!f->caption().isEmpty()) {
0323                     f->setCaption(QString::fromLatin1("%1 %2").arg(f->caption()).arg(i));
0324                 }
0325             }
0326             if (!t->addField(f.data())) {
0327                 return false;
0328             }
0329             f.take();
0330             fieldRecords.append(fieldsRecord->toList());
0331         }
0332     }
0333     if (!destConn->drv_createTable(*t)) {
0334         return false;
0335     }
0336     KDbTableSchema *kexi__objectsTable = destConn->tableSchema("kexi__objects");
0337     KDbTableSchema *kexi__fieldsTable = destConn->tableSchema("kexi__fields");
0338     if (!kexi__objectsTable || !kexi__fieldsTable) {
0339         return false;
0340     }
0341     // copy the kexi__objects record
0342     if (!destConn->insertRecord(kexi__objectsTable, record->toList())) {
0343         return false;
0344     }
0345     // copy the kexi__fields records
0346     for (const QList<QVariant> &fieldRecordData : fieldRecords) {
0347         if (!destConn->insertRecord(kexi__fieldsTable, fieldRecordData)) {
0348             return false;
0349         }
0350     }
0351     d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.append(t.take());
0352     return true;
0353 }
0354 
0355 bool KexiMigrate::performImport(Kexi::ObjectStatus* result)
0356 {
0357     if (result)
0358         result->clearStatus();
0359 
0360     // Step 1 - connect
0361     qDebug() << "CONNECTING...";
0362     if (!connectInternal(result)) {
0363         return false;
0364     }
0365 
0366     // "Real" steps
0367     bool ok = performImportInternal(result);
0368 
0369     if (!disconnectInternal()) {
0370         ok = false;
0371     }
0372     return ok;
0373 }
0374 
0375 bool KexiMigrate::performImportInternal(Kexi::ObjectStatus* result)
0376 {
0377     // Step 1 - destination driver
0378     KDbDriverManager drvManager;
0379     KDbDriver *destDriver = drvManager.driver(
0380                                      d->migrateData->destinationProjectData()->connectionData()->driverId());
0381     if (!destDriver) {
0382         result->setStatus(drvManager.resultable(), d->couldNotCreateDatabaseErrorMessage());
0383         return false;
0384     }
0385 
0386     // Step 2 - get table names
0387     qDebug() << "GETTING TABLENAMES...";
0388     QStringList tables;
0389     if (!tableNames(&tables)) {
0390         qWarning() << "Couldn't get list of tables";
0391         if (result)
0392             result->setStatus(
0393                 xi18n("Could not get a list of table names for database %1.",
0394                       d->migrateData->sourceDatabaseInfoString()), QString());
0395         return false;
0396     }
0397 
0398     // Check if there are any tables
0399     if (tables.isEmpty()) {
0400         qDebug() << "There were no tables to import";
0401         if (result)
0402             result->setStatus(
0403                 xi18n("No tables have been found in database %1.",
0404                       d->migrateData->sourceDatabaseInfoString()), QString());
0405         return false;
0406     }
0407 
0408     // Step 3 - Read KDb-compatible table schemas
0409     tables.sort();
0410     d->tableSchemas.clear();
0411     const bool kexi__objects_exists = tables.contains("kexi__objects");
0412     QStringList kexiDBTables;
0413     if (kexi__objects_exists) {
0414         tristate res = drv_queryStringListFromSql(
0415                            KDbEscapedString("SELECT o_name FROM kexi__objects WHERE o_type=%1")
0416                            .arg(int(KDb::TableObjectType)), 0, &kexiDBTables, -1);
0417         if (res == true) {
0418             // Skip KDb-compatible schemas that have no physical tables
0419             QMutableListIterator<QString> kdbTablesIt(kexiDBTables);
0420             while (kdbTablesIt.hasNext()) {
0421                 if (true != d->sourceConnection->resultExists(KDbEscapedString("SELECT * FROM %1")
0422                         .arg(sourceConnection()->escapeIdentifier(kdbTablesIt.next()))))
0423                 {
0424                     qDebug() << "KDb table does not exist:" << kdbTablesIt.value();
0425                     kdbTablesIt.remove();
0426                 }
0427             }
0428             // Separate KDb-compatible tables from the KDb-incompatible tables
0429             // so KDb-compatible tables can be later deeply copied without altering their IDs.
0430             kexiDBTables.sort();
0431             const QSet<QString> kdbTablesSet(kexiDBTables.toSet());
0432             QMutableListIterator<QString> tablesIt(tables);
0433             while (tablesIt.hasNext()) {
0434                 if (kdbTablesSet.contains(tablesIt.next())) {
0435                     tablesIt.remove();
0436                 }
0437             }
0438         //qDebug() << "KDb-compatible tables: " << kexiDBTables;
0439         //qDebug() << "non-KDb tables: " << tables;
0440         }
0441     }
0442 
0443     // -- read non-KDb-compatible tables schemas and create them in memory
0444     QMap<QString, QString> nativeNames;
0445     foreach(const QString& tableCaption, tables) {
0446         if (destDriver->isSystemObjectName(tableCaption)
0447             || KDbDriver::isKDbSystemObjectName(tableCaption) // "kexi__objects", etc.
0448                // other "kexi__*" tables at KexiProject level, e.g. "kexi__blobs"
0449             || tableCaption.startsWith(QLatin1String("kexi__"), Qt::CaseInsensitive))
0450         {
0451             continue;
0452         }
0453         // this is a non-KDb table: generate schema from native data source
0454         const QString tableIdentifier(KDb::stringToIdentifier(tableCaption.toLower()));
0455         nativeNames.insert(tableIdentifier, tableCaption);
0456         QScopedPointer<KDbTableSchema> tableSchema(new KDbTableSchema(tableIdentifier));
0457         tableSchema->setCaption(tableCaption);   //caption is equal to the original name
0458 
0459         if (!drv_readTableSchema(tableCaption, tableSchema.data())) {
0460             if (result)
0461                 result->setStatus(
0462                     xi18nc("@info",
0463                            "Could not import project from database %1. Error reading table <resource>%2</resource>.",
0464                            d->migrateData->sourceDatabaseInfoString(), tableCaption), QString());
0465             return false;
0466         }
0467         //yeah, got a table
0468         //Add it to list of tables which we will create if all goes well
0469         d->tableSchemas.append(tableSchema.take());
0470     }
0471 
0472     // Step 4 - Create a new database as we have all required info
0473     KexiProject destProject(
0474         *d->migrateData->destinationProjectData(), result ? (KDbMessageHandler*)*result : 0);
0475     bool ok = true == destProject.create(true /*forceOverwrite*/)
0476               && destProject.dbConnection();
0477 
0478     QScopedPointer<KDbConnectionProxy> destConn;
0479 
0480     if (ok) {
0481         destConn.reset(new KDbConnectionProxy(destProject.dbConnection()));
0482         destConn->setParentConnectionIsOwned(false);
0483     }
0484 
0485     KDbTransaction trans;
0486     if (ok) {
0487         trans = destConn->beginTransaction();
0488         if (trans.isNull()) {
0489             ok = false;
0490             if (result) {
0491                 result->setStatus(destConn->parentConnection(), d->couldNotCreateDatabaseErrorMessage());
0492             }
0493         }
0494     }
0495 
0496     if (ok) {
0497         if (drv_progressSupported()) {
0498             ok = progressInitialise();
0499         }
0500     }
0501 
0502     if (ok) {
0503         // Step 5 - Create the copies of KDb-compatible tables in memory (to maintain the same IDs)
0504         // Step 6.1 - Copy kexi__objects NOW because we'll soon create new objects with new IDs
0505         // Step 6.2 - Copy kexi__fields
0506         d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear();
0507         foreach(const QString& tableName, kexiDBTables) {
0508             if (!importTable(tableName, destConn.data())) {
0509                 if (!m_result.isError()) {
0510                     m_result.setCode();
0511                 }
0512                 m_result.prependMessage(
0513                     xi18nc("@info", "Could not import table <resource>%1</resource>.", tableName));
0514                 return false;
0515             }
0516         }
0517     }
0518 
0519     // Step 7 - Create non-KDb-compatible tables: new IDs will be assigned to them
0520     if (ok) {
0521         foreach(KDbTableSchema* ts, d->tableSchemas) {
0522             ok = destConn->createTable(ts);
0523             if (!ok) {
0524                 qWarning() << "Failed to create a table " << ts->name();
0525                 qWarning() << destConn->result();
0526                 if (result) {
0527                     result->setStatus(destConn->parentConnection()->result(), nullptr,
0528                                       d->couldNotCreateDatabaseErrorMessage());
0529                 }
0530                 d->tableSchemas.removeAt(d->tableSchemas.indexOf(ts));
0531                 break;
0532             }
0533             updateProgress((qulonglong)NUM_OF_ROWS_PER_CREATE_TABLE);
0534         }
0535     }
0536 
0537     if (ok)
0538         ok = destConn->commitTransaction(trans);
0539 
0540     if (ok) {
0541         //add KDb-compatible tables to the list, so data will be copied, if needed
0542         if (d->migrateData->shouldCopyData()) {
0543             foreach(KDbTableSchema* table,
0544                     d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport) {
0545                 d->tableSchemas.append(table);
0546             }
0547         } else
0548             d->tableSchemas.clear();
0549     }
0550 
0551     if (ok) {
0552         if (destProject.result().isError()) {
0553             ok = false;
0554             if (result)
0555                 result->setStatus(destProject.result(), nullptr,
0556                                   xi18n("Could not import project from data source %1.",
0557                                        d->migrateData->sourceDatabaseInfoString()));
0558         }
0559     }
0560 
0561     // Step 8 - Copy data if asked to
0562     if (ok) {
0563         trans = destConn->beginTransaction();
0564         ok = !trans.isNull();
0565     }
0566     if (ok) {
0567         if (d->migrateData->shouldCopyData()) {
0568 //! @todo check detailed "copy forms/blobs/tables" flags here when we add them
0569 //! @todo don't copy kexi__objectdata and kexi__userdata for tables that do not exist
0570             // Copy data for "kexi__objectdata" as well, if available in the source db
0571             if (tables.contains("kexi__objectdata"))
0572                 d->tableSchemas.append(destConn->tableSchema("kexi__objectdata"));
0573             // Copy data for "kexi__blobs" as well, if available in the source db
0574             if (tables.contains("kexi__blobs"))
0575                 d->tableSchemas.append(destConn->tableSchema("kexi__blobs"));
0576         }
0577 
0578         foreach(KDbTableSchema *ts, d->tableSchemas) {
0579             if (!ok)
0580                 break;
0581             if ((destConn->driver()->isSystemObjectName(ts->name())
0582                  || KDbDriver::isKDbSystemObjectName(ts->name()))
0583 //! @todo what if these two tables are not compatible with tables created in destination db
0584 //!       because newer db format was used?
0585                     && ts->name() != "kexi__objectdata" //copy this too
0586                     && ts->name() != "kexi__blobs" //copy this too
0587                     && ts->name() != "kexi__userdata" //copy this too
0588                )
0589             {
0590                 qDebug() << "Won't copy data to system table" << ts->name();
0591 //! @todo copy kexi__db contents!
0592                 continue;
0593             }
0594             QString tsName = nativeNames.value(ts->name());
0595             qDebug() << "Copying data for table: " << tsName;
0596             if (tsName.isEmpty()) {
0597                 tsName = ts->name();
0598             }
0599             ok = drv_copyTable(tsName, destConn->parentConnection(), ts);
0600             if (!ok) {
0601                 qWarning() << "Failed to copy table " << tsName;
0602                 if (result)
0603                     result->setStatus(destConn->parentConnection()->result(), nullptr,
0604                                       xi18nc("@info",
0605                                              "Could not copy table <resource>%1</resource> to destination database.", tsName));
0606                 break;
0607             }
0608         }//for
0609     }
0610 
0611     // Done.
0612     if (ok)
0613         ok = destConn->commitTransaction(trans);
0614 
0615     d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear();
0616 
0617     if (ok) {
0618         if (destConn)
0619             ok = destConn->disconnect();
0620         return ok;
0621     }
0622 
0623     // Finally: error handling
0624     if (result && result->error())
0625         result->setStatus(destConn->parentConnection()->result(), nullptr,
0626                           xi18n("Could not import data from data source %1.",
0627                                d->migrateData->sourceDatabaseInfoString()));
0628     if (destConn) {
0629         qWarning() << destConn->result();
0630         destConn->rollbackTransaction(trans);
0631         destConn->disconnect();
0632         destConn->dropDatabase(d->migrateData->destinationProjectData()->databaseName());
0633     }
0634     return false;
0635 }
0636 //=============================================================================
0637 
0638 bool KexiMigrate::performExport(Kexi::ObjectStatus* result)
0639 {
0640     if (result)
0641         result->clearStatus();
0642 
0643     //! @todo performExport
0644 
0645     return false;
0646 }
0647 
0648 //=============================================================================
0649 // Progress functions
0650 bool KexiMigrate::progressInitialise()
0651 {
0652     emit progressPercent(0);
0653 
0654     //! @todo Don't copy table names here
0655     QStringList tables;
0656     if (!tableNames(&tables))
0657         return false;
0658 
0659     // 1) Get the number of rows/bytes to import
0660     int tableNumber = 1;
0661     quint64 sum = 0;
0662     foreach(const QString& tableName, tables) {
0663         quint64 size;
0664         if (drv_getTableSize(tableName, &size)) {
0665             qDebug() << "table:" << tableName << "size: " << (ulong)size;
0666             sum += size;
0667             emit progressPercent(tableNumber * 5 /* 5% */ / tables.count());
0668             tableNumber++;
0669         } else {
0670             return false;
0671         }
0672     }
0673 
0674     qDebug() << "job size:" << sum;
0675     d->progressTotal = sum;
0676     d->progressTotal += tables.count() * NUM_OF_ROWS_PER_CREATE_TABLE;
0677     d->progressTotal = d->progressTotal * 105 / 100; //add 5 percent for above task 1)
0678     d->progressNextReport = sum / 100;
0679     d->progressDone = d->progressTotal * 5 / 100; //5 perecent already done in task 1)
0680     return true;
0681 }
0682 
0683 
0684 void KexiMigrate::updateProgress(qulonglong step)
0685 {
0686     d->progressDone += step;
0687     if (d->progressTotal > 0 && d->progressDone >= d->progressNextReport) {
0688         int percent = (d->progressDone + 1) * 100 / d->progressTotal;
0689         d->progressNextReport = ((percent + 1) * d->progressTotal) / 100;
0690         qDebug() << (ulong)d->progressDone << "/"
0691             << (ulong)d->progressTotal << " (" << percent << "%) next report at"
0692             << (ulong)d->progressNextReport;
0693         emit progressPercent(percent);
0694     }
0695 }
0696 
0697 //=============================================================================
0698 // Prompt the user to choose a field type
0699 KDbField::Type KexiMigrate::userType(const QString& fname)
0700 {
0701     const QStringList typeNames(KDbField::typeNames());
0702     bool ok;
0703     const QString res
0704         = QInputDialog::getItem(nullptr, xi18nc("@title:window", "Field Type"),
0705             xi18nc("@info",
0706                    "The data type for field <resource>%1</resource> could not be determined. "
0707                    "Please select one of the following data types.", fname),
0708             typeNames, 0, false/* !editable */, &ok);
0709 
0710     if (!ok || res.isEmpty())
0711 //! @todo OK?
0712         return KDbField::Text;
0713 
0714     return KDb::intToFieldType(int(KDbField::FirstType) + typeNames.indexOf(res));
0715 }
0716 
0717 QString KexiMigrate::drv_escapeIdentifier(const QString& str) const
0718 {
0719     return d->kexiDBDriver ? d->kexiDBDriver->escapeIdentifier(str) : str;
0720 }
0721 
0722 KDbDriver *KexiMigrate::driver()
0723 {
0724     return d->kexiDBDriver;
0725 }
0726 
0727 void KexiMigrate::setDriver(KDbDriver *driver)
0728 {
0729     d->kexiDBDriver = driver;
0730 }
0731 
0732 QVariant KexiMigrate::propertyValue(const QByteArray& propertyName)
0733 {
0734     return d->properties.value(propertyName.toLower());
0735 }
0736 
0737 QString KexiMigrate::propertyCaption(const QByteArray& propertyName) const
0738 {
0739     return d->propertyCaptions.value(propertyName.toLower());
0740 }
0741 
0742 void KexiMigrate::setPropertyValue(const QByteArray& propertyName, const QVariant& value)
0743 {
0744     d->properties.insert(propertyName.toLower(), value);
0745 }
0746 
0747 void KexiMigrate::setPropertyCaption(const QByteArray& propertyName, const QString &caption)
0748 {
0749     d->propertyCaptions.insert(propertyName.toLower(), caption);
0750 }
0751 
0752 QList<QByteArray> KexiMigrate::propertyNames() const
0753 {
0754     QList<QByteArray> names = d->properties.keys();
0755     std::sort(names.begin(), names.end());
0756     return names;
0757 }
0758 
0759 /* moved to MigrateManagerInternal::driver():
0760 bool KexiMigrate::isValid()
0761 {
0762     if (KexiMigration::versionMajor() != versionMajor()
0763             || KexiMigration::versionMinor() != versionMinor()) {
0764         setError(ERR_INCOMPAT_DRIVER_VERSION,
0765                  xi18n(
0766                      "Incompatible migration driver's \"%1\" version: found version %2, expected version %3.",
0767                      objectName(),
0768                      QString("%1.%2").arg(versionMajor()).arg(versionMinor()),
0769                      QString("%1.%2").arg(KexiMigration::versionMajor()).arg(KexiMigration::versionMinor()))
0770                 );
0771         return false;
0772     }
0773     return true;
0774 }
0775 */
0776 
0777 bool KexiMigrate::drv_queryMaxNumber(const QString& tableName,
0778                                      const QString& columnName, int *result)
0779 {
0780     QString string;
0781     tristate r = drv_querySingleStringFromSql(
0782                      KDbEscapedString("SELECT MAX(%1) FROM %2")
0783                      .arg(drv_escapeIdentifier(columnName))
0784                      .arg(drv_escapeIdentifier(tableName)), 0, &string);
0785     if (r == false)
0786         return false;
0787     if (~r) {
0788         result = 0;
0789         return true;
0790     }
0791     bool ok;
0792     int tmpResult = string.toInt(&ok);
0793     if (ok)
0794         *result = tmpResult;
0795     return ok;
0796 }
0797 
0798 tristate KexiMigrate::drv_querySingleStringFromSql(
0799     const KDbEscapedString& sqlStatement, int columnNumber, QString *string)
0800 {
0801     QStringList stringList;
0802     const tristate res = drv_queryStringListFromSql(sqlStatement, columnNumber, &stringList, 1);
0803     if (true == res)
0804         *string = stringList.first();
0805     return res;
0806 }
0807 
0808 bool KexiMigrate::connectSource(Kexi::ObjectStatus* result)
0809 {
0810     return connectInternal(result);
0811 }
0812 
0813 bool KexiMigrate::disconnectSource()
0814 {
0815     return disconnectInternal();
0816 }
0817 
0818 bool KexiMigrate::readTableSchema(const QString& originalName, KDbTableSchema *tableSchema)
0819 {
0820   return drv_readTableSchema(originalName, tableSchema);
0821 }
0822 
0823 bool KexiMigrate::tableNames(QStringList *tn)
0824 {
0825     //! @todo Cache list of table names
0826     qDebug() << "Reading list of tables...";
0827     tn->clear();
0828     return drv_tableNames(tn);
0829 }
0830 
0831 QSharedPointer<KDbSqlResult> KexiMigrate::readFromTable(const QString & tableName)
0832 {
0833   return drv_readFromTable(tableName);
0834 }
0835 
0836 bool KexiMigrate::moveNext()
0837 {
0838   return drv_moveNext();
0839 }
0840 
0841 bool KexiMigrate::movePrevious()
0842 {
0843   return drv_movePrevious();
0844 }
0845 
0846 bool KexiMigrate::moveFirst()
0847 {
0848   return drv_moveFirst();
0849 }
0850 
0851 bool KexiMigrate::moveLast()
0852 {
0853   return drv_moveLast();
0854 }
0855 
0856 QVariant KexiMigrate::value(int i)
0857 {
0858   return drv_value(i);
0859 }
0860 
0861 //------------------------
0862 
0863 KDbVersionInfo KexiMigration::version()
0864 {
0865     return KDbVersionInfo(KEXI_MIGRATION_VERSION_MAJOR, KEXI_MIGRATION_VERSION_MINOR, 0);
0866 }
0867