00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "keximigrate.h"
00023
00024 #include <kdebug.h>
00025 #include <kinputdialog.h>
00026 #include <kapplication.h>
00027
00028 #include <kexiutils/identifier.h>
00029 #include <core/kexi.h>
00030 #include <core/kexiproject.h>
00031 #include <kexidb/drivermanager.h>
00032
00033 using namespace KexiDB;
00034 using namespace KexiMigration;
00035
00036 KexiMigrate::KexiMigrate(QObject *parent, const char *name,
00037 const QStringList&)
00038 : QObject( parent, name )
00039 , m_migrateData(0)
00040 , m_destPrj(0)
00041 {
00042 m_kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.setAutoDelete(true);
00043 }
00044
00047 #define NUM_OF_ROWS_PER_CREATE_TABLE 20
00048
00049
00050
00051
00052 void KexiMigrate::setData(KexiMigration::Data* migrateData)
00053 {
00054 m_migrateData = migrateData;
00055 }
00056
00057
00058
00059 KexiMigrate::~KexiMigrate()
00060 {
00061 delete m_destPrj;
00062 }
00063
00064 bool KexiMigrate::checkIfDestinationDatabaseOverwritingNeedsAccepting(Kexi::ObjectStatus* result,
00065 bool& acceptingNeeded)
00066 {
00067 acceptingNeeded = false;
00068 if (result)
00069 result->clearStatus();
00070
00071 KexiDB::DriverManager drvManager;
00072 KexiDB::Driver *destDriver = drvManager.driver(
00073 m_migrateData->destination->connectionData()->driverName);
00074 if (!destDriver) {
00075 result->setStatus(&drvManager,
00076 i18n("Could not create database \"%1\".")
00077 .arg(m_migrateData->destination->databaseName()));
00078 return false;
00079 }
00080
00081
00082
00083 if (destDriver->isFileDriver())
00084 return true;
00085 KexiDB::Connection *tmpConn
00086 = destDriver->createConnection( *m_migrateData->destination->connectionData() );
00087 if (!tmpConn || destDriver->error() || !tmpConn->connect()) {
00088 delete tmpConn;
00089 return true;
00090 }
00091 if (tmpConn->databaseExists( m_migrateData->destination->databaseName() )) {
00092 acceptingNeeded = true;
00093 }
00094 tmpConn->disconnect();
00095 delete tmpConn;
00096 return true;
00097 }
00098
00099 bool KexiMigrate::isSourceAndDestinationDataSourceTheSame() const
00100 {
00101 KexiDB::ConnectionData* sourcedata = m_migrateData->source;
00102 KexiDB::ConnectionData* destinationdata = m_migrateData->destination->connectionData();
00103 return (
00104 sourcedata && destinationdata &&
00105 m_migrateData->sourceName == m_migrateData->destination->databaseName() &&
00106 sourcedata->driverName == destinationdata->driverName &&
00107 sourcedata->hostName == destinationdata->hostName &&
00108 sourcedata->fileName() == destinationdata->fileName() &&
00109 sourcedata->dbPath() == destinationdata->dbPath() &&
00110 sourcedata->dbFileName() == destinationdata->dbFileName()
00111 );
00112 }
00113
00114
00115
00116 bool KexiMigrate::performImport(Kexi::ObjectStatus* result)
00117 {
00118 if (result)
00119 result->clearStatus();
00120
00121 KexiDB::DriverManager drvManager;
00122 KexiDB::Driver *destDriver = drvManager.driver(
00123 m_migrateData->destination->connectionData()->driverName);
00124 if (!destDriver) {
00125 result->setStatus(&drvManager,
00126 i18n("Could not create database \"%1\".")
00127 .arg(m_migrateData->destination->databaseName()));
00128 return false;
00129 }
00130
00131 QStringList tables;
00132
00133
00134 kdDebug() << "KexiMigrate::performImport() CONNECTING..." << endl;
00135 if (!drv_connect()) {
00136 kdDebug() << "Couldnt connect to database server" << endl;
00137 if (result)
00138 result->setStatus(i18n("Could not connect to data source \"%1\".")
00139 .arg(m_migrateData->source->serverInfoString()), "");
00140 return false;
00141 }
00142
00143
00144 kdDebug() << "KexiMigrate::performImport() GETTING TABLENAMES..." << endl;
00145 if (!tableNames(tables)) {
00146 kdDebug() << "Couldnt get list of tables" << endl;
00147 if (result)
00148 result->setStatus(
00149 i18n("Could not get a list of table names for data source \"%1\".")
00150 .arg(m_migrateData->source->serverInfoString()), "");
00151 return false;
00152 }
00153
00154
00155 if (tables.isEmpty()) {
00156 kdDebug() << "There were no tables to import" << endl;
00157 if (result)
00158 result->setStatus(
00159 i18n("No tables to import found in data source \"%1\".")
00160 .arg(m_migrateData->source->serverInfoString()), "");
00161 return false;
00162 }
00163
00164
00165 tables.sort();
00166 m_tableSchemas.clear();
00167 if (!destDriver) {
00168 result->setStatus(&drvManager);
00169 return false;
00170 }
00171 const bool kexi__objects_exists = tables.find("kexi__objects")!=tables.end();
00172 QStringList kexiDBTables;
00173 if (kexi__objects_exists) {
00174 tristate res = drv_queryStringListFromSQL(
00175 QString::fromLatin1("SELECT o_name FROM kexi__objects WHERE o_type=%1")
00176 .arg((int)KexiDB::TableObjectType), 0, kexiDBTables, -1);
00177 if (res == true) {
00178
00179
00180 kexiDBTables.sort();
00181 foreach(QStringList::ConstIterator, it, kexiDBTables)
00182 tables.remove( *it );
00183
00184
00185 }
00186 }
00187
00188
00189
00190 foreach (QStringList::ConstIterator, it, tables) {
00191 if (destDriver->isSystemObjectName( *it )
00192 || (*it).lower().startsWith("kexi__"))
00193 continue;
00194
00195 const QString tableName( KexiUtils::string2Identifier(*it) );
00196 KexiDB::TableSchema *tableSchema = new KexiDB::TableSchema(tableName);
00197 tableSchema->setCaption( *it );
00198
00199 if (!drv_readTableSchema(*it, *tableSchema)) {
00200 delete tableSchema;
00201 if (result)
00202 result->setStatus(
00203 i18n("Could not import project from data source \"%1\". Error reading table \"%2\".")
00204 .arg(m_migrateData->source->serverInfoString()).arg(tableName), "");
00205 return false;
00206 }
00207
00208
00209 m_tableSchemas.append(tableSchema);
00210 }
00211
00212
00213
00214
00215 delete m_destPrj;
00216 m_destPrj = new KexiProject(m_migrateData->destination,
00217 result ? (KexiDB::MessageHandler*)*result : 0);
00218 bool ok = true == m_destPrj->create(true );
00219
00220 KexiDB::Connection *destConn = 0;
00221
00222 if (ok)
00223 ok = (destConn = m_destPrj->dbConnection());
00224
00225 KexiDB::Transaction trans;
00226 if (ok) {
00227 trans = destConn->beginTransaction();
00228 if (trans.isNull()) {
00229 ok = false;
00230 if (result)
00231 result->setStatus(destConn,
00232 i18n("Could not create database \"%1\".")
00233 .arg(m_migrateData->destination->databaseName()));
00234
00235
00236
00237 }
00238 }
00239
00240 if (ok) {
00241 if (drv_progressSupported())
00242 progressInitialise();
00243
00244
00245 m_kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear();
00246 foreach (QStringList::ConstIterator, it, kexiDBTables) {
00247
00248 TableSchema *t = new TableSchema();
00249 RowData data;
00250 bool firstRecord = true;
00251 if (true == drv_fetchRecordFromSQL(
00252 QString("SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects "
00253 "WHERE o_name='%1' AND o_type=%1").arg(*it).arg((int)KexiDB::TableObjectType),
00254 data, firstRecord)
00255 && destConn->setupObjectSchemaData( data, *t ))
00256 {
00258
00259 firstRecord = true;
00260 QString sql(
00261 QString::fromLatin1("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
00262 "f_options, f_default, f_order, f_caption, f_help"
00263 " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->id()) );
00264 while (ok) {
00265 tristate res = drv_fetchRecordFromSQL(sql, data, firstRecord);
00266 if (res != true) {
00267 if (false == res)
00268 ok = false;
00269 break;
00270 }
00271 KexiDB::Field* f = destConn->setupField( data );
00272 if (f)
00273 t->addField(f);
00274 else
00275 ok = false;
00276 }
00277 if (ok)
00278 ok = destConn->drv_createTable(*t);
00279 if (ok)
00280 m_kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.append(t);
00281 }
00282 if (!ok)
00283 delete t;
00284 }
00285 }
00286
00287
00288 if (ok) {
00289 if (kexi__objects_exists)
00290 ok = drv_copyTable("kexi__objects", destConn, destConn->tableSchema("kexi__objects"));
00291 }
00292
00293
00294 if (ok) {
00295 KexiDB::TableSchema *ts;
00296 for (QPtrListIterator<TableSchema> it (m_tableSchemas); (ts = it.current()); ++it) {
00297 ok = destConn->createTable( ts );
00298 if (!ok) {
00299 kdDebug() << "Failed to create a table " << ts->name() << endl;
00300 destConn->debugError();
00301 if (result)
00302 result->setStatus(destConn,
00303 i18n("Could not create database \"%1\".")
00304 .arg(m_migrateData->destination->databaseName()));
00305 m_tableSchemas.remove(ts);
00306 break;
00307 }
00308 updateProgress((Q_ULLONG)NUM_OF_ROWS_PER_CREATE_TABLE);
00309 }
00310 }
00311
00312 if (ok)
00313 ok = destConn->commitTransaction(trans);
00314
00315 if (ok) {
00316
00317 if (m_migrateData->keepData) {
00318 for(QPtrListIterator<TableSchema> it (m_kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport);
00319 it.current(); ++it)
00320 {
00321 m_tableSchemas.append(it.current());
00322 }
00323 }
00324 else
00325 m_tableSchemas.clear();
00326 }
00327
00328 if (ok) {
00329 if (m_destPrj->error()) {
00330 ok = false;
00331 if (result)
00332 result->setStatus(m_destPrj,
00333 i18n("Could not import project from data source \"%1\".")
00334 .arg(m_migrateData->source->serverInfoString()));
00335 }
00336 }
00337
00338
00339 if (ok) {
00340 trans = destConn->beginTransaction();
00341 ok = !trans.isNull();
00342 }
00343 if (ok) {
00344 if (m_migrateData->keepData) {
00346
00347 if (tables.find("kexi__objectdata")!=tables.end())
00348 m_tableSchemas.append(destConn->tableSchema("kexi__objectdata"));
00349
00350 if (tables.find("kexi__blobs")!=tables.end())
00351 m_tableSchemas.append(destConn->tableSchema("kexi__blobs"));
00352
00353 if (tables.find("kexi__fields")!=tables.end())
00354 m_tableSchemas.append(destConn->tableSchema("kexi__fields"));
00355 }
00356
00357 for(QPtrListIterator<TableSchema> ts(m_tableSchemas); ok && ts.current() != 0 ; ++ts)
00358 {
00359 const QString tname( ts.current()->name().lower() );
00360 if (destConn->driver()->isSystemObjectName( tname )
00363 && tname!="kexi__objectdata"
00364 && tname!="kexi__blobs"
00365 && tname!="kexi__fields"
00366 )
00367 {
00368 kdDebug() << "Do not copy data for system table: " << tname << endl;
00370 continue;
00371 }
00372 kdDebug() << "Copying data for table: " << tname << endl;
00373 QString originalTableName;
00374 if (kexiDBTables.find(tname)==kexiDBTables.end())
00375
00376 originalTableName = ts.current()->caption().isEmpty() ? tname : ts.current()->caption();
00377 else
00378 originalTableName = tname;
00379 ok = drv_copyTable(originalTableName, destConn, ts.current());
00380 if (!ok) {
00381 kdDebug() << "Failed to copy table " << tname << endl;
00382 if (result)
00383 result->setStatus(destConn,
00384 i18n("Could not copy table \"%1\" to destination database.").arg(tname));
00385 break;
00386 }
00387 }
00388 }
00389
00390
00391 if (ok)
00392 ok = destConn->commitTransaction(trans);
00393
00394 if (ok)
00395 ok = drv_disconnect();
00396
00397 m_kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear();
00398
00399 if (ok) {
00400 if (destConn)
00401 ok = destConn->disconnect();
00402 return ok;
00403 }
00404
00405
00406 if (result && result->error())
00407 result->setStatus(destConn,
00408 i18n("Could not import data from data source \"%1\".")
00409 .arg(m_migrateData->source->serverInfoString()));
00410 if (destConn) {
00411 destConn->debugError();
00412 destConn->rollbackTransaction(trans);
00413 }
00414 drv_disconnect();
00415 if (destConn) {
00416 destConn->disconnect();
00417 destConn->dropDatabase(m_migrateData->destination->databaseName());
00418 }
00419 return false;
00420 }
00421
00422
00423 bool KexiMigrate::performExport(Kexi::ObjectStatus* result)
00424 {
00425 if (result)
00426 result->clearStatus();
00427
00429
00430 return false;
00431 }
00432
00433
00434
00435 bool KexiMigrate::tableNames(QStringList & tn)
00436 {
00438 kdDebug() << "Reading list of tables..." << endl;
00439 return drv_tableNames(tn);
00440 }
00441
00442
00443
00444 bool KexiMigrate::progressInitialise() {
00445 Q_ULLONG sum = 0, size;
00446 emit progressPercent(0);
00447
00449 QStringList tables;
00450 if(!tableNames(tables))
00451 return false;
00452
00453
00454 int tableNumber = 1;
00455 for(QStringList::Iterator it = tables.begin();
00456 it != tables.end(); ++it, tableNumber++)
00457 {
00458 if(drv_getTableSize(*it, size)) {
00459 kdDebug() << "KexiMigrate::progressInitialise() - table: " << *it
00460 << "size: " << (ulong)size << endl;
00461 sum += size;
00462 emit progressPercent(tableNumber * 5 / tables.count());
00463 } else {
00464 return false;
00465 }
00466 }
00467
00468 kdDebug() << "KexiMigrate::progressInitialise() - job size: " << (ulong)sum << endl;
00469 m_progressTotal = sum;
00470 m_progressTotal += tables.count() * NUM_OF_ROWS_PER_CREATE_TABLE;
00471 m_progressTotal = m_progressTotal * 105 / 100;
00472 m_progressNextReport = sum / 100;
00473 m_progressDone = m_progressTotal * 5 / 100;
00474 return true;
00475 }
00476
00477
00478 void KexiMigrate::updateProgress(Q_ULLONG step) {
00479 m_progressDone += step;
00480 if (m_progressDone >= m_progressNextReport) {
00481 int percent = (m_progressDone+1) * 100 / m_progressTotal;
00482 m_progressNextReport = ((percent + 1) * m_progressTotal) / 100;
00483 kdDebug() << "KexiMigrate::updateProgress(): " << (ulong)m_progressDone << "/"
00484 << (ulong)m_progressTotal << " (" << percent << "%) next report at "
00485 << (ulong)m_progressNextReport << endl;
00486 emit progressPercent(percent);
00487 }
00488 }
00489
00490
00491
00492 KexiDB::Field::Type KexiMigrate::userType(const QString& fname)
00493 {
00494 KInputDialog *dlg;
00495 QStringList types;
00496 QString res;
00497
00498 types << "Byte";
00499 types << "Short Integer";
00500 types << "Integer";
00501 types << "Big Integer";
00502 types << "Boolean";
00503 types << "Date";
00504 types << "Date Time";
00505 types << "Time";
00506 types << "Float";
00507 types << "Double";
00508 types << "Text";
00509 types << "Long Text";
00510 types << "Binary Large Object";
00511
00512 res = dlg->getItem( i18n("Field Type"),
00513 i18n("The data type for %1 could not be determined. "
00514 "Please select one of the following data "
00515 "types").arg(fname),
00516 types, 0, false);
00517
00519 if (res == *types.at(0))
00520 return KexiDB::Field::Byte;
00521 else if (res == *types.at(1))
00522 return KexiDB::Field::ShortInteger;
00523 else if (res == *types.at(2))
00524 return KexiDB::Field::Integer;
00525 else if (res == *types.at(3))
00526 return KexiDB::Field::BigInteger;
00527 else if (res == *types.at(4))
00528 return KexiDB::Field::Boolean;
00529 else if (res == *types.at(5))
00530 return KexiDB::Field::Date;
00531 else if (res == *types.at(6))
00532 return KexiDB::Field::DateTime;
00533 else if (res == *types.at(7))
00534 return KexiDB::Field::Time;
00535 else if (res == *types.at(8))
00536 return KexiDB::Field::Float;
00537 else if (res == *types.at(9))
00538 return KexiDB::Field::Double;
00539 else if (res == *types.at(10))
00540 return KexiDB::Field::Text;
00541 else if (res == *types.at(11))
00542 return KexiDB::Field::LongText;
00543 else if (res == *types.at(12))
00544 return KexiDB::Field::BLOB;
00545 else
00546 return KexiDB::Field::Text;
00547 }
00548
00549 QVariant KexiMigrate::propertyValue( const QCString& propName )
00550 {
00551 return m_properties[propName.lower()];
00552 }
00553
00554 QString KexiMigrate::propertyCaption( const QCString& propName ) const
00555 {
00556 return m_propertyCaptions[propName.lower()];
00557 }
00558
00559 void KexiMigrate::setPropertyValue( const QCString& propName, const QVariant& value )
00560 {
00561 m_properties[propName.lower()] = value;
00562 }
00563
00564 QValueList<QCString> KexiMigrate::propertyNames() const
00565 {
00566 QValueList<QCString> names = m_properties.keys();
00567 qHeapSort(names);
00568 return names;
00569 }
00570
00571 bool KexiMigrate::isValid()
00572 {
00573 if (KexiMigration::versionMajor() != versionMajor()
00574 || KexiMigration::versionMinor() != versionMinor())
00575 {
00576 setError(ERR_INCOMPAT_DRIVER_VERSION,
00577 i18n("Incompatible migration driver's \"%1\" version: found version %2, expected version %3.")
00578 .arg(name())
00579 .arg(QString("%1.%2").arg(versionMajor()).arg(versionMinor()))
00580 .arg(QString("%1.%2").arg(KexiMigration::versionMajor()).arg(KexiMigration::versionMinor())));
00581 return false;
00582 }
00583 return true;
00584 }
00585
00586 bool KexiMigrate::drv_queryMaxNumber(const QString& tableName,
00587 const QString& columnName, int& result)
00588 {
00589 QString string;
00590 tristate r = drv_querySingleStringFromSQL(
00591 QString::fromLatin1("SELECT MAX(%1) FROM %2").arg(drv_escapeIdentifier(columnName))
00592 .arg(drv_escapeIdentifier(tableName)), 0, string);
00593 if (r == false)
00594 return false;
00595 if (~r) {
00596 result = 0;
00597 return true;
00598 }
00599 bool ok;
00600 int tmpResult = string.toInt(&ok);
00601 if (ok)
00602 result = tmpResult;
00603 return ok;
00604 }
00605
00606 tristate KexiMigrate::drv_querySingleStringFromSQL(
00607 const QString& sqlStatement, uint columnNumber, QString& string)
00608 {
00609 QStringList stringList;
00610 const tristate res = drv_queryStringListFromSQL(sqlStatement, columnNumber, stringList, 1);
00611 if (true == res)
00612 string = stringList.first();
00613 return res;
00614 }
00615
00616 #include "keximigrate.moc"