summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/mysql/qsql_mysql.cpp')
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp1665
1 files changed, 1665 insertions, 0 deletions
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
new file mode 100644
index 0000000000..56caf52dcb
--- /dev/null
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -0,0 +1,1665 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtSql module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsql_mysql_p.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 <qfile.h>
+#include <qdebug.h>
+#include <QtSql/private/qsqldriver_p.h>
+#include <QtSql/private/qsqlresult_p.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 QSqlDriverPrivate
+{
+ Q_DECLARE_PUBLIC(QMYSQLDriver)
+
+public:
+ QMYSQLDriverPrivate() : QSqlDriverPrivate(), mysql(0),
+#ifndef QT_NO_TEXTCODEC
+ tc(QTextCodec::codecForLocale()),
+#else
+ tc(0),
+#endif
+ preparedQuerysEnabled(false) { dbmsType = QSqlDriver::MySqlServer; }
+ 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;
+
+class QMYSQLResult : public QSqlResult
+{
+ Q_DECLARE_PRIVATE(QMYSQLResult)
+ friend class QMYSQLDriver;
+
+public:
+ explicit QMYSQLResult(const QMYSQLDriver *db);
+ ~QMYSQLResult();
+
+ QVariant handle() const Q_DECL_OVERRIDE;
+protected:
+ void cleanup();
+ bool fetch(int i) Q_DECL_OVERRIDE;
+ bool fetchNext() Q_DECL_OVERRIDE;
+ bool fetchLast() Q_DECL_OVERRIDE;
+ bool fetchFirst() Q_DECL_OVERRIDE;
+ QVariant data(int field) Q_DECL_OVERRIDE;
+ bool isNull(int field) Q_DECL_OVERRIDE;
+ bool reset (const QString& query) Q_DECL_OVERRIDE;
+ int size() Q_DECL_OVERRIDE;
+ int numRowsAffected() Q_DECL_OVERRIDE;
+ QVariant lastInsertId() const Q_DECL_OVERRIDE;
+ QSqlRecord record() const Q_DECL_OVERRIDE;
+ void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ bool nextResult() Q_DECL_OVERRIDE;
+
+#if MYSQL_VERSION_ID >= 40108
+ bool prepare(const QString &stmt) Q_DECL_OVERRIDE;
+ bool exec() Q_DECL_OVERRIDE;
+#endif
+};
+
+class QMYSQLResultPrivate: public QSqlResultPrivate
+{
+ Q_DECLARE_PUBLIC(QMYSQLResult)
+
+public:
+ Q_DECLARE_SQLDRIVER_PRIVATE(QMYSQLDriver)
+
+ QMYSQLResultPrivate(QMYSQLResult *q, const QMYSQLDriver *drv)
+ : QSqlResultPrivate(q, drv),
+ result(0),
+ rowsAffected(0),
+ hasBlobs(false)
+#if MYSQL_VERSION_ID >= 40108
+ , stmt(0), meta(0), inBinds(0), outBinds(0)
+#endif
+ , preparedQuery(false)
+ { }
+
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ 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;
+};
+
+#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 :
+ type = static_cast<QVariant::Type>((flags & UNSIGNED_FLAG) ? QMetaType::UChar : QMetaType::Char);
+ break;
+ case FIELD_TYPE_SHORT :
+ type = static_cast<QVariant::Type>((flags & UNSIGNED_FLAG) ? QMetaType::UShort : QMetaType::Short);
+ break;
+ 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;
+}
+
+static bool qIsInteger(int t)
+{
+ return t == QMetaType::Char || t == QMetaType::UChar
+ || t == QMetaType::Short || t == QMetaType::UShort
+ || t == QMetaType::Int || t == QMetaType::UInt
+ || t == QMetaType::LongLong || t == QMetaType::ULongLong;
+}
+
+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 if (qIsInteger(f.type)) {
+ fieldInfo->length = 8;
+ } 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(*new QMYSQLResultPrivate(this, db))
+{
+}
+
+QMYSQLResult::~QMYSQLResult()
+{
+ cleanup();
+}
+
+QVariant QMYSQLResult::handle() const
+{
+ Q_D(const QMYSQLResult);
+#if MYSQL_VERSION_ID >= 40108
+ if(d->preparedQuery)
+ return d->meta ? QVariant::fromValue(d->meta) : QVariant::fromValue(d->stmt);
+ else
+#endif
+ return QVariant::fromValue(d->result);
+}
+
+void QMYSQLResult::cleanup()
+{
+ Q_D(QMYSQLResult);
+ 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 (driver() && d->drv_d_func()->mysql && mysql_next_result(d->drv_d_func()->mysql) == 0) {
+ MYSQL_RES *res = mysql_store_result(d->drv_d_func()->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)
+{
+ Q_D(QMYSQLResult);
+ if (!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()
+{
+ Q_D(QMYSQLResult);
+ if (!driver())
+ return false;
+ if (d->preparedQuery) {
+#if MYSQL_VERSION_ID >= 40108
+ 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 // MYSQL_DATA_TRUNCATED
+ setLastError(qMakeStmtError(QCoreApplication::translate("QMYSQLResult",
+ "Unable to fetch data"), QSqlError::StatementError, 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()
+{
+ Q_D(QMYSQLResult);
+ if (!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)
+{
+ Q_D(QMYSQLResult);
+ if (!isSelect() || field >= d->fields.count()) {
+ qWarning("QMYSQLResult::data: column %d out of range", field);
+ return QVariant();
+ }
+
+ if (!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 (qIsInteger(f.type))
+ return QVariant(f.type, f.outField);
+
+ if (f.type != QVariant::ByteArray)
+ val = toUnicode(d->drv_d_func()->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->drv_d_func()->tc, d->row[field], fieldLength);
+ }
+
+ switch (static_cast<int>(f.type)) {
+ case QVariant::LongLong:
+ return QVariant(val.toLongLong());
+ case QVariant::ULongLong:
+ return QVariant(val.toULongLong());
+ case QMetaType::Char:
+ case QMetaType::Short:
+ case QVariant::Int:
+ return QVariant(val.toInt());
+ case QMetaType::UChar:
+ case QMetaType::UShort:
+ 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)
+{
+ Q_D(const QMYSQLResult);
+ if (field < 0 || field >= d->fields.count())
+ return true;
+ if (d->preparedQuery)
+ return d->fields.at(field).nullIndicator;
+ else
+ return d->row[field] == NULL;
+}
+
+bool QMYSQLResult::reset (const QString& query)
+{
+ Q_D(QMYSQLResult);
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return false;
+
+ d->preparedQuery = false;
+
+ cleanup();
+
+ const QByteArray encQuery(fromUnicode(d->drv_d_func()->tc, query));
+ if (mysql_real_query(d->drv_d_func()->mysql, encQuery.data(), encQuery.length())) {
+ setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute query"),
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ d->result = mysql_store_result(d->drv_d_func()->mysql);
+ if (!d->result && mysql_field_count(d->drv_d_func()->mysql) > 0) {
+ setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store result"),
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ int numFields = mysql_field_count(d->drv_d_func()->mysql);
+ setSelect(numFields != 0);
+ d->fields.resize(numFields);
+ d->rowsAffected = mysql_affected_rows(d->drv_d_func()->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()
+{
+ Q_D(const QMYSQLResult);
+ if (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()
+{
+ Q_D(const QMYSQLResult);
+ return d->rowsAffected;
+}
+
+QVariant QMYSQLResult::lastInsertId() const
+{
+ Q_D(const QMYSQLResult);
+ if (!isActive() || !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->drv_d_func()->mysql);
+ if (id)
+ return QVariant(id);
+ }
+ return QVariant();
+}
+
+QSqlRecord QMYSQLResult::record() const
+{
+ Q_D(const QMYSQLResult);
+ QSqlRecord info;
+ MYSQL_RES *res;
+ if (!isActive() || !isSelect() || !driver())
+ return info;
+
+#if MYSQL_VERSION_ID >= 40108
+ res = d->preparedQuery ? d->meta : d->result;
+#else
+ res = d->result;
+#endif
+
+ if (!mysql_errno(d->drv_d_func()->mysql)) {
+ mysql_field_seek(res, 0);
+ MYSQL_FIELD* field = mysql_fetch_field(res);
+ while(field) {
+ info.append(qToField(field, d->drv_d_func()->tc));
+ field = mysql_fetch_field(res);
+ }
+ }
+ mysql_field_seek(res, 0);
+ return info;
+}
+
+bool QMYSQLResult::nextResult()
+{
+ Q_D(QMYSQLResult);
+ if (!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->drv_d_func()->mysql);
+ if (status > 0) {
+ setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to execute next query"),
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ } else if (status == -1) {
+ return false; // No more result sets
+ }
+
+ d->result = mysql_store_result(d->drv_d_func()->mysql);
+ int numFields = mysql_field_count(d->drv_d_func()->mysql);
+ if (!d->result && numFields > 0) {
+ setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to store next result"),
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ setSelect(numFields > 0);
+ d->fields.resize(numFields);
+ d->rowsAffected = mysql_affected_rows(d->drv_d_func()->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)
+{
+ 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() * 1000;
+ }
+ 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)
+{
+ Q_D(QMYSQLResult);
+ if (!driver())
+ return false;
+#if MYSQL_VERSION_ID >= 40108
+ cleanup();
+ if (!d->drv_d_func()->preparedQuerysEnabled)
+ return QSqlResult::prepare(query);
+
+ int r;
+
+ if (query.isEmpty())
+ return false;
+
+ if (!d->stmt)
+ d->stmt = mysql_stmt_init(d->drv_d_func()->mysql);
+ if (!d->stmt) {
+ setLastError(qMakeError(QCoreApplication::translate("QMYSQLResult", "Unable to prepare statement"),
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ const QByteArray encQuery(fromUnicode(d->drv_d_func()->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()
+{
+ Q_D(QMYSQLResult);
+ if (!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:
+ currBind->buffer_type = MYSQL_TYPE_LONG;
+ currBind->buffer = data;
+ currBind->buffer_length = sizeof(int);
+ currBind->is_unsigned = (val.type() != QVariant::Int);
+ break;
+ case QVariant::Bool:
+ currBind->buffer_type = MYSQL_TYPE_TINY;
+ currBind->buffer = data;
+ currBind->buffer_length = sizeof(bool);
+ currBind->is_unsigned = false;
+ 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->drv_d_func()->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(*new QMYSQLDriverPrivate, 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(*new QMYSQLDriverPrivate, parent)
+{
+ Q_D(QMYSQLDriver);
+ 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()
+{
+ Q_D(QMYSQLDriver);
+ d->mysql = 0;
+ qMySqlConnectionCount++;
+}
+
+QMYSQLDriver::~QMYSQLDriver()
+{
+ qMySqlConnectionCount--;
+ if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+ qLibraryEnd();
+}
+
+bool QMYSQLDriver::hasFeature(DriverFeature f) const
+{
+ Q_D(const QMYSQLDriver);
+ 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:
+ case CancelQuery:
+ 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"))
+ qWarning("QMYSQLDriver: SSL_KEY, SSL_CERT and SSL_CA should be used instead of 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)
+{
+ Q_D(QMYSQLDriver);
+ 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;
+ QString sslCert;
+ QString sslCA;
+ QString sslKey;
+ QString sslCAPath;
+ QString sslCipher;
+#if MYSQL_VERSION_ID >= 50000
+ my_bool reconnect=false;
+ uint connectTimeout = 0;
+ uint readTimeout = 0;
+ uint writeTimeout = 0;
+#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;
+ } else if (opt == QLatin1String("MYSQL_OPT_CONNECT_TIMEOUT")) {
+ connectTimeout = val.toInt();
+ } else if (opt == QLatin1String("MYSQL_OPT_READ_TIMEOUT")) {
+ readTimeout = val.toInt();
+ } else if (opt == QLatin1String("MYSQL_OPT_WRITE_TIMEOUT")) {
+ writeTimeout = val.toInt();
+ }
+#endif
+ else if (opt == QLatin1String("SSL_KEY"))
+ sslKey = val;
+ else if (opt == QLatin1String("SSL_CERT"))
+ sslCert = val;
+ else if (opt == QLatin1String("SSL_CA"))
+ sslCA = val;
+ else if (opt == QLatin1String("SSL_CAPATH"))
+ sslCAPath = val;
+ else if (opt == QLatin1String("SSL_CIPHER"))
+ sslCipher = val;
+ 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))) {
+ setLastError(qMakeError(tr("Unable to allocate a MYSQL object"),
+ QSqlError::ConnectionError, d));
+ setOpenError(true);
+ return false;
+ }
+
+ if (!sslKey.isNull() || !sslCert.isNull() || !sslCA.isNull() ||
+ !sslCAPath.isNull() || !sslCipher.isNull()) {
+ mysql_ssl_set(d->mysql,
+ sslKey.isNull() ? static_cast<const char *>(0)
+ : QFile::encodeName(sslKey).constData(),
+ sslCert.isNull() ? static_cast<const char *>(0)
+ : QFile::encodeName(sslCert).constData(),
+ sslCA.isNull() ? static_cast<const char *>(0)
+ : QFile::encodeName(sslCA).constData(),
+ sslCAPath.isNull() ? static_cast<const char *>(0)
+ : QFile::encodeName(sslCAPath).constData(),
+ sslCipher.isNull() ? static_cast<const char *>(0)
+ : sslCipher.toLocal8Bit().constData());
+ }
+
+#if MYSQL_VERSION_ID >= 50000
+ if (connectTimeout != 0)
+ mysql_options(d->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &connectTimeout);
+ if (readTimeout != 0)
+ mysql_options(d->mysql, MYSQL_OPT_READ_TIMEOUT, &readTimeout);
+ if (writeTimeout != 0)
+ mysql_options(d->mysql, MYSQL_OPT_WRITE_TIMEOUT, &writeTimeout);
+#endif
+ MYSQL *mysql = 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 (mysql == d->mysql) {
+ if (!db.isEmpty() && mysql_select_db(d->mysql, db.toLocal8Bit().constData())) {
+ setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), 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()
+{
+ Q_D(QMYSQLDriver);
+ 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
+{
+ Q_D(const QMYSQLDriver);
+ 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) {
+ QString sql = QLatin1String("select table_name from information_schema.tables where table_schema = '") + QLatin1String(d->mysql->db) + QLatin1String("' and table_type = 'BASE TABLE'");
+ q.exec(sql);
+
+ while(q.next())
+ tl.append(q.value(0).toString());
+ }
+ if(type & QSql::Views) {
+ QString sql = QLatin1String("select table_name from information_schema.tables where table_schema = '") + QLatin1String(d->mysql->db) + QLatin1String("' and table_type = 'VIEW'");
+ q.exec(sql);
+
+ while(q.next())
+ tl.append(q.value(0).toString());
+ }
+ }
+#endif
+ return tl;
+}
+
+QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
+{
+ QSqlIndex idx;
+ 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
+{
+ Q_D(const QMYSQLDriver);
+ 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
+{
+ Q_D(const QMYSQLDriver);
+ return QVariant::fromValue(d->mysql);
+}
+
+bool QMYSQLDriver::beginTransaction()
+{
+ Q_D(QMYSQLDriver);
+#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()
+{
+ Q_D(QMYSQLDriver);
+#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()
+{
+ Q_D(QMYSQLDriver);
+#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
+{
+ Q_D(const QMYSQLDriver);
+ QString r;
+ if (field.isNull()) {
+ r = QLatin1String("NULL");
+ } else {
+ switch(field.type()) {
+ case QVariant::Double:
+ r = QString::number(field.value().toDouble(), 'g', field.precision());
+ break;
+ 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::isIdentifierEscaped(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