From 0c15604fa65935b393d37dc13ffa4b9b71cdd1f3 Mon Sep 17 00:00:00 2001 From: stoeckerb Date: Fri, 10 Apr 2020 12:27:34 +0200 Subject: [PATCH] init Qt 5.12.8 --- .qmake.conf | 19 + README | 5 + configure.json | 234 ++++++ configure.pri | 85 +++ mysql/README | 6 + mysql/main.cpp | 73 ++ mysql/mysql.json | 3 + mysql/mysql.pro | 20 + mysql/qsql_mysql.cpp | 1694 ++++++++++++++++++++++++++++++++++++++++++ mysql/qsql_mysql_p.h | 110 +++ qsqldriverbase.pri | 9 + sqldrivers.pro | 8 + sqldrivers.pro.user | 1636 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 3902 insertions(+) create mode 100644 .qmake.conf create mode 100644 README create mode 100644 configure.json create mode 100644 configure.pri create mode 100644 mysql/README create mode 100644 mysql/main.cpp create mode 100644 mysql/mysql.json create mode 100644 mysql/mysql.pro create mode 100644 mysql/qsql_mysql.cpp create mode 100644 mysql/qsql_mysql_p.h create mode 100644 qsqldriverbase.pri create mode 100644 sqldrivers.pro create mode 100644 sqldrivers.pro.user diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..15ba71a --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,19 @@ +# This file detaches this sub-tree from the rest of qtbase, +# so it can be configured stand-alone. +# Of course, under normal circumstances, this _is_ part of qtbase, +# so we have to make some contortions to restore normality. + +isEmpty(_QMAKE_CONF_): return() # Pre-scan during spec loading. + +SQLDRV_SRC_TREE = $$dirname(_QMAKE_CONF_) +QTBASE_SRC_TREE = $$section(SQLDRV_SRC_TREE, /, 0, -4) + +QTBASE_BLD_TREE = $$shadowed($$QTBASE_SRC_TREE) +!isEmpty(QTBASE_BLD_TREE):exists($$QTBASE_BLD_TREE/.qmake.cache) { + # This tricks qt_build_config.prf and qt_build_paths.prf + _QMAKE_CONF_ = $$QTBASE_SRC_TREE/.qmake.conf +} else { + CONFIG += sqldrivers_standalone +} + +include($$QTBASE_SRC_TREE/.qmake.conf) diff --git a/README b/README new file mode 100644 index 0000000..7000fb5 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +Please note that the DB2, Oracle and TDS client drivers are not distributed +with the Qt Open Source Editions. + +This is because the client libraries are distributed under a license which +is not compatible with the GPL license. diff --git a/configure.json b/configure.json new file mode 100644 index 0000000..cd20eef --- /dev/null +++ b/configure.json @@ -0,0 +1,234 @@ +{ + "module": "sqldrivers", + "depends": [ + "core" + ], + "testDir": "../../../config.tests", + + "commandline": { + "assignments": { + "MYSQL_PATH": "mysql.prefix", + "SYBASE": "tds.prefix", + "SYBASE_LIBS": "tds.libs" + }, + "options": { + "mysql_config": "string", + "psql_config": "string", + "sqlite": { "type": "enum", "name": "system-sqlite", "values": { "qt": "no", "system": "yes" } }, + "sql-db2": "boolean", + "sql-ibase": "boolean", + "sql-mysql": "boolean", + "sql-oci": "boolean", + "sql-odbc": "boolean", + "sql-psql": "boolean", + "sql-sqlite": "boolean", + "sql-sqlite2": "boolean", + "sql-tds": "boolean", + "plugin-sql-db2": { "type": "void", "name": "sql-db2" }, + "plugin-sql-ibase": { "type": "void", "name": "sql-ibase" }, + "plugin-sql-mysql": { "type": "void", "name": "sql-mysql" }, + "plugin-sql-oci": { "type": "void", "name": "sql-oci" }, + "plugin-sql-odbc": { "type": "void", "name": "sql-odbc" }, + "plugin-sql-psql": { "type": "void", "name": "sql-psql" }, + "plugin-sql-sqlite": { "type": "void", "name": "sql-sqlite" }, + "plugin-sql-sqlite2": { "type": "void", "name": "sql-sqlite2" }, + "plugin-sql-tds": { "type": "void", "name": "sql-tds" } + } + }, + + "libraries": { + "db2": { + "label": "DB2 (IBM)", + "test": {}, + "headers": [ "sqlcli.h", "sqlcli1.h" ], + "sources": [ + { "libs": "-ldb2cli", "condition": "config.win32" }, + { "libs": "-ldb2", "condition": "!config.win32" } + ] + }, + "ibase": { + "label": "InterBase", + "test": {}, + "headers": "ibase.h", + "sources": [ + { "libs": "-lgds32_ms", "condition": "config.win32" }, + { "libs": "-lgds", "condition": "!config.win32" } + ] + }, + "mysql": { + "label": "MySQL", + "test": { + "head": [ + "#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)", + "# include ", + "#endif" + ], + "main": "mysql_get_client_version();" + }, + "headers": "mysql.h", + "sources": [ + { "type": "mysqlConfig", "query": "--libs_r", "cleanlibs": true }, + { "type": "mysqlConfig", "query": "--libs", "cleanlibs": true }, + { "type": "mysqlConfig", "query": "--libs_r", "cleanlibs": false }, + { "type": "mysqlConfig", "query": "--libs", "cleanlibs": false }, + { "libs": "-lmysqlclient_r", "condition": "!config.win32" }, + { "libs": "-llibmysql", "condition": "config.win32" }, + { "libs": "-lmysqlclient", "condition": "!config.win32" } + ] + }, + "psql": { + "label": "PostgreSQL", + "test": { + "main": [ + "PQescapeBytea(0, 0, 0);", + "PQunescapeBytea(0, 0);" + ] + }, + "headers": "libpq-fe.h", + "sources": [ + { "type": "pkgConfig", "args": "libpq" }, + { "type": "psqlConfig" }, + { "type": "psqlEnv", "libs": "-llibpq -lws2_32 -ladvapi32", "condition": "config.win32" }, + { "type": "psqlEnv", "libs": "-lpq", "condition": "!config.win32" } + ] + }, + "tds": { + "label": "TDS (Sybase)", + "test": {}, + "headers": [ "sybfront.h", "sybdb.h" ], + "sources": [ + { "type": "sybaseEnv", "libs": "-lNTWDBLIB", "condition": "config.win32" }, + { "type": "sybaseEnv", "libs": "-lsybdb", "condition": "!config.win32" } + ] + }, + "oci": { + "label": "OCI (Oracle)", + "test": {}, + "headers": "oci.h", + "sources": [ + { "libs": "-loci", "condition": "config.win32" }, + { "libs": "-lclntsh", "condition": "!config.win32" } + ] + }, + "odbc": { + "label": "ODBC", + "test": { + "head": [ + "#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)", + "# include ", + "#endif" + ], + "main": [ + "SQLHANDLE env;", + "SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);" + ] + }, + "headers": [ "sql.h", "sqlext.h" ], + "sources": [ + { "libs": "-lodbc32", "condition": "config.win32" }, + { "libs": "-liodbc", "condition": "config.darwin" }, + { "libs": "-lodbc", "condition": "!config.win32 && !config.darwin" } + ] + }, + "sqlite2": { + "label": "SQLite (version 2)", + "test": {}, + "headers": "sqlite.h", + "sources": [ + "-lsqlite" + ] + }, + "sqlite3": { + "label": "SQLite (version 3)", + "export": "sqlite", + "test": { + "main": "sqlite3_open_v2(0, 0, 0, 0);" + }, + "headers": "sqlite3.h", + "sources": [ + { "type": "pkgConfig", "args": "sqlite3" }, + "-lsqlite3" + ], + "use": [ + { "lib": "zlib", "condition": "!config.win32 && features.system-zlib" } + ] + } + }, + + "tests": { + }, + + "features": { + "sql-db2": { + "label": "DB2 (IBM)", + "condition": "libs.db2", + "output": [ "privateFeature" ] + }, + "sql-ibase": { + "label": "InterBase", + "condition": "libs.ibase", + "output": [ "privateFeature" ] + }, + "sql-mysql": { + "label": "MySql", + "condition": "libs.mysql", + "output": [ "privateFeature" ] + }, + "sql-oci": { + "label": "OCI (Oracle)", + "condition": "libs.oci", + "output": [ "privateFeature" ] + }, + "sql-odbc": { + "label": "ODBC", + "condition": "features.datestring && libs.odbc", + "output": [ "privateFeature" ] + }, + "sql-psql": { + "label": "PostgreSQL", + "condition": "libs.psql", + "output": [ "privateFeature" ] + }, + "sql-sqlite2": { + "label": "SQLite2", + "condition": "libs.sqlite2", + "output": [ "privateFeature" ] + }, + "sql-sqlite": { + "label": "SQLite", + "condition": "features.datestring", + "output": [ "privateFeature" ] + }, + "system-sqlite": { + "label": " Using system provided SQLite", + "autoDetect": false, + "condition": "features.sql-sqlite && libs.sqlite3", + "output": [ "privateFeature" ] + }, + "sql-tds": { + "label": "TDS (Sybase)", + "condition": "features.datestring && libs.tds", + "output": [ "privateFeature" ] + } + }, + + "report": [ + { + "type": "warning", + "condition": "config.win32 && !config.msvc && features.sql-oci", + "message": "Qt does not support compiling the Oracle database driver with +MinGW, due to lack of such support from Oracle. Consider disabling the +Oracle driver, as the current build will most likely fail." + } + ], + + "summary": [ + { + "section": "Qt Sql Drivers", + "entries": [ + "sql-db2", "sql-ibase", "sql-mysql", "sql-oci", "sql-odbc", "sql-psql", + "sql-sqlite2", "sql-sqlite", "system-sqlite", "sql-tds" + ] + } + ] +} diff --git a/configure.pri b/configure.pri new file mode 100644 index 0000000..be6a930 --- /dev/null +++ b/configure.pri @@ -0,0 +1,85 @@ +# custom tests + +defineTest(qtConfLibrary_psqlConfig) { + pg_config = $$config.input.psql_config + isEmpty(pg_config):!cross_compile: \ + pg_config = $$qtConfFindInPath("pg_config") + !win32:!isEmpty(pg_config) { + qtRunLoggedCommand("$$pg_config --libdir", libdir)|return(false) + !qtConfResolvePathLibs($${1}.libs, $$libdir, -lpq): \ + return(false) + qtRunLoggedCommand("$$pg_config --includedir", includedir)|return(false) + !qtConfResolvePathIncs($${1}.includedir, $$includedir, $$2): \ + return(false) + return(true) + } + qtLog("pg_config not found.") + return(false) +} + +defineTest(qtConfLibrary_psqlEnv) { + # Respect PSQL_LIBS if set + PSQL_LIBS = $$getenv(PSQL_LIBS) + !isEmpty(PSQL_LIBS) { + eval(libs = $$PSQL_LIBS) + !qtConfResolveLibs($${1}.libs, $$libs): \ + return(false) + } else { + !qtConfLibrary_inline($$1, $$2): \ + return(false) + } + return(true) +} + +defineTest(qtConfLibrary_mysqlConfig) { + mysql_config = $$config.input.mysql_config + isEmpty(mysql_config):!cross_compile: \ + mysql_config = $$qtConfFindInPath("mysql_config") + !isEmpty(mysql_config) { + qtRunLoggedCommand("$$mysql_config --version", version)|return(false) + version = $$split(version, '.') + version = $$first(version) + isEmpty(version)|lessThan(version, 4): return(false)] + + # query is either --libs or --libs_r + query = $$eval($${1}.query) + qtRunLoggedCommand("$$mysql_config $$query", libs)|return(false) + qtRunLoggedCommand("$$mysql_config --include", includedir)|return(false) + eval(libs = $$libs) + # -rdynamic should not be returned by mysql_config, but is on RHEL 6.6 + libs -= -rdynamic + equals($${1}.cleanlibs, true) { + for(l, libs) { + # Drop all options besides the -L one and the -lmysqlclient one + # so we don't unnecessarily link to libs like OpenSSL + contains(l, "^(-L|-lmysqlclient).*"): cleanlibs += $$l + } + libs = $$cleanlibs + } + !qtConfResolveLibs($${1}.libs, $$libs): \ + return(false) + eval(rawincludedir = $$includedir) + rawincludedir ~= s/^-I//g + includedir = + for (id, rawincludedir): \ + includedir += $$clean_path($$id) + !qtConfResolvePathIncs($${1}.includedir, $$includedir, $$2): \ + return(false) + return(true) + } + qtLog("mysql_config not found.") + return(false) +} + +defineTest(qtConfLibrary_sybaseEnv) { + libdir = + sybase = $$getenv(SYBASE) + !isEmpty(sybase): \ + libdir += $${sybase}/lib + eval(libs = $$getenv(SYBASE_LIBS)) + isEmpty(libs): \ + libs = $$eval($${1}.libs) + !qtConfResolvePathLibs($${1}.libs, $$libdir, $$libs): \ + return(false) + return(true) +} diff --git a/mysql/README b/mysql/README new file mode 100644 index 0000000..d1b3722 --- /dev/null +++ b/mysql/README @@ -0,0 +1,6 @@ +You will need the MySQL development headers and libraries installed +before compiling this plugin. + +See the Qt SQL documentation for more information on compiling Qt SQL +driver plugins (sql-driver.html). + diff --git a/mysql/main.cpp b/mysql/main.cpp new file mode 100644 index 0000000..d8d7048 --- /dev/null +++ b/mysql/main.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 +#include +#include "qsql_mysql_p.h" + +QT_BEGIN_NAMESPACE + +class QMYSQLDriverPlugin : public QSqlDriverPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "mysql.json") + +public: + QMYSQLDriverPlugin(); + + QSqlDriver* create(const QString &) override; +}; + +QMYSQLDriverPlugin::QMYSQLDriverPlugin() + : QSqlDriverPlugin() +{ +} + +QSqlDriver* QMYSQLDriverPlugin::create(const QString &name) +{ + if (name == QLatin1String("QMYSQL") || name == QLatin1String("QMYSQL3")) { + QMYSQLDriver* driver = new QMYSQLDriver(); + return driver; + } + return 0; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/mysql/mysql.json b/mysql/mysql.json new file mode 100644 index 0000000..0caaadb --- /dev/null +++ b/mysql/mysql.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "QMYSQL3", "QMYSQL" ] +} diff --git a/mysql/mysql.pro b/mysql/mysql.pro new file mode 100644 index 0000000..391b577 --- /dev/null +++ b/mysql/mysql.pro @@ -0,0 +1,20 @@ +TARGET = qsqlmysql + +HEADERS += $$PWD/qsql_mysql_p.h +SOURCES += $$PWD/qsql_mysql.cpp $$PWD/main.cpp + +#QMAKE_USE += mysql + +OTHER_FILES += mysql.json +load(qt_build_config) +PLUGIN_CLASS_NAME = QMYSQLDriverPlugin +include($$PWD/../qsqldriverbase.pri) + + +contains(QT_ARCH, i386) { +INCLUDEPATH += $$PWD/../../mysql-connector-c-6.1.11-win32\include +LIBS += -L$$PWD/../../mysql-connector-c-6.1.11-win32\lib -llibmysql +} else { +INCLUDEPATH += $$PWD/../../mysql-connector-c-6.1.11-winx64\include +LIBS += -L$$PWD/../../mysql-connector-c-6.1.11-winx64\lib -llibmysql +} diff --git a/mysql/qsql_mysql.cpp b/mysql/qsql_mysql.cpp new file mode 100644 index 0000000..f2ae3fb --- /dev/null +++ b/mysql/qsql_mysql.cpp @@ -0,0 +1,1694 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_CONFIG(textcodec) +#include +#endif +#include +#include +#include +#include +#include + +#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 + +// MySQL above version 8 removed my_bool typedef while MariaDB kept it, +// by redefining it we can regain source compatibility. +using my_bool = decltype(mysql_stmt_bind_result(nullptr, nullptr)); + +QT_BEGIN_NAMESPACE + +class QMYSQLDriverPrivate : public QSqlDriverPrivate +{ + Q_DECLARE_PUBLIC(QMYSQLDriver) + +public: + QMYSQLDriverPrivate() : QSqlDriverPrivate(), mysql(0), +#if QT_CONFIG(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) +{ +#if !QT_CONFIG(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) +{ +#if !QT_CONFIG(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) +{ +#if !QT_CONFIG(textcodec) + Q_UNUSED(tc); + return str.toLatin1(); +#else + return tc->fromUnicode(str); +#endif +} + +static inline QVariant qDateFromString(const QString &val) +{ +#if !QT_CONFIG(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) +{ +#if !QT_CONFIG(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) +{ +#if !QT_CONFIG(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 override; +protected: + void cleanup(); + bool fetch(int i) override; + bool fetchNext() override; + bool fetchLast() override; + bool fetchFirst() override; + QVariant data(int field) override; + bool isNull(int field) override; + bool reset (const QString& query) override; + int size() override; + int numRowsAffected() override; + QVariant lastInsertId() const override; + QSqlRecord record() const override; + void virtual_hook(int id, void *data) override; + bool nextResult() override; + +#if MYSQL_VERSION_ID >= 40108 + bool prepare(const QString &stmt) override; + bool exec() 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 fields; + +#if MYSQL_VERSION_ID >= 40108 + MYSQL_STMT* stmt; + MYSQL_RES* meta; + + MYSQL_BIND *inBinds; + MYSQL_BIND *outBinds; +#endif + + bool preparedQuery; +}; + +#if QT_CONFIG(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 // 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, QString::number(mysql_errno(p->mysql))); +} + + +static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags) +{ + QVariant::Type type; + switch (mysqltype) { + case FIELD_TYPE_TINY : + type = static_cast((flags & UNSIGNED_FLAG) ? QMetaType::UChar : QMetaType::Char); + break; + case FIELD_TYPE_SHORT : + type = static_cast((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 : + // A time field can be within the range '-838:59:59' to '838:59:59' so + // use QString instead of QTime since QTime is limited to 24 hour clock + type = QVariant::String; + 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), + toUnicode(tc, field->table)); + 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, QString::number(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(bind->buffer); + bind->buffer = new char[fieldInfo->max_length]; + fields[i].outField = static_cast(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; + bind->is_unsigned = fieldInfo->flags & UNSIGNED_FLAG ? 1 : 0; + 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)) { + QVariant variant(f.type, f.outField); + // we never want to return char variants here, see QTBUG-53397 + if (static_cast(f.type) == QMetaType::UChar) + return variant.toUInt(); + else if (static_cast(f.type) == QMetaType::Char) + return variant.toInt(); + return variant; + } + + 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(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; + return QVariant(); + } + 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); + } + case QVariant::String: + default: + return QVariant(val); + } + Q_UNREACHABLE(); +} + +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 timeVector; + QVector stringVector; + QVector nullVector; + + const QVector 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(val.constData()); + + currBind = &d->outBinds[i]; + + nullVector[i] = static_cast(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(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(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 + +#if defined(MARIADB_BASE_VERSION) || defined(MARIADB_VERSION_ID) + qAddPostRoutine([]() { mysql_server_end(); }); +#endif +} + +static void qLibraryEnd() +{ +#if !defined(MARIADB_BASE_VERSION) && !defined(MARIADB_VERSION_ID) +# if !defined(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 +#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; +#if QT_CONFIG(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(0) + : QFile::encodeName(sslKey).constData(), + sslCert.isNull() ? static_cast(0) + : QFile::encodeName(sslCert).constData(), + sslCA.isNull() ? static_cast(0) + : QFile::encodeName(sslCA).constData(), + sslCAPath.isNull() ? static_cast(0) + : QFile::encodeName(sslCAPath).constData(), + sslCipher.isNull() ? static_cast(0) + : sslCipher.toLocal8Bit().constData()); + } + +#if MYSQL_VERSION_ID >= 50100 + 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(0) + : host.toLocal8Bit().constData(), + user.isNull() ? static_cast(0) + : user.toLocal8Bit().constData(), + password.isNull() ? static_cast(0) + : password.toLocal8Bit().constData(), + db.isNull() ? static_cast(0) + : db.toLocal8Bit().constData(), + (port > -1) ? port : 0, + unixSocket.isNull() ? static_cast(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 >= 50100 + 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 + if (mysql_get_client_version() >= 50503 && mysql_get_server_version(d->mysql) >= 50503) { + // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters) + mysql_set_character_set(d->mysql, "utf8mb4"); +#if QT_CONFIG(textcodec) + d->tc = QTextCodec::codecForName("UTF-8"); +#endif + } else + { + // force the communication to be utf8 + mysql_set_character_set(d->mysql, "utf8"); +#if QT_CONFIG(textcodec) + d->tc = codec(d->mysql); +#endif + } +#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 + +#if QT_CONFIG(thread) + mysql_thread_init(); +#endif + + + setOpen(true); + setOpenError(false); + return true; +} + +void QMYSQLDriver::close() +{ + Q_D(QMYSQLDriver); + if (isOpen()) { +#if QT_CONFIG(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 = QStringLiteral("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 diff --git a/mysql/qsql_mysql_p.h b/mysql/qsql_mysql_p.h new file mode 100644 index 0000000..48b04fb --- /dev/null +++ b/mysql/qsql_mysql_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSQL_MYSQL_H +#define QSQL_MYSQL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#if defined (Q_OS_WIN32) +#include +#endif + +#include + +#ifdef QT_PLUGIN +#define Q_EXPORT_SQLDRIVER_MYSQL +#else +#define Q_EXPORT_SQLDRIVER_MYSQL Q_SQL_EXPORT +#endif + +QT_BEGIN_NAMESPACE + +class QMYSQLDriverPrivate; + +class Q_EXPORT_SQLDRIVER_MYSQL QMYSQLDriver : public QSqlDriver +{ + friend class QMYSQLResultPrivate; + Q_DECLARE_PRIVATE(QMYSQLDriver) + Q_OBJECT +public: + explicit QMYSQLDriver(QObject *parent=0); + explicit QMYSQLDriver(MYSQL *con, QObject * parent=0); + ~QMYSQLDriver(); + bool hasFeature(DriverFeature f) const override; + bool open(const QString & db, + const QString & user, + const QString & password, + const QString & host, + int port, + const QString& connOpts) override; + void close() override; + QSqlResult *createResult() const override; + QStringList tables(QSql::TableType) const override; + QSqlIndex primaryIndex(const QString& tablename) const override; + QSqlRecord record(const QString& tablename) const override; + QString formatValue(const QSqlField &field, + bool trimStrings) const override; + QVariant handle() const override; + QString escapeIdentifier(const QString &identifier, IdentifierType type) const override; + + bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const override; + +protected: + bool beginTransaction() override; + bool commitTransaction() override; + bool rollbackTransaction() override; +private: + void init(); +}; + +QT_END_NAMESPACE + +#endif // QSQL_MYSQL_H diff --git a/qsqldriverbase.pri b/qsqldriverbase.pri new file mode 100644 index 0000000..4b78fa9 --- /dev/null +++ b/qsqldriverbase.pri @@ -0,0 +1,9 @@ +QT = core core-private sql-private + +# For QMAKE_USE in the parent projects. +include($$shadowed($$PWD)/qtsqldrivers-config.pri) + +PLUGIN_TYPE = sqldrivers +load(qt_plugin) + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII diff --git a/sqldrivers.pro b/sqldrivers.pro new file mode 100644 index 0000000..a703792 --- /dev/null +++ b/sqldrivers.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +sqldrivers_standalone { + _QMAKE_CACHE_ = $$shadowed($$SQLDRV_SRC_TREE)/.qmake.conf + load(qt_configure) +} + +SUBDIRS += mysql diff --git a/sqldrivers.pro.user b/sqldrivers.pro.user new file mode 100644 index 0000000..2939fd2 --- /dev/null +++ b/sqldrivers.pro.user @@ -0,0 +1,1636 @@ + + + + + + EnvironmentId + {a8272da8-d1ab-4d1a-b36c-33a27e22daec} + + + ProjectExplorer.Project.ActiveTarget + 2 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + -fno-delayed-template-parsing + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.12.8 MSVC2017 32bit + Desktop Qt 5.12.8 MSVC2017 32bit + qt.qt5.5128.win32_msvc2017_kit + 0 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MSVC2017_32bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MSVC2017_32bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop Qt 5.12.8 MSVC2017 64bit + Desktop Qt 5.12.8 MSVC2017 64bit + qt.qt5.5128.win64_msvc2017_64_kit + 0 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MSVC2017_64bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MSVC2017_64bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.2 + + Desktop Qt 5.12.8 MinGW 32-bit + Desktop Qt 5.12.8 MinGW 32-bit + qt.qt5.5128.win32_mingw73_kit + 0 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MinGW_32_bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MinGW_32_bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.3 + + Desktop Qt 5.12.8 MinGW 64-bit + Desktop Qt 5.12.8 MinGW 64-bit + qt.qt5.5128.win64_mingw73_kit + 1 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MinGW_64_bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_12_8_MinGW_64_bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.4 + + Desktop Qt 5.14.2 MSVC2017 32bit + Desktop Qt 5.14.2 MSVC2017 32bit + qt.qt5.5142.win32_msvc2017_kit + 0 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MSVC2017_32bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MSVC2017_32bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.5 + + Desktop Qt 5.14.2 MSVC2017 64bit + Desktop Qt 5.14.2 MSVC2017 64bit + qt.qt5.5142.win64_msvc2017_64_kit + 0 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MSVC2017_64bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MSVC2017_64bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.6 + + Desktop Qt 5.14.2 MinGW 32-bit + Desktop Qt 5.14.2 MinGW 32-bit + qt.qt5.5142.win32_mingw73_kit + 1 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MinGW_32_bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MinGW_32_bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.Target.7 + + Desktop Qt 5.14.2 MinGW 64-bit + Desktop Qt 5.14.2 MinGW 64-bit + qt.qt5.5142.win64_mingw73_kit + 1 + 0 + 0 + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MinGW_64_bit + + + true + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + D:/Bernhard/qt_creator/build/sqldrivers-Desktop_Qt_5_14_2_MinGW_64_bit + + + true + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + 2 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 8 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + +