src/sql/drivers/mysql/qsql_mysql.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sql/drivers/mysql/qsql_mysql.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1518 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSql module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsql_mysql.h"
+
+#include <qcoreapplication.h>
+#include <qvariant.h>
+#include <qdatetime.h>
+#include <qsqlerror.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+#include <qsqlquery.h>
+#include <qsqlrecord.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <qvector.h>
+
+#include <qdebug.h>
+
+#ifdef Q_OS_WIN32
+// comment the next line out if you want to use MySQL/embedded on Win32 systems.
+// note that it will crash if you don't statically link to the mysql/e library!
+# define Q_NO_MYSQL_EMBEDDED
+#endif
+
+Q_DECLARE_METATYPE(MYSQL_RES*)
+Q_DECLARE_METATYPE(MYSQL*)
+
+#if MYSQL_VERSION_ID >= 40108
+Q_DECLARE_METATYPE(MYSQL_STMT*)
+#endif
+
+#if MYSQL_VERSION_ID >= 40100
+#  define Q_CLIENT_MULTI_STATEMENTS CLIENT_MULTI_STATEMENTS
+#else
+#  define Q_CLIENT_MULTI_STATEMENTS 0
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMYSQLDriverPrivate
+{
+public:
+    QMYSQLDriverPrivate() : mysql(0),
+#ifndef QT_NO_TEXTCODEC
+        tc(QTextCodec::codecForLocale()),
+#else
+        tc(0),
+#endif
+        preparedQuerysEnabled(false) {}
+    MYSQL *mysql;
+    QTextCodec *tc;
+
+    bool preparedQuerysEnabled;
+};
+
+static inline QString toUnicode(QTextCodec *tc, const char *str)
+{
+#ifdef QT_NO_TEXTCODEC
+    Q_UNUSED(tc);
+    return QString::fromLatin1(str);
+#else
+    return tc->toUnicode(str);
+#endif
+}
+
+static inline QString toUnicode(QTextCodec *tc, const char *str, int length)
+{
+#ifdef QT_NO_TEXTCODEC
+    Q_UNUSED(tc);
+    return QString::fromLatin1(str, length);
+#else
+    return tc->toUnicode(str, length);
+#endif
+}
+
+static inline QByteArray fromUnicode(QTextCodec *tc, const QString &str)
+{
+#ifdef QT_NO_TEXTCODEC
+    Q_UNUSED(tc);
+    return str.toLatin1();
+#else
+    return tc->fromUnicode(str);
+#endif
+}
+
+static inline QVariant qDateFromString(const QString &val)
+{
+#ifdef QT_NO_DATESTRING
+    Q_UNUSED(val);
+    return QVariant(val);
+#else
+    if (val.isEmpty())
+        return QVariant(QDate());
+    return QVariant(QDate::fromString(val, Qt::ISODate));
+#endif
+}
+
+static inline QVariant qTimeFromString(const QString &val)
+{
+#ifdef QT_NO_DATESTRING
+    Q_UNUSED(val);
+    return QVariant(val);
+#else
+    if (val.isEmpty())
+        return QVariant(QTime());
+    return QVariant(QTime::fromString(val, Qt::ISODate));
+#endif
+}
+
+static inline QVariant qDateTimeFromString(QString &val)
+{
+#ifdef QT_NO_DATESTRING
+    Q_UNUSED(val);
+    return QVariant(val);
+#else
+    if (val.isEmpty())
+        return QVariant(QDateTime());
+    if (val.length() == 14)
+        // TIMESTAMPS have the format yyyyMMddhhmmss
+        val.insert(4, QLatin1Char('-')).insert(7, QLatin1Char('-')).insert(10,
+                    QLatin1Char('T')).insert(13, QLatin1Char(':')).insert(16, QLatin1Char(':'));
+    return QVariant(QDateTime::fromString(val, Qt::ISODate));
+#endif
+}
+
+class QMYSQLResultPrivate : public QObject
+{
+    Q_OBJECT
+public:
+    QMYSQLResultPrivate(const QMYSQLDriver* dp, const QMYSQLResult* d) : driver(dp), result(0), q(d), 
+        rowsAffected(0), hasBlobs(false)
+#if MYSQL_VERSION_ID >= 40108
+        , stmt(0), meta(0), inBinds(0), outBinds(0)
+#endif
+        , preparedQuery(false)
+        {
+            connect(dp, SIGNAL(destroyed()), this, SLOT(driverDestroyed()));
+        }
+
+    const QMYSQLDriver* driver;
+    MYSQL_RES *result;
+    MYSQL_ROW row;
+    const QMYSQLResult* q;
+
+    int rowsAffected;
+
+    bool bindInValues();
+    void bindBlobs();
+
+    bool hasBlobs;
+    struct QMyField
+    {
+        QMyField()
+            : outField(0), nullIndicator(false), bufLength(0ul),
+              myField(0), type(QVariant::Invalid)
+        {}
+        char *outField;
+        my_bool nullIndicator;
+        ulong bufLength;
+        MYSQL_FIELD *myField;
+        QVariant::Type type;
+    };
+
+    QVector<QMyField> fields;
+
+#if MYSQL_VERSION_ID >= 40108
+    MYSQL_STMT* stmt;
+    MYSQL_RES* meta;
+
+    MYSQL_BIND *inBinds;
+    MYSQL_BIND *outBinds;
+#endif
+
+    bool preparedQuery;
+
+private Q_SLOTS:
+    void driverDestroyed() { driver = NULL; }
+};
+
+#ifndef QT_NO_TEXTCODEC
+static QTextCodec* codec(MYSQL* mysql)
+{
+#if MYSQL_VERSION_ID >= 32321
+    QTextCodec* heuristicCodec = QTextCodec::codecForName(mysql_character_set_name(mysql));
+    if (heuristicCodec)
+        return heuristicCodec;
+#endif
+    return QTextCodec::codecForLocale();
+}
+#endif // QT_NO_TEXTCODEC
+
+static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
+                            const QMYSQLDriverPrivate* p)
+{
+    const char *cerr = p->mysql ? mysql_error(p->mysql) : 0;
+    return QSqlError(QLatin1String("QMYSQL: ") + err,
+                     p->tc ? toUnicode(p->tc, cerr) : QString::fromLatin1(cerr),
+                     type, mysql_errno(p->mysql));
+}
+
+
+static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
+{
+    QVariant::Type type;
+    switch (mysqltype) {
+    case FIELD_TYPE_TINY :
+    case FIELD_TYPE_SHORT :
+    case FIELD_TYPE_LONG :
+    case FIELD_TYPE_INT24 :
+        type = (flags & UNSIGNED_FLAG) ? QVariant::UInt : QVariant::Int;
+        break;
+    case FIELD_TYPE_YEAR :
+        type = QVariant::Int;
+        break;
+    case FIELD_TYPE_LONGLONG :
+        type = (flags & UNSIGNED_FLAG) ? QVariant::ULongLong : QVariant::LongLong;
+        break;
+    case FIELD_TYPE_FLOAT :
+    case FIELD_TYPE_DOUBLE :
+    case FIELD_TYPE_DECIMAL :
+#if defined(FIELD_TYPE_NEWDECIMAL)
+    case FIELD_TYPE_NEWDECIMAL:
+#endif
+        type = QVariant::Double;
+        break;
+    case FIELD_TYPE_DATE :
+        type = QVariant::Date;
+        break;
+    case FIELD_TYPE_TIME :
+        type = QVariant::Time;
+        break;
+    case FIELD_TYPE_DATETIME :
+    case FIELD_TYPE_TIMESTAMP :
+        type = QVariant::DateTime;
+        break;
+    case FIELD_TYPE_STRING :
+    case FIELD_TYPE_VAR_STRING :
+    case FIELD_TYPE_BLOB :
+    case FIELD_TYPE_TINY_BLOB :
+    case FIELD_TYPE_MEDIUM_BLOB :
+    case FIELD_TYPE_LONG_BLOB :
+        type = (flags & BINARY_FLAG) ? QVariant::ByteArray : QVariant::String;
+        break;
+    default:
+    case FIELD_TYPE_ENUM :
+    case FIELD_TYPE_SET :
+        type = QVariant::String;
+        break;
+    }
+    return type;
+}
+
+static QSqlField qToField(MYSQL_FIELD *field, QTextCodec *tc)
+{
+    QSqlField f(toUnicode(tc, field->name),
+                qDecodeMYSQLType(int(field->type), field->flags));
+    f.setRequired(IS_NOT_NULL(field->flags));
+    f.setLength(field->length);
+    f.setPrecision(field->decimals);
+    f.setSqlType(field->type);
+    f.setAutoValue(field->flags & AUTO_INCREMENT_FLAG);
+    return f;
+}
+
+#if MYSQL_VERSION_ID >= 40108
+
+static QSqlError qMakeStmtError(const QString& err, QSqlError::ErrorType type,
+                            MYSQL_STMT* stmt)
+{
+    const char *cerr = mysql_stmt_error(stmt);
+    return QSqlError(QLatin1String("QMYSQL3: ") + err,
+                     QString::fromLatin1(cerr),
+                     type, mysql_stmt_errno(stmt));
+}
+
+static bool qIsBlob(int t)
+{
+    return t == MYSQL_TYPE_TINY_BLOB
+           || t == MYSQL_TYPE_BLOB
+           || t == MYSQL_TYPE_MEDIUM_BLOB
+           || t == MYSQL_TYPE_LONG_BLOB;
+}
+
+void QMYSQLResultPrivate::bindBlobs()
+{
+    int i;
+    MYSQL_FIELD *fieldInfo;
+    MYSQL_BIND *bind;
+
+    for(i = 0; i < fields.count(); ++i) {
+        fieldInfo = fields.at(i).myField;
+        if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
+            bind = &inBinds[i];
+            bind->buffer_length = fieldInfo->max_length;
+            delete[] static_cast<char*>(bind->buffer);
+            bind->buffer = new char[fieldInfo->max_length];
+            fields[i].outField = static_cast<char*>(bind->buffer);
+        }
+    }
+}
+
+bool QMYSQLResultPrivate::bindInValues()
+{
+    MYSQL_BIND *bind;
+    char *field;
+    int i = 0;
+
+    if (!meta)
+        meta = mysql_stmt_result_metadata(stmt);
+    if (!meta)
+        return false;
+
+    fields.resize(mysql_num_fields(meta));
+
+    inBinds = new MYSQL_BIND[fields.size()];
+    memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND));
+
+    MYSQL_FIELD *fieldInfo;
+
+    while((fieldInfo = mysql_fetch_field(meta))) {
+        QMyField &f = fields[i];
+        f.myField = fieldInfo;
+
+        f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
+        if (qIsBlob(fieldInfo->type)) {
+            // the size of a blob-field is available as soon as we call
+            // mysql_stmt_store_result()
+            // after mysql_stmt_exec() in QMYSQLResult::exec()
+            fieldInfo->length = 0;
+            hasBlobs = true;
+        } else {
+            fieldInfo->type = MYSQL_TYPE_STRING;
+        }
+        bind = &inBinds[i];
+        field = new char[fieldInfo->length + 1];
+        memset(field, 0, fieldInfo->length + 1);
+
+        bind->buffer_type = fieldInfo->type;
+        bind->buffer = field;
+        bind->buffer_length = f.bufLength = fieldInfo->length + 1;
+        bind->is_null = &f.nullIndicator;
+        bind->length = &f.bufLength;
+        f.outField=field;
+
+        ++i;
+    }
+    return true;
+}
+#endif
+
+QMYSQLResult::QMYSQLResult(const QMYSQLDriver* db)
+: QSqlResult(db)
+{
+    d = new QMYSQLResultPrivate(db, this);
+}
+
+QMYSQLResult::~QMYSQLResult()
+{
+    cleanup();
+    delete d;
+}
+
+QVariant QMYSQLResult::handle() const
+{
+#if MYSQL_VERSION_ID >= 40108
+    if(d->preparedQuery)
+        return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt);
+    else
+#endif
+        return qVariantFromValue(d->result);
+}
+
+void QMYSQLResult::cleanup()
+{
+    if (d->result)
+        mysql_free_result(d->result);
+
+// must iterate trough leftover result sets from multi-selects or stored procedures
+// if this isn't done subsequent queries will fail with "Commands out of sync"
+#if MYSQL_VERSION_ID >= 40100
+    while (d->driver && d->driver->d->mysql && mysql_next_result(d->driver->d->mysql) == 0) {
+        MYSQL_RES *res = mysql_store_result(d->driver->d->mysql);
+        if (res)
+            mysql_free_result(res);
+    }
+#endif
+
+#if MYSQL_VERSION_ID >= 40108
+    if (d->stmt) {
+        if (mysql_stmt_close(d->stmt))
+            qWarning("QMYSQLResult::cleanup: unable to free statement handle");
+        d->stmt = 0;
+    }
+
+    if (d->meta) {
+        mysql_free_result(d->meta);
+        d->meta = 0;
+    }
+
+    int i;
+    for (i = 0; i < d->fields.count(); ++i)
+        delete[] d->fields[i].outField;
+
+    if (d->outBinds) {
+        delete[] d->outBinds;
+        d->outBinds = 0;
+    }
+
+    if (d->inBinds) {
+        delete[] d->inBinds;
+        d->inBinds = 0;
+    }
+#endif
+
+    d->hasBlobs = false;
+    d->fields.clear();
+    d->result = NULL;
+    d->row = NULL;
+    setAt(-1);
+    setActive(false);
+}
+
+bool QMYSQLResult::fetch(int i)
+{
+    if(!d->driver)
+        return false;
+    if (isForwardOnly()) { // fake a forward seek
+        if (at() < i) {
+            int x = i - at();
+            while (--x && fetchNext()) {};
+            return fetchNext();
+        } else {
+            return false;
+        }
+    }
+    if (at() == i)
+        return true;
+    if (d->preparedQuery) {
+#if MYSQL_VERSION_ID >= 40108
+        mysql_stmt_data_seek(d->stmt, i);
+
+        int nRC = mysql_stmt_fetch(d->stmt);
+        if (nRC) {
+#ifdef MYSQL_DATA_TRUNCATED
+            if (nRC == 1 || nRC == MYSQL_DATA_TRUNCATED)
+#else
+            if (nRC == 1)
+#endif
+                setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                         "Unable to fetch data"), QSqlError::StatementError, d->stmt));
+            return false;
+        }
+#else
+        return false;
+#endif
+    } else {
+        mysql_data_seek(d->result, i);
+        d->row = mysql_fetch_row(d->result);
+        if (!d->row)
+            return false;
+    }
+
+    setAt(i);
+    return true;
+}
+
+bool QMYSQLResult::fetchNext()
+{
+    if(!d->driver)
+        return false;
+    if (d->preparedQuery) {
+#if MYSQL_VERSION_ID >= 40108
+        if (mysql_stmt_fetch(d->stmt))
+            return false;
+#else
+        return false;
+#endif
+    } else {
+    d->row = mysql_fetch_row(d->result);
+    if (!d->row)
+        return false;
+    }
+    setAt(at() + 1);
+    return true;
+}
+
+bool QMYSQLResult::fetchLast()
+{
+    if(!d->driver)
+        return false;
+    if (isForwardOnly()) { // fake this since MySQL can't seek on forward only queries
+        bool success = fetchNext(); // did we move at all?
+        while (fetchNext()) {};
+        return success;
+    }
+
+    my_ulonglong numRows;
+    if (d->preparedQuery) {
+#if MYSQL_VERSION_ID >= 40108
+        numRows = mysql_stmt_num_rows(d->stmt);
+#else
+        numRows = 0;
+#endif
+    } else {
+        numRows = mysql_num_rows(d->result);
+    }
+    if (at() == int(numRows))
+        return true;
+    if (!numRows)
+        return false;
+    return fetch(numRows - 1);
+}
+
+bool QMYSQLResult::fetchFirst()
+{
+    if (at() == 0)
+        return true;
+
+    if (isForwardOnly())
+        return (at() == QSql::BeforeFirstRow) ? fetchNext() : false;
+    return fetch(0);
+}
+
+QVariant QMYSQLResult::data(int field)
+{
+
+    if (!isSelect() || field >= d->fields.count()) {
+        qWarning("QMYSQLResult::data: column %d out of range", field);
+        return QVariant();
+    }
+
+    if (!d->driver)
+        return QVariant();
+
+    int fieldLength = 0;
+    const QMYSQLResultPrivate::QMyField &f = d->fields.at(field);
+    QString val;
+    if (d->preparedQuery) {
+        if (f.nullIndicator)
+            return QVariant(f.type);
+
+        if (f.type != QVariant::ByteArray)
+            val = toUnicode(d->driver->d->tc, f.outField, f.bufLength);
+    } else {
+        if (d->row[field] == NULL) {
+            // NULL value
+            return QVariant(f.type);
+        }
+        fieldLength = mysql_fetch_lengths(d->result)[field];
+        if (f.type != QVariant::ByteArray)
+            val = toUnicode(d->driver->d->tc, d->row[field], fieldLength);
+    }
+
+    switch(f.type) {
+    case QVariant::LongLong:
+        return QVariant(val.toLongLong());
+    case QVariant::ULongLong:
+        return QVariant(val.toULongLong());
+    case QVariant::Int:
+        return QVariant(val.toInt());
+    case QVariant::UInt:
+        return QVariant(val.toUInt());
+    case QVariant::Double: {
+        QVariant v;
+        bool ok=false;
+        double dbl = val.toDouble(&ok);
+        switch(numericalPrecisionPolicy()) {
+            case QSql::LowPrecisionInt32:
+                v=QVariant(dbl).toInt();
+                break;
+            case QSql::LowPrecisionInt64:
+                v = QVariant(dbl).toLongLong();
+                break;
+            case QSql::LowPrecisionDouble:
+                v = QVariant(dbl);
+                break;
+            case QSql::HighPrecision:
+            default:
+                v = val;
+                ok = true;
+                break;
+        }
+        if(ok)
+            return v;
+        else
+            return QVariant();
+    }
+        return QVariant(val.toDouble());
+    case QVariant::Date:
+        return qDateFromString(val);
+    case QVariant::Time:
+        return qTimeFromString(val);
+    case QVariant::DateTime:
+        return qDateTimeFromString(val);
+    case QVariant::ByteArray: {
+
+        QByteArray ba;
+        if (d->preparedQuery) {
+            ba = QByteArray(f.outField, f.bufLength);
+        } else {
+            ba = QByteArray(d->row[field], fieldLength);
+        }
+        return QVariant(ba);
+    }
+    default:
+    case QVariant::String:
+        return QVariant(val);
+    }
+    qWarning("QMYSQLResult::data: unknown data type");
+    return QVariant();
+}
+
+bool QMYSQLResult::isNull(int field)
+{
+   if (d->preparedQuery)
+       return d->fields.at(field).nullIndicator;
+   else
+       return d->row[field] == NULL;
+}
+
+bool QMYSQLResult::reset (const QString& query)
+{
+    if (!driver() || !driver()->isOpen() || driver()->isOpenError() || !d->driver)
+        return false;
+
+    d->preparedQuery = false;
+
+    cleanup();
+
+    const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
+    if (mysql_real_query(d->driver->d->mysql, encQuery.data(), encQuery.length())) {
+        setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute query"),
+                     QSqlError::StatementError, d->driver->d));
+        return false;
+    }
+    d->result = mysql_store_result(d->driver->d->mysql);
+    if (!d->result && mysql_field_count(d->driver->d->mysql) > 0) {
+        setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store result"),
+                    QSqlError::StatementError, d->driver->d));
+        return false;
+    }
+    int numFields = mysql_field_count(d->driver->d->mysql);
+    setSelect(numFields != 0);
+    d->fields.resize(numFields);
+    d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
+
+    if (isSelect()) {
+        for(int i = 0; i < numFields; i++) {
+            MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
+            d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
+        }
+        setAt(QSql::BeforeFirstRow);
+    }
+    setActive(true);
+    return isActive();
+}
+
+int QMYSQLResult::size()
+{
+    if (d->driver && isSelect())
+        if (d->preparedQuery)
+#if MYSQL_VERSION_ID >= 40108
+            return mysql_stmt_num_rows(d->stmt);
+#else
+            return -1;
+#endif
+        else
+            return int(mysql_num_rows(d->result));
+    else
+        return -1;
+}
+
+int QMYSQLResult::numRowsAffected()
+{
+    return d->rowsAffected;
+}
+
+QVariant QMYSQLResult::lastInsertId() const
+{
+    if (!isActive() || !d->driver)
+        return QVariant();
+
+    if (d->preparedQuery) {
+#if MYSQL_VERSION_ID >= 40108
+        quint64 id = mysql_stmt_insert_id(d->stmt);
+        if (id)
+            return QVariant(id);
+#endif
+    } else {
+        quint64 id = mysql_insert_id(d->driver->d->mysql);
+        if (id)
+            return QVariant(id);
+    }
+    return QVariant();
+}
+
+QSqlRecord QMYSQLResult::record() const
+{
+    QSqlRecord info;
+    MYSQL_RES *res;
+    if (!isActive() || !isSelect() || !d->driver)
+        return info;
+
+#if MYSQL_VERSION_ID >= 40108
+    res = d->preparedQuery ? d->meta : d->result;
+#else
+    res = d->result;
+#endif
+
+    if (!mysql_errno(d->driver->d->mysql)) {
+        mysql_field_seek(res, 0);
+        MYSQL_FIELD* field = mysql_fetch_field(res);
+        while(field) {
+            info.append(qToField(field, d->driver->d->tc));
+            field = mysql_fetch_field(res);
+        }
+    }
+    mysql_field_seek(res, 0);
+    return info;
+}
+
+bool QMYSQLResult::nextResult()
+{
+    if(!d->driver)
+        return false;
+#if MYSQL_VERSION_ID >= 40100 
+    setAt(-1);
+    setActive(false);
+
+    if (d->result && isSelect())
+        mysql_free_result(d->result);
+    d->result = 0;
+    setSelect(false);
+   
+    for (int i = 0; i < d->fields.count(); ++i)
+        delete[] d->fields[i].outField;
+    d->fields.clear();
+
+    int status = mysql_next_result(d->driver->d->mysql);
+    if (status > 0) {
+        setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute next query"),
+                     QSqlError::StatementError, d->driver->d));
+        return false;
+    } else if (status == -1) {
+        return false;   // No more result sets
+    }
+
+    d->result = mysql_store_result(d->driver->d->mysql);
+    int numFields = mysql_field_count(d->driver->d->mysql);
+    if (!d->result && numFields > 0) {
+        setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store next result"),
+                     QSqlError::StatementError, d->driver->d));
+        return false;
+    }
+
+    setSelect(numFields > 0);
+    d->fields.resize(numFields);
+    d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
+
+    if (isSelect()) {
+        for (int i = 0; i < numFields; i++) {
+            MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
+            d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
+        }
+    }
+
+    setActive(true);
+    return true;
+#else
+    return false;
+#endif
+}
+
+void QMYSQLResult::virtual_hook(int id, void *data)
+{
+    switch (id) {
+    case QSqlResult::NextResult:
+        Q_ASSERT(data);
+        *static_cast<bool*>(data) = nextResult();
+        break;
+    default:
+        QSqlResult::virtual_hook(id, data);
+    }
+}
+
+
+#if MYSQL_VERSION_ID >= 40108
+
+static MYSQL_TIME *toMySqlDate(QDate date, QTime time, QVariant::Type type)
+{
+    Q_ASSERT(type == QVariant::Time || type == QVariant::Date
+             || type == QVariant::DateTime);
+
+    MYSQL_TIME *myTime = new MYSQL_TIME;
+    memset(myTime, 0, sizeof(MYSQL_TIME));
+
+    if (type == QVariant::Time || type == QVariant::DateTime) {
+        myTime->hour = time.hour();
+        myTime->minute = time.minute();
+        myTime->second = time.second();
+        myTime->second_part = time.msec();
+    }
+    if (type == QVariant::Date || type == QVariant::DateTime) {
+        myTime->year = date.year();
+        myTime->month = date.month();
+        myTime->day = date.day();
+    }
+
+    return myTime;
+}
+
+bool QMYSQLResult::prepare(const QString& query)
+{
+    if(!d->driver)
+        return false;
+#if MYSQL_VERSION_ID >= 40108
+    cleanup();
+    if (!d->driver->d->preparedQuerysEnabled)
+        return QSqlResult::prepare(query);
+
+    int r;
+
+    if (query.isEmpty())
+        return false;
+
+    if (!d->stmt)
+        d->stmt = mysql_stmt_init(d->driver->d->mysql);
+    if (!d->stmt) {
+        setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to prepare statement"),
+                     QSqlError::StatementError, d->driver->d));
+        return false;
+    }
+
+    const QByteArray encQuery(fromUnicode(d->driver->d->tc, query));
+    r = mysql_stmt_prepare(d->stmt, encQuery.constData(), encQuery.length());
+    if (r != 0) {
+        setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                     "Unable to prepare statement"), QSqlError::StatementError, d->stmt));
+        cleanup();
+        return false;
+    }
+
+    if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues
+        d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)];
+    }
+
+    setSelect(d->bindInValues());
+    d->preparedQuery = true;
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool QMYSQLResult::exec()
+{
+    if (!d->driver)
+        return false;
+    if (!d->preparedQuery)
+        return QSqlResult::exec();
+    if (!d->stmt)
+        return false;
+
+    int r = 0;
+    MYSQL_BIND* currBind;
+    QVector<MYSQL_TIME *> timeVector;
+    QVector<QByteArray> stringVector;
+    QVector<my_bool> nullVector;
+
+    const QVector<QVariant> values = boundValues();
+
+    r = mysql_stmt_reset(d->stmt);
+    if (r != 0) {
+        setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                     "Unable to reset statement"), QSqlError::StatementError, d->stmt));
+        return false;
+    }
+
+    if (mysql_stmt_param_count(d->stmt) > 0 &&
+        mysql_stmt_param_count(d->stmt) == (uint)values.count()) {
+
+        nullVector.resize(values.count());
+        for (int i = 0; i < values.count(); ++i) {
+            const QVariant &val = boundValues().at(i);
+            void *data = const_cast<void *>(val.constData());
+
+            currBind = &d->outBinds[i];
+
+            nullVector[i] = static_cast<my_bool>(val.isNull());
+            currBind->is_null = &nullVector[i];
+            currBind->length = 0;
+            currBind->is_unsigned = 0;
+
+            switch (val.type()) {
+                case QVariant::ByteArray:
+                    currBind->buffer_type = MYSQL_TYPE_BLOB;
+                    currBind->buffer = const_cast<char *>(val.toByteArray().constData());
+                    currBind->buffer_length = val.toByteArray().size();
+                    break;
+
+                case QVariant::Time:
+                case QVariant::Date:
+                case QVariant::DateTime: {
+                    MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.type());
+                    timeVector.append(myTime);
+
+                    currBind->buffer = myTime;
+                    switch(val.type()) {
+                    case QVariant::Time:
+                        currBind->buffer_type = MYSQL_TYPE_TIME;
+                        myTime->time_type = MYSQL_TIMESTAMP_TIME;
+                        break;
+                    case QVariant::Date:
+                        currBind->buffer_type = MYSQL_TYPE_DATE;
+                        myTime->time_type = MYSQL_TIMESTAMP_DATE;
+                        break;
+                    case QVariant::DateTime:
+                        currBind->buffer_type = MYSQL_TYPE_DATETIME;
+                        myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
+                        break;
+                    default:
+                        break;
+                    }
+                    currBind->buffer_length = sizeof(MYSQL_TIME);
+                    currBind->length = 0;
+                    break; }
+                case QVariant::UInt:
+                case QVariant::Int:
+                case QVariant::Bool:
+                    currBind->buffer_type = MYSQL_TYPE_LONG;
+                    currBind->buffer = data;
+                    currBind->buffer_length = sizeof(int);
+                    currBind->is_unsigned = (val.type() != QVariant::Int);
+                    break;
+                case QVariant::Double:
+                    currBind->buffer_type = MYSQL_TYPE_DOUBLE;
+                    currBind->buffer = data;
+                    currBind->buffer_length = sizeof(double);
+                    break;
+                case QVariant::LongLong:
+                case QVariant::ULongLong:
+                    currBind->buffer_type = MYSQL_TYPE_LONGLONG;
+                    currBind->buffer = data;
+                    currBind->buffer_length = sizeof(qint64);
+                    currBind->is_unsigned = (val.type() == QVariant::ULongLong);
+                    break;
+                case QVariant::String:
+                default: {
+                    QByteArray ba = fromUnicode(d->driver->d->tc, val.toString());
+                    stringVector.append(ba);
+                    currBind->buffer_type = MYSQL_TYPE_STRING;
+                    currBind->buffer = const_cast<char *>(ba.constData());
+                    currBind->buffer_length = ba.length();
+                    break; }
+            }
+        }
+
+        r = mysql_stmt_bind_param(d->stmt, d->outBinds);
+        if (r != 0) {
+            setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                         "Unable to bind value"), QSqlError::StatementError, d->stmt));
+            qDeleteAll(timeVector);
+            return false;
+        }
+    }
+    r = mysql_stmt_execute(d->stmt);
+
+    qDeleteAll(timeVector);
+
+    if (r != 0) {
+        setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                     "Unable to execute statement"), QSqlError::StatementError, d->stmt));
+        return false;
+    }
+    //if there is meta-data there is also data
+    setSelect(d->meta);
+
+    d->rowsAffected = mysql_stmt_affected_rows(d->stmt);
+
+    if (isSelect()) {
+        my_bool update_max_length = true;
+
+        r = mysql_stmt_bind_result(d->stmt, d->inBinds);
+        if (r != 0) {
+            setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                         "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
+            return false;
+        }
+        if (d->hasBlobs)
+            mysql_stmt_attr_set(d->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length);
+
+        r = mysql_stmt_store_result(d->stmt);
+        if (r != 0) {
+            setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                         "Unable to store statement results"), QSqlError::StatementError, d->stmt));
+            return false;
+        }
+
+        if (d->hasBlobs) {
+            // mysql_stmt_store_result() with STMT_ATTR_UPDATE_MAX_LENGTH set to true crashes
+            // when called without a preceding call to mysql_stmt_bind_result()
+            // in versions < 4.1.8
+            d->bindBlobs();
+            r = mysql_stmt_bind_result(d->stmt, d->inBinds);
+            if (r != 0) {
+                setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+                             "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
+                return false;
+            }
+        }
+        setAt(QSql::BeforeFirstRow);
+    }
+    setActive(true);
+    return true;
+}
+#endif
+/////////////////////////////////////////////////////////
+
+static int qMySqlConnectionCount = 0;
+static bool qMySqlInitHandledByUser = false;
+
+static void qLibraryInit()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+    if (qMySqlInitHandledByUser || qMySqlConnectionCount > 1)
+        return;
+
+# if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
+    if (mysql_library_init(0, 0, 0)) {
+# else
+    if (mysql_server_init(0, 0, 0)) {
+# endif
+        qWarning("QMYSQLDriver::qServerInit: unable to start server.");
+    }
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+static void qLibraryEnd()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID > 40000
+#  if (MYSQL_VERSION_ID >= 40110 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50003
+    mysql_library_end();
+#  else
+    mysql_server_end();
+#  endif
+# endif
+#endif
+}
+
+QMYSQLDriver::QMYSQLDriver(QObject * parent)
+    : QSqlDriver(parent)
+{
+    init();
+    qLibraryInit();
+}
+
+/*!
+    Create a driver instance with the open connection handle, \a con.
+    The instance's parent (owner) is \a parent.
+*/
+
+QMYSQLDriver::QMYSQLDriver(MYSQL * con, QObject * parent)
+    : QSqlDriver(parent)
+{
+    init();
+    if (con) {
+        d->mysql = (MYSQL *) con;
+#ifndef QT_NO_TEXTCODEC
+        d->tc = codec(con);
+#endif
+        setOpen(true);
+        setOpenError(false);
+        if (qMySqlConnectionCount == 1)
+            qMySqlInitHandledByUser = true;
+    } else {
+        qLibraryInit();
+    }
+}
+
+void QMYSQLDriver::init()
+{
+    d = new QMYSQLDriverPrivate();
+    d->mysql = 0;
+    qMySqlConnectionCount++;
+}
+
+QMYSQLDriver::~QMYSQLDriver()
+{
+    qMySqlConnectionCount--;
+    if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+        qLibraryEnd();
+    delete d;
+}
+
+bool QMYSQLDriver::hasFeature(DriverFeature f) const
+{
+    switch (f) {
+    case Transactions:
+// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
+#ifdef CLIENT_TRANSACTIONS
+        if (d->mysql) {
+            if ((d->mysql->server_capabilities & CLIENT_TRANSACTIONS) == CLIENT_TRANSACTIONS)
+                return true;
+        }
+#endif
+        return false;
+    case NamedPlaceholders:
+    case BatchOperations:
+    case SimpleLocking:
+    case EventNotifications:
+    case FinishQuery:
+        return false;
+    case QuerySize:
+    case BLOB:
+    case LastInsertId:
+    case Unicode:
+    case LowPrecisionNumbers:
+        return true;
+    case PreparedQueries:
+    case PositionalPlaceholders:
+#if MYSQL_VERSION_ID >= 40108
+        return d->preparedQuerysEnabled;
+#else
+        return false;
+#endif
+    case MultipleResultSets:
+#if MYSQL_VERSION_ID >= 40100
+        return true;
+#else
+        return false;
+#endif
+    }
+    return false;
+}
+
+static void setOptionFlag(uint &optionFlags, const QString &opt)
+{
+    if (opt == QLatin1String("CLIENT_COMPRESS"))
+        optionFlags |= CLIENT_COMPRESS;
+    else if (opt == QLatin1String("CLIENT_FOUND_ROWS"))
+        optionFlags |= CLIENT_FOUND_ROWS;
+    else if (opt == QLatin1String("CLIENT_IGNORE_SPACE"))
+        optionFlags |= CLIENT_IGNORE_SPACE;
+    else if (opt == QLatin1String("CLIENT_INTERACTIVE"))
+        optionFlags |= CLIENT_INTERACTIVE;
+    else if (opt == QLatin1String("CLIENT_NO_SCHEMA"))
+        optionFlags |= CLIENT_NO_SCHEMA;
+    else if (opt == QLatin1String("CLIENT_ODBC"))
+        optionFlags |= CLIENT_ODBC;
+    else if (opt == QLatin1String("CLIENT_SSL"))
+        optionFlags |= CLIENT_SSL;
+    else
+        qWarning("QMYSQLDriver::open: Unknown connect option '%s'", opt.toLocal8Bit().constData());
+}
+
+bool QMYSQLDriver::open(const QString& db,
+                         const QString& user,
+                         const QString& password,
+                         const QString& host,
+                         int port,
+                         const QString& connOpts)
+{
+    if (isOpen())
+        close();
+
+    /* This is a hack to get MySQL's stored procedure support working.
+       Since a stored procedure _may_ return multiple result sets,
+       we have to enable CLIEN_MULTI_STATEMENTS here, otherwise _any_
+       stored procedure call will fail.
+    */
+    unsigned int optionFlags = Q_CLIENT_MULTI_STATEMENTS;
+    const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
+    QString unixSocket;
+#if MYSQL_VERSION_ID >= 50000
+    my_bool reconnect=false;
+#endif
+
+    // extract the real options from the string
+    for (int i = 0; i < opts.count(); ++i) {
+        QString tmp(opts.at(i).simplified());
+        int idx;
+        if ((idx = tmp.indexOf(QLatin1Char('='))) != -1) {
+            QString val = tmp.mid(idx + 1).simplified();
+            QString opt = tmp.left(idx).simplified();
+            if (opt == QLatin1String("UNIX_SOCKET"))
+                unixSocket = val;
+#if MYSQL_VERSION_ID >= 50000
+            else if (opt == QLatin1String("MYSQL_OPT_RECONNECT")) {
+                if (val == QLatin1String("TRUE") || val == QLatin1String("1") || val.isEmpty())
+                    reconnect = true;
+            }
+#endif
+            else if (val == QLatin1String("TRUE") || val == QLatin1String("1"))
+                setOptionFlag(optionFlags, tmp.left(idx).simplified());
+            else
+                qWarning("QMYSQLDriver::open: Illegal connect option value '%s'",
+                         tmp.toLocal8Bit().constData());
+        } else {
+            setOptionFlag(optionFlags, tmp);
+        }
+    }
+
+    if ((d->mysql = mysql_init((MYSQL*) 0)) &&
+            mysql_real_connect(d->mysql,
+                               host.isNull() ? static_cast<const char *>(0)
+                                             : host.toLocal8Bit().constData(),
+                               user.isNull() ? static_cast<const char *>(0)
+                                             : user.toLocal8Bit().constData(),
+                               password.isNull() ? static_cast<const char *>(0)
+                                                 : password.toLocal8Bit().constData(),
+                               db.isNull() ? static_cast<const char *>(0)
+                                           : db.toLocal8Bit().constData(),
+                               (port > -1) ? port : 0,
+                               unixSocket.isNull() ? static_cast<const char *>(0)
+                                           : unixSocket.toLocal8Bit().constData(),
+                               optionFlags))
+    {
+        if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
+            setLastError(qMakeError(tr("Unable to open database '") + db +
+                         QLatin1Char('\''), QSqlError::ConnectionError, d));
+            mysql_close(d->mysql);
+            setOpenError(true);
+            return false;
+        }
+#if MYSQL_VERSION_ID >= 50000
+        if(reconnect)
+            mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
+#endif
+    } else {
+        setLastError(qMakeError(tr("Unable to connect"),
+                     QSqlError::ConnectionError, d));
+        mysql_close(d->mysql);
+        d->mysql = NULL;
+        setOpenError(true);
+        return false;
+    }
+
+#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
+    // force the communication to be utf8
+    mysql_set_character_set(d->mysql, "utf8");
+#endif
+#ifndef QT_NO_TEXTCODEC
+    d->tc = codec(d->mysql);
+#endif
+
+#if MYSQL_VERSION_ID >= 40108
+    d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
+                        && mysql_get_server_version(d->mysql) >= 40100;
+#else
+    d->preparedQuerysEnabled = false;
+#endif
+
+#ifndef QT_NO_THREAD
+    mysql_thread_init();
+#endif
+
+
+    setOpen(true);
+    setOpenError(false);
+    return true;
+}
+
+void QMYSQLDriver::close()
+{
+    if (isOpen()) {
+#ifndef QT_NO_THREAD
+        mysql_thread_end();
+#endif
+        mysql_close(d->mysql);
+        d->mysql = NULL;
+        setOpen(false);
+        setOpenError(false);
+    }
+}
+
+QSqlResult *QMYSQLDriver::createResult() const
+{
+    return new QMYSQLResult(this);
+}
+
+QStringList QMYSQLDriver::tables(QSql::TableType type) const
+{
+    QStringList tl;
+#if MYSQL_VERSION_ID >= 40100
+    if( mysql_get_server_version(d->mysql) < 50000)
+    {
+#endif
+        if (!isOpen())
+            return tl;
+        if (!(type & QSql::Tables))
+            return tl;
+
+        MYSQL_RES* tableRes = mysql_list_tables(d->mysql, NULL);
+        MYSQL_ROW row;
+        int i = 0;
+        while (tableRes) {
+            mysql_data_seek(tableRes, i);
+            row = mysql_fetch_row(tableRes);
+            if (!row)
+                break;
+            tl.append(toUnicode(d->tc, row[0]));
+            i++;
+        }
+        mysql_free_result(tableRes);
+#if MYSQL_VERSION_ID >= 40100
+    } else {
+        QSqlQuery q(createResult());
+        if(type & QSql::Tables) {
+            q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'BASE TABLE'"));
+            while(q.next())
+                tl.append(q.value(0).toString());
+        }
+        if(type & QSql::Views) {
+            q.exec(QLatin1String("select table_name from information_schema.tables where table_type = 'VIEW'"));
+            while(q.next())
+                tl.append(q.value(0).toString());
+        }
+    }
+#endif
+    return tl;
+}
+
+QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
+{
+    QSqlIndex idx;
+    bool prepQ;
+    if (!isOpen())
+        return idx;
+
+    QSqlQuery i(createResult());
+    QString stmt(QLatin1String("show index from %1;"));
+    QSqlRecord fil = record(tablename);
+    i.exec(stmt.arg(tablename));
+    while (i.isActive() && i.next()) {
+        if (i.value(2).toString() == QLatin1String("PRIMARY")) {
+            idx.append(fil.field(i.value(4).toString()));
+            idx.setCursorName(i.value(0).toString());
+            idx.setName(i.value(2).toString());
+        }
+    }
+
+    return idx;
+}
+
+QSqlRecord QMYSQLDriver::record(const QString& tablename) const
+{
+    QString table=tablename;
+    if(isIdentifierEscaped(table, QSqlDriver::TableName))
+        table = stripDelimiters(table, QSqlDriver::TableName);
+
+    QSqlRecord info;
+    if (!isOpen())
+        return info;
+    MYSQL_RES* r = mysql_list_fields(d->mysql, table.toLocal8Bit().constData(), 0);
+    if (!r) {
+        return info;
+    }
+    MYSQL_FIELD* field;
+
+    while ((field = mysql_fetch_field(r)))
+        info.append(qToField(field, d->tc));
+    mysql_free_result(r);
+    return info;
+}
+
+QVariant QMYSQLDriver::handle() const
+{
+    return qVariantFromValue(d->mysql);
+}
+
+bool QMYSQLDriver::beginTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+    return false;
+#endif
+    if (!isOpen()) {
+        qWarning("QMYSQLDriver::beginTransaction: Database not open");
+        return false;
+    }
+    if (mysql_query(d->mysql, "BEGIN WORK")) {
+        setLastError(qMakeError(tr("Unable to begin transaction"),
+                                QSqlError::StatementError, d));
+        return false;
+    }
+    return true;
+}
+
+bool QMYSQLDriver::commitTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+    return false;
+#endif
+    if (!isOpen()) {
+        qWarning("QMYSQLDriver::commitTransaction: Database not open");
+        return false;
+    }
+    if (mysql_query(d->mysql, "COMMIT")) {
+        setLastError(qMakeError(tr("Unable to commit transaction"),
+                                QSqlError::StatementError, d));
+        return false;
+    }
+    return true;
+}
+
+bool QMYSQLDriver::rollbackTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+    return false;
+#endif
+    if (!isOpen()) {
+        qWarning("QMYSQLDriver::rollbackTransaction: Database not open");
+        return false;
+    }
+    if (mysql_query(d->mysql, "ROLLBACK")) {
+        setLastError(qMakeError(tr("Unable to rollback transaction"),
+                                QSqlError::StatementError, d));
+        return false;
+    }
+    return true;
+}
+
+QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
+{
+    QString r;
+    if (field.isNull()) {
+        r = QLatin1String("NULL");
+    } else {
+        switch(field.type()) {
+        case QVariant::String:
+            // Escape '\' characters
+            r = QSqlDriver::formatValue(field, trimStrings);
+            r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
+            break;
+        case QVariant::ByteArray:
+            if (isOpen()) {
+                const QByteArray ba = field.value().toByteArray();
+                // buffer has to be at least length*2+1 bytes
+                char* buffer = new char[ba.size() * 2 + 1];
+                int escapedSize = int(mysql_real_escape_string(d->mysql, buffer,
+                                      ba.data(), ba.size()));
+                r.reserve(escapedSize + 3);
+                r.append(QLatin1Char('\'')).append(toUnicode(d->tc, buffer)).append(QLatin1Char('\''));
+                delete[] buffer;
+                break;
+            } else {
+                qWarning("QMYSQLDriver::formatValue: Database not open");
+            }
+            // fall through
+        default:
+            r = QSqlDriver::formatValue(field, trimStrings);
+        }
+    }
+    return r;
+}
+
+QString QMYSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
+{
+    QString res = identifier;
+    if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
+        res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
+        res.replace(QLatin1Char('.'), QLatin1String("`.`"));
+    }
+    return res;
+}
+
+bool QMYSQLDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const
+{
+    Q_UNUSED(type);
+    return identifier.size() > 2
+        && identifier.startsWith(QLatin1Char('`')) //left delimited
+        && identifier.endsWith(QLatin1Char('`')); //right delimited
+}
+
+QT_END_NAMESPACE
+
+#include "qsql_mysql.moc"