diff --git a/src/migration/importtablewizard.cpp b/src/migration/importtablewizard.cpp index 32d479077..74046232f 100644 --- a/src/migration/importtablewizard.cpp +++ b/src/migration/importtablewizard.cpp @@ -1,761 +1,759 @@ /* This file is part of the KDE project Copyright (C) 2009 Adam Pigg Copyright (C) 2014-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "importtablewizard.h" #include "migratemanager.h" #include "keximigrate.h" #include "keximigratedata.h" #include "AlterSchemaWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; #define RECORDS_FOR_PREVIEW 3 ImportTableWizard::ImportTableWizard ( KDbConnection* curDB, QWidget* parent, QMap* args, Qt::WindowFlags flags) : KAssistantDialog ( parent, flags ), m_args(args) { m_connection = curDB; m_migrateDriver = 0; m_prjSet = 0; m_importComplete = false; m_importWasCanceled = false; KexiMainWindowIface::global()->setReasonableDialogSize(this); setupIntroPage(); setupSrcConn(); setupSrcDB(); setupTableSelectPage(); setupAlterTablePage(); setupImportingPage(); setupProgressPage(); setupFinishPage(); setValid(m_srcConnPageItem, false); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slot_currentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); //! @todo Change this to message prompt when we move to non-dialog wizard. connect(m_srcConnSel, SIGNAL(connectionSelected(bool)), this, SLOT(slotConnPageItemSelected(bool))); } ImportTableWizard::~ImportTableWizard() { delete m_prjSet; delete m_srcConnSel; } void ImportTableWizard::back() { KAssistantDialog::back(); } void ImportTableWizard::next() { if (currentPage() == m_srcConnPageItem) { if (fileBasedSrcSelected()) { setAppropriate(m_srcDBPageItem, false); } else { setAppropriate(m_srcDBPageItem, true); } } else if (currentPage() == m_alterTablePageItem) { if (m_alterSchemaWidget->nameExists(m_alterSchemaWidget->nameWidget()->nameText())) { KMessageBox::information(this, xi18nc("@info", "%1 name is already used by an existing table. " "Enter different table name to continue.", m_alterSchemaWidget->nameWidget()->nameText()), xi18n("Name Already Used")); return; } } KAssistantDialog::next(); } void ImportTableWizard::accept() { if (m_args) { if (m_finishCheckBox->isChecked()) { m_args->insert("destinationTableName",m_alterSchemaWidget->nameWidget()->nameText()); } else { m_args->remove("destinationTableName"); } } QDialog::accept(); } void ImportTableWizard::reject() { QDialog::reject(); } //=========================================================== // void ImportTableWizard::setupIntroPage() { m_introPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); m_introPageWidget->setLayout(vbox); KexiUtils::setStandardMarginsAndSpacing(vbox); QLabel *lblIntro = new QLabel(m_introPageWidget); lblIntro->setAlignment(Qt::AlignTop | Qt::AlignLeft); lblIntro->setWordWrap(true); lblIntro->setText( xi18nc("@info", "Table Importing Assistant allows you to import a table from an existing " "database into the current Kexi project." "Click Next button to continue or " "Cancel button to exit this assistant.")); vbox->addWidget(lblIntro); m_introPageItem = new KPageWidgetItem(m_introPageWidget, xi18n("Welcome to the Table Importing Assistant")); addPage(m_introPageItem); } void ImportTableWizard::setupSrcConn() { m_srcConnPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(m_srcConnPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_srcConnSel = new KexiConnectionSelectorWidget(&Kexi::connset(), "kfiledialog:///ProjectMigrationSourceDir", KFileWidget::Opening, m_srcConnPageWidget); m_srcConnSel->hideConnectonIcon(); m_srcConnSel->showSimpleConn(); QSet excludedFilters; //! @todo remove when support for kexi files as source prj is added in migration excludedFilters << KDb::defaultFileBasedDriverMimeType() << "application/x-kexiproject-shortcut" << "application/x-kexi-connectiondata"; m_srcConnSel->fileWidget->setExcludedFilters(excludedFilters); vbox->addWidget(m_srcConnSel); m_srcConnPageItem = new KPageWidgetItem(m_srcConnPageWidget, xi18n("Select Location for Source Database")); addPage(m_srcConnPageItem); } void ImportTableWizard::setupSrcDB() { // arrivesrcdbPage creates widgets on that page m_srcDBPageWidget = new QWidget(this); m_srcDBName = NULL; m_srcDBPageItem = new KPageWidgetItem(m_srcDBPageWidget, xi18n("Select Source Database")); addPage(m_srcDBPageItem); } void ImportTableWizard::setupTableSelectPage() { m_tablesPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(m_tablesPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_tableListWidget = new QListWidget(this); m_tableListWidget->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_tableListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotTableListWidgetSelectionChanged())); vbox->addWidget(m_tableListWidget); m_tablesPageItem = new KPageWidgetItem(m_tablesPageWidget, xi18n("Select the Table to Import")); addPage(m_tablesPageItem); } //=========================================================== // void ImportTableWizard::setupImportingPage() { m_importingPageWidget = new QWidget(this); m_importingPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_importingPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_lblImportingTxt = new QLabel(m_importingPageWidget); m_lblImportingTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_lblImportingTxt->setWordWrap(true); m_lblImportingErrTxt = new QLabel(m_importingPageWidget); m_lblImportingErrTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_lblImportingErrTxt->setWordWrap(true); vbox->addWidget(m_lblImportingTxt); vbox->addWidget(m_lblImportingErrTxt); vbox->addStretch(1); QWidget *options_widget = new QWidget(m_importingPageWidget); vbox->addWidget(options_widget); QVBoxLayout *options_vbox = new QVBoxLayout(options_widget); options_vbox->setSpacing(KexiUtils::spacingHint()); m_importOptionsButton = new QPushButton(koIcon("configure"), xi18n("Advanced Options"), options_widget); connect(m_importOptionsButton, SIGNAL(clicked()),this, SLOT(slotOptionsButtonClicked())); options_vbox->addWidget(m_importOptionsButton); options_vbox->addStretch(1); m_importingPageWidget->show(); m_importingPageItem = new KPageWidgetItem(m_importingPageWidget, xi18n("Importing")); addPage(m_importingPageItem); } void ImportTableWizard::setupAlterTablePage() { m_alterTablePageWidget = new QWidget(this); m_alterTablePageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_alterTablePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_alterSchemaWidget = new KexiMigration::AlterSchemaWidget(this); vbox->addWidget(m_alterSchemaWidget); m_alterTablePageWidget->show(); m_alterTablePageItem = new KPageWidgetItem(m_alterTablePageWidget, xi18n("Alter the Detected Table Design")); addPage(m_alterTablePageItem); } void ImportTableWizard::setupProgressPage() { m_progressPageWidget = new QWidget(this); m_progressPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_progressPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_progressPageWidget->setLayout(vbox); m_progressLbl = new QLabel(m_progressPageWidget); m_progressLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_progressLbl->setWordWrap(true); m_rowsImportedLbl = new QLabel(m_progressPageWidget); m_importingProgressBar = new QProgressBar(m_progressPageWidget); m_importingProgressBar->setMinimum(0); m_importingProgressBar->setMaximum(0); m_importingProgressBar->setValue(0); vbox->addWidget(m_progressLbl); vbox->addWidget(m_rowsImportedLbl); vbox->addWidget(m_importingProgressBar); vbox->addStretch(1); m_progressPageItem = new KPageWidgetItem(m_progressPageWidget, xi18n("Processing Import")); addPage(m_progressPageItem); } void ImportTableWizard::setupFinishPage() { m_finishPageWidget = new QWidget(this); m_finishPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_finishPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_finishLbl = new QLabel(m_finishPageWidget); m_finishLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_finishLbl->setWordWrap(true); vbox->addWidget(m_finishLbl); m_finishCheckBox = new QCheckBox(xi18n("Open imported table"), m_finishPageWidget); vbox->addSpacing(KexiUtils::spacingHint()); vbox->addWidget(m_finishCheckBox); vbox->addStretch(1); m_finishPageItem = new KPageWidgetItem(m_finishPageWidget, xi18n("Success")); addPage(m_finishPageItem); } void ImportTableWizard::slot_currentPageChanged(KPageWidgetItem* curPage,KPageWidgetItem* prevPage) { Q_UNUSED(prevPage); if (curPage == m_introPageItem) { } else if (curPage == m_srcConnPageItem) { arriveSrcConnPage(); } else if (curPage == m_srcDBPageItem) { arriveSrcDBPage(); } else if (curPage == m_tablesPageItem) { arriveTableSelectPage(prevPage); } else if (curPage == m_alterTablePageItem) { if (prevPage == m_tablesPageItem) { arriveAlterTablePage(); } } else if (curPage == m_importingPageItem) { arriveImportingPage(); } else if (curPage == m_progressPageItem) { arriveProgressPage(); } else if (curPage == m_finishPageItem) { arriveFinishPage(); } } void ImportTableWizard::arriveSrcConnPage() { qDebug(); } void ImportTableWizard::arriveSrcDBPage() { if (fileBasedSrcSelected()) { //! @todo Back button doesn't work after selecting a file to import } else if (!m_srcDBName) { m_srcDBPageWidget->hide(); qDebug() << "Looks like we need a project selector widget!"; KDbConnectionData* conndata = m_srcConnSel->selectedConnectionData(); if (conndata) { KexiGUIMessageHandler handler; m_prjSet = new KexiProjectSet(&handler); if (!m_prjSet->setConnectionData(conndata)) { handler.showErrorMessage(m_prjSet->result()); delete m_prjSet; m_prjSet = 0; return; } QVBoxLayout *vbox = new QVBoxLayout(m_srcDBPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_srcDBName = new KexiProjectSelectorWidget(m_srcDBPageWidget, m_prjSet); vbox->addWidget(m_srcDBName); m_srcDBName->label()->setText(xi18n("Select source database you wish to import:")); } m_srcDBPageWidget->show(); } } void ImportTableWizard::arriveTableSelectPage(KPageWidgetItem *prevPage) { if (prevPage == m_alterTablePageItem) { if (m_tableListWidget->count() == 1) { //we was skiping it before back(); } } else { Kexi::ObjectStatus result; KexiUtils::WaitCursor wait; m_tableListWidget->clear(); m_migrateDriver = prepareImport(&result); bool ok = m_migrateDriver; if (ok) { ok = m_migrateDriver->connectSource(&result); } if (ok) { QStringList tableNames; if (m_migrateDriver->tableNames(&tableNames)) { m_tableListWidget->addItems(tableNames); } if (m_tableListWidget->item(0)) { m_tableListWidget->item(0)->setSelected(true); if (m_tableListWidget->count() == 1) { KexiUtils::removeWaitCursor(); next(); } } } KexiUtils::removeWaitCursor(); if (!ok) { QString errMessage =result.message.isEmpty() ? xi18n("Unknown error") : result.message; QString errDescription = result.description.isEmpty() ? errMessage : result.description; KMessageBox::error(this, errMessage, errDescription); setValid(m_tablesPageItem, false); } } } void ImportTableWizard::arriveAlterTablePage() { //! @todo handle errors if (m_tableListWidget->selectedItems().isEmpty()) return; //! @todo (js) support multiple tables? #if 0 foreach(QListWidgetItem *table, m_tableListWidget->selectedItems()) { m_importTableName = table->text(); } #else m_importTableName = m_tableListWidget->selectedItems().first()->text(); #endif QScopedPointer ts(new KDbTableSchema); if (!m_migrateDriver->readTableSchema(m_importTableName, ts.data())) { return; } setValid(m_alterTablePageItem, ts->fieldCount() > 0); if (isValid(m_alterTablePageItem)) { connect(m_alterSchemaWidget->nameWidget(), SIGNAL(textChanged()), this, SLOT(slotNameChanged()), Qt::UniqueConnection); } m_alterSchemaWidget->setTableSchema(ts.take()); if (!readFromTable()) { m_alterSchemaWidget->setTableSchema(nullptr); back(); KMessageBox::information(this, xi18nc("@info", "Could not import table %1. " "Select different table or cancel importing.", m_importTableName)); } } bool ImportTableWizard::readFromTable() { QScopedPointer tableResult(m_migrateDriver->readFromTable(m_importTableName)); KDbTableSchema *newSchema = m_alterSchemaWidget->newSchema(); if (!tableResult || tableResult->lastResult().isError() || tableResult->fieldsCount() != newSchema->fieldCount()) { back(); KMessageBox::information(this, xi18nc("@info", "Could not import table %1. " "Select different table or cancel importing.", m_importTableName)); return false; } QScopedPointer> data(new QList); for (int i = 0; i < RECORDS_FOR_PREVIEW; ++i) { QScopedPointer record(tableResult->fetchRecordData()); if (!record) { if (tableResult->lastResult().isError()) { return false; } break; } data->append(record.data()); } if (data->isEmpty()) { back(); KMessageBox::information(this, xi18nc("@info", "No data has been found in table %1. " "Select different table or cancel importing.", m_importTableName)); return false; } m_alterSchemaWidget->model()->setRowCount(data->count()); m_alterSchemaWidget->setData(data.take()); return true; } void ImportTableWizard::arriveImportingPage() { m_importingPageWidget->hide(); #if 0 if (checkUserInput()) { //setNextEnabled(m_importingPageWidget, true); user2Button->setEnabled(true); } else { //setNextEnabled(m_importingPageWidget, false); user2Button->setEnabled(false); } #endif QString txt; txt = xi18nc("@info Table import wizard, final message", "All required information has now been gathered. " "Click Next button to start importing table %1." "Depending on size of the table this may take some time.", m_alterSchemaWidget->nameWidget()->nameText()); m_lblImportingTxt->setText(txt); //temp. hack for MS Access driver only //! @todo for other databases we will need KexiMigration::Conenction //! and KexiMigration::Driver classes bool showOptions = false; if (fileBasedSrcSelected()) { Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(&result); if (sourceDriver) { showOptions = !result.error() && sourceDriver->propertyValue("source_database_has_nonunicode_encoding").toBool(); - KexiMigration::Data *data = sourceDriver->data(); - sourceDriver->setData(0); - delete data; + sourceDriver->setData(nullptr); } } if (showOptions) m_importOptionsButton->show(); else m_importOptionsButton->hide(); m_importingPageWidget->show(); } void ImportTableWizard::arriveProgressPage() { m_progressLbl->setText(xi18nc("@info", "Please wait while the table is imported.")); backButton()->setEnabled(false); nextButton()->setEnabled(false); connect(button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &ImportTableWizard::slotCancelClicked); QApplication::setOverrideCursor(Qt::BusyCursor); m_importComplete = doImport(); QApplication::restoreOverrideCursor(); disconnect(button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &ImportTableWizard::slotCancelClicked); next(); } void ImportTableWizard::arriveFinishPage() { if (m_importComplete) { m_finishLbl->setText(xi18nc("@info", "Table %1 has been imported.", m_alterSchemaWidget->nameWidget()->nameText())); } else { m_finishCheckBox->setEnabled(false); m_finishLbl->setText(xi18n("An error occured.")); } m_migrateDriver->disconnectSource(); button(QDialogButtonBox::Cancel)->setEnabled(false); } bool ImportTableWizard::fileBasedSrcSelected() const { return m_srcConnSel->selectedConnectionType() == KexiConnectionSelectorWidget::FileBased; } KexiMigrate* ImportTableWizard::prepareImport(Kexi::ObjectStatus *result) { // Find a source (migration) driver name QString sourceDriverId = driverIdForSelectedSource(); if (sourceDriverId.isEmpty()) { result->setStatus(xi18n("No appropriate migration driver found."), m_migrateManager.possibleProblemsMessage()); } // Get a source (migration) driver KexiMigrate* sourceDriver = 0; if (!result->error()) { sourceDriver = m_migrateManager.driver(sourceDriverId); if (!sourceDriver || m_migrateManager.result().isError()) { qDebug() << "Import migrate driver error..."; result->setStatus(m_migrateManager.resultable()); } } // Set up source (migration) data required for connection if (sourceDriver && !result->error()) { #if 0 // Setup progress feedback for the GUI if (sourceDriver->progressSupported()) { m_progressBar->updateGeometry(); disconnect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); connect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); progressUpdated(0); } #endif bool keepData = true; #if 0 if (m_importTypeStructureAndDataCheckBox->isChecked()) { qDebug() << "Structure and data selected"; keepData = true; } else if (m_importTypeStructureOnlyCheckBox->isChecked()) { qDebug() << "structure only selected"; keepData = false; } else { qDebug() << "Neither radio button is selected (not possible?) presume keep data"; keepData = true; } #endif KexiMigration::Data* md = new KexiMigration::Data(); if (fileBasedSrcSelected()) { KDbConnectionData* conn_data = new KDbConnectionData(); conn_data->setDatabaseName(m_srcConnSel->selectedFileName()); md->source = conn_data; md->sourceName.clear(); } else { md->source = m_srcConnSel->selectedConnectionData(); md->sourceName = m_srcDBName->selectedProjectData()->databaseName(); } md->keepData = keepData; sourceDriver->setData(md); return sourceDriver; } return 0; } //=========================================================== // QString ImportTableWizard::driverIdForSelectedSource() { if (fileBasedSrcSelected()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(m_srcConnSel->selectedFileName()); if (!mime.isValid() || mime.name() == "application/octet-stream" || mime.name() == "text/plain") { //try by URL: mime = db.mimeTypeForFile(m_srcConnSel->selectedFileName()); } if (!mime.isValid()) { return QString(); } const QStringList ids(m_migrateManager.driverIdsForMimeType(mime.name())); //! @todo do we want to return first migrate driver for the mime type or allow to select it? return ids.isEmpty() ? QString() : ids.first(); } return m_srcConnSel->selectedConnectionData() ? m_srcConnSel->selectedConnectionData()->databaseName() : QString(); } bool ImportTableWizard::doImport() { KexiGUIMessageHandler msg; KexiProject* project = KexiMainWindowIface::global()->project(); if (!project) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No project available.")); return false; } KexiPart::Part *part = Kexi::partManager().partForPluginId("org.kexi-project.table"); if (!part) { msg.showErrorMessage(Kexi::partManager().result()); return false; } KDbTableSchema* newSchema = m_alterSchemaWidget->newSchema(); if (!newSchema) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No table was selected to import.")); return false; } newSchema->setName(m_alterSchemaWidget->nameWidget()->nameText()); newSchema->setCaption(m_alterSchemaWidget->nameWidget()->captionText()); KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), newSchema->name()); if (!partItemForSavedTable) { msg.showErrorMessage(project->result()); return false; } //Create the table if (!m_connection->createTable(newSchema, true)) { msg.showErrorMessage(KDbMessageHandler::Error, xi18nc("@info", "Unable to create table %1.", newSchema->name())); return false; } m_alterSchemaWidget->takeTableSchema(); //m_connection takes ownership of the KDbTableSchema object //Import the data QApplication::setOverrideCursor(Qt::BusyCursor); QList row; unsigned int fieldCount = newSchema->fieldCount(); m_migrateDriver->moveFirst(); KDbTransactionGuard tg(m_connection); if (m_connection->result().isError()) { QApplication::restoreOverrideCursor(); return false; } do { for (unsigned int i = 0; i < fieldCount; ++i) { if (m_importWasCanceled) { return false; } if (i % 100 == 0) { QApplication::processEvents(); } row.append(m_migrateDriver->value(i)); } m_connection->insertRecord(newSchema, row); row.clear(); } while (m_migrateDriver->moveNext()); if (!tg.commit()) { QApplication::restoreOverrideCursor(); return false; } QApplication::restoreOverrideCursor(); //Done so save part and update gui partItemForSavedTable->setIdentifier(newSchema->id()); project->addStoredItem(part->info(), partItemForSavedTable); return true; } void ImportTableWizard::slotConnPageItemSelected(bool isSelected) { setValid(m_srcConnPageItem, isSelected); } void ImportTableWizard::slotTableListWidgetSelectionChanged() { setValid(m_tablesPageItem, !m_tableListWidget->selectedItems().isEmpty()); } void ImportTableWizard::slotNameChanged() { setValid(m_alterTablePageItem, !m_alterSchemaWidget->nameWidget()->captionText().isEmpty()); } void ImportTableWizard::slotCancelClicked() { m_importWasCanceled = true; } diff --git a/src/migration/importwizard.cpp b/src/migration/importwizard.cpp index 5a7ba1bae..9bc270c06 100644 --- a/src/migration/importwizard.cpp +++ b/src/migration/importwizard.cpp @@ -1,1131 +1,1130 @@ /* This file is part of the KDE project Copyright (C) 2004-2009 Adam Pigg Copyright (C) 2004-2016 Jarosław Staniek Copyright (C) 2005 Martin Ellis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "importwizard.h" #include "keximigrate.h" #include "importoptionsdlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; class Q_DECL_HIDDEN ImportWizard::Private { public: Private(QMap* args_) : srcProjectSelector(0) , fileBasedDstWasPresented(false) , setupFileBasedSrcNeeded(true) , importExecuted(false) , prjSet(0) , args(args_) { } ~Private() { delete prjSet; } QWidget *introPageWidget, *srcConnPageWidget, *srcDBPageWidget, *dstTypePageWidget, *dstPageWidget, *importTypePageWidget, *importingPageWidget, *finishPageWidget; KPageWidgetItem *introPageItem, *srcConnPageItem, *srcDBPageItem, *dstTypePageItem, *dstPageItem, *importTypePageItem, *importingPageItem, *finishPageItem; QGroupBox *importTypeGroupBox; QRadioButton *importTypeStructureAndDataCheckBox; QRadioButton *importTypeStructureOnlyCheckBox; KexiDBTitlePage* dstTitlePageWidget; KPageWidgetItem *dstTitlePageItem; KexiPrjTypeSelector *dstPrjTypeSelector; KexiConnectionSelectorWidget *srcConn, *dstConn; QString driverIdForSelectedSource; QLineEdit *dstNewDBTitleLineEdit; QLabel *dstNewDBNameLabel; QLineEdit *dstNewDBNameLineEdit; QLabel *dstNewDBNameUrlLabel; KUrlRequester *dstNewDBNameUrl; KexiStartupFileHandler *dstNewDBFileHandler; KexiProjectSelectorWidget *srcProjectSelector; QLabel *lblImportingTxt, *lblImportingErrTxt, *finishLbl; QCheckBox *openImportedProjectCheckBox; bool fileBasedDstWasPresented; bool setupFileBasedSrcNeeded; bool importExecuted; //!< used in import() KexiProjectSet* prjSet; QProgressBar *progressBar; QPushButton* importOptionsButton; QMap *args; QString predefinedDatabaseName, predefinedMimeType; KDbConnectionData *predefinedConnectionData; MigrateManager migrateManager; //!< object lives here, so status messages can be globally preserved //! Encoding for source db. Currently only used for MDB driver. //! @todo Hardcoded. Move to KexiMigrate driver's impl. QString sourceDBEncoding; }; //=========================================================== // ImportWizard::ImportWizard(QWidget *parent, QMap* args) : KAssistantDialog(parent) , d(new Private(args)) { setModal(true); setWindowTitle(xi18nc("@title:window", "Import Database")); setWindowIcon(KexiIcon("database-import")); KexiMainWindowIface::global()->setReasonableDialogSize(this); parseArguments(); setupIntro(); setupSrcConn(); setupSrcDB(); setupDstType(); setupDstTitle(); setupDst(); setupImportType(); setupImporting(); setupFinish(); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slot_currentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &ImportWizard::helpClicked); if (d->predefinedConnectionData) { // setup wizard for predefined server source d->srcConn->showAdvancedConn(); setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); } else if (!d->predefinedDatabaseName.isEmpty()) { // setup wizard for predefined source // (used when external project type was opened in Kexi, e.g. mdb file) setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); d->srcConn->showSimpleConn(); d->srcConn->setSelectedFileName(d->predefinedDatabaseName); #if 0 //disable all prev pages except "welcome" page for (int i = 0; i < indexOf(d->dstTypePage); i++) { if (page(i) != d->introPage) setAppropriate(page(i), false); } #endif } d->sourceDBEncoding = QString::fromLatin1(KexiUtils::encoding()); //default } //=========================================================== // ImportWizard::~ImportWizard() { delete d; } //=========================================================== // void ImportWizard::parseArguments() { d->predefinedConnectionData = 0; if (!d->args) return; if (!(*d->args)["databaseName"].isEmpty() && !(*d->args)["mimeType"].isEmpty()) { d->predefinedDatabaseName = (*d->args)["databaseName"]; d->predefinedMimeType = (*d->args)["mimeType"]; if (d->args->contains("connectionData")) { bool ok; d->predefinedConnectionData = new KDbConnectionData( KDbUtils::deserializeMap((*d->args)["connectionData"]), &ok); if (!ok) { delete d->predefinedConnectionData; d->predefinedConnectionData = 0; } } } d->args->clear(); } QString ImportWizard::selectedSourceFileName() const { if (d->predefinedDatabaseName.isEmpty()) return d->srcConn->selectedFileName(); return d->predefinedDatabaseName; } //=========================================================== // void ImportWizard::setupIntro() { d->introPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); d->introPageWidget->setLayout(vbox); KexiUtils::setStandardMarginsAndSpacing(vbox); QLabel *lblIntro = new QLabel(d->introPageWidget); lblIntro->setAlignment(Qt::AlignTop | Qt::AlignLeft); lblIntro->setWordWrap(true); lblIntro->setTextFormat(Qt::RichText); QString msg; if (d->predefinedConnectionData) { //predefined import: server source msg = xi18nc("@info", "Database Importing Assistant is about to import %1 database " "(connection %2) into a Kexi project.", d->predefinedDatabaseName, d->predefinedConnectionData->toUserVisibleString()); } else if (!d->predefinedDatabaseName.isEmpty()) { //predefined import: file source //! @todo this message is currently ok for files only QMimeDatabase db; QMimeType mime = db.mimeTypeForName(d->predefinedMimeType); if (!mime.isValid()) { qWarning() << QString("'%1' mimetype not installed!").arg(d->predefinedMimeType); } d->driverIdForSelectedSource = driverIdForMimeType(mime); msg = xi18nc("@info", "Database Importing Assistant is about to import %1 file " "of type %2 into a Kexi project.", QDir::toNativeSeparators(d->predefinedDatabaseName), mime.isValid() ? mime.comment() : "???"); } else { msg = xi18nc("@info", "Database Importing Assistant allows you to import an existing database " "into a Kexi project."); } // note: we're using .arg() here because the msg argument is already in rich-text format QString finalMessage = xi18nc("@info", "%1" "Click Next button to continue or " "Cancel button to exit this assistant.").arg(msg); lblIntro->setText(finalMessage); vbox->addWidget(lblIntro); d->introPageItem = new KPageWidgetItem(d->introPageWidget, xi18n("Welcome to the Database Importing Assistant")); addPage(d->introPageItem); } //=========================================================== // void ImportWizard::setupSrcConn() { d->srcConnPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->srcConnPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcConn = new KexiConnectionSelectorWidget(&Kexi::connset(), "kfiledialog:///ProjectMigrationSourceDir", KFileWidget::Opening, d->srcConnPageWidget); d->srcConn->hideConnectonIcon(); d->srcConn->showSimpleConn(); QSet excludedFilters; //! @todo remove when support for kexi files as source prj is added in migration excludedFilters += KDb::defaultFileBasedDriverMimeType(); excludedFilters += "application/x-kexiproject-shortcut"; excludedFilters += "application/x-kexi-connectiondata"; d->srcConn->fileWidget->setExcludedFilters(excludedFilters); vbox->addWidget(d->srcConn); d->srcConnPageItem = new KPageWidgetItem(d->srcConnPageWidget, xi18n("Select Location for Source Database")); addPage(d->srcConnPageItem); } //=========================================================== // void ImportWizard::setupSrcDB() { // arrivesrcdbPage creates widgets on that page d->srcDBPageWidget = new QWidget(this); d->srcDBPageItem = new KPageWidgetItem(d->srcDBPageWidget, xi18n("Select Source Database")); addPage(d->srcDBPageItem); } //=========================================================== // void ImportWizard::setupDstType() { d->dstTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); KexiUtils::setStandardMarginsAndSpacing(hbox); QLabel *lbl = new QLabel(xi18n("Destination database type:") /*+ ' '*/, d->dstTypePageWidget); lbl->setAlignment(Qt::AlignLeft | Qt::AlignTop); lbl->setTextFormat(Qt::RichText); hbox->addWidget(lbl); d->dstPrjTypeSelector = new KexiPrjTypeSelector(d->dstTypePageWidget); hbox->addWidget(d->dstPrjTypeSelector); d->dstPrjTypeSelector->option_file->setText(xi18n("Database project stored in a file")); d->dstPrjTypeSelector->option_server->setText(xi18n("Database project stored on a server")); hbox->addStretch(1); vbox->addStretch(1); d->dstTypePageItem = new KPageWidgetItem(d->dstTypePageWidget, xi18n("Select Destination Database Type")); addPage(d->dstTypePageItem); } //=========================================================== // void ImportWizard::setupDstTitle() { d->dstTitlePageWidget = new KexiDBTitlePage(xi18n("Destination project's caption:"), this); d->dstTitlePageWidget->layout()->setMargin(KexiUtils::marginHint()); d->dstTitlePageWidget->updateGeometry(); d->dstNewDBTitleLineEdit = d->dstTitlePageWidget->le_title; connect(d->dstNewDBTitleLineEdit, SIGNAL(textChanged(QString)), this, SLOT(destinationTitleTextChanged(QString))); d->dstNewDBNameUrlLabel = d->dstTitlePageWidget->label_requester; d->dstNewDBNameUrl = d->dstTitlePageWidget->file_requester; d->dstNewDBFileHandler = new KexiStartupFileHandler( QUrl("kfiledialog:///ProjectMigrationDestinationDir"), KexiStartupFileHandler::SavingFileBasedDB, d->dstTitlePageWidget->file_requester); d->dstNewDBNameLabel = new QLabel(xi18n("Destination project's name:"), d->dstTitlePageWidget); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::LabelRole, d->dstNewDBNameLabel); d->dstNewDBNameLineEdit = new QLineEdit(d->dstTitlePageWidget); d->dstNewDBNameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); KDbIdentifierValidator *idValidator = new KDbIdentifierValidator(this); idValidator->setLowerCaseForced(true); d->dstNewDBNameLineEdit->setValidator(idValidator); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::FieldRole, d->dstNewDBNameLineEdit); d->dstTitlePageItem = new KPageWidgetItem(d->dstTitlePageWidget, xi18n("Enter Destination Database Project's Caption")); addPage(d->dstTitlePageItem); } void ImportWizard::destinationTitleTextChanged(const QString & text) { Q_UNUSED(text); updateDestinationDBFileName(); } void ImportWizard::updateDestinationDBFileName() { d->dstNewDBFileHandler->updateUrl(d->dstNewDBTitleLineEdit->text()); d->dstNewDBNameLineEdit->setText(d->dstNewDBTitleLineEdit->text()); } //=========================================================== // void ImportWizard::setupDst() { d->dstPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->dstConn = new KexiConnectionSelectorWidget(&Kexi::connset(), "kfiledialog:///ProjectMigrationDestinationDir", KFileWidget::Saving, d->dstPageWidget); d->dstConn->hideHelpers(); vbox->addWidget(d->dstConn); connect(d->dstConn, SIGNAL(connectionItemExecuted(ConnectionDataLVItem*)), this, SLOT(next())); d->dstConn->showSimpleConn(); //anyway, db files will be _saved_ d->dstConn->fileWidget->setMode(KexiFileWidget::SavingFileBasedDB); d->dstPageItem = new KPageWidgetItem(d->dstPageWidget, xi18n("Select Location for Destination Database Project")); addPage(d->dstPageItem); } //=========================================================== // void ImportWizard::setupImportType() { d->importTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->importTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->importTypeGroupBox = new QGroupBox(d->importTypePageWidget); vbox->addWidget(d->importTypeGroupBox); QVBoxLayout *importTypeGroupBoxLyr = new QVBoxLayout; importTypeGroupBoxLyr->addWidget( d->importTypeStructureAndDataCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure and data"), d->importTypeGroupBox)); d->importTypeStructureAndDataCheckBox->setChecked(true); importTypeGroupBoxLyr->addWidget( d->importTypeStructureOnlyCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure only"), d->importTypeGroupBox)); importTypeGroupBoxLyr->addStretch(1); d->importTypeGroupBox->setLayout(importTypeGroupBoxLyr); d->importTypePageItem = new KPageWidgetItem(d->importTypePageWidget, xi18n("Select Scope of Import")); addPage(d->importTypePageItem); } //=========================================================== // void ImportWizard::setupImporting() { d->importingPageWidget = new QWidget(this); d->importingPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->importingPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->lblImportingTxt = new QLabel(d->importingPageWidget); d->lblImportingTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingTxt->setWordWrap(true); d->lblImportingTxt->setTextFormat(Qt::RichText); d->lblImportingErrTxt = new QLabel(d->importingPageWidget); d->lblImportingErrTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingErrTxt->setWordWrap(true); d->lblImportingErrTxt->setTextFormat(Qt::RichText); d->progressBar = new QProgressBar(d->importingPageWidget); d->progressBar->setRange(0, 100); d->progressBar->hide(); vbox->addWidget(d->lblImportingTxt); vbox->addWidget(d->lblImportingErrTxt); vbox->addStretch(1); QWidget *options_widget = new QWidget(d->importingPageWidget); vbox->addWidget(options_widget); QVBoxLayout *options_vbox = new QVBoxLayout(options_widget); options_vbox->setSpacing(KexiUtils::spacingHint()); QHBoxLayout *importOptionsButtonLyr = new QHBoxLayout; options_vbox->addLayout(importOptionsButtonLyr); d->importOptionsButton = new QPushButton(koIcon("configure"), xi18n("Advanced Options"), options_widget); connect(d->importOptionsButton, SIGNAL(clicked()), this, SLOT(slotOptionsButtonClicked())); importOptionsButtonLyr->addStretch(1); importOptionsButtonLyr->addWidget(d->importOptionsButton); importOptionsButtonLyr->addStretch(1); options_vbox->addStretch(1); vbox->addWidget(d->progressBar); vbox->addStretch(2); d->importingPageWidget->show(); d->importingPageItem = new KPageWidgetItem(d->importingPageWidget, xi18n("Importing")); addPage(d->importingPageItem); } //=========================================================== // void ImportWizard::setupFinish() { d->finishPageWidget = new QWidget(this); d->finishPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->finishPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->finishLbl = new QLabel(d->finishPageWidget); d->finishLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->finishLbl->setWordWrap(true); d->finishLbl->setTextFormat(Qt::RichText); vbox->addWidget(d->finishLbl); d->openImportedProjectCheckBox = new QCheckBox(xi18n("Open imported project"), d->finishPageWidget); d->openImportedProjectCheckBox->setChecked(true); vbox->addSpacing(KexiUtils::spacingHint()); vbox->addWidget(d->openImportedProjectCheckBox); vbox->addStretch(1); d->finishPageItem = new KPageWidgetItem(d->finishPageWidget, xi18n("Success")); addPage(d->finishPageItem); } //=========================================================== // bool ImportWizard::checkUserInput() { QString issues; if (d->dstNewDBTitleLineEdit->text().isEmpty()) { issues = xi18nc("@info", "No new database name was entered."); } Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver && sourceDriver->isSourceAndDestinationDataSourceTheSame()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format issues = xi18nc("@info", "%1Source database is the same as destination.") .arg(issues); } if (!issues.isEmpty()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format d->lblImportingErrTxt->setText( xi18nc("@info", "Following issues were found with the data you entered:" "%1" "Please click Back button and correct these issues.") .arg(issues)); return false; } return true; } void ImportWizard::arriveSrcConnPage() { d->srcConnPageWidget->hide(); /*! @todo KexiFileWidget needs "open file" and "open server" modes in addition to just "open" */ if (d->setupFileBasedSrcNeeded) { d->setupFileBasedSrcNeeded = false; QSet additionalMimeTypes; d->srcConn->fileWidget->setMode(KexiFileWidget::Opening); d->srcConn->fileWidget->setAdditionalFilters(additionalMimeTypes); } /*! @todo Support different file extensions based on MigrationDriver */ d->srcConnPageWidget->show(); } void ImportWizard::arriveSrcDBPage() { if (fileBasedSrcSelected()) { //! @todo Back button doesn't work after selecting a file to import } else { if (!d->srcProjectSelector) { QVBoxLayout *vbox = new QVBoxLayout(d->srcDBPageWidget); d->srcProjectSelector = new KexiProjectSelectorWidget(d->srcDBPageWidget); vbox->addWidget(d->srcProjectSelector); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcProjectSelector->label()->setText(xi18n("Select source database you wish to import:")); } d->srcDBPageWidget->hide(); KDbConnectionData* condata = d->srcConn->selectedConnectionData(); Q_ASSERT(condata); Q_ASSERT(d->prjSet); d->srcProjectSelector->setProjectSet(d->prjSet); d->srcDBPageWidget->show(); } } void ImportWizard::arriveDstTitlePage() { d->dstNewDBNameUrlLabel->setVisible(fileBasedDstSelected()); d->dstNewDBNameUrl->setVisible(fileBasedDstSelected()); d->dstNewDBNameLabel->setVisible(!fileBasedDstSelected()); d->dstNewDBNameLineEdit->setVisible(!fileBasedDstSelected()); if (fileBasedSrcSelected()) { const QString fname(selectedSourceFileName()); QString suggestedDBName(QFileInfo(fname).fileName()); const QFileInfo fi(suggestedDBName); suggestedDBName = suggestedDBName.left(suggestedDBName.length() - (fi.completeSuffix().isEmpty() ? 0 : (fi.completeSuffix().length() + 1))); d->dstNewDBTitleLineEdit->setText(suggestedDBName); } else { if (d->predefinedConnectionData) { // server source db is predefined d->dstNewDBTitleLineEdit->setText(d->predefinedDatabaseName); } else { if (!d->srcProjectSelector || !d->srcProjectSelector->selectedProjectData()) { back(); //!< @todo return; } d->dstNewDBTitleLineEdit->setText(d->srcProjectSelector->selectedProjectData()->databaseName()); } } d->dstNewDBTitleLineEdit->selectAll(); d->dstNewDBTitleLineEdit->setFocus(); updateDestinationDBFileName(); } void ImportWizard::arriveDstPage() { if (fileBasedDstSelected()) { d->dstPageWidget->hide(); KAssistantDialog::next(); return; } else { d->dstConn->showAdvancedConn(); } d->dstPageWidget->show(); } void ImportWizard::arriveImportingPage() { d->importingPageWidget->hide(); nextButton()->setEnabled(checkUserInput()); d->lblImportingTxt->setText(xi18nc("@info", "All required information has now " "been gathered. Click Next button to start importing." "Depending on size of the database this may take some time." /*"Note: You may be asked for extra " "information such as field types if " "the wizard could not automatically " "determine this for you."*/)); //temp. hack for MS Access driver only //! @todo for other databases we will need KexiMigration::Conenction //! and KexiMigration::Driver classes bool showOptions = false; if (fileBasedSrcSelected()) { Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver) { showOptions = !result.error() && sourceDriver->propertyValue("source_database_has_nonunicode_encoding").toBool(); - KexiMigration::Data *data = sourceDriver->data(); - sourceDriver->setData(0); - delete data; + sourceDriver->setData(nullptr); } } if (showOptions) d->importOptionsButton->show(); else d->importOptionsButton->hide(); d->importingPageWidget->show(); } void ImportWizard::arriveFinishPage() { } bool ImportWizard::fileBasedSrcSelected() const { if (d->predefinedConnectionData) return false; // qDebug() << (d->srcConn->selectedConnectionType()==KexiConnectionSelectorWidget::FileBased); return d->srcConn->selectedConnectionType() == KexiConnectionSelectorWidget::FileBased; } bool ImportWizard::fileBasedDstSelected() const { return d->dstPrjTypeSelector->option_file->isChecked(); } void ImportWizard::progressUpdated(int percent) { d->progressBar->setValue(percent); qApp->processEvents(); } QString ImportWizard::driverIdForMimeType(const QMimeType &mime) const { if (!mime.isValid()) { return QString(); } const QStringList ids(d->migrateManager.driverIdsForMimeType(mime.name())); //! @todo do we want to return first migrate driver for the mime type or allow to select it? return ids.isEmpty() ? QString() : ids.first(); } QString ImportWizard::findDriverIdForSelectedSource() { if (fileBasedSrcSelected()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(selectedSourceFileName()); if (!mime.isValid() || mime.name() == "application/octet-stream" || mime.name() == "text/plain" || mime.name() == "application/zip") { //try by URL: mime = db.mimeTypeForFile(selectedSourceFileName()); } return driverIdForMimeType(mime); } //server-based QString sourceDriverId; if (d->predefinedConnectionData) { sourceDriverId = d->predefinedConnectionData->driverId(); } else if (d->srcConn->selectedConnectionData()) { sourceDriverId = d->srcConn->selectedConnectionData()->driverId(); } const QStringList migrationDriverIds(d->migrateManager.driverIdsForSourceDriver(sourceDriverId)); //! @todo First found driver ID is picked. It's OK as long as there is one migration //! driver per source database type. How about allowing users to pick migration driver? return migrationDriverIds.isEmpty() ? QString() : migrationDriverIds.first(); } //=========================================================== // void ImportWizard::accept() { if (d->args) { if ((!fileBasedDstSelected() && !d->args->contains("destinationConnectionShortcut")) || !d->openImportedProjectCheckBox->isChecked()) { //do not open dest db if used didn't want it //for server connections, destinationConnectionShortcut must be defined d->args->remove("destinationDatabaseName"); } } KAssistantDialog::accept(); } KexiMigrate* ImportWizard::prepareImport(Kexi::ObjectStatus& result) { KexiUtils::WaitCursor wait; // Start with a driver manager KDbDriverManager manager; //qDebug() << "Creating destination driver..."; // Get a driver to the destination database KDbDriver *destDriver = manager.driver( d->dstConn->selectedConnectionData() ? d->dstConn->selectedConnectionData()->driverId() : KDb::defaultFileBasedDriverId()); if (!destDriver || manager.result().isError()) { result.setStatus(manager.resultable()); qWarning() << "Manager error:" << manager.result(); } // Set up destination connection data KDbConnectionData *cdata = 0; QString dbname; if (!result.error()) { if (d->dstConn->selectedConnectionData()) { //server-based project qDebug() << "Server destination..."; cdata = d->dstConn->selectedConnectionData(); dbname = d->dstNewDBNameLineEdit->text(); } else { //file-based project qDebug() << "File Destination..."; cdata = new KDbConnectionData(); cdata->setCaption(d->dstNewDBTitleLineEdit->text()); cdata->setDriverId(KDb::defaultFileBasedDriverId()); dbname = d->dstTitlePageWidget->file_requester->url().toLocalFile(); cdata->setDatabaseName(dbname); qDebug() << "Current file name:" << dbname; } } // Find a source (migration) driver name if (!result.error()) { if (d->driverIdForSelectedSource.isEmpty()) result.setStatus(xi18n("No appropriate migration driver found."), d->migrateManager.possibleProblemsMessage()); } // Get a source (migration) driver KexiMigrate* sourceDriver = 0; if (!result.error()) { sourceDriver = d->migrateManager.driver(d->driverIdForSelectedSource); if (!sourceDriver || d->migrateManager.result().isError()) { qDebug() << "Import migrate driver error..."; result.setStatus(d->migrateManager.resultable()); } } KexiUtils::removeWaitCursor(); // Set up source (migration) data required for connection if (sourceDriver && !result.error() && cdata) { // Setup progress feedback for the GUI if (sourceDriver->progressSupported()) { d->progressBar->updateGeometry(); disconnect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); connect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); progressUpdated(0); } bool keepData; if (d->importTypeStructureAndDataCheckBox->isChecked()) { qDebug() << "Structure and data selected"; keepData = true; } else if (d->importTypeStructureOnlyCheckBox->isChecked()) { qDebug() << "structure only selected"; keepData = false; } else { qDebug() << "Neither radio button is selected (not possible?) presume keep data"; keepData = true; } KexiMigration::Data* md = new KexiMigration::Data(); - md->destination = new KexiProjectData(*cdata, dbname); + md->setDestinationProjectData(new KexiProjectData(*cdata, dbname)); if (fileBasedSrcSelected()) { KDbConnectionData* conn_data = new KDbConnectionData(); conn_data->setDatabaseName(selectedSourceFileName()); md->source = conn_data; md->sourceName.clear(); } else { if (d->predefinedConnectionData) md->source = d->predefinedConnectionData; else md->source = d->srcConn->selectedConnectionData(); if (!d->predefinedDatabaseName.isEmpty()) md->sourceName = d->predefinedDatabaseName; else md->sourceName = d->srcProjectSelector->selectedProjectData()->databaseName(); //! @todo Aah, this is so C-like. Move to performImport(). } md->keepData = keepData; sourceDriver->setData(md); return sourceDriver; } return 0; } tristate ImportWizard::import() { d->importExecuted = true; Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); bool acceptingNeeded = false; // Perform import if (sourceDriver && !result.error()) { if (!d->sourceDBEncoding.isEmpty()) { sourceDriver->setPropertyValue("source_database_nonunicode_encoding", QVariant(d->sourceDBEncoding.toUpper().remove(' ')) // "CP1250", not "cp 1250" ); } if (!sourceDriver->checkIfDestinationDatabaseOverwritingNeedsAccepting(&result, &acceptingNeeded)) { qDebug() << "Abort import cause checkIfDestinationDatabaseOverwritingNeedsAccepting " "returned false."; return false; } - qDebug() << sourceDriver->data()->destination->databaseName(); + qDebug() << sourceDriver->data()->destinationProjectData()->databaseName(); qDebug() << "Performing import..."; } if (sourceDriver && !result.error() && acceptingNeeded) { // ok, the destination-db already exists... if (KMessageBox::Yes != KMessageBox::warningYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database %1 already exists." "Do you want to replace it with a new one?", - KexiUtils::localizedStringToHtmlSubstring(sourceDriver->data()->destination->infoString())), + KexiUtils::localizedStringToHtmlSubstring( + sourceDriver->data()->destinationProjectData()->infoString())), 0, KGuiItem(xi18nc("@action:button Replace Database", "&Replace")), KStandardGuiItem::no())) { return cancelled; } } if (sourceDriver && !result.error() && sourceDriver->progressSupported()) { d->progressBar->show(); } if (sourceDriver && !result.error() && sourceDriver->performImport(&result)) { if (d->args) { d->args->insert("destinationDatabaseName", fileBasedDstSelected() - ? sourceDriver->data()->destination->connectionData()->databaseName() - : sourceDriver->data()->destination->databaseName()); + ? sourceDriver->data()->destinationProjectData()->connectionData()->databaseName() + : sourceDriver->data()->destinationProjectData()->databaseName()); QString destinationConnectionShortcut; if (d->dstConn->selectedConnectionData()) { destinationConnectionShortcut = Kexi::connset().fileNameForConnectionData(*d->dstConn->selectedConnectionData()); } if (!destinationConnectionShortcut.isEmpty()) { d->args->insert("destinationConnectionShortcut", destinationConnectionShortcut); } } d->finishPageItem->setHeader(xi18n("Success")); return true; } if (!sourceDriver || result.error()) { d->progressBar->setValue(0); d->progressBar->hide(); QString msg, details; KexiTextMessageHandler handler(&msg, &details); handler.showErrorMessage(&result); qDebug() << msg << "\n" << details; d->finishPageItem->setHeader(xi18n("Failure")); // note: we're using .arg() here because the msg and details arguments are already in rich-text format d->finishLbl->setText( xi18nc("@info", "Import failed." "%1" "%2" "You can click Back button and try again.") .arg(msg).arg(details)); return false; } return true; } void ImportWizard::reject() { KAssistantDialog::reject(); } //=========================================================== // void ImportWizard::next() { if (currentPage() == d->srcConnPageItem) { if (fileBasedSrcSelected() && /*! @todo use QUrl? */!QFileInfo(selectedSourceFileName()).isFile()) { KMessageBox::sorry(this, xi18n("Select source database filename.")); return; } KDbConnectionData* conndata = d->srcConn->selectedConnectionData(); if (!fileBasedSrcSelected() && !conndata) { KMessageBox::sorry(this, xi18n("Select source database.")); return; } d->driverIdForSelectedSource = findDriverIdForSelectedSource(); // cache KexiMigrate* import = d->migrateManager.driver(d->driverIdForSelectedSource); if (!import || d->migrateManager.result().isError()) { QString dbname; if (fileBasedSrcSelected()) dbname = selectedSourceFileName(); else dbname = conndata ? conndata->toUserVisibleString() : QString(); KMessageBox::error(this, dbname.isEmpty() ? xi18n("Could not import database. This type is not supported.") : xi18nc("@info", "Could not import database %1. " "This type is not supported.", dbname)); return; } if (!fileBasedSrcSelected()) { // make sure we have password if needed tristate passwordNeeded = false; if (conndata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(conndata, this); } bool ok = passwordNeeded != cancelled; if (ok) { KexiGUIMessageHandler handler; d->prjSet = new KexiProjectSet(&handler); if (!d->prjSet->setConnectionData(conndata)) { handler.showErrorMessage(d->prjSet->result()); ok = false; } } if (!ok) { if (passwordNeeded == true) { conndata->setPassword(QString::null); // not clear(), we have to remove password } delete d->prjSet; d->prjSet = 0; return; } } } else if (currentPage() == d->dstTitlePageItem) { if (fileBasedDstSelected()) { if (QFileInfo::exists(d->dstNewDBNameUrl->url().toLocalFile())) { if (!KexiFileWidget::askForOverwriting(d->dstNewDBNameUrl->url().toLocalFile(), this)) { return; } } } } else if (currentPage() == d->importTypePageItem) { if (!fileBasedDstSelected()) { // make sure we have password if needed tristate passwordNeeded = false; KDbConnectionData* condata = d->dstConn->selectedConnectionData(); if (condata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(condata, this); } bool ok = passwordNeeded != cancelled; if (!ok) { if (passwordNeeded == true) { condata->setPassword(QString::null); // not clear(), we have to remove password } return; } } } else if (currentPage() == d->importingPageItem) { if (!d->importExecuted) { d->importOptionsButton->hide(); backButton()->setEnabled(false); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->lblImportingTxt->setText(xi18n("Importing in progress...")); tristate res = import(); if (true == res) { d->finishLbl->setText( xi18nc("@info", "Database has been imported into Kexi project %1.", d->dstNewDBNameLineEdit->text())); button(QDialogButtonBox::Cancel)->setEnabled(false); backButton()->setEnabled(false); nextButton()->setEnabled(true); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->show(); next(); return; } d->progressBar->hide(); button(QDialogButtonBox::Cancel)->setEnabled(true); backButton()->setEnabled(true); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->hide(); if (!res) next(); else if (~res) { arriveImportingPage(); } d->importExecuted = false; return; } } setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); setAppropriate(d->dstPageItem, !fileBasedDstSelected()); KAssistantDialog::next(); } void ImportWizard::back() { setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); KAssistantDialog::back(); } void ImportWizard::slot_currentPageChanged(KPageWidgetItem* curPage,KPageWidgetItem* prevPage) { Q_UNUSED(prevPage); if (curPage == d->introPageItem) { } else if (curPage == d->srcConnPageItem) { arriveSrcConnPage(); } else if (curPage == d->srcDBPageItem) { arriveSrcDBPage(); } else if (curPage == d->dstTypePageItem) { } else if (curPage == d->dstTitlePageItem) { arriveDstTitlePage(); } else if (curPage == d->dstPageItem) { if (fileBasedDstSelected()) { if (prevPage == d->importTypePageItem) { KAssistantDialog::back(); } else { KAssistantDialog::next(); } } else { arriveDstPage(); } } else if (curPage == d->importingPageItem) { arriveImportingPage(); } else if (curPage == d->finishPageItem) { arriveFinishPage(); } } void ImportWizard::helpClicked() { if (currentPage() == d->introPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } else if (currentPage() == d->srcConnPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to import data from."), xi18n("Help")); } else if (currentPage() == d->srcDBPageItem) { KMessageBox::information(this, xi18n("Here you can choose the actual database to import data from."), xi18n("Help")); } else if (currentPage() == d->dstTypePageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data."), xi18n("Help")); } else if (currentPage() == d->dstPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data in and the new database name."), xi18n("Help")); } else if (currentPage() == d->finishPageItem || currentPage() == d->importingPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } } void ImportWizard::slotOptionsButtonClicked() { QPointer dlg = new OptionsDialog(selectedSourceFileName(), d->sourceDBEncoding, this); if (QDialog::Accepted == dlg->exec()) { if (d->sourceDBEncoding != dlg->encodingComboBox()->selectedEncoding()) { d->sourceDBEncoding = dlg->encodingComboBox()->selectedEncoding(); } } delete dlg; } diff --git a/src/migration/keximigrate.cpp b/src/migration/keximigrate.cpp index 516924e2d..ee85ae26b 100644 --- a/src/migration/keximigrate.cpp +++ b/src/migration/keximigrate.cpp @@ -1,866 +1,869 @@ /* This file is part of the KDE project Copyright (C) 2004 Adam Pigg Copyright (C) 2004-2016 Jarosław Staniek Copyright (C) 2005 Martin Ellis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "keximigrate.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; class Q_DECL_HIDDEN KexiMigrate::Private { public: Private() : metaData(nullptr) , migrateData(nullptr) , sourceConnection(nullptr) { } ~Private() { qDeleteAll(kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport); kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear(); delete migrateData; } QString couldNotCreateDatabaseErrorMessage() const { return xi18nc("@info", "Could not create database %1.", - migrateData->destination->databaseName()); + migrateData->destinationProjectData()->databaseName()); } //! Info about the driver's plugin const KexiMigratePluginMetaData *metaData; //! @todo Remove this! KexiMigrate should be usable for multiple concurrent migrations! //! Migrate Options KexiMigration::Data* migrateData; /*! Driver properties dictionary (indexed by name), useful for presenting properties to the user. Set available properties here in driver implementation. */ QMap properties; /*! i18n'd captions for properties. You do not need to set predefined properties' caption in driver implementation -it's done automatically. */ QMap propertyCaptions; //! KDb driver. For instance, it is used for escaping identifiers QPointer kexiDBDriver; /* private */ //! Table schemas from source DB QList tableSchemas; QList kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport; KDbConnectionProxy* sourceConnection; //! Size of migration job quint64 progressTotal; //! Amount of migration job complete quint64 progressDone; //! Don't recalculate progress done until this value is reached. quint64 progressNextReport; }; KexiMigrate::KexiMigrate(QObject *parent, const QVariantList&) : QObject(parent) , KDbResultable() , d(new Private) { } //! Used for computing progress: //! let's assume that each table creation costs the same as inserting 20 rows #define NUM_OF_ROWS_PER_CREATE_TABLE 20 //============================================================================= // Migration parameters KexiMigration::Data* KexiMigrate::data() { return d->migrateData; } void KexiMigrate::setData(KexiMigration::Data* migrateData) { + if (d->migrateData && d->migrateData != migrateData) { + delete d->migrateData; + } d->migrateData = migrateData; } //============================================================================= // Destructor KexiMigrate::~KexiMigrate() { disconnectInternal(); delete d; } const KexiMigratePluginMetaData* KexiMigrate::metaData() const { return d->metaData; } void KexiMigrate::setMetaData(const KexiMigratePluginMetaData *metaData) { d->metaData = metaData; //! @todo KEXI3 d->initInternalProperties(); } KDbConnectionProxy* KexiMigrate::sourceConnection() { return d->sourceConnection; } bool KexiMigrate::checkIfDestinationDatabaseOverwritingNeedsAccepting(Kexi::ObjectStatus* result, bool *acceptingNeeded) { Q_ASSERT(acceptingNeeded); *acceptingNeeded = false; if (result) result->clearStatus(); KDbDriverManager drvManager; KDbDriver *destDriver = drvManager.driver( - d->migrateData->destination->connectionData()->driverId()); + d->migrateData->destinationProjectData()->connectionData()->driverId()); if (!destDriver) { result->setStatus(drvManager.resultable(), d->couldNotCreateDatabaseErrorMessage()); return false; } // For file-based dest. projects, we've already asked about overwriting // existing project but for server-based projects we need to ask now. if (destDriver->metaData()->isFileBased()) { return true; //nothing to check } QScopedPointer tmpConn( - destDriver->createConnection(*d->migrateData->destination->connectionData())); + destDriver->createConnection(*d->migrateData->destinationProjectData()->connectionData())); if (!tmpConn || destDriver->result().isError() || !tmpConn->connect()) { m_result = destDriver->result(); return true; } - if (tmpConn->databaseExists(d->migrateData->destination->databaseName())) { + if (tmpConn->databaseExists(d->migrateData->destinationProjectData()->databaseName())) { *acceptingNeeded = true; } tmpConn->disconnect(); return true; } bool KexiMigrate::isSourceAndDestinationDataSourceTheSame() const { KDbConnectionData* sourcedata = d->migrateData->source; - KDbConnectionData* destinationdata = d->migrateData->destination->connectionData(); + KDbConnectionData* destinationdata = d->migrateData->destinationProjectData()->connectionData(); return sourcedata && destinationdata && - d->migrateData->sourceName == d->migrateData->destination->databaseName() && // same database name + d->migrateData->sourceName == d->migrateData->destinationProjectData()->databaseName() && // same database name sourcedata->driverId() == destinationdata->driverId()&& // same driver sourcedata->hostName() == destinationdata->hostName() && // same host sourcedata->databaseName() == destinationdata->databaseName(); // same database name/filename } bool KexiMigrate::connectInternal(Kexi::ObjectStatus* result) { Q_ASSERT(!d->sourceConnection); KDbConnection* conn = drv_createConnection(); bool ok = !this->result().isError(); if (ok) { // note: conn == nullptr does not mean failure if (conn) { d->sourceConnection = new KDbConnectionProxy(conn); } ok = drv_connect(); } if (ok) { return true; } delete d->sourceConnection; // should not exist but do it for sanity d->sourceConnection = nullptr; QString message(xi18n("Could not connect to database %1.", d->migrateData->sourceDatabaseInfoString())); qWarning() << message; if (result) { result->setStatus(this, message); } return false; } bool KexiMigrate::drv_connect() { if (!d->sourceConnection) { return false; } if (!d->sourceConnection->drv_connect() || !d->sourceConnection->drv_useDatabase(data()->sourceName)) { m_result = d->sourceConnection->result(); return false; } return true; } bool KexiMigrate::disconnectInternal() { const bool ok = drv_disconnect(); if (!ok) { if (!m_result.isError()) { if (d->sourceConnection) { m_result = d->sourceConnection->result(); } } } delete d->sourceConnection; d->sourceConnection = 0; return ok; } bool KexiMigrate::drv_disconnect() { if (d->sourceConnection) { return d->sourceConnection->disconnect(); } return false; } bool KexiMigrate::importTable(const QString& tableName, KDbConnectionProxy *destConn) { QScopedPointer t(new KDbTableSchema()); KDbEscapedString sqlStatement = KDbEscapedString( "SELECT o_id, o_type, o_name, o_caption, o_desc " "FROM kexi__objects WHERE o_name=%1 AND o_type=%2") .arg(d->sourceConnection->escapeString(tableName)) .arg(int(KDb::TableObjectType)); QScopedPointer record; { QScopedPointer result(d->sourceConnection->executeSQL(sqlStatement)); if (!result) { m_result = d->sourceConnection->result(); return false; } record.reset(result->fetchRecordData()); if (!record) { return !result->lastResult().isError(); } if (!destConn->setupObjectData(*record, t.data())) { m_result = d->sourceConnection->result(); return false; } } sqlStatement = KDbEscapedString("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, " "f_options, f_default, f_order, f_caption, f_help" " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->id()); QVector> fieldRecords; { QScopedPointer fieldsResult(d->sourceConnection->executeSQL(sqlStatement)); if (!fieldsResult) { m_result = d->sourceConnection->result(); return false; } Q_FOREVER { QScopedPointer fieldsRecord(fieldsResult->fetchRecordData()); if (!fieldsRecord) { if (!fieldsResult->lastResult().isError()) { break; } m_result = fieldsResult->lastResult(); return false; } QScopedPointer f(destConn->setupField(*fieldsRecord)); if (!f) { return false; } QString testName(f->name()); int i = 1; Q_FOREVER { // try to find unique name if (!t->field(testName)) { break; } ++i; testName = f->name() + QString::number(i); } if (testName != f->name()) { f->setName(testName); if (!f->caption().isEmpty()) { f->setCaption(QString::fromLatin1("%1 %2").arg(f->caption()).arg(i)); } } if (!t->addField(f.data())) { return false; } f.take(); fieldRecords.append(fieldsRecord->toList()); } } if (!destConn->drv_createTable(*t)) { return false; } KDbTableSchema *kexi__objectsTable = destConn->tableSchema("kexi__objects"); KDbTableSchema *kexi__fieldsTable = destConn->tableSchema("kexi__fields"); if (!kexi__objectsTable || !kexi__fieldsTable) { return false; } // copy the kexi__objects record if (!destConn->insertRecord(kexi__objectsTable, record->toList())) { return false; } // copy the kexi__fields records for (const QList &fieldRecordData : fieldRecords) { if (!destConn->insertRecord(kexi__fieldsTable, fieldRecordData)) { return false; } } d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.append(t.take()); return true; } bool KexiMigrate::performImport(Kexi::ObjectStatus* result) { if (result) result->clearStatus(); // Step 1 - connect qDebug() << "CONNECTING..."; if (!connectInternal(result)) { return false; } // "Real" steps bool ok = performImportInternal(result); if (!disconnectInternal()) { ok = false; } return ok; } bool KexiMigrate::performImportInternal(Kexi::ObjectStatus* result) { // Step 1 - destination driver KDbDriverManager drvManager; KDbDriver *destDriver = drvManager.driver( - d->migrateData->destination->connectionData()->driverId()); + d->migrateData->destinationProjectData()->connectionData()->driverId()); if (!destDriver) { result->setStatus(drvManager.resultable(), d->couldNotCreateDatabaseErrorMessage()); return false; } // Step 2 - get table names qDebug() << "GETTING TABLENAMES..."; QStringList tables; if (!tableNames(&tables)) { qWarning() << "Couldn't get list of tables"; if (result) result->setStatus( xi18n("Could not get a list of table names for database %1.", d->migrateData->sourceDatabaseInfoString()), QString()); return false; } // Check if there are any tables if (tables.isEmpty()) { qDebug() << "There were no tables to import"; if (result) result->setStatus( xi18n("No tables have been found in database %1.", d->migrateData->sourceDatabaseInfoString()), QString()); return false; } // Step 3 - Read KDb-compatible table schemas tables.sort(); d->tableSchemas.clear(); if (!destDriver) { result->setStatus(drvManager.resultable()); return false; } const bool kexi__objects_exists = tables.contains("kexi__objects"); QStringList kexiDBTables; if (kexi__objects_exists) { tristate res = drv_queryStringListFromSQL( KDbEscapedString("SELECT o_name FROM kexi__objects WHERE o_type=%1") .arg(int(KDb::TableObjectType)), 0, &kexiDBTables, -1); if (res == true) { // Skip KDb-compatible schemas that have no physical tables QMutableListIterator kdbTablesIt(kexiDBTables); while (kdbTablesIt.hasNext()) { if (true != d->sourceConnection->resultExists(KDbEscapedString("SELECT * FROM %1") .arg(sourceConnection()->escapeIdentifier(kdbTablesIt.next())))) { qDebug() << "KDb table does not exist:" << kdbTablesIt.value(); kdbTablesIt.remove(); } } // Separate KDb-compatible tables from the KDb-incompatible tables // so KDb-compatible tables can be later deeply copied without altering their IDs. kexiDBTables.sort(); const QSet kdbTablesSet(kexiDBTables.toSet()); QMutableListIterator tablesIt(tables); while (tablesIt.hasNext()) { if (kdbTablesSet.contains(tablesIt.next())) { tablesIt.remove(); } } //qDebug() << "KDb-compatible tables: " << kexiDBTables; //qDebug() << "non-KDb tables: " << tables; } } // -- read non-KDb-compatible tables schemas and create them in memory QMap nativeNames; foreach(const QString& tableCaption, tables) { if (destDriver->isSystemObjectName(tableCaption) || KDbDriver::isKDbSystemObjectName(tableCaption) // "kexi__objects", etc. // other "kexi__*" tables at KexiProject level, e.g. "kexi__blobs" || tableCaption.startsWith(QLatin1String("kexi__"), Qt::CaseInsensitive)) { continue; } // this is a non-KDb table: generate schema from native data source const QString tableIdentifier(KDb::stringToIdentifier(tableCaption.toLower())); nativeNames.insert(tableIdentifier, tableCaption); QScopedPointer tableSchema(new KDbTableSchema(tableIdentifier)); tableSchema->setCaption(tableCaption); //caption is equal to the original name if (!drv_readTableSchema(tableCaption, tableSchema.data())) { if (result) result->setStatus( xi18nc("@info", "Could not import project from database %1. Error reading table %2.", d->migrateData->sourceDatabaseInfoString(), tableCaption), QString()); return false; } //yeah, got a table //Add it to list of tables which we will create if all goes well d->tableSchemas.append(tableSchema.take()); } // Step 4 - Create a new database as we have all required info KexiProject destProject( - *d->migrateData->destination, result ? (KDbMessageHandler*)*result : 0); + *d->migrateData->destinationProjectData(), result ? (KDbMessageHandler*)*result : 0); bool ok = true == destProject.create(true /*forceOverwrite*/) && destProject.dbConnection(); QScopedPointer destConn; if (ok) { destConn.reset(new KDbConnectionProxy(destProject.dbConnection())); destConn->setParentConnectionIsOwned(false); } KDbTransaction trans; if (ok) { trans = destConn->beginTransaction(); if (trans.isNull()) { ok = false; if (result) { result->setStatus(destConn->parentConnection(), d->couldNotCreateDatabaseErrorMessage()); } } } if (ok) { if (drv_progressSupported()) { ok = progressInitialise(); } } if (ok) { // Step 5 - Create the copies of KDb-compatible tables in memory (to maintain the same IDs) // Step 6.1 - Copy kexi__objects NOW because we'll soon create new objects with new IDs // Step 6.2 - Copy kexi__fields d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear(); foreach(const QString& tableName, kexiDBTables) { if (!importTable(tableName, destConn.data())) { if (!m_result.isError()) { m_result.setCode(); } m_result.prependMessage( xi18nc("@info", "Could not import table %1.", tableName)); return false; } } } // Step 7 - Create non-KDb-compatible tables: new IDs will be assigned to them if (ok) { foreach(KDbTableSchema* ts, d->tableSchemas) { ok = destConn->createTable(ts); if (!ok) { qWarning() << "Failed to create a table " << ts->name(); qWarning() << destConn->result(); if (result) { result->setStatus(destConn->parentConnection()->result(), nullptr, d->couldNotCreateDatabaseErrorMessage()); } d->tableSchemas.removeAt(d->tableSchemas.indexOf(ts)); break; } updateProgress((qulonglong)NUM_OF_ROWS_PER_CREATE_TABLE); } } if (ok) ok = destConn->commitTransaction(trans); if (ok) { //add KDb-compatible tables to the list, so data will be copied, if needed if (d->migrateData->keepData) { foreach(KDbTableSchema* table, d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport) { d->tableSchemas.append(table); } } else d->tableSchemas.clear(); } if (ok) { if (destProject.result().isError()) { ok = false; if (result) result->setStatus(destProject.result(), nullptr, xi18n("Could not import project from data source %1.", d->migrateData->sourceDatabaseInfoString())); } } // Step 8 - Copy data if asked to if (ok) { trans = destConn->beginTransaction(); ok = !trans.isNull(); } if (ok) { if (d->migrateData->keepData) { //! @todo check detailed "copy forms/blobs/tables" flags here when we add them //! @todo don't copy kexi__objectdata and kexi__userdata for tables that do not exist // Copy data for "kexi__objectdata" as well, if available in the source db if (tables.contains("kexi__objectdata")) d->tableSchemas.append(destConn->tableSchema("kexi__objectdata")); // Copy data for "kexi__blobs" as well, if available in the source db if (tables.contains("kexi__blobs")) d->tableSchemas.append(destConn->tableSchema("kexi__blobs")); } foreach(KDbTableSchema *ts, d->tableSchemas) { if (!ok) break; if ((destConn->driver()->isSystemObjectName(ts->name()) || KDbDriver::isKDbSystemObjectName(ts->name())) //! @todo what if these two tables are not compatible with tables created in destination db //! because newer db format was used? && ts->name() != "kexi__objectdata" //copy this too && ts->name() != "kexi__blobs" //copy this too && ts->name() != "kexi__userdata" //copy this too ) { qDebug() << "Won't copy data to system table" << ts->name(); //! @todo copy kexi__db contents! continue; } QString tsName = nativeNames.value(ts->name()); qDebug() << "Copying data for table: " << tsName; if (tsName.isEmpty()) { tsName = ts->name(); } ok = drv_copyTable(tsName, destConn->parentConnection(), ts); if (!ok) { qWarning() << "Failed to copy table " << tsName; if (result) result->setStatus(destConn->parentConnection()->result(), nullptr, xi18nc("@info", "Could not copy table %1 to destination database.", tsName)); break; } }//for } // Done. if (ok) ok = destConn->commitTransaction(trans); d->kexiDBCompatibleTableSchemasToRemoveFromMemoryAfterImport.clear(); if (ok) { if (destConn) ok = destConn->disconnect(); return ok; } // Finally: error handling if (result && result->error()) result->setStatus(destConn->parentConnection()->result(), nullptr, xi18n("Could not import data from data source %1.", d->migrateData->sourceDatabaseInfoString())); if (destConn) { qWarning() << destConn->result(); destConn->rollbackTransaction(trans); destConn->disconnect(); - destConn->dropDatabase(d->migrateData->destination->databaseName()); + destConn->dropDatabase(d->migrateData->destinationProjectData()->databaseName()); } return false; } //============================================================================= bool KexiMigrate::performExport(Kexi::ObjectStatus* result) { if (result) result->clearStatus(); //! @todo performExport return false; } //============================================================================= // Progress functions bool KexiMigrate::progressInitialise() { emit progressPercent(0); //! @todo Don't copy table names here QStringList tables; if (!tableNames(&tables)) return false; // 1) Get the number of rows/bytes to import int tableNumber = 1; quint64 sum = 0; foreach(const QString& tableName, tables) { quint64 size; if (drv_getTableSize(tableName, &size)) { qDebug() << "table:" << tableName << "size: " << (ulong)size; sum += size; emit progressPercent(tableNumber * 5 /* 5% */ / tables.count()); tableNumber++; } else { return false; } } qDebug() << "job size:" << sum; d->progressTotal = sum; d->progressTotal += tables.count() * NUM_OF_ROWS_PER_CREATE_TABLE; d->progressTotal = d->progressTotal * 105 / 100; //add 5 percent for above task 1) d->progressNextReport = sum / 100; d->progressDone = d->progressTotal * 5 / 100; //5 perecent already done in task 1) return true; } void KexiMigrate::updateProgress(qulonglong step) { d->progressDone += step; if (d->progressTotal > 0 && d->progressDone >= d->progressNextReport) { int percent = (d->progressDone + 1) * 100 / d->progressTotal; d->progressNextReport = ((percent + 1) * d->progressTotal) / 100; qDebug() << (ulong)d->progressDone << "/" << (ulong)d->progressTotal << " (" << percent << "%) next report at" << (ulong)d->progressNextReport; emit progressPercent(percent); } } //============================================================================= // Prompt the user to choose a field type KDbField::Type KexiMigrate::userType(const QString& fname) { const QStringList typeNames(KDbField::typeNames()); bool ok; const QString res = QInputDialog::getItem(nullptr, xi18nc("@title:window", "Field Type"), xi18nc("@info", "The data type for field %1 could not be determined. " "Please select one of the following data types.", fname), typeNames, 0, false/* !editable */, &ok); if (!ok || res.isEmpty()) //! @todo OK? return KDbField::Text; return KDb::intToFieldType(int(KDbField::FirstType) + typeNames.indexOf(res)); } QString KexiMigrate::drv_escapeIdentifier(const QString& str) const { return d->kexiDBDriver ? d->kexiDBDriver->escapeIdentifier(str) : str; } KDbDriver *KexiMigrate::driver() { return d->kexiDBDriver; } void KexiMigrate::setDriver(KDbDriver *driver) { d->kexiDBDriver = driver; } QVariant KexiMigrate::propertyValue(const QByteArray& propertyName) { return d->properties.value(propertyName.toLower()); } QString KexiMigrate::propertyCaption(const QByteArray& propertyName) const { return d->propertyCaptions.value(propertyName.toLower()); } void KexiMigrate::setPropertyValue(const QByteArray& propertyName, const QVariant& value) { d->properties.insert(propertyName.toLower(), value); } void KexiMigrate::setPropertyCaption(const QByteArray& propertyName, const QString &caption) { d->propertyCaptions.insert(propertyName.toLower(), caption); } QList KexiMigrate::propertyNames() const { QList names = d->properties.keys(); qSort(names); return names; } /* moved to MigrateManagerInternal::driver(): bool KexiMigrate::isValid() { if (KexiMigration::versionMajor() != versionMajor() || KexiMigration::versionMinor() != versionMinor()) { setError(ERR_INCOMPAT_DRIVER_VERSION, xi18n( "Incompatible migration driver's \"%1\" version: found version %2, expected version %3.", objectName(), QString("%1.%2").arg(versionMajor()).arg(versionMinor()), QString("%1.%2").arg(KexiMigration::versionMajor()).arg(KexiMigration::versionMinor())) ); return false; } return true; } */ bool KexiMigrate::drv_queryMaxNumber(const QString& tableName, const QString& columnName, int *result) { QString string; tristate r = drv_querySingleStringFromSQL( KDbEscapedString("SELECT MAX(%1) FROM %2") .arg(drv_escapeIdentifier(columnName)) .arg(drv_escapeIdentifier(tableName)), 0, &string); if (r == false) return false; if (~r) { result = 0; return true; } bool ok; int tmpResult = string.toInt(&ok); if (ok) *result = tmpResult; return ok; } tristate KexiMigrate::drv_querySingleStringFromSQL( const KDbEscapedString& sqlStatement, int columnNumber, QString *string) { QStringList stringList; const tristate res = drv_queryStringListFromSQL(sqlStatement, columnNumber, &stringList, 1); if (true == res) *string = stringList.first(); return res; } bool KexiMigrate::connectSource(Kexi::ObjectStatus* result) { return connectInternal(result); } bool KexiMigrate::disconnectSource() { return disconnectInternal(); } bool KexiMigrate::readTableSchema(const QString& originalName, KDbTableSchema *tableSchema) { return drv_readTableSchema(originalName, tableSchema); } bool KexiMigrate::tableNames(QStringList *tn) { //! @todo Cache list of table names qDebug() << "Reading list of tables..."; tn->clear(); return drv_tableNames(tn); } KDbSqlResult* KexiMigrate::readFromTable(const QString & tableName) { return drv_readFromTable(tableName); } bool KexiMigrate::moveNext() { return drv_moveNext(); } bool KexiMigrate::movePrevious() { return drv_movePrevious(); } bool KexiMigrate::moveFirst() { return drv_moveFirst(); } bool KexiMigrate::moveLast() { return drv_moveLast(); } QVariant KexiMigrate::value(int i) { return drv_value(i); } //------------------------ KDbVersionInfo KexiMigration::version() { return KDbVersionInfo(KEXI_MIGRATION_VERSION_MAJOR, KEXI_MIGRATION_VERSION_MINOR, 0); } diff --git a/src/migration/keximigrate.h b/src/migration/keximigrate.h index 08004a382..e3a9d7ce0 100644 --- a/src/migration/keximigrate.h +++ b/src/migration/keximigrate.h @@ -1,381 +1,383 @@ /* This file is part of the KDE project Copyright (C) 2004 Adam Pigg Copyright (C) 2004-2016 Jarosław Staniek Copyright (C) 2005 Martin Ellis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_MIGRATE_H #define KEXI_MIGRATE_H #include "keximigratedata.h" #include "KexiVersion.h" #include #include #include #include class KDbConnectionProxy; namespace Kexi { class ObjectStatus; } class KexiMigratePluginMetaData; /*! KexiMigration implementation version. It is altered after every change: - major number is increased after incompatible change ofthe plugin interface/behavior - minor number is increased after minor changes @note Do not use these constants to get library version information in external code. Use KexiMigratePluginMetaData::*version() functions instead. */ #define KEXI_MIGRATION_VERSION_MAJOR KEXI_STABLE_VERSION_MAJOR #define KEXI_MIGRATION_VERSION_MINOR KEXI_STABLE_VERSION_MINOR /*! * \namespace KexiMigration * \brief Framework for importing databases into native KDb databases. */ namespace KexiMigration { //! \return KexiMigration library version info KEXIMIGRATE_EXPORT KDbVersionInfo version(); //! @short A base class for a migrate plugin that imports native databases into Kexi projects /*! A generic API for importing schema and data from an existing database into a new Kexi project. Can be also used for importing native Kexi databases. Basic idea is this: -# User selects an existing DB and new project (file or server based) -# User specifies whether to import structure and data or structure only. -# Import tool connects to db -# Checks if it is already a kexi project (not implemented yet) -# If not, then read structure and construct new project -# Ask user what to do if column type is not supported See kexi/doc/dev/kexi_import.txt for more info. */ class KEXIMIGRATE_EXPORT KexiMigrate : public QObject, public KDbResultable { Q_OBJECT public: virtual ~KexiMigrate(); //! Info about the driver's plugin const KexiMigratePluginMetaData* metaData() const; //! @todo Remove this! KexiMigrate should be usable for multiple concurrent migrations! KexiMigration::Data* data(); //! @todo Remove this! KexiMigrate should be usable for multiple concurrent migrations! //! Data Setup. Requires two connection objects, a name and a bool + //! Ownership of @a migrateData is transferred to the KexiMigrate object. + //! Any previous data is removed if it is different than @a migrateData. void setData(KexiMigration::Data* migrateData); /*! Checks whether the destination database exists. For file-based dest. projects, we've already asked about overwriting existing project but for server-based projects it's better to ask user. This method should be called before performImport() or performExport(). \return true if no connection-related errors occurred. \a acceptingNeeded is set to true if destination database exists. In this case you should ask about accepting database overwriting. Used in ImportWizard::import(). */ bool checkIfDestinationDatabaseOverwritingNeedsAccepting(Kexi::ObjectStatus* result, bool* acceptingNeeded); /*! Checks if the source- and the destination databases are identical. \return true if they are identical else false. */ bool isSourceAndDestinationDataSourceTheSame() const; //! Connects, perform an import operation, and disconnects bool performImport(Kexi::ObjectStatus* result = 0); //! Perform an export operation bool performExport(Kexi::ObjectStatus* result = 0); //! Returns true if the migration driver supports progress updates. inline bool progressSupported() { return drv_progressSupported(); } //! @todo This is copied from KDbDriver. One day it will be merged with KDb. //! \return property value for \a propertyName available for this driver. //! If there's no such property defined for driver, Null QVariant value is returned. virtual QVariant propertyValue(const QByteArray& propertyName); //! @todo This is copied from KDbDriver. One day it will be merged with KDb. void setPropertyValue(const QByteArray& propertyName, const QVariant& value); //! @todo This is copied from KDbDriver. One day it will be merged with KDb. //! \return translated property caption for \a propertyName. //! If there's no such property defined for driver, empty string value is returned. QString propertyCaption(const QByteArray& propertyName) const; //! @todo This is copied from KDbDriver. One day it will be merged with KDb. //! Set translated property caption for \a propertyName. void setPropertyCaption(const QByteArray& propertyName, const QString &caption); //! @todo This is copied from KDbDriver. One day it will be merged with KDb. //! \return a list of property names available for this driver. QList propertyNames() const; //! Extension of existing API to provide generic row access to external data for ImportTableWizard. //! @todo refactor bool connectSource(Kexi::ObjectStatus* result); //! Extension of existing API to close connection for ImportTableWizard. //! @todo refactor bool disconnectSource(); //! Get table names in source database (driver specific) bool tableNames(QStringList *tablenames); //! Read schema for a given table (driver specific) bool readTableSchema(const QString& originalName, KDbTableSchema *tableSchema); //! Starts reading data from the source dataset's table KDbSqlResult* readFromTable(const QString& tableName); //!Move to the next row bool moveNext(); //!Move to the previous row bool movePrevious(); //!Move to the next row bool moveFirst(); //!Move to the previous row bool moveLast(); //!Read the data at the given row/field QVariant value(int i); Q_SIGNALS: void progressPercent(int percent); protected: //! Used by MigrateManager. explicit KexiMigrate(QObject *parent, const QVariantList &args = QVariantList()); /*! Used by the migration driver manager to set metaData for just loaded driver. */ void setMetaData(const KexiMigratePluginMetaData *metaData); //! Creates connection to source database and connects. //! If it is a database supported by low-level routines of KDb, sourceConnection() will //! be available afterwards. //! If not, connectInternal() can still return true but sourceConnection() will return //! @c nullptr. //! @see drv_createConnection() drv_connect() bool connectInternal(Kexi::ObjectStatus* result); //! Disconnects from source database. If it is a database supported by low-level routines //! of KDb, destroys the connection object pointed by sourceConnection() too. //! @return true on success. //! @see drv_disconnect() connectInternal() bool disconnectInternal(); //! @return connection to source database if it is a database supported by low-level //! routines of KDb. In other cases such as importing from a TSV file, this function //! returns @c nullptr. KDbConnectionProxy* sourceConnection(); //! Migration drivers that use low-level routines KDb to access the source database //! should create and return a driver-specific KDbConnection object that handles //! connection to the source database. //! Migration drivers that use custom data sources (not KDb-compatible) should return //! @c nullptr. //! @note KexiMigrate::m_result should be set to a result of the operation. virtual KDbConnection* drv_createConnection() = 0; //! Connects to source database using (driver specific). //! Default implementation calls sourceConnection()->drv_connect() and if it succeeds, //! it calls sourceConnection()->drv_useDatabase(data()->sourceName), then returns //! the result. If this is enough for connecting for a migration driver, there is no need //! to reimplement drv_connect(). //! If sourceConnection() is @c nullptr (custom types of sources), //! default implementation just returns @c false. In this case drv_connect() should //! be implemented. virtual bool drv_connect(); //! Disconnect from source database (driver specific). //! If the source database is supported by low-level routines KDb, //! KDbConnection::disconnect() is called for this connection. //! For other types of sources @c false is returned so in these cases this method //! should be reimplemented. virtual bool drv_disconnect(); //! Get table names in source database (driver specific) /*! @return List of table names available for this connection. The names are in lower case. The method should return true only if there was no error on getting database names list from the server. */ virtual bool drv_tableNames(QStringList *tablenames) = 0; //! Read schema for a given table (driver specific) virtual bool drv_readTableSchema( const QString& originalName, KDbTableSchema *tableSchema) = 0; /*! Fetches maximum number from table \a tableName, column \a columnName into \a result. On success true is returned. If there is no records in the table, \a result is set to 0 and true is returned. - Note 1: implement only if the database can already contain kexidb__* tables (so e.g. keximdb driver doea not need this). - Note 2: default implementation uses drv_querySingleStringFromSQL() with "SELECT MAX(columName) FROM tableName" statement, assuming SQL-compliant backend. */ virtual bool drv_queryMaxNumber(const QString& tableName, const QString& columnName, int *result); /*! Fetches single string at column \a columnNumber for each record from result obtained by running \a sqlStatement. \a numRecords can be specified to limit number of records read. If \a numRecords is -1, all records are loaded. On success the result is stored in \a stringList and true is returned. \return cancelled if there are no records available. - Note: implement only if the database can already contain kexidb__* tables (so e.g. keximdb driver does not need this). */ //! @todo SQL-dependent! virtual tristate drv_queryStringListFromSQL( const KDbEscapedString& sqlStatement, int columnNumber, QStringList *stringList, int numRecords = -1) { Q_UNUSED(sqlStatement); Q_UNUSED(columnNumber); Q_UNUSED(stringList); Q_UNUSED(numRecords); return cancelled; } /*! Fetches single string at column \a columnNumber from result obtained by running \a sqlStatement. On success the result is stored in \a string and true is returned. \return cancelled if there are no records available. This implementation uses drv_queryStringListFromSQL() with numRecords == 1. */ //! @todo SQL-dependent! virtual tristate drv_querySingleStringFromSQL(const KDbEscapedString& sqlStatement, int columnNumber, QString *string); //! A functor for filtering records //! @see drv_copyTable() class RecordFilter { public: RecordFilter() {} virtual ~RecordFilter() {} virtual bool operator() (const KDbSqlRecord &record) const = 0; virtual bool operator() (const QList &record) const = 0; }; //! Copy a table from source DB to target DB (driver specific) //! - create copies of KDb tables //! - create copies of non-KDb tables virtual bool drv_copyTable(const QString& srcTable, KDbConnection *destConn, KDbTableSchema* dstTable, const RecordFilter *recordFilter = nullptr) = 0; virtual bool drv_progressSupported() { return false; } /*! \return the size of a table to be imported, or 0 if not supported Finds the size of the named table, in order to provide feedback on migration progress. The units of the return type are deliberately unspecified. Migration drivers may return the number of records in the table, or the size in bytes, etc. Units should be chosen in order that the driver can return the size in the fastest way possible (e.g. migration from CSV files should use file size to avoid counting the number of rows, and migration from MDB files should return the number of rows as this is stored within the file). Obviously, the driver should use the same units when reporting migration progress. \return size of the specified table */ virtual bool drv_getTableSize(const QString&, quint64*) { return false; } void updateProgress(quint64 step = 1ULL); //! @todo user should be asked ONCE using a convenient wizard's page, not a popup dialog //! Prompt user to select a field type for unrecognized fields KDbField::Type userType(const QString& fname); virtual QString drv_escapeIdentifier(const QString& str) const; //Extended API //! Position the source dataset at the start of a table virtual KDbSqlResult* drv_readFromTable(const QString & tableName) { Q_UNUSED(tableName); return nullptr; } //! Move to the next row virtual bool drv_moveNext() { return false; } //! Move to the previous row virtual bool drv_movePrevious() { return false; } //! Move to the next row virtual bool drv_moveFirst() { return false; } //! Move to the previous row virtual bool drv_moveLast() { return false; } //! Read the data at the given row/field virtual QVariant drv_value(int i) { Q_UNUSED(i); return QVariant(); }; //! @return Database driver for this migration. KDbDriver *driver(); //! Sets database driver for this migration. void setDriver(KDbDriver *driver); private: /*! Estimate size of migration job Calls drv_getTableSize for each table to be copied. \return sum of the size of all tables to be copied. */ bool progressInitialise(); //! Perform an import operation. It is assumed that source connection is established. //! @see performImport() bool performImportInternal(Kexi::ObjectStatus* result); //! Reads schema for table @a tableName from kexi__objects and kexi__fields. //! On success: //! - copies one record from the original kexi__objects table to the destination //! database's table kexi__objects //! - copies all related records from the original kexi__fields table to the destination //! database's table kexi__fields bool importTable(const QString& tableName, KDbConnectionProxy *destConn); class Private; Private * const d; friend class MigrateManager; friend class MigrateManagerInternal; }; } //namespace KexiMigration #endif diff --git a/src/migration/keximigratedata.cpp b/src/migration/keximigratedata.cpp index c73b79edb..8e302327e 100644 --- a/src/migration/keximigratedata.cpp +++ b/src/migration/keximigratedata.cpp @@ -1,40 +1,66 @@ /* This file is part of the KDE project Copyright (C) 2004 Adam Pigg Copyright (C) 2004 Jarosław Staniek Copyright (C) 2005 Martin Ellis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "keximigratedata.h" using namespace KexiMigration; +class Q_DECL_HIDDEN Data::Private +{ +public: + Private() {} + KexiProjectData *destinationProjectData = nullptr; +}; + Data::Data() - : source(0) - , destination(0) + : source(nullptr) + , d(new Private) { } Data::~Data() { + delete d; +} + +KexiProjectData* Data::destinationProjectData() +{ + return d->destinationProjectData; +} + +const KexiProjectData* Data::destinationProjectData() const +{ + return d->destinationProjectData; +} + +void Data::setDestinationProjectData(KexiProjectData* destinationProjectData) +{ + if (d->destinationProjectData && d->destinationProjectData != destinationProjectData) { + delete d->destinationProjectData; + } + d->destinationProjectData = destinationProjectData; } QString Data::sourceDatabaseInfoString() const { return source ? KexiProjectData::infoString(sourceName, *source).toString(Kuit::PlainText) : QString(); } diff --git a/src/migration/keximigratedata.h b/src/migration/keximigratedata.h index 5c3609d7d..400d44776 100644 --- a/src/migration/keximigratedata.h +++ b/src/migration/keximigratedata.h @@ -1,55 +1,68 @@ /* This file is part of the KDE project Copyright (C) 2004 Adam Pigg Copyright (C) 2004 Jarosław Staniek Copyright (C) 2005 Martin Ellis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_MIGRATE_DATA_H #define KEXI_MIGRATE_DATA_H #include #include "keximigrate_export.h" #include namespace KexiMigration { //Use this class to store all possible options that could be used by keximigrate. //The current members are not meant to be a definite set, for example, i envisage //adding table/field lists if we allow only importing certain tables/fields class KEXIMIGRATE_EXPORT Data { public: Data(); ~Data(); QString sourceDatabaseInfoString() const; //! Connection data for the source database KDbConnectionData* source; //! Name of the source database QString sourceName; - //! Destination project data - KexiProjectData* destination; + //! @return destination project data + KexiProjectData* destinationProjectData(); + + //! @overload KexiProjectData* destinationProjectData() + const KexiProjectData* destinationProjectData() const; + + //! Sets destination project data to @a destinationProjectData. + //! The object wil be owned by KexiMigration::Data. + //! Previous project data other than @a destinationProjectData will be deleted. + void setDestinationProjectData(KexiProjectData* destinationProjectData); //! Flag to determine structure copy, or structure + data bool keepData; + +private: + class Private; + Private * const d; + Q_DISABLE_COPY(Data) }; }//namespace KexiMigration #endif