kexi

keximigrate.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Adam Pigg <adam@piggz.co.uk>
00003    Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
00004    Copyright (C) 2005 Martin Ellis <martin.ellis@kdemail.net>
00005 
00006    This program is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this program; see the file COPYING.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
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 //  , m_copyOfKexi__objects(0)
00042 {
00043 }
00044 
00047 #define NUM_OF_ROWS_PER_CREATE_TABLE 20
00048 
00049 
00050 //=============================================================================
00051 // Migration parameters
00052 void KexiMigrate::setData(KexiMigration::Data* migrateData)
00053 {
00054     m_migrateData = migrateData;
00055 }
00056 
00057 //=============================================================================
00058 // Destructor
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     // For file-based dest. projects, we've already asked about overwriting 
00082     // existing project but for server-based projects we need to ask now.
00083     if (destDriver->isFileDriver())
00084         return true; //nothing to check
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 //=============================================================================
00100 // Perform Import operation
00101 bool KexiMigrate::performImport(Kexi::ObjectStatus* result)
00102 {
00103     if (result)
00104         result->clearStatus();
00105 
00106     KexiDB::DriverManager drvManager;
00107     KexiDB::Driver *destDriver = drvManager.driver(
00108         m_migrateData->destination->connectionData()->driverName);
00109     if (!destDriver) {
00110         result->setStatus(&drvManager,
00111             i18n("Could not create database \"%1\".")
00112             .arg(m_migrateData->destination->databaseName()));
00113         return false;
00114     }
00115 
00116     QStringList tables;
00117 
00118     // Step 1 - connect
00119     kdDebug() << "KexiMigrate::performImport() CONNECTING..." << endl;
00120     if (!drv_connect()) {
00121         kdDebug() << "Couldnt connect to database server" << endl;
00122         if (result)
00123             result->setStatus(i18n("Could not connect to data source \"%1\".")
00124                 .arg(m_migrateData->source->serverInfoString()), "");
00125         return false;
00126     }
00127 
00128     // Step 2 - get table names
00129     kdDebug() << "KexiMigrate::performImport() GETTING TABLENAMES..." << endl;
00130     if (!tableNames(tables)) {
00131         kdDebug() << "Couldnt get list of tables" << endl;
00132         if (result)
00133             result->setStatus(
00134                 i18n("Could not get a list of table names for data source \"%1\".")
00135                     .arg(m_migrateData->source->serverInfoString()), "");
00136         return false;
00137     }
00138 
00139     //tmp to force error!!!
00140     //tables.prepend("$$$$$");
00141 
00142     // Check if there are any tables
00143     if (tables.isEmpty()) {
00144         kdDebug() << "There were no tables to import" << endl;
00145         if (result)
00146             result->setStatus(
00147                 i18n("No tables to import found in data source \"%1\".")
00148                     .arg(m_migrateData->source->serverInfoString()), "");
00149         return false;
00150     }
00151 
00152     // Step 3 - Read table schemas
00153     m_tableSchemas.clear();
00154     if (!destDriver) {
00155         result->setStatus(&drvManager);
00156         return false;
00157     }
00158     foreach(QStringList::ConstIterator, it, tables) {
00159         if (destDriver->isSystemObjectName( *it ) //"kexi__objects", etc.
00160             || (*it).lower().startsWith("kexi__")) //tables at KexiProject level, e.g. "kexi__blobs"
00161             continue;
00162 
00163         const QString tableName( KexiUtils::string2Identifier(*it) );
00164         KexiDB::TableSchema *tableSchema;
00165 //      if (tableName.lower().startsWith("kexi__"))
00166 //          tableSchema = new KexiDB::InternalTableSchema(tableName);
00167 //      else
00168         tableSchema = new KexiDB::TableSchema(tableName);
00169 
00170         tableSchema->setCaption( *it ); //caption is equal to the original name
00171 
00172         if (drv_readTableSchema(*it, *tableSchema)) {
00173             //yeah, got a table
00174             //Add it to list of tables which we will create if all goes well
00175             m_tableSchemas.append(tableSchema);
00176         } else {
00177             delete tableSchema;
00178             if (result)
00179                 result->setStatus(
00180                     i18n("Could not import project from data source \"%1\". Error reading table \"%2\".")
00181                     .arg(m_migrateData->source->serverInfoString()).arg(tableName), "");
00182             return false;
00183         }
00184     }
00185 
00186     // Step 4 - Create new database as we have all required info
00187     delete m_destPrj;
00188     m_destPrj = createProject(result);
00189     if (!m_destPrj || m_destPrj->error()) {
00190         if (result)
00191             result->setStatus(m_destPrj,
00192                 i18n("Could not import project from data source \"%1\".")
00193                 .arg(m_migrateData->source->serverInfoString()));
00194         return false;
00195     }
00196 
00197     // Step 5 - Copy data if asked to
00198     bool ok = true;
00199     KexiDB::Transaction trans;
00200     if (!m_migrateData->keepData)
00201         m_tableSchemas.clear();
00202 
00203     KexiDB::Connection *destConn = m_destPrj->dbConnection();
00204     ok = destConn;
00205     if (ok) {
00206         trans = destConn->beginTransaction();
00207         ok = !trans.isNull();
00208     }
00209     if (ok) {
00210         // Copy data for "kexi__objectdata" as well, if available in the source db
00211         if (tables.find("kexi__objectdata")!=tables.end())
00212             m_tableSchemas.append(destConn->tableSchema("kexi__objectdata")); 
00213     }
00214 
00215     for(QPtrListIterator<TableSchema> ts(m_tableSchemas); ok && ts.current() != 0 ; ++ts)
00216     {
00217         const QString tname( ts.current()->name().lower() );
00218         if (destConn->driver()->isSystemObjectName( tname )
00221             && tname!="kexi__objectdata" //copy this too
00222         )
00223         {
00224             kdDebug() << "Do not copy data for system table: " << tname << endl;
00226             continue;
00227         }
00228         kdDebug() << "Copying data for table: " << tname << endl;
00229         ok = drv_copyTable(
00230             ts.current()->caption().isEmpty() ? tname : ts.current()->caption(), //caption is equal to the original name
00231             destConn, 
00232             ts.current()
00233         );
00234         if (!ok) {
00235             kdDebug() << "Failed to copy table " << tname << endl;
00236             if (result)
00237                 result->setStatus(destConn,
00238                     i18n("Could not copy table \"%1\" to destination database.").arg(tname));
00239             break;
00240         }
00241     }//for
00242 
00243     // 5.1. Copy remaining "kexi__objects" contents (queries, forms, etc.) 
00244     //      if "kexi__objects" table is available in the source db
00245     if (ok && tables.find("kexi__objects")!=tables.end()) {
00246         // At 'source' side, we can only can use drv_copyTable, so let's create 
00247         // a temporary copy of "kexi__objects" and copy everything there
00248         KexiDB::TableSchema *kexi__objectsCopy = 
00249             new KexiDB::TableSchema( *destConn->tableSchema("kexi__objects") );
00250         kexi__objectsCopy->setName("kexi__objects__copy");
00251         ok = destConn->createTable( kexi__objectsCopy );
00252         if (!ok) {
00253             kdDebug() << "Failed to create a table " << kexi__objectsCopy->name() << endl;
00254             delete kexi__objectsCopy;
00255             kexi__objectsCopy = 0;
00256             destConn->debugError();
00257             if (result)
00258                 result->setStatus(destConn,
00259                     i18n("Could not create database \"%1\".")
00260                     .arg(m_migrateData->destination->databaseName()));
00261         }
00262         if (ok) {
00263             ok = drv_copyTable("kexi__objects", destConn, kexi__objectsCopy);
00264         }
00267         if (ok) {
00268             ok = destConn->executeSQL(
00269                 QString::fromLatin1("INSERT INTO kexi__objects SELECT * FROM kexi__objects__copy "
00270                     "WHERE o_type<>%1").arg((int)KexiDB::TableObjectType));
00271         }
00272         if (kexi__objectsCopy && !destConn->dropTable( kexi__objectsCopy )) {
00273             ok = false;
00274         }
00275     }
00276 
00277     // Done.
00278     if (ok) {
00279         ok = destConn->commitTransaction(trans);
00280     }
00281 
00282     if (ok)
00283         ok = drv_disconnect(); // && m_migrateData->dest->disconnect();
00284 
00285     if (!ok) {
00286         if (result && result->error())
00287             result->setStatus(destConn,
00288                 i18n("Could not import data from data source \"%1\".")
00289                     .arg(m_migrateData->source->serverInfoString()));
00290         if (destConn) {
00291             destConn->debugError();
00292             destConn->rollbackTransaction(trans);
00293         }
00294         drv_disconnect();
00295         if (destConn) {
00296             destConn->disconnect();
00297             destConn->dropDatabase(m_migrateData->destination->databaseName());
00298         }
00299         //later     delete prj;
00300         return false;
00301     }
00302     if (destConn)
00303         ok = destConn->disconnect();
00304     //later delete prj;
00305     return ok;
00306 }
00307 //=============================================================================
00308 
00309 bool KexiMigrate::performExport(Kexi::ObjectStatus* result)
00310 {
00311     if (result)
00312         result->clearStatus();
00313 
00315 
00316     return false;
00317 }
00318 
00319 //=============================================================================
00320 // Create the final database project
00321 KexiProject *KexiMigrate::createProject(Kexi::ObjectStatus* result)
00322 {
00323     kdDebug() << "Creating database [" << m_migrateData->destination->databaseName() 
00324         << "]" << endl;
00325 
00326     KexiProject *prj = new KexiProject(m_migrateData->destination,
00327         (KexiDB::MessageHandler*)*result);
00328     tristate r = prj->create(true /*forceOverwrite*/);
00329     if (r!=true) {
00330         //don't delete prj, otherwise eror message will be deleted      delete prj;
00331         return prj;
00332     }
00333 
00334     KexiDB::TransactionGuard tg(*prj->dbConnection());
00335     if (tg.transaction().isNull()) {
00336         if (result)
00337             result->setStatus(prj->dbConnection(),
00338                 i18n("Could not create database \"%1\".")
00339                 .arg(m_migrateData->destination->databaseName()));
00340         prj->dbConnection()->dropDatabase(m_migrateData->destination->databaseName());
00341         //don't delete prj, otherwise eror message will be deleted      delete prj;
00342         return prj;
00343     }
00344 
00345     if(drv_progressSupported()) {
00346         progressInitialise();
00347     }
00348 
00349     //Right, were connected..create the tables
00350     KexiDB::TableSchema *ts;
00351     for(QPtrListIterator<TableSchema> it (m_tableSchemas); (ts = it.current()) != 0;++it) {
00352         if(!prj->dbConnection()->createTable( ts )) {
00353             kdDebug() << "Failed to create a table " << ts->name() << endl;
00354             prj->dbConnection()->debugError();
00355             if (result)
00356                 result->setStatus(prj->dbConnection(),
00357                     i18n("Could not create database \"%1\".")
00358                     .arg(m_migrateData->destination->databaseName()));
00359             m_tableSchemas.remove(ts);
00360             prj->dbConnection()->dropDatabase(m_migrateData->destination->databaseName());
00361             //don't delete prj, otherwise eror message will be deleted          delete prj;
00362             return prj;
00363         }
00364         if(drv_progressSupported()) {
00365             updateProgress((Q_ULLONG)NUM_OF_ROWS_PER_CREATE_TABLE);
00366         }
00367     }
00368     if (!tg.commit()) {
00369         prj->dbConnection()->dropDatabase(m_migrateData->destination->databaseName());
00370         //don't delete prj, otherwise eror message will be deleted      delete prj;
00371         return prj;
00372     }
00373     return prj;
00374 }
00375 
00376 //=============================================================================
00377 // Functions for getting table data
00378 bool KexiMigrate::tableNames(QStringList & tn)
00379 {
00381     kdDebug() << "Reading list of tables..." << endl;
00382     return drv_tableNames(tn);
00383 }
00384 
00385 //=============================================================================
00386 // Progress functions
00387 bool KexiMigrate::progressInitialise() {
00388     Q_ULLONG sum = 0, size;
00389     emit progressPercent(0);
00390 
00392     QStringList tables;
00393     if(!tableNames(tables))
00394         return false;
00395 
00396     // 1) Get the number of rows/bytes to import
00397     int tableNumber = 1;
00398     for(QStringList::Iterator it = tables.begin();
00399         it != tables.end(); ++it, tableNumber++)
00400     {
00401         if(drv_getTableSize(*it, size)) {
00402             kdDebug() << "KexiMigrate::progressInitialise() - table: " << *it 
00403                       << "size: " << (ulong)size << endl;
00404             sum += size;
00405             emit progressPercent(tableNumber * 5 /* 5% */ / tables.count());
00406         } else {
00407             return false;
00408         }
00409     }
00410 
00411     kdDebug() << "KexiMigrate::progressInitialise() - job size: " << (ulong)sum << endl;
00412     m_progressTotal = sum;
00413     m_progressTotal += tables.count() * NUM_OF_ROWS_PER_CREATE_TABLE;
00414     m_progressTotal = m_progressTotal * 105 / 100; //add 5 percent for above task 1)
00415     m_progressNextReport = sum / 100;
00416     m_progressDone = m_progressTotal * 5 / 100; //5 perecent already done in task 1)
00417     return true;
00418 }
00419 
00420 
00421 void KexiMigrate::updateProgress(Q_ULLONG step) {
00422     m_progressDone += step;
00423     if (m_progressDone >= m_progressNextReport) {
00424         int percent = (m_progressDone+1) * 100 / m_progressTotal;
00425         m_progressNextReport = ((percent + 1) * m_progressTotal) / 100;
00426         kdDebug() << "KexiMigrate::updateProgress(): " << (ulong)m_progressDone << "/"
00427                   << (ulong)m_progressTotal << " (" << percent << "%) next report at " 
00428                   << (ulong)m_progressNextReport << endl;
00429         emit progressPercent(percent);
00430     }
00431 }
00432 
00433 //=============================================================================
00434 // Prompt the user to choose a field type
00435 KexiDB::Field::Type KexiMigrate::userType(const QString& fname)
00436 {
00437     KInputDialog *dlg;
00438     QStringList  types;
00439     QString res;
00440 
00441     types << "Byte";
00442     types << "Short Integer";
00443     types << "Integer";
00444     types << "Big Integer";
00445     types << "Boolean";
00446     types << "Date";
00447     types << "Date Time";
00448     types << "Time";
00449     types << "Float";
00450     types << "Double";
00451     types << "Text";
00452     types << "Long Text";
00453     types << "Binary Large Object";
00454 
00455     res = dlg->getItem( i18n("Field Type"),
00456                         i18n("The data type for %1 could not be determined. "
00457                  "Please select one of the following data "
00458                  "types").arg(fname),
00459                       types, 0, false);
00460 
00462     if (res == types[0])
00463         return KexiDB::Field::Byte;
00464     else if (res == types[1])
00465         return KexiDB::Field::ShortInteger;
00466     else if (res == types[2])
00467         return KexiDB::Field::Integer;
00468     else if (res == types[3])
00469         return KexiDB::Field::BigInteger;
00470     else if (res == types[4])
00471         return KexiDB::Field::Boolean;
00472     else if (res == types[5])
00473         return KexiDB::Field::Date;
00474     else if (res == types[6])
00475         return KexiDB::Field::DateTime;
00476     else if (res == types[7])
00477         return KexiDB::Field::Time;
00478     else if (res == types[8])
00479         return KexiDB::Field::Float;
00480     else if (res == types[9])
00481         return KexiDB::Field::Double;
00482     else if (res == types[10])
00483         return KexiDB::Field::Text;
00484     else if (res == types[11])
00485         return KexiDB::Field::LongText;
00486     else if (res == types[12])
00487         return KexiDB::Field::BLOB;
00488     else
00489         return KexiDB::Field::Text;
00490 }
00491 
00492 QVariant KexiMigrate::propertyValue( const QCString& propName )
00493 {
00494     return m_properties[propName.lower()];
00495 }
00496 
00497 QString KexiMigrate::propertyCaption( const QCString& propName ) const
00498 {
00499     return m_propertyCaptions[propName.lower()];
00500 }
00501 
00502 void KexiMigrate::setPropertyValue( const QCString& propName, const QVariant& value )
00503 {
00504     m_properties[propName.lower()] = value;
00505 }
00506 
00507 QValueList<QCString> KexiMigrate::propertyNames() const
00508 {
00509     QValueList<QCString> names = m_properties.keys();
00510     qHeapSort(names);
00511     return names;
00512 }
00513 
00514 bool KexiMigrate::isValid()
00515 {
00516     if (KexiMigration::versionMajor() != versionMajor()
00517         || KexiMigration::versionMinor() != versionMinor())
00518     {
00519         setError(ERR_INCOMPAT_DRIVER_VERSION,
00520         i18n("Incompatible migration driver's \"%1\" version: found version %2, expected version %3.")
00521         .arg(name())
00522         .arg(QString("%1.%2").arg(versionMajor()).arg(versionMinor()))
00523         .arg(QString("%1.%2").arg(KexiMigration::versionMajor()).arg(KexiMigration::versionMinor())));
00524         return false;
00525     }
00526     return true;
00527 }
00528 
00529 #include "keximigrate.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys