diff --git a/src/core/kexiproject.cpp b/src/core/kexiproject.cpp index 14c7bc2fe..2a15e81c1 100644 --- a/src/core/kexiproject.cpp +++ b/src/core/kexiproject.cpp @@ -1,1489 +1,1515 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2018 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 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 "kexiproject.h" #include "kexiprojectdata.h" #include "kexipartmanager.h" #include "kexipartitem.h" #include "kexipartinfo.h" #include "kexipart.h" #include "KexiWindow.h" #include "KexiWindowData.h" #include "kexi.h" #include "kexiblobbuffer.h" #include "kexiguimsghandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! @return a real plugin ID for @a pluginId and @a partMime //! for compatibility with Kexi 1.x static QString realPluginId(const QString &pluginId, const QString &partMime) { if (pluginId.startsWith(QLatin1String("http://"))) { // for compatibility with Kexi 1.x // part mime was used at the time return QLatin1String("org.kexi-project.") + QString(partMime).remove("kexi/"); } return pluginId; } class Q_DECL_HIDDEN KexiProject::Private { public: explicit Private(KexiProject *qq) : q(qq) , connection(0) , data(0) , tempPartItemID_Counter(-1) , sqlParser(0) , versionMajor(0) , versionMinor(0) , privateIDCounter(0) , itemsRetrieved(false) { } ~Private() { delete data; data = 0; delete sqlParser; foreach(KexiPart::ItemDict* dict, itemDicts) { qDeleteAll(*dict); dict->clear(); } qDeleteAll(itemDicts); qDeleteAll(unstoredItems); unstoredItems.clear(); } void savePluginId(const QString& pluginId, int typeId) { if (!typeIds.contains(pluginId) && !pluginIdsForTypeIds.contains(typeId)) { typeIds.insert(pluginId, typeId); pluginIdsForTypeIds.insert(typeId, pluginId); } //! @todo what to do with extra plugin IDs for the same type ID or extra type ID name for the plugin ID? } //! @return user name for the current project //! @todo the name is taken from connection but it also can be specified otherwise //! if the same connection data is shared by multiple users. This will be especially //! true for 3-tier architectures. QString userName() const { QString name = connection->data().userName(); return name.isNull() ? "" : name; } bool setNameOrCaption(KexiPart::Item* item, const QString* _newName, const QString* _newCaption) { q->clearResult(); if (data->userMode()) { return false; } KexiUtils::WaitCursor wait; QString newName; if (_newName) { newName = _newName->trimmed(); KDbMessageTitleSetter ts(q); if (newName.isEmpty()) { q->m_result = KDbResult(xi18n("Could not set empty name for this object.")); return false; } if (q->itemForPluginId(item->pluginId(), newName) != 0) { q->m_result = KDbResult( xi18nc("@info", "Could not use this name. Object %1 already exists.", newName)); return false; } } QString newCaption; if (_newCaption) { newCaption = _newCaption->trimmed(); } KDbMessageTitleSetter et(q, xi18nc("@info", "Could not rename object %1.", item->name())); if (!q->checkWritable()) return false; KexiPart::Part *part = q->findPartFor(*item); if (!part) return false; KDbTransactionGuard tg(connection); if (!tg.transaction().isActive()) { q->m_result = connection->result(); return false; } if (_newName) { if (!part->rename(item, newName)) { q->m_result = KDbResult(part->lastOperationStatus().description); q->m_result.setMessageTitle(part->lastOperationStatus().message); return false; } if (!connection->executeSql(KDbEscapedString("UPDATE kexi__objects SET o_name=%1 WHERE o_id=%2") .arg(connection->escapeString(newName)) .arg(connection->driver()->valueToSql(KDbField::Integer, item->identifier())))) { q->m_result = connection->result(); return false; } } if (_newCaption) { if (!connection->executeSql(KDbEscapedString("UPDATE kexi__objects SET o_caption=%1 WHERE o_id=%2") .arg(connection->escapeString(newCaption)) .arg(connection->driver()->valueToSql(KDbField::Integer, item->identifier())))) { q->m_result = connection->result(); return false; } } if (!tg.commit()) { q->m_result = connection->result(); return false; } QString oldName(item->name()); if (_newName) { item->setName(newName); emit q->itemRenamed(*item, oldName); } QString oldCaption(item->caption()); if (_newCaption) { item->setCaption(newCaption); emit q->itemCaptionChanged(*item, oldCaption); } return true; } KexiProject *q; //! @todo KEXI3 use equivalent of QPointer KDbConnection* connection; //! @todo KEXI3 use equivalent of QPointer or make KexiProjectData implicitly shared like KDbConnectionData KexiProjectData *data; QString error_title; KexiPart::MissingPartsList missingParts; QHash typeIds; QHash pluginIdsForTypeIds; //! a cache for item() method, indexed by plugin IDs QHash itemDicts; QSet unstoredItems; //! helper for getting unique //! temporary identifiers for unstored items int tempPartItemID_Counter; KDbParser* sqlParser; int versionMajor; int versionMinor; int privateIDCounter; //!< counter: ID for private "document" like Relations window bool itemsRetrieved; }; //--------------------------- KexiProject::KexiProject(const KexiProjectData& pdata, KDbMessageHandler* handler) : QObject(), KDbObject(), KDbResultable() , d(new Private(this)) { d->data = new KexiProjectData(pdata); setMessageHandler(handler); } KexiProject::KexiProject(const KexiProjectData& pdata, KDbMessageHandler* handler, KDbConnection* conn) : QObject(), KDbObject(), KDbResultable() , d(new Private(this)) { d->data = new KexiProjectData(pdata); setMessageHandler(handler); if (*d->data->connectionData() == d->connection->data()) d->connection = conn; else qWarning() << "passed connection's data (" << conn->data().toUserVisibleString() << ") is not compatible with project's conn. data (" << d->data->connectionData()->toUserVisibleString() << ")"; } KexiProject::~KexiProject() { closeConnection(); delete d; } KDbConnection *KexiProject::dbConnection() const { return d->connection; } KexiProjectData* KexiProject::data() const { return d->data; } int KexiProject::versionMajor() const { return d->versionMajor; } int KexiProject::versionMinor() const { return d->versionMinor; } tristate KexiProject::open(bool *incompatibleWithKexi) { Q_ASSERT(incompatibleWithKexi); KDbMessageGuard mg(this); return openInternal(incompatibleWithKexi); } tristate KexiProject::open() { KDbMessageGuard mg(this); return openInternal(0); } tristate KexiProject::openInternal(bool *incompatibleWithKexi) { if (!Kexi::partManager().infoList()) { m_result = Kexi::partManager().result(); return cancelled; } if (incompatibleWithKexi) *incompatibleWithKexi = false; //qDebug() << d->data->databaseName() << d->data->connectionData()->driverId(); KDbMessageTitleSetter et(this, xi18nc("@info", "Could not open project %1.", d->data->databaseName())); if (!d->data->connectionData()->databaseName().isEmpty()) { QFileInfo finfo(d->data->connectionData()->databaseName()); if (!finfo.exists()) { KMessageBox::sorry(0, xi18nc("@info", "Could not open project. " "The project file %1 does not exist.", QDir::toNativeSeparators(finfo.absoluteFilePath())), xi18nc("@title:window", "Could Not Open File")); return cancelled; } if (!d->data->isReadOnly() && !finfo.isWritable()) { if (KexiProject::askForOpeningNonWritableFileAsReadOnly(0, finfo)) { d->data->setReadOnly(true); } else { return cancelled; } } } if (!createConnection()) { qWarning() << "!createConnection()"; return false; } bool cancel = false; if (!d->connection->useDatabase(d->data->databaseName(), true, &cancel)) { m_result = d->connection->result(); if (cancel) { return cancelled; } qWarning() << "!d->connection->useDatabase() " << d->data->databaseName() << " " << d->data->connectionData()->driverId(); if (d->connection->result().code() == ERR_NO_DB_PROPERTY) { // //! @todo this is temporary workaround as we have no import driver for SQLite if (/*supported?*/ !d->data->connectionData()->driverId().contains("sqlite")) { // if (incompatibleWithKexi) *incompatibleWithKexi = true; } else { KDbMessageTitleSetter et(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database project %1 does not " "appear to have been created using Kexi and cannot be opened. " "It is an SQLite file created using other tools.", KexiUtils::localizedStringToHtmlSubstring(d->data->infoString()))); m_result = d->connection->result(); } closeConnectionInternal(); return false; } m_result = d->connection->result(); closeConnectionInternal(); return false; } if (!initProject()) return false; return createInternalStructures(/*insideTransaction*/true); } tristate KexiProject::create(bool forceOverwrite) { KDbMessageGuard mg(this); KDbMessageTitleSetter et(this, xi18nc("@info", "Could not create project %1.", d->data->databaseName())); if (!createConnection()) return false; if (!checkWritable()) return false; if (d->connection->databaseExists(d->data->databaseName())) { if (!forceOverwrite) return cancelled; if (!d->connection->dropDatabase(d->data->databaseName())) { m_result = d->connection->result(); closeConnectionInternal(); return false; } //qDebug() << "--- DB '" << d->data->databaseName() << "' dropped ---"; } if (!d->connection->createDatabase(d->data->databaseName())) { m_result = d->connection->result(); closeConnectionInternal(); return false; } //qDebug() << "--- DB '" << d->data->databaseName() << "' created ---"; // and now: open if (!d->connection->useDatabase(d->data->databaseName())) { qWarning() << "--- DB '" << d->data->databaseName() << "' USE ERROR ---"; m_result = d->connection->result(); closeConnectionInternal(); return false; } //qDebug() << "--- DB '" << d->data->databaseName() << "' used ---"; // KDbTransaction trans = d->connection->beginTransaction(); if (trans.isNull()) return false; if (!createInternalStructures(/*!insideTransaction*/false)) return false; //add some metadata //! @todo put more props. todo - creator, created date, etc. (also to KexiProjectData) KDbProperties props = d->connection->databaseProperties(); if (!props.setValue("kexiproject_major_ver", d->versionMajor) || !props.setCaption("kexiproject_major_ver", xi18n("Project major version")) || !props.setValue("kexiproject_minor_ver", d->versionMinor) || !props.setCaption("kexiproject_minor_ver", xi18n("Project minor version")) || !props.setValue("project_caption", d->data->caption()) || !props.setCaption("project_caption", xi18n("Project caption")) || !props.setValue("project_desc", d->data->description()) || !props.setCaption("project_desc", xi18n("Project description"))) { m_result = props.result(); return false; } if (trans.isActive() && !d->connection->commitTransaction(trans)) return false; // if (!Kexi::partManager().infoList()) { m_result = Kexi::partManager().result(); return cancelled; } return initProject(); } bool KexiProject::createInternalStructures(bool insideTransaction) { KDbTransactionGuard tg; if (insideTransaction) { tg.setTransaction(d->connection->beginTransaction()); if (tg.transaction().isNull()) return false; } //Get information about kexiproject version. //kexiproject version is a version of data layer above kexidb layer. KDbProperties props = d->connection->databaseProperties(); bool ok; int storedMajorVersion = props.value("kexiproject_major_ver").toInt(&ok); if (!ok) storedMajorVersion = 0; int storedMinorVersion = props.value("kexiproject_minor_ver").toInt(&ok); if (!ok) storedMinorVersion = 1; const tristate containsKexi__blobsTable = d->connection->containsTable("kexi__blobs"); if (~containsKexi__blobsTable) { return false; } int dummy; bool contains_o_folder_id = false; if (true == containsKexi__blobsTable) { const tristate res = d->connection->querySingleNumber( KDbEscapedString("SELECT COUNT(o_folder_id) FROM kexi__blobs"), &dummy, 0, KDbConnection::QueryRecordOptions(KDbConnection::QueryRecordOption::Default) & ~KDbConnection::QueryRecordOptions(KDbConnection::QueryRecordOption::AddLimitTo1)); if (res == false) { m_result = d->connection->result(); } else if (res == true) { contains_o_folder_id = true; } } bool add_folder_id_column = false; //! @todo what about read-only db access? if (storedMajorVersion <= 0) { d->versionMajor = KEXIPROJECT_VERSION_MAJOR; d->versionMinor = KEXIPROJECT_VERSION_MINOR; //For compatibility for projects created before Kexi 1.0 beta 1: //1. no kexiproject_major_ver and kexiproject_minor_ver -> add them if (!d->connection->options()->isReadOnly()) { if (!props.setValue("kexiproject_major_ver", d->versionMajor) || !props.setCaption("kexiproject_major_ver", xi18n("Project major version")) || !props.setValue("kexiproject_minor_ver", d->versionMinor) || !props.setCaption("kexiproject_minor_ver", xi18n("Project minor version"))) { return false; } } if (true == containsKexi__blobsTable) { //! @todo what to do for readonly connections? Should we alter kexi__blobs in memory? if (!d->connection->options()->isReadOnly()) { if (!contains_o_folder_id) { add_folder_id_column = true; } } } } if (storedMajorVersion != d->versionMajor || storedMajorVersion != d->versionMinor) { //! @todo version differs: should we change something? d->versionMajor = storedMajorVersion; d->versionMinor = storedMinorVersion; } QScopedPointer t_blobs(new KDbInternalTableSchema("kexi__blobs")); t_blobs->addField(new KDbField("o_id", KDbField::Integer, KDbField::PrimaryKey | KDbField::AutoInc, KDbField::Unsigned)); t_blobs->addField(new KDbField("o_data", KDbField::BLOB)); t_blobs->addField(new KDbField("o_name", KDbField::Text)); t_blobs->addField(new KDbField("o_caption", KDbField::Text)); t_blobs->addField(new KDbField("o_mime", KDbField::Text, KDbField::NotNull)); t_blobs->addField(new KDbField("o_folder_id", KDbField::Integer, 0, KDbField::Unsigned) //references kexi__gallery_folders.f_id //If null, the BLOB only points to virtual "All" folder //WILL BE USED in Kexi >=2.0 ); //*** create global BLOB container, if not present if (true == containsKexi__blobsTable) { if (add_folder_id_column && !d->connection->options()->isReadOnly()) { // 2. "kexi__blobs" table contains no "o_folder_id" column -> add it // (by copying table to avoid data loss) QScopedPointer kexi__blobsCopy( new KDbInternalTableSchema(*t_blobs)); //won't be not needed - will be physically renamed to kexi_blobs kexi__blobsCopy->setName("kexi__blobs__copy"); if (!d->connection->createTable(kexi__blobsCopy.data(), KDbConnection::CreateTableOption::Default | KDbConnection::CreateTableOption::DropDestination)) { m_result = d->connection->result(); return false; } KDbInternalTableSchema *ts = kexi__blobsCopy.take(); // createTable() took ownerhip of kexi__blobsCopy // 2.1 copy data (insert 0's into o_folder_id column) if (!d->connection->executeSql(KDbEscapedString( "INSERT INTO kexi__blobs (o_data, o_name, o_caption, o_mime, o_folder_id) " "SELECT o_data, o_name, o_caption, o_mime, 0 FROM kexi__blobs")) // 2.2 remove the original kexi__blobs || !d->connection->executeSql( KDbEscapedString("DROP TABLE kexi__blobs")) // lowlevel // 2.3 rename the copy back into kexi__blobs || !d->connection->alterTableName( ts, "kexi__blobs", KDbConnection::AlterTableNameOptions(KDbConnection::AlterTableNameOption::Default) & ~KDbConnection::AlterTableNameOptions(KDbConnection::AlterTableNameOption::DropDestination))) { //(no need to drop the copy, ROLLBACK will drop it) m_result = d->connection->result(); return false; } } //! just insert this schema, proper table exists d->connection->createTable(t_blobs.take()); } else { if (!d->connection->options()->isReadOnly()) { if (!d->connection->createTable(t_blobs.data(), KDbConnection::CreateTableOption::Default | KDbConnection::CreateTableOption::DropDestination)) { m_result = d->connection->result(); return false; } (void)t_blobs.take(); // createTable() took ownerhip of t_blobs } } //Store default part information. //Information for other parts (forms, reports...) are created on demand in KexiWindow::storeNewData() const tristate containsKexi__partsTable = d->connection->containsTable("kexi__parts"); if (~containsKexi__partsTable) { return false; } QScopedPointer t_parts(new KDbInternalTableSchema("kexi__parts")); t_parts->addField( new KDbField("p_id", KDbField::Integer, KDbField::PrimaryKey | KDbField::AutoInc, KDbField::Unsigned) ); t_parts->addField(new KDbField("p_name", KDbField::Text)); t_parts->addField(new KDbField("p_mime", KDbField::Text)); t_parts->addField(new KDbField("p_url", KDbField::Text)); if (true == containsKexi__partsTable) { //! just insert this schema d->connection->createTable(t_parts.take()); } else { if (!d->connection->options()->isReadOnly()) { bool partsTableOk = d->connection->createTable(t_parts.data(), KDbConnection::CreateTableOption::Default | KDbConnection::CreateTableOption::DropDestination); if (!partsTableOk) { m_result = d->connection->result(); return false; } KDbInternalTableSchema *ts = t_parts.take(); // createTable() took ownerhip of t_parts QScopedPointer fl(ts->subList("p_id", "p_name", "p_mime", "p_url")); #define INSERT_RECORD(typeId, groupName, name) \ if (partsTableOk) { \ partsTableOk = d->connection->insertRecord(fl.data(), QVariant(int(KexiPart::typeId)), \ QVariant(groupName), \ QVariant("kexi/" name), QVariant("org.kexi-project." name)); \ if (partsTableOk) { \ d->savePluginId("org.kexi-project." name, int(KexiPart::typeId)); \ } \ } INSERT_RECORD(TableObjectType, "Tables", "table") INSERT_RECORD(QueryObjectType, "Queries", "query") INSERT_RECORD(FormObjectType, "Forms", "form") INSERT_RECORD(ReportObjectType, "Reports", "report") INSERT_RECORD(ScriptObjectType, "Scripts", "script") INSERT_RECORD(WebObjectType, "Web pages", "web") INSERT_RECORD(MacroObjectType, "Macros", "macro") #undef INSERT_RECORD if (!partsTableOk) { m_result = d->connection->result(); // note: kexi__parts object still exists because createTable() succeeded return false; } } } // User data storage const tristate containsKexi__userdataTable = d->connection->containsTable("kexi__userdata"); if (~containsKexi__userdataTable) { return false; } QScopedPointer t_userdata(new KDbInternalTableSchema("kexi__userdata")); t_userdata->addField(new KDbField("d_user", KDbField::Text, KDbField::NotNull)); t_userdata->addField(new KDbField("o_id", KDbField::Integer, KDbField::NotNull, KDbField::Unsigned)); t_userdata->addField(new KDbField("d_sub_id", KDbField::Text, KDbField::NotNull | KDbField::NotEmpty)); t_userdata->addField(new KDbField("d_data", KDbField::LongText)); if (true == containsKexi__userdataTable) { d->connection->createTable(t_userdata.take()); } else if (!d->connection->options()->isReadOnly()) { if (!d->connection->createTable(t_userdata.data(), KDbConnection::CreateTableOption::Default | KDbConnection::CreateTableOption::DropDestination)) { m_result = d->connection->result(); return false; } (void)t_userdata.take(); // createTable() took ownerhip of t_userdata } if (insideTransaction) { if (tg.transaction().isActive() && !tg.commit()) { m_result = d->connection->result(); return false; } } return true; } bool KexiProject::createConnection() { clearResult(); KDbMessageGuard mg(this); if (d->connection) { return true; } KDbMessageTitleSetter et(this); KDbDriver *driver = Kexi::driverManager().driver(d->data->connectionData()->driverId()); if (!driver) { m_result = Kexi::driverManager().result(); return false; } KDbConnectionOptions connectionOptions; if (d->data->isReadOnly()) { connectionOptions.setReadOnly(true); } d->connection = driver->createConnection(*d->data->connectionData(), connectionOptions); if (!d->connection) { m_result = driver->result(); qWarning() << "error create connection: " << m_result; return false; } if (!d->connection->connect()) { m_result = d->connection->result(); qWarning() << "error connecting: " << m_result; delete d->connection; //this will also clear connection for BLOB buffer d->connection = 0; return false; } //re-init BLOB buffer //! @todo won't work for subsequent connection KexiBLOBBuffer::setConnection(d->connection); return true; } bool KexiProject::closeConnectionInternal() { if (!m_result.isError()) { clearResult(); } if (!d->connection) { return true; } if (!d->connection->disconnect()) { if (!m_result.isError()) { m_result = d->connection->result(); } return false; } delete d->connection; //this will also clear connection for BLOB buffer d->connection = 0; return true; } bool KexiProject::closeConnection() { clearResult(); KDbMessageGuard mg(this); if (!d->connection) return true; if (!d->connection->disconnect()) { m_result = d->connection->result(); return false; } delete d->connection; //this will also clear connection for BLOB buffer d->connection = 0; return true; } bool KexiProject::initProject() { //qDebug() << "checking project parts..."; if (!checkProject()) { return false; } // !@todo put more props. todo - creator, created date, etc. (also to KexiProjectData) KDbProperties props = d->connection->databaseProperties(); QString str(props.value("project_caption").toString()); if (!str.isEmpty()) d->data->setCaption(str); str = props.value("project_desc").toString(); if (!str.isEmpty()) d->data->setDescription(str); return true; } bool KexiProject::isConnected() { if (d->connection && d->connection->isDatabaseUsed()) return true; return false; } KexiPart::ItemDict* KexiProject::items(KexiPart::Info *i) { clearResult(); KDbMessageGuard mg(this); if (!i || !isConnected()) return 0; //trying in cache... KexiPart::ItemDict *dict = d->itemDicts.value(i->id()); if (dict) return dict; if (d->itemsRetrieved) return 0; if (!retrieveItems()) return 0; return items(i); // try again } bool KexiProject::retrieveItems() { d->itemsRetrieved = true; KDbCursor *cursor = d->connection->executeQuery( KDbEscapedString("SELECT o_id, o_name, o_caption, o_type FROM kexi__objects ORDER BY o_type")); if (!cursor) { m_result = d->connection->result(); return 0; } int recentTypeId = -1000; QString pluginId; KexiPart::ItemDict *dict = 0; + QSet tableNamesSet; for (cursor->moveFirst(); !cursor->eof(); cursor->moveNext()) { bool ok; const int typeId = cursor->value(3).toInt(&ok); if (!ok || typeId <= 0) { qInfo() << "object of unknown type id" << cursor->value(3) << "id=" << cursor->value(0) << "name=" << cursor->value(1); continue; } if (recentTypeId == typeId) { - if (pluginId.isEmpty()) // still the same unknown plugin ID + if (pluginId.isEmpty()) { // still the same unknown plugin ID continue; + } } else { // a new type ID: create another plugin items dict if it's an ID for a known type recentTypeId = typeId; pluginId = pluginIdForTypeId(typeId); if (pluginId.isEmpty()) continue; dict = new KexiPart::ItemDict(); d->itemDicts.insert(pluginId, dict); + if (typeId == KDb::TableObjectType) { + // Starting to load table names: initialize. + // This list since 3.2 does not contain names without physical tables so we can + // catch these cases below. + const QStringList tableNames(d->connection->tableNames(false /*public*/, &ok)); + if (!ok) { + m_result = KDbResult(ERR_OBJECT_NOT_FOUND, xi18n("Could not load list of tables.")); + qDeleteAll(d->itemDicts); + return false; + } + for (const QString &name : tableNames) { + tableNamesSet.insert(name.toLower()); + } + } } const int ident = cursor->value(0).toInt(&ok); const QString objName(cursor->value(1).toString()); - if (ok && (ident > 0) && !d->connection->isInternalTableSchema(objName) - && KDb::isIdentifier(objName)) + if (!ok || ident <= 0 || !KDb::isIdentifier(objName)) { - KexiPart::Item *it = new KexiPart::Item(); - it->setIdentifier(ident); - it->setPluginId(pluginId); - it->setName(objName); - it->setCaption(cursor->value(2).toString()); - dict->insert(it->identifier(), it); + continue; // invalid ID or invalid name + } + if (typeId == KDb::TableObjectType) { + if (d->connection->isInternalTableSchema(objName)) { + qInfo() << "table" << objName << "id=" << ident << "is internal, skipping"; + continue; + } + if (!tableNamesSet.contains(objName.toLower())) { + qInfo() << "table" << objName << "id=" << ident + << "does not correspondent with physical table"; + continue; + } } - //qDebug() << "ITEM ADDED == "<* staticObjectArgs) { clearResult(); KDbMessageGuard mg(this); if (viewMode != Kexi::DataViewMode && data()->userMode()) return 0; KDbMessageTitleSetter et(this); KexiPart::Part *part = findPartFor(*item); if (!part) return 0; KexiWindow *window = part->openInstance(parent, item, viewMode, staticObjectArgs); if (!window) { if (part->lastOperationStatus().error()) { m_result = KDbResult(xi18nc("@info", "Opening object %1 failed.\n%2 %3", item->name()) .arg(part->lastOperationStatus().message) .arg(part->lastOperationStatus().description) .replace("(I18N_ARGUMENT_MISSING)", " ")); // a hack until there's other solution } return 0; } return window; } KexiWindow* KexiProject::openObject(QWidget* parent, const QString &pluginId, const QString& name, Kexi::ViewMode viewMode) { KexiPart::Item *it = itemForPluginId(pluginId, name); return it ? openObject(parent, it, viewMode) : 0; } bool KexiProject::checkWritable() { if (!d->connection->options()->isReadOnly()) return true; m_result = KDbResult(xi18n("This project is opened as read only.")); return false; } bool KexiProject::removeObject(KexiPart::Item *item) { Q_ASSERT(item); clearResult(); if (data()->userMode()) return false; KDbMessageTitleSetter et(this); if (!checkWritable()) return false; KexiPart::Part *part = findPartFor(*item); if (!part) return false; if (!item->neverSaved() && !part->remove(item)) { //! @todo check for errors return false; } if (!item->neverSaved()) { KDbTransactionGuard tg(d->connection); if (!tg.transaction().isActive()) { m_result = d->connection->result(); return false; } if (!d->connection->removeObject(item->identifier())) { m_result = d->connection->result(); return false; } if (!removeUserDataBlock(item->identifier())) { m_result = KDbResult(ERR_DELETE_SERVER_ERROR, xi18n("Could not delete object's user data.")); return false; } if (!tg.commit()) { m_result = d->connection->result(); return false; } } emit itemRemoved(*item); //now: remove this item from cache if (part->info()) { KexiPart::ItemDict *dict = d->itemDicts.value(part->info()->pluginId()); if (!(dict && dict->remove(item->identifier()))) d->unstoredItems.remove(item);//remove temp. } return true; } bool KexiProject::renameObject(KexiPart::Item *item, const QString& newName) { KDbMessageGuard mg(this); return d->setNameOrCaption(item, &newName, 0); } bool KexiProject::setObjectCaption(KexiPart::Item *item, const QString& newCaption) { KDbMessageGuard mg(this); return d->setNameOrCaption(item, 0, &newCaption); } KexiPart::Item* KexiProject::createPartItem(KexiPart::Info *info, const QString& suggestedCaption) { clearResult(); KDbMessageGuard mg(this); if (data()->userMode()) return 0; KDbMessageTitleSetter et(this); KexiPart::Part *part = Kexi::partManager().part(info); if (!part) { m_result = Kexi::partManager().result(); return 0; } KexiPart::ItemDict *dict = items(info); if (!dict) { dict = new KexiPart::ItemDict(); d->itemDicts.insert(info->pluginId(), dict); } QSet storedItemNames; foreach(KexiPart::Item* item, *dict) { storedItemNames.insert(item->name()); } QSet unstoredItemNames; foreach(KexiPart::Item* item, d->unstoredItems) { unstoredItemNames.insert(item->name()); } //find new, unique default name for this item int n; QString new_name; QString base_name; if (suggestedCaption.isEmpty()) { n = 1; base_name = part->instanceName(); } else { n = 0; //means: try not to add 'n' base_name = KDb::stringToIdentifier(suggestedCaption).toLower(); } base_name = KDb::stringToIdentifier(base_name).toLower(); do { new_name = base_name; if (n >= 1) new_name += QString::number(n); if (storedItemNames.contains(new_name)) { n++; continue; //stored exists! } if (!unstoredItemNames.contains(new_name)) break; //unstored doesn't exist n++; } while (n < 1000/*sanity*/); if (n >= 1000) return 0; QString new_caption(suggestedCaption.isEmpty() ? part->info()->name() : suggestedCaption); if (n >= 1) new_caption += QString::number(n); KexiPart::Item *item = new KexiPart::Item(); item->setIdentifier(--d->tempPartItemID_Counter); //temporary item->setPluginId(info->pluginId()); item->setName(new_name); item->setCaption(new_caption); item->setNeverSaved(true); d->unstoredItems.insert(item); return item; } KexiPart::Item* KexiProject::createPartItem(KexiPart::Part *part, const QString& suggestedCaption) { Q_ASSERT(part); return createPartItem(part->info(), suggestedCaption); } void KexiProject::deleteUnstoredItem(KexiPart::Item *item) { if (!item) return; d->unstoredItems.remove(item); delete item; } KDbParser* KexiProject::sqlParser() { if (!d->sqlParser) { if (!d->connection) return 0; d->sqlParser = new KDbParser(d->connection); } return d->sqlParser; } const char warningNoUndo[] = I18N_NOOP2("warning", "Entire project's data and design will be deleted."); /*static*/ KexiProject* KexiProject::createBlankProject(bool *cancelled, const KexiProjectData& data, KDbMessageHandler* handler) { Q_ASSERT(cancelled); *cancelled = false; KexiProject *prj = new KexiProject(data, handler); tristate res = prj->create(false); if (~res) { //! @todo move to KexiMessageHandler if (KMessageBox::Yes != KMessageBox::warningYesNo(0, xi18nc("@info (don't add tags around %1, it's done already)", "The project %1 already exists." "Do you want to replace it with a new, blank one?" "%2", KexiUtils::localizedStringToHtmlSubstring(prj->data()->infoString()), xi18n(warningNoUndo)), QString(), KGuiItem(xi18nc("@action:button", "Replace")), KStandardGuiItem::cancel())) //! @todo add toUserVisibleString() for server-based prj { delete prj; *cancelled = true; return 0; } res = prj->create(true/*overwrite*/); } if (res != true) { delete prj; return 0; } //qDebug() << "new project created --- "; //! @todo Kexi::recentProjects().addProjectData( data ); return prj; } /*static*/ tristate KexiProject::dropProject(const KexiProjectData& data, KDbMessageHandler* handler, bool dontAsk) { if (!dontAsk && KMessageBox::Yes != KMessageBox::questionYesNo(0, xi18nc("@info", "Do you want to delete the project %1?" "%2", static_cast(&data)->name(), i18n(warningNoUndo)), QString(), KGuiItem(xi18nc("@action:button", "Delete Project"), koIconName("edit-delete")), KStandardGuiItem::no(), QString(), KMessageBox::Notify | KMessageBox::Dangerous)) { return cancelled; } KexiProject prj(data, handler); if (!prj.open()) return false; if (prj.dbConnection()->options()->isReadOnly()) { handler->showErrorMessage( KDbMessageHandler::Error, xi18n("Could not delete this project. Database connection for this project has been opened as read only.")); return false; } KDbMessageGuard mg(prj.dbConnection()->result(), handler); return prj.dbConnection()->dropDatabase(); } bool KexiProject::checkProject(const QString& singlePluginId) { clearResult(); //! @todo catch errors! if (!d->connection->isDatabaseUsed()) { m_result = d->connection->result(); return false; } const tristate containsKexi__partsTable = d->connection->containsTable("kexi__parts"); if (~containsKexi__partsTable) { return false; } if (true == containsKexi__partsTable) { // check if kexi__parts exists, if missing, createInternalStructures() will create it KDbEscapedString sql = KDbEscapedString("SELECT p_id, p_name, p_mime, p_url FROM kexi__parts ORDER BY p_id"); if (!singlePluginId.isEmpty()) { sql.append(KDbEscapedString(" WHERE p_url=%1").arg(d->connection->escapeString(singlePluginId))); } KDbCursor *cursor = d->connection->executeQuery(sql); if (!cursor) { m_result = d->connection->result(); return false; } bool saved = false; for (cursor->moveFirst(); !cursor->eof(); cursor->moveNext()) { const QString partMime(cursor->value(2).toString()); QString pluginId(cursor->value(3).toString()); pluginId = realPluginId(pluginId, partMime); if (pluginId == QLatin1String("uk.co.piggz.report")) { // compatibility pluginId = QLatin1String("org.kexi-project.report"); } KexiPart::Info *info = Kexi::partManager().infoForPluginId(pluginId); bool ok; const int typeId = cursor->value(0).toInt(&ok); if (!ok || typeId <= 0) { qWarning() << "Invalid type ID" << typeId << "; part with ID" << pluginId << "will not be used"; } if (info && ok && typeId > 0) { d->savePluginId(pluginId, typeId); saved = true; } else { KexiPart::MissingPart m; m.name = cursor->value(1).toString(); m.id = pluginId; d->missingParts.append(m); } } d->connection->deleteCursor(cursor); if (!saved && !singlePluginId.isEmpty()) { return false; // failure is single part class was not found } } return true; } int KexiProject::generatePrivateID() { return --d->privateIDCounter; } bool KexiProject::createIdForPart(const KexiPart::Info& info) { KDbMessageGuard mg(this); int typeId = typeIdForPluginId(info.pluginId()); if (typeId > 0) { return true; } // try again, perhaps the id is already created if (checkProject(info.pluginId())) { return true; } // Find first available custom part ID by taking the greatest // existing custom ID (if it exists) and adding 1. typeId = int(KexiPart::UserObjectType); tristate success = d->connection->querySingleNumber(KDbEscapedString("SELECT max(p_id) FROM kexi__parts"), &typeId); if (!success) { // Couldn't read part id's from the kexi__parts table m_result = d->connection->result(); return false; } else { // Got a maximum part ID, or there were no parts typeId = typeId + 1; typeId = qMax(typeId, (int)KexiPart::UserObjectType); } //this part's ID is not stored within kexi__parts: KDbTableSchema *ts = d->connection->tableSchema("kexi__parts"); if (!ts) { m_result = d->connection->result(); return false; } QScopedPointer fl(ts->subList("p_id", "p_name", "p_mime", "p_url")); //qDebug() << "fieldlist: " << (fl ? *fl : QString()); if (!fl) return false; //qDebug() << info.ptr()->untranslatedGenericName(); // QStringList sl = part()->info()->ptr()->propertyNames(); // for (QStringList::ConstIterator it=sl.constBegin();it!=sl.constEnd();++it) //qDebug() << *it << " " << part()->info()->ptr()->property(*it).toString(); if (!d->connection->insertRecord( fl.data(), QVariant(typeId), QVariant(info.untranslatedGroupName()), QVariant(QString::fromLatin1("kexi/") + info.typeName()/*ok?*/), QVariant(info.id() /*always ok?*/))) { m_result = d->connection->result(); return false; } //qDebug() << "insert success!"; d->savePluginId(info.id(), typeId); //qDebug() << "new id is: " << p_id; return true; } KexiPart::MissingPartsList KexiProject::missingParts() const { return d->missingParts; } static bool checkObjectId(const char* method, int objectID) { if (objectID <= 0) { qWarning() << method << ": Invalid objectID" << objectID; return false; } return true; } tristate KexiProject::loadUserDataBlock(int objectID, const QString& dataID, QString *dataString) { KDbMessageGuard mg(this); if (!checkObjectId("loadUserDataBlock", objectID)) { return false; } if (!d->connection->querySingleString( KDbEscapedString("SELECT d_data FROM kexi__userdata WHERE o_id=%1 AND ") .arg(d->connection->driver()->valueToSql(KDbField::Integer, objectID)) + KDb::sqlWhere(d->connection->driver(), KDbField::Text, "d_user", d->userName()) + " AND " + KDb::sqlWhere(d->connection->driver(), KDbField::Text, "d_sub_id", dataID), dataString)) { m_result = d->connection->result(); return false; } return true; } bool KexiProject::storeUserDataBlock(int objectID, const QString& dataID, const QString &dataString) { KDbMessageGuard mg(this); if (!checkObjectId("storeUserDataBlock", objectID)) { return false; } KDbEscapedString sql = KDbEscapedString("SELECT kexi__userdata.o_id FROM kexi__userdata WHERE o_id=%1").arg(objectID); KDbEscapedString sql_sub = KDb::sqlWhere(d->connection->driver(), KDbField::Text, "d_user", d->userName()) + " AND " + KDb::sqlWhere(d->connection->driver(), KDbField::Text, "d_sub_id", dataID); const tristate result = d->connection->resultExists(sql + " AND " + sql_sub); if (~result) { m_result = d->connection->result(); return false; } if (result == true) { if (!d->connection->executeSql( KDbEscapedString("UPDATE kexi__userdata SET d_data=" + d->connection->driver()->valueToSql(KDbField::LongText, dataString) + " WHERE o_id=" + QString::number(objectID) + " AND " + sql_sub))) { m_result = d->connection->result(); return false; } return true; } if (!d->connection->executeSql( KDbEscapedString("INSERT INTO kexi__userdata (d_user, o_id, d_sub_id, d_data) VALUES (") + d->connection->driver()->valueToSql(KDbField::Text, d->userName()) + ", " + QString::number(objectID) + ", " + d->connection->driver()->valueToSql(KDbField::Text, dataID) + ", " + d->connection->driver()->valueToSql(KDbField::LongText, dataString) + ")")) { m_result = d->connection->result(); return false; } return true; } bool KexiProject::copyUserDataBlock(int sourceObjectID, int destObjectID, const QString &dataID) { KDbMessageGuard mg(this); if (!checkObjectId("storeUserDataBlock(sourceObjectID)", sourceObjectID)) { return false; } if (!checkObjectId("storeUserDataBlock(destObjectID)", destObjectID)) { return false; } if (sourceObjectID == destObjectID) return true; if (!removeUserDataBlock(destObjectID, dataID)) // remove before copying return false; KDbEscapedString sql = KDbEscapedString("INSERT INTO kexi__userdata SELECT t.d_user, %2, t.d_sub_id, t.d_data " "FROM kexi__userdata AS t WHERE d_user=%1 AND o_id=%3") .arg(d->connection->escapeString(d->userName())) .arg(d->connection->driver()->valueToSql(KDbField::Integer, destObjectID)) .arg(d->connection->driver()->valueToSql(KDbField::Integer, sourceObjectID)); if (!dataID.isEmpty()) { sql += " AND " + KDb::sqlWhere(d->connection->driver(), KDbField::Text, "d_sub_id", dataID); } if (!d->connection->executeSql(sql)) { m_result = d->connection->result(); return false; } return true; } bool KexiProject::removeUserDataBlock(int objectID, const QString& dataID) { KDbMessageGuard mg(this); if (!checkObjectId("removeUserDataBlock", objectID)) { return false; } if (dataID.isEmpty()) { if (!KDb::deleteRecords(d->connection, "kexi__userdata", "o_id", KDbField::Integer, objectID, "d_user", KDbField::Text, d->userName())) { m_result = d->connection->result(); return false; } else if (!KDb::deleteRecords(d->connection, "kexi__userdata", "o_id", KDbField::Integer, objectID, "d_user", KDbField::Text, d->userName(), "d_sub_id", KDbField::Text, dataID)) { m_result = d->connection->result(); return false; } } return true; } // static bool KexiProject::askForOpeningNonWritableFileAsReadOnly(QWidget *parent, const QFileInfo &finfo) { KGuiItem openItem(KStandardGuiItem::open()); openItem.setText(xi18n("Open As Read Only")); return KMessageBox::Yes == KMessageBox::questionYesNo( parent, xi18nc("@info", "Could not open file %1 for reading and writing." "Do you want to open the file as read only?", QDir::toNativeSeparators(finfo.filePath())), xi18nc("@title:window", "Could Not Open File" ), openItem, KStandardGuiItem::cancel(), QString()); } diff --git a/src/migration/KexiSqlMigrate.cpp b/src/migration/KexiSqlMigrate.cpp index 117922915..749cc8182 100644 --- a/src/migration/KexiSqlMigrate.cpp +++ b/src/migration/KexiSqlMigrate.cpp @@ -1,207 +1,195 @@ /* This file is part of the KDE project Copyright (C) 2004 Martin Ellis Copyright (C) 2006-2016 Jarosław Staniek 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 "KexiSqlMigrate.h" #include #include #include #include #include #include #include #include KexiSqlMigrate::KexiSqlMigrate(const QString &kdbDriverId, QObject *parent, const QVariantList& args) : KexiMigration::KexiMigrate(parent, args) , m_kdbDriverId(kdbDriverId) { Q_ASSERT(!m_kdbDriverId.isEmpty()); } KexiSqlMigrate::~KexiSqlMigrate() { } KDbConnection* KexiSqlMigrate::drv_createConnection() { KDbDriverManager manager; KDbDriver *driver = manager.driver(m_kdbDriverId); if (!driver) { m_result = manager.result(); return nullptr; } KDbConnection *c = driver->createConnection(*data()->source); m_result = c ? KDbResult() : driver->result(); return c; } bool KexiSqlMigrate::drv_readTableSchema( const QString& originalName, KDbTableSchema *tableSchema) { //! @todo IDEA: ask for user input for captions //Perform a query on the table to get some data KDbEscapedString sql = KDbEscapedString("SELECT * FROM %1 LIMIT 0") .arg(sourceConnection()->escapeIdentifier(tableSchema->name())); QSharedPointer result = sourceConnection()->prepareSql(sql); if (!result) { return false; } bool ok = true; const int fieldsCount = result->fieldsCount(); for (int i = 0; i < fieldsCount; i++) { KDbField *field = result->createField(originalName, i); if (field->type() == KDbField::InvalidType) { field->setType(userType(originalName + '.' + field->name())); } if (!tableSchema->addField(field)) { delete field; tableSchema->clear(); ok = false; break; } } return ok; } bool KexiSqlMigrate::drv_tableNames(QStringList *tableNames) { - QSharedPointer result = sourceConnection()->prepareSql(m_tableNamesSql); - if (!result || result->fieldsCount() < 1) { - return false; - } - Q_FOREVER { - QSharedPointer record = result->fetchRecord(); - if (!record) { - if (result->lastResult().isError()) { - return false; - } - break; - } - tableNames->append(record->stringValue(0)); - } - return true; + bool ok; + *tableNames = sourceConnection()->drv_getTableNames(&ok); + return ok; } tristate KexiSqlMigrate::drv_queryStringListFromSql( const KDbEscapedString& sqlStatement, int fieldIndex, QStringList *stringList, int numRecords) { QSharedPointer result= sourceConnection()->prepareSql(sqlStatement); if (!result) { return true; } if (result->fieldsCount() < (fieldIndex+1)) { qWarning() << sqlStatement << ": fieldIndex too large (" << fieldIndex << "), expected 0.." << result->fieldsCount() - 1; return false; } for (int i = 0; numRecords == -1 || i < numRecords; i++) { QSharedPointer record = result->fetchRecord(); if (!record) { if (numRecords != -1 || result->lastResult().isError()) { return false; } return true; } stringList->append(record->stringValue(fieldIndex)); } return true; } bool KexiSqlMigrate::drv_copyTable(const QString& srcTable, KDbConnection *destConn, KDbTableSchema* dstTable, const RecordFilter *recordFilter) { QSharedPointer result = sourceConnection()->prepareSql( KDbEscapedString("SELECT * FROM %1").arg(sourceConnection()->escapeIdentifier(srcTable))); if (!result) { return false; } const KDbQueryColumnInfo::Vector fieldsExpanded(dstTable->query()->fieldsExpanded(destConn)); const int numFields = qMin(fieldsExpanded.count(), result->fieldsCount()); Q_FOREVER { QSharedPointer record = result->fetchRecord(); if (!record) { if (!result->lastResult().isError()) { break; } return false; } if (recordFilter) { if (!(*recordFilter)(record)) { continue; } } QList vals; for(int i = 0; i < numFields; ++i) { const KDbSqlString s(record->cstringValue(i)); vals.append(KDb::cstringToVariant( s.string, fieldsExpanded.at(i)->field()->type(), 0, s.length)); } updateProgress(); if (recordFilter) { if (!(*recordFilter)(vals)) { continue; } } if (!destConn->insertRecord(dstTable, vals)) { return false; } } /*! @todo Check that wasn't an error, rather than end of result set */ return true; } bool KexiSqlMigrate::drv_getTableSize(const QString& table, quint64 *size) { Q_ASSERT(size); QSharedPointer result = sourceConnection()->prepareSql(KDbEscapedString("SELECT COUNT(*) FROM %1") .arg(sourceConnection()->escapeIdentifier(table))); if (!result) { return false; } QSharedPointer record = result->fetchRecord(); if (!result || result->fieldsCount() == 0) { return false; } bool ok; quint64 value = record->toByteArray(0).toULongLong(&ok); if (!ok) { value = -1; } *size = value; return ok; } QSharedPointer KexiSqlMigrate::drv_readFromTable(const QString& tableName) { QSharedPointer result = sourceConnection()->prepareSql( KDbEscapedString("SELECT * FROM %1").arg(sourceConnection()->escapeIdentifier(tableName))); if (!result || result->lastResult().isError()) { m_result = sourceConnection()->result(); qWarning() << m_result; result.clear(); } return result; } diff --git a/src/migration/KexiSqlMigrate.h b/src/migration/KexiSqlMigrate.h index f15b23834..1cda1c23e 100644 --- a/src/migration/KexiSqlMigrate.h +++ b/src/migration/KexiSqlMigrate.h @@ -1,84 +1,81 @@ /* This file is part of the KDE project Copyright (C) 2004 Martin Ellis Copyright (C) 2006-2016 Jarosław Staniek 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 KEXISQLMIGRATE_H #define KEXISQLMIGRATE_H #include //! @short A specialized class for a migrate plugin that imports native SQL databases into Kexi projects //! Compared to more generic KexiMigrate, KexiSqlMigrate implements needed methods using //! low level APIs of KDb. This is used for example by MySQL and PostgreSQL migration plugins. class KEXIMIGRATE_EXPORT KexiSqlMigrate : public KexiMigration::KexiMigrate { Q_OBJECT public: //! Constructs a migrate plugin that uses low level APIs of KDb driver @a kdbDriverId. //! @a kdbDriverId must not be empty. explicit KexiSqlMigrate(const QString &kdbDriverId, QObject *parent, const QVariantList& args = QVariantList()); virtual ~KexiSqlMigrate(); protected: //! Driver specific function to return table names bool drv_tableNames(QStringList *tablenames) Q_DECL_OVERRIDE; //! Driver specific implementation to read a table schema bool drv_readTableSchema( const QString& originalName, KDbTableSchema *tableSchema) Q_DECL_OVERRIDE; //! Driver specific connection creation KDbConnection* drv_createConnection() Q_DECL_OVERRIDE; /*! 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. @see KexiMigrate::drv_queryStringListFromSql() */ tristate drv_queryStringListFromSql( const KDbEscapedString& sqlStatement, int columnNumber, QStringList *stringList, int numRecords = -1) Q_DECL_OVERRIDE; //! Copy a table from source DB to target DB (driver specific) bool drv_copyTable(const QString& srcTable, KDbConnection *destConn, KDbTableSchema* dstTable, const RecordFilter *recordFilter = nullptr) Q_DECL_OVERRIDE; bool drv_progressSupported() Q_DECL_OVERRIDE { return true; } bool drv_getTableSize(const QString& table, quint64* size) Q_DECL_OVERRIDE; //! @todo move this somewhere to low level class (MIGRATION?) virtual bool drv_getTablesList( QStringList &list ); //! @todo move this somewhere to low level class (MIGRATION?) virtual bool drv_containsTable( const QString &tableName ); //Extended API //! Starts reading data from the source dataset's table QSharedPointer drv_readFromTable(const QString & tableName) Q_DECL_OVERRIDE; const QString m_kdbDriverId; - - //! Used by drv_tableNames, should be filled in constructor of a subclass - KDbEscapedString m_tableNamesSql; }; #endif diff --git a/src/migration/mysql/mysqlmigrate.cpp b/src/migration/mysql/mysqlmigrate.cpp index 411c87f69..7edadd983 100644 --- a/src/migration/mysql/mysqlmigrate.cpp +++ b/src/migration/mysql/mysqlmigrate.cpp @@ -1,39 +1,38 @@ /* This file is part of the KDE project Copyright (C) 2004 Martin Ellis Copyright (C) 2006-2016 Jarosław Staniek 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 "mysqlmigrate.h" #include #include /* This is the implementation for the MySQL specific import routines. */ KEXI_PLUGIN_FACTORY(MysqlMigrate, "keximigrate_mysql.json") MysqlMigrate::MysqlMigrate(QObject *parent, const QVariantList& args) : KexiSqlMigrate(QLatin1String("org.kde.kdb.mysql"), parent, args) { - m_tableNamesSql = KDbEscapedString("SHOW TABLES"); } MysqlMigrate::~MysqlMigrate() { } #include "mysqlmigrate.moc" diff --git a/src/migration/postgresql/PostgresqlMigrate.cpp b/src/migration/postgresql/PostgresqlMigrate.cpp index be2a12f29..6e24e6e87 100644 --- a/src/migration/postgresql/PostgresqlMigrate.cpp +++ b/src/migration/postgresql/PostgresqlMigrate.cpp @@ -1,39 +1,38 @@ /* This file is part of the KDE project Copyright (C) 2004 Adam Pigg Copyright (C) 2006-2016 Jarosław Staniek 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 "PostgresqlMigrate.h" #include #include /* This is the implementation for the specific import routines. */ KEXI_PLUGIN_FACTORY(PostgresqlMigrate, "keximigrate_postgresql.json") PostgresqlMigrate::PostgresqlMigrate(QObject *parent, const QVariantList& args) : KexiSqlMigrate(QLatin1String("org.kde.kdb.postgresql"), parent, args) { - m_tableNamesSql = KDbEscapedString("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')"); } PostgresqlMigrate::~PostgresqlMigrate() { } #include "PostgresqlMigrate.moc"