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