diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/migration/mysql/mysqlmigrate.cpp | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/migration/mysql/mysqlmigrate.cpp')
-rw-r--r-- | kexi/migration/mysql/mysqlmigrate.cpp | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/kexi/migration/mysql/mysqlmigrate.cpp b/kexi/migration/mysql/mysqlmigrate.cpp new file mode 100644 index 00000000..a2c62dd5 --- /dev/null +++ b/kexi/migration/mysql/mysqlmigrate.cpp @@ -0,0 +1,522 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Martin Ellis <m.a.ellis@ncl.ac.uk> + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + 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 <qstring.h> +#include <qregexp.h> +#include <qfile.h> +#include <qvariant.h> +#include <qvaluelist.h> +#include <kdebug.h> + +#include <mysql_version.h> +#include <mysql.h> + +#include "migration/keximigratedata.h" +#include <kexidb/cursor.h> +#include <kexidb/field.h> +#include <kexidb/utils.h> +#include <kexidb/drivers/mySQL/mysqlconnection_p.cpp> +#include <kexidb/drivermanager.h> +#include <kexiutils/identifier.h> + +using namespace KexiMigration; + +/* This is the implementation for the MySQL specific import routines. */ + +KEXIMIGRATE_DRIVER_INFO( MySQLMigrate, mysql ) + +/* ************************************************************************** */ +//! Constructor +/*MySQLMigrate::MySQLMigrate() : + d(new MySqlConnectionInternal()) +{ +}*/ + +//! Constructor (needed for trading interface) +MySQLMigrate::MySQLMigrate(QObject *parent, const char *name, + const QStringList &args) : + KexiMigrate(parent, name, args) + ,d(new MySqlConnectionInternal(0)) + ,m_mysqlres(0) +{ + KexiDB::DriverManager manager; + m_kexiDBDriver = manager.driver("mysql"); +} + +/* ************************************************************************** */ +//! Destructor +MySQLMigrate::~MySQLMigrate() { + if (m_mysqlres) + mysql_free_result(m_mysqlres); + m_mysqlres = 0; +} + + +/* ************************************************************************** */ +/*! Connect to the db backend */ +bool MySQLMigrate::drv_connect() { + if(d->db_connect(*m_migrateData->source)) { + return d->useDatabase(m_migrateData->sourceName); + } else { + return false; + } +} + + +/*! Disconnect from the db backend */ +bool MySQLMigrate::drv_disconnect() +{ + return d->db_disconnect(); +} + + +/* ************************************************************************** */ +/*! Get the types and properties for each column. */ +bool MySQLMigrate::drv_readTableSchema( + const QString& originalName, KexiDB::TableSchema& tableSchema) +{ +// m_table = new KexiDB::TableSchema(table); + +// //TODO IDEA: ask for user input for captions +// tableSchema.setCaption(table + " table"); + + //Perform a query on the table to get some data + QString query = QString("SELECT * FROM `") + drv_escapeIdentifier(originalName) + "` LIMIT 0"; + if(d->executeSQL(query)) { + MYSQL_RES *res = mysql_store_result(d->mysql); + if (res != NULL) { + + unsigned int numFlds = mysql_num_fields(res); + MYSQL_FIELD *fields = mysql_fetch_fields(res); + + for(unsigned int i = 0; i < numFlds; i++) { + QString fldName(fields[i].name); + QString fldID( KexiUtils::string2Identifier(fldName) ); + + KexiDB::Field *fld = + new KexiDB::Field(fldID, type(originalName, &fields[i])); + + if(fld->type() == KexiDB::Field::Enum) { + QStringList values = examineEnumField(originalName, &fields[i]); + } + + fld->setCaption(fldName); + getConstraints(fields[i].flags, fld); + getOptions(fields[i].flags, fld); + tableSchema.addField(fld); + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::drv_tableNames: null result" << endl; + } + return true; + } else { + return false; + } +} + + +/*! Get a list of tables and put into the supplied string list */ +bool MySQLMigrate::drv_tableNames(QStringList& tableNames) +{ + if(d->executeSQL("SHOW TABLES")) { + MYSQL_RES *res = mysql_store_result(d->mysql); + if (res != NULL) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(res)) != NULL) { + tableNames << QString::fromUtf8(row[0]); //utf8.. ok? + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::drv_tableNames: null result" << endl; + } + return true; + } else { + return false; + } +} + +/*! Fetches single string at column \a columnNumber for each record from result obtained + by running \a sqlStatement. + On success the result is stored in \a stringList and true is returned. + \return cancelled if there are no records available. */ +tristate MySQLMigrate::drv_queryStringListFromSQL( + const QString& sqlStatement, uint columnNumber, QStringList& stringList, int numRecords) +{ + stringList.clear(); + if (d->executeSQL(sqlStatement)) { + MYSQL_RES *res = mysql_use_result(d->mysql); + if (res != NULL) { + for (int i=0; numRecords == -1 || i < numRecords; i++) { + MYSQL_ROW row = mysql_fetch_row(res); + if (!row) { + tristate r; + if (mysql_errno(d->mysql)) + r = false; + else + r = (numRecords == -1) ? true : cancelled; + mysql_free_result(res); + return r; + } + uint numFields = mysql_num_fields(res); + if (columnNumber > (numFields-1)) { + kdWarning() << "MySQLMigrate::drv_querySingleStringFromSQL("<<sqlStatement + << "): columnNumber too large (" + << columnNumber << "), expected 0.." << numFields << endl; + mysql_free_result(res); + return false; + } + unsigned long *lengths = mysql_fetch_lengths(res); + if (!lengths) { + mysql_free_result(res); + return false; + } + stringList.append( QString::fromUtf8(row[columnNumber], lengths[columnNumber]) ); //ok? utf8? + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::drv_querySingleStringFromSQL(): null result" << endl; + } + return true; + } else { + return false; + } +} + +/*! Fetches single record from result obtained + by running \a sqlStatement. */ +tristate MySQLMigrate::drv_fetchRecordFromSQL(const QString& sqlStatement, + KexiDB::RowData& data, bool &firstRecord) +{ + if (firstRecord || !m_mysqlres) { + if (m_mysqlres) { + mysql_free_result(m_mysqlres); + m_mysqlres = 0; + } + if (!d->executeSQL(sqlStatement) || !(m_mysqlres = mysql_use_result(d->mysql))) + return false; + firstRecord = false; + } + + MYSQL_ROW row = mysql_fetch_row(m_mysqlres); + if (!row) { + tristate r = cancelled; + if (mysql_errno(d->mysql)) + r = false; + mysql_free_result(m_mysqlres); + m_mysqlres = 0; + return r; + } + const int numFields = mysql_num_fields(m_mysqlres); + unsigned long *lengths = mysql_fetch_lengths(m_mysqlres); + if (!lengths) { + mysql_free_result(m_mysqlres); + m_mysqlres = 0; + return false; + } + data.resize(numFields); + for (int i=0; i < numFields; i++) + data[i] = QString::fromUtf8(row[i], lengths[i] ); //ok? utf8? + return true; +} + +/*! Copy MySQL table to KexiDB database */ +bool MySQLMigrate::drv_copyTable(const QString& srcTable, KexiDB::Connection *destConn, + KexiDB::TableSchema* dstTable) +{ + if(d->executeSQL("SELECT * FROM `" + drv_escapeIdentifier(srcTable)) + "`") { + MYSQL_RES *res = mysql_use_result(d->mysql); + if (res != NULL) { + MYSQL_ROW row; + const KexiDB::QueryColumnInfo::Vector fieldsExpanded( dstTable->query()->fieldsExpanded() ); + while ((row = mysql_fetch_row(res)) != NULL) { + const int numFields = QMIN((int)fieldsExpanded.count(), (int)mysql_num_fields(res)); + QValueList<QVariant> vals; + unsigned long *lengths = mysql_fetch_lengths(res); + if (!lengths) { + mysql_free_result(res); + return false; + } + for(int i = 0; i < numFields; i++) + vals.append( KexiDB::cstringToVariant(row[i], fieldsExpanded.at(i)->field, (int)lengths[i]) ); + if (!destConn->insertRecord(*dstTable, vals)) { + mysql_free_result(res); + return false; + } + updateProgress(); + } + if (!row && mysql_errno(d->mysql)) { + mysql_free_result(res); + return false; + } + /*! @todo Check that wasn't an error, rather than end of result set */ + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::drv_copyTable: null result" << endl; + } + return true; + } else { + return false; + } +} + + +bool MySQLMigrate::drv_getTableSize(const QString& table, Q_ULLONG& size) { + if(d->executeSQL("SELECT COUNT(*) FROM `" + drv_escapeIdentifier(table)) + "`") { + MYSQL_RES *res = mysql_store_result(d->mysql); + if (res != NULL) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(res)) != NULL) { + //! @todo check result valid + size = QString(row[0]).toULongLong(); + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::drv_getTableSize: null result" << endl; + } + return true; + } else { + return false; + } +} + +//! Convert a MySQL type to a KexiDB type, prompting user if necessary. +KexiDB::Field::Type MySQLMigrate::type(const QString& table, + const MYSQL_FIELD *fld) +{ + // Field type + KexiDB::Field::Type kexiType = KexiDB::Field::InvalidType; + + switch(fld->type) + { + // These are in the same order as mysql_com.h. + // MySQL names given on the right + case FIELD_TYPE_DECIMAL: // DECIMAL or NUMERIC + break; + case FIELD_TYPE_TINY: // TINYINT (-2^7..2^7-1 or 2^8) + kexiType = KexiDB::Field::Byte; + break; + case FIELD_TYPE_SHORT: // SMALLINT (-2^15..2^15-1 or 2^16) + kexiType = KexiDB::Field::ShortInteger; + break; + case FIELD_TYPE_LONG: // INTEGER (-2^31..2^31-1 or 2^32) + kexiType = KexiDB::Field::Integer; + break; + case FIELD_TYPE_FLOAT: // FLOAT + kexiType = KexiDB::Field::Float; + break; + case FIELD_TYPE_DOUBLE: // DOUBLE or REAL (8 byte) + kexiType = KexiDB::Field::Double; + break; + case FIELD_TYPE_NULL: // WTF? + break; + case FIELD_TYPE_TIMESTAMP: // TIMESTAMP (promote?) + kexiType = KexiDB::Field::DateTime; + break; + case FIELD_TYPE_LONGLONG: // BIGINT (-2^63..2^63-1 or 2^64) + case FIELD_TYPE_INT24: // MEDIUMINT (-2^23..2^23-1 or 2^24) (promote) + kexiType = KexiDB::Field::BigInteger; + break; + case FIELD_TYPE_DATE: // DATE + kexiType = KexiDB::Field::Date; + break; + case FIELD_TYPE_TIME: // TIME + kexiType = KexiDB::Field::Time; + break; + case FIELD_TYPE_DATETIME: // DATETIME + kexiType = KexiDB::Field::DateTime; + break; + case FIELD_TYPE_YEAR: // YEAR (promote) + kexiType = KexiDB::Field::ShortInteger; + break; + case FIELD_TYPE_NEWDATE: // WTF? + case FIELD_TYPE_ENUM: // ENUM + // If MySQL did what it's documentation said it did, we would come here + // for enum fields ... + kexiType = KexiDB::Field::Enum; + break; + case FIELD_TYPE_SET: // SET + //! @todo: Support set column type + break; + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_BLOB: // BLOB or TEXT + case FIELD_TYPE_VAR_STRING: // VARCHAR + case FIELD_TYPE_STRING: // CHAR + + if (fld->flags & ENUM_FLAG) { + // ... instead we come here, using the ENUM_FLAG which is supposed to + // be deprecated! Duh. + kexiType = KexiDB::Field::Enum; + break; + } + kexiType = examineBlobField(table, fld); + break; + default: + kexiType = KexiDB::Field::InvalidType; + } + + if (kexiType == KexiDB::Field::InvalidType) { + return userType(table); + } + return kexiType; +} + + +//! Distinguish between a BLOB and a TEXT field +/*! MySQL uses the same field type to identify BLOB and TEXT fields. + This method queries the server to find out if a field is a binary + field or a text field. It also considers the length of CHAR and VARCHAR + fields to see whether Text or LongText is the appropriate Kexi field type. + Assumes fld is a CHAR, VARCHAR, one of the BLOBs or TEXTs. + \return KexiDB::Field::Text, KexiDB::Field::LongText or KexiDB::Field::BLOB +*/ +KexiDB::Field::Type MySQLMigrate::examineBlobField(const QString& table, + const MYSQL_FIELD* fld) { + QString mysqlType; + KexiDB::Field::Type kexiType; + QString query = "SHOW COLUMNS FROM `" + drv_escapeIdentifier(table) + + "` LIKE '" + QString::fromLatin1(fld->name) + "'"; + + if(d->executeSQL(query)) { + MYSQL_RES *res = mysql_store_result(d->mysql); + + if (res != NULL) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(res)) != NULL) { + mysqlType = QString(row[1]); + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::examineBlobField: null result" << endl; + } + } else { + // Huh? MySQL wont tell us what kind of field it is! Lets guess. + return KexiDB::Field::LongText; + } + + kdDebug() << "MySQLMigrate::examineBlobField: considering " + << mysqlType << endl; + if(mysqlType.contains("blob", false) != 0) { + // Doesn't matter how big it is, it's binary + kexiType = KexiDB::Field::BLOB; + } else if(mysqlType.contains("text", false) != 0) { + // All the TEXT types are too big for Kexi text. + kexiType = KexiDB::Field::BLOB; + } else if(fld->length < 200) { + kexiType = KexiDB::Field::Text; + } else { + kexiType = KexiDB::Field::LongText; + } + return kexiType; +} + + +//! Get the strings that identify values in an enum field +/*! Parse the type of a MySQL enum field as returned by the server in a + 'DESCRIBE table' or 'SHOW COLUMNS FROM table' statement. The string + returned by the server is in the form 'enum('option1','option2'). + In this example, the result should be a string list containing two + strings, "option1", "option2". + \return list of possible values the field can take + */ +QStringList MySQLMigrate::examineEnumField(const QString& table, + const MYSQL_FIELD* fld) { + QString vals; + QString query = "SHOW COLUMNS FROM `" + drv_escapeIdentifier(table) + + "` LIKE '" + QString::fromLatin1(fld->name) + "'"; + + if(d->executeSQL(query)) { + MYSQL_RES *res = mysql_store_result(d->mysql); + + if (res != NULL) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(res)) != NULL) { + vals = QString(row[1]); + } + mysql_free_result(res); + } else { + kdDebug() << "MySQLMigrate::examineEnumField: null result" << endl; + } + } else { + // Huh? MySQL wont tell us what values it can take. + return QStringList(); + } + + kdDebug() << "MySQLMigrate::examineEnumField: considering " + << vals << endl; + + // Crash and burn if we get confused... + if(!vals.startsWith("enum(")) { + // Huh? We're supposed to be parsing an enum! + kdDebug() << "MySQLMigrate::examineEnumField:1 not an enum!" << endl; + return QStringList(); + } + if(!vals.endsWith(")")) { + kdDebug() << "MySQLMigrate::examineEnumField:2 not an enum!" << endl; + return QStringList(); + } + + // It'd be nice to use QString.section or QStringList.split, but we need + // to be careful as enum values can have commas and quote marks in them + // e.g. CREATE TABLE t(f enum('option,''') gives one option: "option,'" + vals = vals.remove(0,5); + QRegExp rx = QRegExp("^'((?:[^,']|,|'')*)'"); + QStringList values = QStringList(); + int index = 0; + + while ((index = rx.search(vals, index, QRegExp::CaretAtOffset)) != -1) { + int len = rx.matchedLength(); + if (len != -1) { + kdDebug() << "MySQLMigrate::examineEnumField:3 " << rx.cap(1) << endl; + values << rx.cap(1); + } else { + kdDebug() << "MySQLMigrate::examineEnumField:4 lost" << endl; + } + + QChar next = vals[index + len]; + if (next != QChar(',') && next != QChar(')')) { + kdDebug() << "MySQLMigrate::examineEnumField:5 " << (char)next << endl; + } + index += len + 1; + } + + return values; +} + + +void MySQLMigrate::getConstraints(int flags, KexiDB::Field* fld) { + fld->setPrimaryKey(flags & PRI_KEY_FLAG); + fld->setAutoIncrement(flags & AUTO_INCREMENT_FLAG); + fld->setNotNull(flags & NOT_NULL_FLAG); + fld->setUniqueKey(flags & UNIQUE_KEY_FLAG); + //! @todo: Keys and uniqueness +} + + +void MySQLMigrate::getOptions(int flags, KexiDB::Field* fld) { + fld->setUnsigned(flags & UNSIGNED_FLAG); +} + + +#include "mysqlmigrate.moc" |