From 0452c25aedb7d2778e5b26fad924ed61b72bbe33 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Mar 2016 13:35:40 +0100 Subject: Always compile sql drivers as plugins Compiling the drivers into Qt Sql does not make a lot of sense anymore, as we handle plugins well enough in the build system these days. [ChangeLog][Build system] SQL drivers are now always compiled as plugins. Change-Id: I364b82a480849399d1fafe4b20e9f08922569260 Reviewed-by: Oswald Buddenhagen Reviewed-by: Mark Brand --- src/sql/drivers/db2/qsql_db2.cpp | 1700 ------------------- src/sql/drivers/db2/qsql_db2.pri | 8 - src/sql/drivers/db2/qsql_db2_p.h | 103 -- src/sql/drivers/drivers.pri | 11 - src/sql/drivers/ibase/qsql_ibase.cpp | 1948 --------------------- src/sql/drivers/ibase/qsql_ibase.pri | 10 - src/sql/drivers/ibase/qsql_ibase_p.h | 114 -- src/sql/drivers/mysql/qsql_mysql.cpp | 1665 ------------------ src/sql/drivers/mysql/qsql_mysql.pri | 16 - src/sql/drivers/mysql/qsql_mysql_p.h | 110 -- src/sql/drivers/oci/qsql_oci.cpp | 2725 ------------------------------ src/sql/drivers/oci/qsql_oci.pri | 9 - src/sql/drivers/oci/qsql_oci_p.h | 106 -- src/sql/drivers/odbc/qsql_odbc.cpp | 2639 ----------------------------- src/sql/drivers/odbc/qsql_odbc.pri | 12 - src/sql/drivers/odbc/qsql_odbc_p.h | 127 -- src/sql/drivers/psql/qsql_psql.cpp | 1508 ----------------- src/sql/drivers/psql/qsql_psql.pri | 10 - src/sql/drivers/psql/qsql_psql_p.h | 128 -- src/sql/drivers/sqlite/qsql_sqlite.cpp | 828 --------- src/sql/drivers/sqlite/qsql_sqlite.pri | 9 - src/sql/drivers/sqlite/qsql_sqlite_p.h | 100 -- src/sql/drivers/sqlite2/qsql_sqlite2.cpp | 615 ------- src/sql/drivers/sqlite2/qsql_sqlite2.pri | 4 - src/sql/drivers/sqlite2/qsql_sqlite2_p.h | 109 -- src/sql/drivers/tds/qsql_tds.cpp | 881 ---------- src/sql/drivers/tds/qsql_tds.pri | 10 - src/sql/drivers/tds/qsql_tds_p.h | 120 -- 28 files changed, 15625 deletions(-) delete mode 100644 src/sql/drivers/db2/qsql_db2.cpp delete mode 100644 src/sql/drivers/db2/qsql_db2.pri delete mode 100644 src/sql/drivers/db2/qsql_db2_p.h delete mode 100644 src/sql/drivers/drivers.pri delete mode 100644 src/sql/drivers/ibase/qsql_ibase.cpp delete mode 100644 src/sql/drivers/ibase/qsql_ibase.pri delete mode 100644 src/sql/drivers/ibase/qsql_ibase_p.h delete mode 100644 src/sql/drivers/mysql/qsql_mysql.cpp delete mode 100644 src/sql/drivers/mysql/qsql_mysql.pri delete mode 100644 src/sql/drivers/mysql/qsql_mysql_p.h delete mode 100644 src/sql/drivers/oci/qsql_oci.cpp delete mode 100644 src/sql/drivers/oci/qsql_oci.pri delete mode 100644 src/sql/drivers/oci/qsql_oci_p.h delete mode 100644 src/sql/drivers/odbc/qsql_odbc.cpp delete mode 100644 src/sql/drivers/odbc/qsql_odbc.pri delete mode 100644 src/sql/drivers/odbc/qsql_odbc_p.h delete mode 100644 src/sql/drivers/psql/qsql_psql.cpp delete mode 100644 src/sql/drivers/psql/qsql_psql.pri delete mode 100644 src/sql/drivers/psql/qsql_psql_p.h delete mode 100644 src/sql/drivers/sqlite/qsql_sqlite.cpp delete mode 100644 src/sql/drivers/sqlite/qsql_sqlite.pri delete mode 100644 src/sql/drivers/sqlite/qsql_sqlite_p.h delete mode 100644 src/sql/drivers/sqlite2/qsql_sqlite2.cpp delete mode 100644 src/sql/drivers/sqlite2/qsql_sqlite2.pri delete mode 100644 src/sql/drivers/sqlite2/qsql_sqlite2_p.h delete mode 100644 src/sql/drivers/tds/qsql_tds.cpp delete mode 100644 src/sql/drivers/tds/qsql_tds.pri delete mode 100644 src/sql/drivers/tds/qsql_tds_p.h (limited to 'src/sql/drivers') diff --git a/src/sql/drivers/db2/qsql_db2.cpp b/src/sql/drivers/db2/qsql_db2.cpp deleted file mode 100644 index 4ccc3aca9e..0000000000 --- a/src/sql/drivers/db2/qsql_db2.cpp +++ /dev/null @@ -1,1700 +0,0 @@ -/**************************************************************************** -** -** 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_db2_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(Q_CC_BOR) -// DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE -// and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to -// the right type before including the header -#define SQL_BIGINT_TYPE qint64 -#define SQL_BIGUINT_TYPE quint64 -#endif - -#define UNICODE - -#include - -#include - -QT_BEGIN_NAMESPACE - -static const int COLNAMESIZE = 255; -static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT }; - -class QDB2DriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QDB2Driver) - -public: - QDB2DriverPrivate() : QSqlDriverPrivate(), hEnv(0), hDbc(0) { dbmsType = QSqlDriver::DB2; } - SQLHANDLE hEnv; - SQLHANDLE hDbc; - QString user; -}; - -class QDB2ResultPrivate; - -class QDB2Result: public QSqlResult -{ - Q_DECLARE_PRIVATE(QDB2Result) - -public: - QDB2Result(const QDB2Driver *drv); - ~QDB2Result(); - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - QVariant data(int field) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - bool fetch(int i) Q_DECL_OVERRIDE; - bool fetchNext() Q_DECL_OVERRIDE; - bool fetchFirst() Q_DECL_OVERRIDE; - bool fetchLast() Q_DECL_OVERRIDE; - bool isNull(int i) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; - void detachFromResultSet() Q_DECL_OVERRIDE; - bool nextResult() Q_DECL_OVERRIDE; -}; - -class QDB2ResultPrivate: public QSqlResultPrivate -{ - Q_DECLARE_PUBLIC(QDB2Result) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QDB2Driver) - QDB2ResultPrivate(QDB2Result *q, const QDB2Driver *drv) - : QSqlResultPrivate(q, drv), - hStmt(0) - {} - ~QDB2ResultPrivate() - { - emptyValueCache(); - } - void clearValueCache() - { - for (int i = 0; i < valueCache.count(); ++i) { - delete valueCache[i]; - valueCache[i] = NULL; - } - } - void emptyValueCache() - { - clearValueCache(); - valueCache.clear(); - } - - SQLHANDLE hStmt; - QSqlRecord recInf; - QVector valueCache; -}; - -static QString qFromTChar(SQLTCHAR* str) -{ - return QString((const QChar *)str); -} - -// dangerous!! (but fast). Don't use in functions that -// require out parameters! -static SQLTCHAR* qToTChar(const QString& str) -{ - return (SQLTCHAR*)str.utf16(); -} - -static QString qWarnDB2Handle(int handleType, SQLHANDLE handle) -{ - SQLINTEGER nativeCode; - SQLSMALLINT msgLen; - SQLRETURN r = SQL_ERROR; - SQLTCHAR state[SQL_SQLSTATE_SIZE + 1]; - SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH]; - r = SQLGetDiagRec(handleType, - handle, - 1, - (SQLTCHAR*) state, - &nativeCode, - (SQLTCHAR*) description, - SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */ - &msgLen); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - return QString(qFromTChar(description)); - return QString(); -} - -static QString qDB2Warn(const QDB2DriverPrivate* d) -{ - return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ') - + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc)); -} - -static QString qDB2Warn(const QDB2ResultPrivate* d) -{ - return (qWarnDB2Handle(SQL_HANDLE_ENV, d->drv_d_func()->hEnv) + QLatin1Char(' ') - + qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->hDbc) - + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt)); -} - -static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d) -{ - qWarning("%s\tError: %s", message.toLocal8Bit().constData(), - qDB2Warn(d).toLocal8Bit().constData()); -} - -static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d) -{ - qWarning("%s\tError: %s", message.toLocal8Bit().constData(), - qDB2Warn(d).toLocal8Bit().constData()); -} - -static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, - const QDB2DriverPrivate* p) -{ - return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type); -} - -static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, - const QDB2ResultPrivate* p) -{ - return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type); -} - -static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype) -{ - QVariant::Type type = QVariant::Invalid; - switch (sqltype) { - case SQL_REAL: - case SQL_FLOAT: - case SQL_DOUBLE: - case SQL_DECIMAL: - case SQL_NUMERIC: - type = QVariant::Double; - break; - case SQL_SMALLINT: - case SQL_INTEGER: - case SQL_BIT: - case SQL_TINYINT: - type = QVariant::Int; - break; - case SQL_BIGINT: - type = QVariant::LongLong; - break; - case SQL_BLOB: - case SQL_BINARY: - case SQL_VARBINARY: - case SQL_LONGVARBINARY: - case SQL_CLOB: - case SQL_DBCLOB: - type = QVariant::ByteArray; - break; - case SQL_DATE: - case SQL_TYPE_DATE: - type = QVariant::Date; - break; - case SQL_TIME: - case SQL_TYPE_TIME: - type = QVariant::Time; - break; - case SQL_TIMESTAMP: - case SQL_TYPE_TIMESTAMP: - type = QVariant::DateTime; - break; - case SQL_WCHAR: - case SQL_WVARCHAR: - case SQL_WLONGVARCHAR: - case SQL_CHAR: - case SQL_VARCHAR: - case SQL_LONGVARCHAR: - type = QVariant::String; - break; - default: - type = QVariant::ByteArray; - break; - } - return type; -} - -static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i) -{ - SQLSMALLINT colNameLen; - SQLSMALLINT colType; - SQLUINTEGER colSize; - SQLSMALLINT colScale; - SQLSMALLINT nullable; - SQLRETURN r = SQL_ERROR; - SQLTCHAR colName[COLNAMESIZE]; - r = SQLDescribeCol(d->hStmt, - i+1, - colName, - (SQLSMALLINT) COLNAMESIZE, - &colNameLen, - &colType, - &colSize, - &colScale, - &nullable); - - if (r != SQL_SUCCESS) { - qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d); - return QSqlField(); - } - QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType)); - // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN - if (nullable == SQL_NO_NULLS) - f.setRequired(true); - else if (nullable == SQL_NULLABLE) - f.setRequired(false); - // else required is unknown - f.setLength(colSize == 0 ? -1 : int(colSize)); - f.setPrecision(colScale == 0 ? -1 : int(colScale)); - f.setSqlType(int(colType)); - return f; -} - -static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull) -{ - SQLINTEGER intbuf; - isNull = false; - SQLINTEGER lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column + 1, - SQL_C_SLONG, - (SQLPOINTER) &intbuf, - 0, - &lengthIndicator); - if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) { - isNull = true; - return 0; - } - return int(intbuf); -} - -static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull) -{ - SQLDOUBLE dblbuf; - isNull = false; - SQLINTEGER lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column+1, - SQL_C_DOUBLE, - (SQLPOINTER) &dblbuf, - 0, - &lengthIndicator); - if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) { - isNull = true; - return 0.0; - } - - return (double) dblbuf; -} - -static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull) -{ - SQLBIGINT lngbuf = Q_INT64_C(0); - isNull = false; - SQLINTEGER lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column+1, - SQL_C_SBIGINT, - (SQLPOINTER) &lngbuf, - 0, - &lengthIndicator); - if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) - isNull = true; - - return lngbuf; -} - -static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull) -{ - QString fieldVal; - SQLRETURN r = SQL_ERROR; - SQLINTEGER lengthIndicator = 0; - - if (colSize <= 0) - colSize = 255; - else if (colSize > 65536) // limit buffer size to 64 KB - colSize = 65536; - else - colSize++; // make sure there is room for more than the 0 termination - SQLTCHAR* buf = new SQLTCHAR[colSize]; - - while (true) { - r = SQLGetData(hStmt, - column + 1, - SQL_C_WCHAR, - (SQLPOINTER)buf, - colSize * sizeof(SQLTCHAR), - &lengthIndicator); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) { - fieldVal.clear(); - isNull = true; - break; - } - fieldVal += qFromTChar(buf); - } else if (r == SQL_NO_DATA) { - break; - } else { - qWarning("qGetStringData: Error while fetching data (%d)", r); - fieldVal.clear(); - break; - } - } - delete[] buf; - return fieldVal; -} - -static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull) -{ - QByteArray fieldVal; - SQLSMALLINT colNameLen; - SQLSMALLINT colType; - SQLUINTEGER colSize; - SQLSMALLINT colScale; - SQLSMALLINT nullable; - SQLRETURN r = SQL_ERROR; - - SQLTCHAR colName[COLNAMESIZE]; - r = SQLDescribeCol(hStmt, - column+1, - colName, - COLNAMESIZE, - &colNameLen, - &colType, - &colSize, - &colScale, - &nullable); - if (r != SQL_SUCCESS) - qWarning("qGetBinaryData: Unable to describe column %d", column); - // SQLDescribeCol may return 0 if size cannot be determined - if (!colSize) - colSize = 255; - else if (colSize > 65536) // read the field in 64 KB chunks - colSize = 65536; - char * buf = new char[colSize]; - while (true) { - r = SQLGetData(hStmt, - column+1, - colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY, - (SQLPOINTER) buf, - colSize, - &lengthIndicator); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - if (lengthIndicator == SQL_NULL_DATA) { - isNull = true; - break; - } else { - int rSize; - r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize; - if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined - rSize = colSize; - fieldVal.append(QByteArray(buf, rSize)); - if (r == SQL_SUCCESS) // the whole field was read in one chunk - break; - } - } else { - break; - } - } - delete [] buf; - return fieldVal; -} - -static void qSplitTableQualifier(const QString & qualifier, QString * catalog, - QString * schema, QString * table) -{ - if (!catalog || !schema || !table) - return; - QStringList l = qualifier.split(QLatin1Char('.')); - if (l.count() > 3) - return; // can't possibly be a valid table qualifier - int i = 0, n = l.count(); - if (n == 1) { - *table = qualifier; - } else { - for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { - if (n == 3) { - if (i == 0) - *catalog = *it; - else if (i == 1) - *schema = *it; - else if (i == 2) - *table = *it; - } else if (n == 2) { - if (i == 0) - *schema = *it; - else if (i == 1) - *table = *it; - } - i++; - } - } -} - -// creates a QSqlField from a valid hStmt generated -// by SQLColumns. The hStmt has to point to a valid position. -static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt) -{ - bool isNull; - int type = qGetIntData(hStmt, 4, isNull); - QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type)); - int required = qGetIntData(hStmt, 10, isNull); // nullable-flag - // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN - if (required == SQL_NO_NULLS) - f.setRequired(true); - else if (required == SQL_NULLABLE) - f.setRequired(false); - // else we don't know. - f.setLength(qGetIntData(hStmt, 6, isNull)); // column size - f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision - f.setSqlType(type); - return f; -} - -static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true) -{ - SQLRETURN r; - if (!d->hStmt) { - r = SQLAllocHandle(SQL_HANDLE_STMT, - d->drv_d_func()->hDbc, - &d->hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d); - return false; - } - } else { - r = SQLFreeStmt(d->hStmt, SQL_CLOSE); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d); - return false; - } - } - - if (!setForwardOnly) - return true; - - if (forwardOnly) { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - } else { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER) SQL_CURSOR_STATIC, - SQL_IS_UINTEGER); - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg( - forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY") - : QLatin1String("SQL_CURSOR_STATIC")), d); - return false; - } - return true; -} - -QVariant QDB2Result::handle() const -{ - Q_D(const QDB2Result); - return QVariant(qRegisterMetaType("SQLHANDLE"), &d->hStmt); -} - -/************************************/ - -QDB2Result::QDB2Result(const QDB2Driver *drv) - : QSqlResult(*new QDB2ResultPrivate(this, drv)) -{ -} - -QDB2Result::~QDB2Result() -{ - Q_D(const QDB2Result); - if (d->hStmt) { - SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") - + QString::number(r), d); - } -} - -bool QDB2Result::reset (const QString& query) -{ - Q_D(QDB2Result); - setActive(false); - setAt(QSql::BeforeFirstRow); - SQLRETURN r; - - d->recInf.clear(); - d->emptyValueCache(); - - if (!qMakeStatement(d, isForwardOnly())) - return false; - - r = SQLExecDirect(d->hStmt, - qToTChar(query), - (SQLINTEGER) query.length()); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to execute statement"), QSqlError::StatementError, d)); - return false; - } - SQLSMALLINT count = 0; - r = SQLNumResultCols(d->hStmt, &count); - if (count) { - setSelect(true); - for (int i = 0; i < count; ++i) { - d->recInf.append(qMakeFieldInfo(d, i)); - } - } else { - setSelect(false); - } - d->valueCache.resize(count); - d->valueCache.fill(NULL); - setActive(true); - return true; -} - -bool QDB2Result::prepare(const QString& query) -{ - Q_D(QDB2Result); - setActive(false); - setAt(QSql::BeforeFirstRow); - SQLRETURN r; - - d->recInf.clear(); - d->emptyValueCache(); - - if (!qMakeStatement(d, isForwardOnly())) - return false; - - r = SQLPrepare(d->hStmt, - qToTChar(query), - (SQLINTEGER) query.length()); - - if (r != SQL_SUCCESS) { - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to prepare statement"), QSqlError::StatementError, d)); - return false; - } - return true; -} - -bool QDB2Result::exec() -{ - Q_D(QDB2Result); - QList tmpStorage; // holds temporary ptrs - QVarLengthArray indicators(boundValues().count()); - - memset(indicators.data(), 0, indicators.size() * sizeof(SQLINTEGER)); - setActive(false); - setAt(QSql::BeforeFirstRow); - SQLRETURN r; - - d->recInf.clear(); - d->emptyValueCache(); - - if (!qMakeStatement(d, isForwardOnly(), false)) - return false; - - - QVector &values = boundValues(); - int i; - for (i = 0; i < values.count(); ++i) { - // bind parameters - only positional binding allowed - SQLINTEGER *ind = &indicators[i]; - if (values.at(i).isNull()) - *ind = SQL_NULL_DATA; - if (bindValueType(i) & QSql::Out) - values[i].detach(); - - switch (values.at(i).type()) { - case QVariant::Date: { - QByteArray ba; - ba.resize(sizeof(DATE_STRUCT)); - DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData(); - QDate qdt = values.at(i).toDate(); - dt->year = qdt.year(); - dt->month = qdt.month(); - dt->day = qdt.day(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_DATE, - SQL_DATE, - 0, - 0, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - tmpStorage.append(ba); - break; } - case QVariant::Time: { - QByteArray ba; - ba.resize(sizeof(TIME_STRUCT)); - TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData(); - QTime qdt = values.at(i).toTime(); - dt->hour = qdt.hour(); - dt->minute = qdt.minute(); - dt->second = qdt.second(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_TIME, - SQL_TIME, - 0, - 0, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - tmpStorage.append(ba); - break; } - case QVariant::DateTime: { - QByteArray ba; - ba.resize(sizeof(TIMESTAMP_STRUCT)); - TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData(); - QDateTime qdt = values.at(i).toDateTime(); - dt->year = qdt.date().year(); - dt->month = qdt.date().month(); - dt->day = qdt.date().day(); - dt->hour = qdt.time().hour(); - dt->minute = qdt.time().minute(); - dt->second = qdt.time().second(); - dt->fraction = qdt.time().msec() * 1000000; - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_TIMESTAMP, - SQL_TIMESTAMP, - 0, - 0, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - tmpStorage.append(ba); - break; } - case QVariant::Int: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_SLONG, - SQL_INTEGER, - 0, - 0, - (void *)values.at(i).constData(), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::Double: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_DOUBLE, - SQL_DOUBLE, - 0, - 0, - (void *)values.at(i).constData(), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::ByteArray: { - int len = values.at(i).toByteArray().size(); - if (*ind != SQL_NULL_DATA) - *ind = len; - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_BINARY, - SQL_LONGVARBINARY, - len, - 0, - (void *)values.at(i).toByteArray().constData(), - len, - ind); - break; } - case QVariant::String: - { - QString str(values.at(i).toString()); - if (*ind != SQL_NULL_DATA) - *ind = str.length() * sizeof(QChar); - if (bindValueType(i) & QSql::Out) { - QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar)); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_WCHAR, - SQL_WVARCHAR, - str.length(), - 0, - (void *)ba.constData(), - ba.size(), - ind); - tmpStorage.append(ba); - } else { - void *data = (void*)str.utf16(); - int len = str.length(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_WCHAR, - SQL_WVARCHAR, - len, - 0, - data, - len * sizeof(QChar), - ind); - } - break; - } - default: { - QByteArray ba = values.at(i).toString().toLatin1(); - int len = ba.length() + 1; - if (*ind != SQL_NULL_DATA) - *ind = ba.length(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & 3], - SQL_C_CHAR, - SQL_VARCHAR, - len, - 0, - (void *) ba.constData(), - len, - ind); - tmpStorage.append(ba); - break; } - } - if (r != SQL_SUCCESS) { - qWarning("QDB2Result::exec: unable to bind variable: %s", - qDB2Warn(d).toLocal8Bit().constData()); - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to bind variable"), QSqlError::StatementError, d)); - return false; - } - } - - r = SQLExecute(d->hStmt); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qWarning("QDB2Result::exec: Unable to execute statement: %s", - qDB2Warn(d).toLocal8Bit().constData()); - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to execute statement"), QSqlError::StatementError, d)); - return false; - } - SQLSMALLINT count = 0; - r = SQLNumResultCols(d->hStmt, &count); - if (count) { - setSelect(true); - for (int i = 0; i < count; ++i) { - d->recInf.append(qMakeFieldInfo(d, i)); - } - } else { - setSelect(false); - } - setActive(true); - d->valueCache.resize(count); - d->valueCache.fill(NULL); - - //get out parameters - if (!hasOutValues()) - return true; - - for (i = 0; i < values.count(); ++i) { - switch (values[i].type()) { - case QVariant::Date: { - DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData()); - values[i] = QVariant(QDate(ds.year, ds.month, ds.day)); - break; } - case QVariant::Time: { - TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData()); - values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second)); - break; } - case QVariant::DateTime: { - TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData()); - values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day), - QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000))); - break; } - case QVariant::Int: - case QVariant::Double: - case QVariant::ByteArray: - break; - case QVariant::String: - if (bindValueType(i) & QSql::Out) - values[i] = QString((const QChar *)tmpStorage.takeFirst().constData()); - break; - default: { - values[i] = QString::fromLatin1(tmpStorage.takeFirst().constData()); - break; } - } - if (indicators[i] == SQL_NULL_DATA) - values[i] = QVariant(values[i].type()); - } - return true; -} - -bool QDB2Result::fetch(int i) -{ - Q_D(QDB2Result); - if (isForwardOnly() && i < at()) - return false; - if (i == at()) - return true; - d->clearValueCache(); - int actualIdx = i + 1; - if (actualIdx <= 0) { - setAt(QSql::BeforeFirstRow); - return false; - } - SQLRETURN r; - if (isForwardOnly()) { - bool ok = true; - while (ok && i > at()) - ok = fetchNext(); - return ok; - } else { - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_ABSOLUTE, - actualIdx); - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) { - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to fetch record %1").arg(i), QSqlError::StatementError, d)); - return false; - } - else if (r == SQL_NO_DATA) - return false; - setAt(i); - return true; -} - -bool QDB2Result::fetchNext() -{ - Q_D(QDB2Result); - SQLRETURN r; - d->clearValueCache(); - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_NEXT, - 0); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", - "Unable to fetch next"), QSqlError::StatementError, d)); - return false; - } - setAt(at() + 1); - return true; -} - -bool QDB2Result::fetchFirst() -{ - Q_D(QDB2Result); - if (isForwardOnly() && at() != QSql::BeforeFirstRow) - return false; - if (isForwardOnly()) - return fetchNext(); - d->clearValueCache(); - SQLRETURN r; - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_FIRST, - 0); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - if(r!= SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"), - QSqlError::StatementError, d)); - return false; - } - setAt(0); - return true; -} - -bool QDB2Result::fetchLast() -{ - Q_D(QDB2Result); - d->clearValueCache(); - - int i = at(); - if (i == QSql::AfterLastRow) { - if (isForwardOnly()) { - return false; - } else { - if (!fetch(0)) - return false; - i = at(); - } - } - - while (fetchNext()) - ++i; - - if (i == QSql::BeforeFirstRow) { - setAt(QSql::AfterLastRow); - return false; - } - - if (!isForwardOnly()) - return fetch(i); - - setAt(i); - return true; -} - - -QVariant QDB2Result::data(int field) -{ - Q_D(QDB2Result); - if (field >= d->recInf.count()) { - qWarning("QDB2Result::data: column %d out of range", field); - return QVariant(); - } - SQLRETURN r = 0; - SQLINTEGER lengthIndicator = 0; - bool isNull = false; - const QSqlField info = d->recInf.field(field); - - if (!info.isValid() || field >= d->valueCache.size()) - return QVariant(); - - if (d->valueCache[field]) - return *d->valueCache[field]; - - - QVariant* v = 0; - switch (info.type()) { - case QVariant::LongLong: - v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull)); - break; - case QVariant::Int: - v = new QVariant(qGetIntData(d->hStmt, field, isNull)); - break; - case QVariant::Date: { - DATE_STRUCT dbuf; - r = SQLGetData(d->hStmt, - field + 1, - SQL_C_DATE, - (SQLPOINTER) &dbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { - v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day)); - } else { - v = new QVariant(QDate()); - isNull = true; - } - break; } - case QVariant::Time: { - TIME_STRUCT tbuf; - r = SQLGetData(d->hStmt, - field + 1, - SQL_C_TIME, - (SQLPOINTER) &tbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { - v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second)); - } else { - v = new QVariant(QTime()); - isNull = true; - } - break; } - case QVariant::DateTime: { - TIMESTAMP_STRUCT dtbuf; - r = SQLGetData(d->hStmt, - field + 1, - SQL_C_TIMESTAMP, - (SQLPOINTER) &dtbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) { - v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day), - QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000))); - } else { - v = new QVariant(QDateTime()); - isNull = true; - } - break; } - case QVariant::ByteArray: - v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull)); - break; - case QVariant::Double: - { - switch(numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - v = new QVariant(qGetIntData(d->hStmt, field, isNull)); - break; - case QSql::LowPrecisionInt64: - v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull)); - break; - case QSql::LowPrecisionDouble: - v = new QVariant(qGetDoubleData(d->hStmt, field, isNull)); - break; - case QSql::HighPrecision: - default: - // length + 1 for the comma - v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull)); - break; - } - break; - } - case QVariant::String: - default: - v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull)); - break; - } - if (isNull) - *v = QVariant(info.type()); - d->valueCache[field] = v; - return *v; -} - -bool QDB2Result::isNull(int i) -{ - Q_D(const QDB2Result); - if (i >= d->valueCache.size()) - return true; - - if (d->valueCache[i]) - return d->valueCache[i]->isNull(); - return data(i).isNull(); -} - -int QDB2Result::numRowsAffected() -{ - Q_D(const QDB2Result); - SQLINTEGER affectedRowCount = 0; - SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - return affectedRowCount; - else - qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d); - return -1; -} - -int QDB2Result::size() -{ - return -1; -} - -QSqlRecord QDB2Result::record() const -{ - Q_D(const QDB2Result); - if (isActive()) - return d->recInf; - return QSqlRecord(); -} - -bool QDB2Result::nextResult() -{ - Q_D(QDB2Result); - setActive(false); - setAt(QSql::BeforeFirstRow); - d->recInf.clear(); - d->emptyValueCache(); - setSelect(false); - - SQLRETURN r = SQLMoreResults(d->hStmt); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - if (r != SQL_NO_DATA) { - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch last"), QSqlError::ConnectionError, d)); - } - return false; - } - - SQLSMALLINT fieldCount = 0; - r = SQLNumResultCols(d->hStmt, &fieldCount); - setSelect(fieldCount > 0); - for (int i = 0; i < fieldCount; ++i) - d->recInf.append(qMakeFieldInfo(d, i)); - - d->valueCache.resize(fieldCount); - d->valueCache.fill(NULL); - setActive(true); - - return true; -} - -void QDB2Result::virtual_hook(int id, void *data) -{ - QSqlResult::virtual_hook(id, data); -} - -void QDB2Result::detachFromResultSet() -{ - Q_D(QDB2Result); - if (d->hStmt) - SQLCloseCursor(d->hStmt); -} - -/************************************/ - -QDB2Driver::QDB2Driver(QObject* parent) - : QSqlDriver(*new QDB2DriverPrivate, parent) -{ -} - -QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent) - : QSqlDriver(*new QDB2DriverPrivate, parent) -{ - Q_D(QDB2Driver); - d->hEnv = reinterpret_cast(env); - d->hDbc = reinterpret_cast(con); - if (env && con) { - setOpen(true); - setOpenError(false); - } -} - -QDB2Driver::~QDB2Driver() -{ - close(); -} - -bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port, - const QString& connOpts) -{ - Q_D(QDB2Driver); - if (isOpen()) - close(); - SQLRETURN r; - r = SQLAllocHandle(SQL_HANDLE_ENV, - SQL_NULL_HANDLE, - &d->hEnv); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d); - setOpenError(true); - return false; - } - - r = SQLAllocHandle(SQL_HANDLE_DBC, - d->hEnv, - &d->hDbc); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d); - setOpenError(true); - return false; - } - - QString protocol; - // Set connection attributes - const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts)); - for (int i = 0; i < opts.count(); ++i) { - const QString tmp(opts.at(i)); - int idx; - if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) { - qWarning("QDB2Driver::open: Illegal connect option value '%s'", - tmp.toLocal8Bit().constData()); - continue; - } - - const QString opt(tmp.left(idx)); - const QString val(tmp.mid(idx + 1).simplified()); - - SQLUINTEGER v = 0; - r = SQL_SUCCESS; - if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) { - if (val == QLatin1String("SQL_MODE_READ_ONLY")) { - v = SQL_MODE_READ_ONLY; - } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) { - v = SQL_MODE_READ_WRITE; - } else { - qWarning("QDB2Driver::open: Unknown option value '%s'", - tmp.toLocal8Bit().constData()); - continue; - } - r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, reinterpret_cast(v), 0); - } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) { - v = val.toUInt(); - r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast(v), 0); - } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) { - protocol = tmp; - } - else { - qWarning("QDB2Driver::open: Unknown connection attribute '%s'", - tmp.toLocal8Bit().constData()); - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - qSqlWarning(QString::fromLatin1("QDB2Driver::open: " - "Unable to set connection attribute '%1'").arg(opt), d); - } - - if (protocol.isEmpty()) - protocol = QLatin1String("PROTOCOL=TCPIP"); - - if (port < 0 ) - port = 50000; - - QString connQStr; - connQStr = protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host - + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user - + QLatin1String(";PWD=") + password; - - - SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH]; - SQLSMALLINT cb; - - r = SQLDriverConnect(d->hDbc, - NULL, - qToTChar(connQStr), - (SQLSMALLINT) connQStr.length(), - connOut, - SQL_MAX_OPTION_STRING_LENGTH, - &cb, - SQL_DRIVER_NOPROMPT); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - setLastError(qMakeError(tr("Unable to connect"), - QSqlError::ConnectionError, d)); - setOpenError(true); - return false; - } - - d->user = user; - setOpen(true); - setOpenError(false); - return true; -} - -void QDB2Driver::close() -{ - Q_D(QDB2Driver); - SQLRETURN r; - if (d->hDbc) { - // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect - if (isOpen()) { - r = SQLDisconnect(d->hDbc); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d); - } - r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d); - d->hDbc = 0; - } - - if (d->hEnv) { - r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d); - d->hEnv = 0; - } - setOpen(false); - setOpenError(false); -} - -QSqlResult *QDB2Driver::createResult() const -{ - return new QDB2Result(this); -} - -QSqlRecord QDB2Driver::record(const QString& tableName) const -{ - Q_D(const QDB2Driver); - QSqlRecord fil; - if (!isOpen()) - return fil; - - SQLHANDLE hStmt; - QString catalog, schema, table; - qSplitTableQualifier(tableName, &catalog, &schema, &table); - if (schema.isEmpty()) - schema = d->user; - - if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) - catalog = stripDelimiters(catalog, QSqlDriver::TableName); - else - catalog = catalog.toUpper(); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = schema.toUpper(); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d); - return fil; - } - - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - - - //Aside: szSchemaName and szTableName parameters of SQLColumns - //are case sensitive search patterns, so no escaping is used. - r = SQLColumns(hStmt, - NULL, - 0, - qToTChar(schema), - schema.length(), - qToTChar(table), - table.length(), - NULL, - 0); - - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - while (r == SQL_SUCCESS) { - fil.append(qMakeFieldInfo(hStmt)); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - } - - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") - + QString::number(r), d); - - return fil; -} - -QStringList QDB2Driver::tables(QSql::TableType type) const -{ - Q_D(const QDB2Driver); - QStringList tl; - if (!isOpen()) - return tl; - - SQLHANDLE hStmt; - - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d); - return tl; - } - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - - QString tableType; - if (type & QSql::Tables) - tableType += QLatin1String("TABLE,"); - if (type & QSql::Views) - tableType += QLatin1String("VIEW,"); - if (type & QSql::SystemTables) - tableType += QLatin1String("SYSTEM TABLE,"); - if (tableType.isEmpty()) - return tl; - tableType.chop(1); - - r = SQLTables(hStmt, - NULL, - 0, - NULL, - 0, - NULL, - 0, - qToTChar(tableType), - tableType.length()); - - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - while (r == SQL_SUCCESS) { - bool isNull; - QString fieldVal = qGetStringData(hStmt, 2, -1, isNull); - QString userVal = qGetStringData(hStmt, 1, -1, isNull); - QString user = d->user; - if ( isIdentifierEscaped(user, QSqlDriver::TableName)) - user = stripDelimiters(user, QSqlDriver::TableName); - else - user = user.toUpper(); - - if (userVal != user) - fieldVal = userVal + QLatin1Char('.') + fieldVal; - tl.append(fieldVal); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - } - - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ") - + QString::number(r), d); - return tl; -} - -QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const -{ - Q_D(const QDB2Driver); - QSqlIndex index(tablename); - if (!isOpen()) - return index; - QSqlRecord rec = record(tablename); - - SQLHANDLE hStmt; - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d); - return index; - } - QString catalog, schema, table; - qSplitTableQualifier(tablename, &catalog, &schema, &table); - - if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) - catalog = stripDelimiters(catalog, QSqlDriver::TableName); - else - catalog = catalog.toUpper(); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = schema.toUpper(); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - - r = SQLPrimaryKeys(hStmt, - NULL, - 0, - qToTChar(schema), - schema.length(), - qToTChar(table), - table.length()); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - - bool isNull; - QString cName, idxName; - // Store all fields in a StringList because the driver can't detail fields in this FETCH loop - while (r == SQL_SUCCESS) { - cName = qGetStringData(hStmt, 3, -1, isNull); // column name - idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name - index.append(rec.field(cName)); - index.setName(idxName); - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - } - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r!= SQL_SUCCESS) - qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ") - + QString::number(r), d); - return index; -} - -bool QDB2Driver::hasFeature(DriverFeature f) const -{ - switch (f) { - case QuerySize: - case NamedPlaceholders: - case BatchOperations: - case LastInsertId: - case SimpleLocking: - case EventNotifications: - case CancelQuery: - return false; - case BLOB: - case Transactions: - case MultipleResultSets: - case PreparedQueries: - case PositionalPlaceholders: - case LowPrecisionNumbers: - case FinishQuery: - return true; - case Unicode: - return true; - } - return false; -} - -bool QDB2Driver::beginTransaction() -{ - if (!isOpen()) { - qWarning("QDB2Driver::beginTransaction: Database not open"); - return false; - } - return setAutoCommit(false); -} - -bool QDB2Driver::commitTransaction() -{ - Q_D(QDB2Driver); - if (!isOpen()) { - qWarning("QDB2Driver::commitTransaction: Database not open"); - return false; - } - SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, - d->hDbc, - SQL_COMMIT); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to commit transaction"), - QSqlError::TransactionError, d)); - return false; - } - return setAutoCommit(true); -} - -bool QDB2Driver::rollbackTransaction() -{ - Q_D(QDB2Driver); - if (!isOpen()) { - qWarning("QDB2Driver::rollbackTransaction: Database not open"); - return false; - } - SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, - d->hDbc, - SQL_ROLLBACK); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to rollback transaction"), - QSqlError::TransactionError, d)); - return false; - } - return setAutoCommit(true); -} - -bool QDB2Driver::setAutoCommit(bool autoCommit) -{ - Q_D(QDB2Driver); - SQLUINTEGER ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; - SQLRETURN r = SQLSetConnectAttr(d->hDbc, - SQL_ATTR_AUTOCOMMIT, - reinterpret_cast(ac), - sizeof(ac)); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to set autocommit"), - QSqlError::TransactionError, d)); - return false; - } - return true; -} - -QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const -{ - if (field.isNull()) - return QLatin1String("NULL"); - - switch (field.type()) { - case QVariant::DateTime: { - // Use an escape sequence for the datetime fields - if (field.value().toDateTime().isValid()) { - QDate dt = field.value().toDateTime().date(); - QTime tm = field.value().toDateTime().time(); - // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10 - return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-') - + QString::number(dt.month()) + QLatin1Char('-') - + QString::number(dt.day()) + QLatin1Char('-') - + QString::number(tm.hour()) + QLatin1Char('.') - + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true) - + QLatin1Char('.') - + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true) - + QLatin1Char('.') - + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true) - + QLatin1Char('\''); - } else { - return QLatin1String("NULL"); - } - } - case QVariant::ByteArray: { - QByteArray ba = field.value().toByteArray(); - QString res = QString::fromLatin1("BLOB(X'"); - static const char hexchars[] = "0123456789abcdef"; - for (int i = 0; i < ba.size(); ++i) { - uchar s = (uchar) ba[i]; - res += QLatin1Char(hexchars[s >> 4]); - res += QLatin1Char(hexchars[s & 0x0f]); - } - res += QLatin1String("')"); - return res; - } - default: - return QSqlDriver::formatValue(field, trimStrings); - } -} - -QVariant QDB2Driver::handle() const -{ - Q_D(const QDB2Driver); - return QVariant(qRegisterMetaType("SQLHANDLE"), &d->hDbc); -} - -QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/db2/qsql_db2.pri b/src/sql/drivers/db2/qsql_db2.pri deleted file mode 100644 index c9e65e2c2e..0000000000 --- a/src/sql/drivers/db2/qsql_db2.pri +++ /dev/null @@ -1,8 +0,0 @@ -HEADERS += $$PWD/qsql_db2_p.h -SOURCES += $$PWD/qsql_db2.cpp - -unix { - !contains(LIBS, .*db2.*):LIBS += -ldb2 -} else { - !contains(LIBS, .*db2.*):LIBS += -ldb2cli -} diff --git a/src/sql/drivers/db2/qsql_db2_p.h b/src/sql/drivers/db2/qsql_db2_p.h deleted file mode 100644 index fa6d739479..0000000000 --- a/src/sql/drivers/db2/qsql_db2_p.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** 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_DB2_H -#define QSQL_DB2_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 - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_DB2 -#else -#define Q_EXPORT_SQLDRIVER_DB2 Q_SQL_EXPORT -#endif - -#include - -QT_BEGIN_NAMESPACE - -class QDB2DriverPrivate; - -class Q_EXPORT_SQLDRIVER_DB2 QDB2Driver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QDB2Driver) - Q_OBJECT - friend class QDB2ResultPrivate; - -public: - explicit QDB2Driver(QObject* parent = 0); - QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent = 0); - ~QDB2Driver(); - bool hasFeature(DriverFeature) const Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlRecord record(const QString &tableName) const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType type) const Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE; - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString& connOpts) Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - -private: - bool setAutoCommit(bool autoCommit); -}; - -QT_END_NAMESPACE - -#endif // QSQL_DB2_H diff --git a/src/sql/drivers/drivers.pri b/src/sql/drivers/drivers.pri deleted file mode 100644 index 87cc0b1d9e..0000000000 --- a/src/sql/drivers/drivers.pri +++ /dev/null @@ -1,11 +0,0 @@ -contains(sql-drivers, all):sql-driver += psql mysql odbc oci tds db2 sqlite ibase - -contains(sql-drivers, psql):include($$PWD/psql/qsql_psql.pri) -contains(sql-drivers, mysql):include($$PWD/mysql/qsql_mysql.pri) -contains(sql-drivers, odbc):include($$PWD/odbc/qsql_odbc.pri) -contains(sql-drivers, oci):include($$PWD/oci/qsql_oci.pri) -contains(sql-drivers, tds):include($$PWD/tds/qsql_tds.pri) -contains(sql-drivers, db2):include($$PWD/db2/qsql_db2.pri) -contains(sql-drivers, ibase):include($$PWD/ibase/qsql_ibase.pri) -contains(sql-drivers, sqlite2):include($$PWD/sqlite2/qsql_sqlite2.pri) -contains(sql-drivers, sqlite):include($$PWD/sqlite/qsql_sqlite.pri) diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp deleted file mode 100644 index 6fd91b6b76..0000000000 --- a/src/sql/drivers/ibase/qsql_ibase.cpp +++ /dev/null @@ -1,1948 +0,0 @@ -/**************************************************************************** -** -** 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_ibase_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -#define FBVERSION SQL_DIALECT_V6 - -#ifndef SQLDA_CURRENT_VERSION -#define SQLDA_CURRENT_VERSION SQLDA_VERSION1 -#endif - -enum { QIBaseChunkSize = SHRT_MAX / 2 }; - -#if defined(FB_API_VER) && FB_API_VER >= 20 -static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc) -#else -static bool getIBaseError(QString& msg, ISC_STATUS* status, ISC_LONG &sqlcode, QTextCodec *tc) -#endif -{ - if (status[0] != 1 || status[1] <= 0) - return false; - - msg.clear(); - sqlcode = isc_sqlcode(status); - char buf[512]; -#if defined(FB_API_VER) && FB_API_VER >= 20 - while(fb_interpret(buf, 512, &status)) { -#else - while(isc_interprete(buf, &status)) { -#endif - if(!msg.isEmpty()) - msg += QLatin1String(" - "); - if (tc) - msg += tc->toUnicode(buf); - else - msg += QString::fromUtf8(buf); - } - return true; -} - -static void createDA(XSQLDA *&sqlda) -{ - sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1)); - if (sqlda == (XSQLDA*)0) return; - sqlda->sqln = 1; - sqlda->sqld = 0; - sqlda->version = SQLDA_CURRENT_VERSION; - sqlda->sqlvar[0].sqlind = 0; - sqlda->sqlvar[0].sqldata = 0; -} - -static void enlargeDA(XSQLDA *&sqlda, int n) -{ - if (sqlda != (XSQLDA*)0) - free(sqlda); - sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n)); - if (sqlda == (XSQLDA*)0) return; - sqlda->sqln = n; - sqlda->version = SQLDA_CURRENT_VERSION; -} - -static void initDA(XSQLDA *sqlda) -{ - for (int i = 0; i < sqlda->sqld; ++i) { - switch (sqlda->sqlvar[i].sqltype & ~1) { - case SQL_INT64: - case SQL_LONG: - case SQL_SHORT: - case SQL_FLOAT: - case SQL_DOUBLE: - case SQL_TIMESTAMP: - case SQL_TYPE_TIME: - case SQL_TYPE_DATE: - case SQL_TEXT: - case SQL_BLOB: - sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen]; - break; - case SQL_ARRAY: - sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)]; - memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD)); - break; - case SQL_VARYING: - sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)]; - break; - default: - // not supported - do not bind. - sqlda->sqlvar[i].sqldata = 0; - break; - } - if (sqlda->sqlvar[i].sqltype & 1) { - sqlda->sqlvar[i].sqlind = new short[1]; - *(sqlda->sqlvar[i].sqlind) = 0; - } else { - sqlda->sqlvar[i].sqlind = 0; - } - } -} - -static void delDA(XSQLDA *&sqlda) -{ - if (!sqlda) - return; - for (int i = 0; i < sqlda->sqld; ++i) { - delete [] sqlda->sqlvar[i].sqlind; - delete [] sqlda->sqlvar[i].sqldata; - } - free(sqlda); - sqlda = 0; -} - -static QVariant::Type qIBaseTypeName(int iType, bool hasScale) -{ - switch (iType) { - case blr_varying: - case blr_varying2: - case blr_text: - case blr_cstring: - case blr_cstring2: - return QVariant::String; - case blr_sql_time: - return QVariant::Time; - case blr_sql_date: - return QVariant::Date; - case blr_timestamp: - return QVariant::DateTime; - case blr_blob: - return QVariant::ByteArray; - case blr_quad: - case blr_short: - case blr_long: - return (hasScale ? QVariant::Double : QVariant::Int); - case blr_int64: - return (hasScale ? QVariant::Double : QVariant::LongLong); - case blr_float: - case blr_d_float: - case blr_double: - return QVariant::Double; - } - qWarning("qIBaseTypeName: unknown datatype: %d", iType); - return QVariant::Invalid; -} - -static QVariant::Type qIBaseTypeName2(int iType, bool hasScale) -{ - switch(iType & ~1) { - case SQL_VARYING: - case SQL_TEXT: - return QVariant::String; - case SQL_LONG: - case SQL_SHORT: - return (hasScale ? QVariant::Double : QVariant::Int); - case SQL_INT64: - return (hasScale ? QVariant::Double : QVariant::LongLong); - case SQL_FLOAT: - case SQL_DOUBLE: - return QVariant::Double; - case SQL_TIMESTAMP: - return QVariant::DateTime; - case SQL_TYPE_TIME: - return QVariant::Time; - case SQL_TYPE_DATE: - return QVariant::Date; - case SQL_ARRAY: - return QVariant::List; - case SQL_BLOB: - return QVariant::ByteArray; - default: - return QVariant::Invalid; - } -} - -static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt) -{ - static const QTime midnight(0, 0, 0, 0); - static const QDate basedate(1858, 11, 17); - ISC_TIMESTAMP ts; - ts.timestamp_time = midnight.msecsTo(dt.time()) * 10; - ts.timestamp_date = basedate.daysTo(dt.date()); - return ts; -} - -static QDateTime fromTimeStamp(char *buffer) -{ - static const QDate bd(1858, 11, 17); - QTime t(0, 0); - QDate d; - - // have to demangle the structure ourselves because isc_decode_time - // strips the msecs - t = t.addMSecs(int(((ISC_TIMESTAMP*)buffer)->timestamp_time / 10)); - d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date)); - - return QDateTime(d, t); -} - -static ISC_TIME toTime(const QTime &t) -{ - static const QTime midnight(0, 0, 0, 0); - return (ISC_TIME)midnight.msecsTo(t) * 10; -} - -static QTime fromTime(char *buffer) -{ - QTime t(0, 0); - // have to demangle the structure ourselves because isc_decode_time - // strips the msecs - t = t.addMSecs(int((*(ISC_TIME*)buffer) / 10)); - - return t; -} - -static ISC_DATE toDate(const QDate &t) -{ - static const QDate basedate(1858, 11, 17); - ISC_DATE date; - - date = basedate.daysTo(t); - return date; -} - -static QDate fromDate(char *buffer) -{ - static const QDate bd(1858, 11, 17); - QDate d; - - // have to demangle the structure ourselves because isc_decode_time - // strips the msecs - d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date)); - - return d; -} - -static QByteArray encodeString(QTextCodec *tc, const QString &str) -{ - if (tc) - return tc->fromUnicode(str); - return str.toUtf8(); -} - -struct QIBaseEventBuffer { -#if defined(FB_API_VER) && FB_API_VER >= 20 - ISC_UCHAR *eventBuffer; - ISC_UCHAR *resultBuffer; -#else - char *eventBuffer; - char *resultBuffer; -#endif - ISC_LONG bufferLength; - ISC_LONG eventId; - - enum QIBaseSubscriptionState { Starting, Subscribed, Finished }; - QIBaseSubscriptionState subscriptionState; -}; - -class QIBaseDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QIBaseDriver) -public: - QIBaseDriverPrivate() : QSqlDriverPrivate(), ibase(0), trans(0), tc(0) { dbmsType = QSqlDriver::Interbase; } - - bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError) - { - Q_Q(QIBaseDriver); - QString imsg; - ISC_LONG sqlcode; - if (!getIBaseError(imsg, status, sqlcode, tc)) - return false; - - q->setLastError(QSqlError(QCoreApplication::translate("QIBaseDriver", msg), - imsg, typ, int(sqlcode))); - return true; - } - -public: - isc_db_handle ibase; - isc_tr_handle trans; - QTextCodec *tc; - ISC_STATUS status[20]; - QMap eventBuffers; -}; - -typedef QMap QIBaseBufferDriverMap; -Q_GLOBAL_STATIC(QIBaseBufferDriverMap, qBufferDriverMap) -Q_GLOBAL_STATIC(QMutex, qMutex); - -static void qFreeEventBuffer(QIBaseEventBuffer* eBuffer) -{ - qMutex()->lock(); - qBufferDriverMap()->remove(reinterpret_cast(eBuffer->resultBuffer)); - qMutex()->unlock(); - delete eBuffer; -} - -class QIBaseResultPrivate; - -class QIBaseResult : public QSqlCachedResult -{ - Q_DECLARE_PRIVATE(QIBaseResult) - -public: - explicit QIBaseResult(const QIBaseDriver* db); - - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) Q_DECL_OVERRIDE; - bool reset (const QString &query) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; -}; - -class QIBaseResultPrivate: public QSqlCachedResultPrivate -{ - Q_DECLARE_PUBLIC(QIBaseResult) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QIBaseDriver) - - QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *drv); - ~QIBaseResultPrivate() { cleanup(); } - - void cleanup(); - bool isError(const char *msg, QSqlError::ErrorType typ = QSqlError::UnknownError) - { - Q_Q(QIBaseResult); - QString imsg; - ISC_LONG sqlcode; - if (!getIBaseError(imsg, status, sqlcode, tc)) - return false; - - q->setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", msg), - imsg, typ, int(sqlcode))); - return true; - } - - bool transaction(); - bool commit(); - - bool isSelect(); - QVariant fetchBlob(ISC_QUAD *bId); - bool writeBlob(int i, const QByteArray &ba); - QVariant fetchArray(int pos, ISC_QUAD *arr); - bool writeArray(int i, const QList &list); - -public: - ISC_STATUS status[20]; - isc_tr_handle trans; - //indicator whether we have a local transaction or a transaction on driver level - bool localTransaction; - isc_stmt_handle stmt; - isc_db_handle ibase; - XSQLDA *sqlda; // output sqlda - XSQLDA *inda; // input parameters - int queryType; - QTextCodec *tc; -}; - - -QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *drv) - : QSqlCachedResultPrivate(q, drv), - trans(0), - localTransaction(!drv_d_func()->ibase), - stmt(0), - ibase(drv_d_func()->ibase), - sqlda(0), - inda(0), - queryType(-1), - tc(drv_d_func()->tc) -{ -} - -void QIBaseResultPrivate::cleanup() -{ - Q_Q(QIBaseResult); - commit(); - if (!localTransaction) - trans = 0; - - if (stmt) { - isc_dsql_free_statement(status, &stmt, DSQL_drop); - stmt = 0; - } - - delDA(sqlda); - delDA(inda); - - queryType = -1; - q->cleanup(); -} - -bool QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba) -{ - isc_blob_handle handle = 0; - ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata; - isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0); - if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"), - QSqlError::StatementError)) { - int i = 0; - while (i < ba.size()) { - isc_put_segment(status, &handle, qMin(ba.size() - i, int(QIBaseChunkSize)), - const_cast(ba.data()) + i); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB"))) - return false; - i += qMin(ba.size() - i, int(QIBaseChunkSize)); - } - } - isc_close_blob(status, &handle); - return true; -} - -QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId) -{ - isc_blob_handle handle = 0; - - isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to open BLOB"), - QSqlError::StatementError)) - return QVariant(); - - unsigned short len = 0; - QByteArray ba; - int chunkSize = QIBaseChunkSize; - ba.resize(chunkSize); - int read = 0; - while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) { - read += len; - ba.resize(read + chunkSize); - } - ba.resize(read); - - bool isErr = (status[1] == isc_segstr_eof ? false : - isError(QT_TRANSLATE_NOOP("QIBaseResult", - "Unable to read BLOB"), - QSqlError::StatementError)); - - isc_close_blob(status, &handle); - - if (isErr) - return QVariant(); - - ba.resize(read); - return ba; -} - -template -static QList toList(char** buf, int count, T* = 0) -{ - QList res; - for (int i = 0; i < count; ++i) { - res.append(*(T*)(*buf)); - *buf += sizeof(T); - } - return res; -} -/* char** ? seems like bad influence from oracle ... */ -template<> -QList toList(char** buf, int count, long*) -{ - QList res; - for (int i = 0; i < count; ++i) { - if (sizeof(int) == sizeof(long)) - res.append(int((*(long*)(*buf)))); - else - res.append((qint64)(*(long*)(*buf))); - *buf += sizeof(long); - } - return res; -} - -static char* readArrayBuffer(QList& list, char *buffer, short curDim, - short* numElements, ISC_ARRAY_DESC *arrayDesc, - QTextCodec *tc) -{ - const short dim = arrayDesc->array_desc_dimensions - 1; - const unsigned char dataType = arrayDesc->array_desc_dtype; - QList valList; - unsigned short strLen = arrayDesc->array_desc_length; - - if (curDim != dim) { - for(int i = 0; i < numElements[curDim]; ++i) - buffer = readArrayBuffer(list, buffer, curDim + 1, numElements, - arrayDesc, tc); - } else { - switch(dataType) { - case blr_varying: - case blr_varying2: - strLen += 2; // for the two terminating null values - case blr_text: - case blr_text2: { - int o; - for (int i = 0; i < numElements[dim]; ++i) { - for(o = 0; o < strLen && buffer[o]!=0; ++o ) - ; - - if (tc) - valList.append(tc->toUnicode(buffer, o)); - else - valList.append(QString::fromUtf8(buffer, o)); - - buffer += strLen; - } - break; } - case blr_long: - valList = toList(&buffer, numElements[dim], static_cast(0)); - break; - case blr_short: - valList = toList(&buffer, numElements[dim]); - break; - case blr_int64: - valList = toList(&buffer, numElements[dim]); - break; - case blr_float: - valList = toList(&buffer, numElements[dim]); - break; - case blr_double: - valList = toList(&buffer, numElements[dim]); - break; - case blr_timestamp: - for(int i = 0; i < numElements[dim]; ++i) { - valList.append(fromTimeStamp(buffer)); - buffer += sizeof(ISC_TIMESTAMP); - } - break; - case blr_sql_time: - for(int i = 0; i < numElements[dim]; ++i) { - valList.append(fromTime(buffer)); - buffer += sizeof(ISC_TIME); - } - break; - case blr_sql_date: - for(int i = 0; i < numElements[dim]; ++i) { - valList.append(fromDate(buffer)); - buffer += sizeof(ISC_DATE); - } - break; - } - } - if (dim > 0) - list.append(valList); - else - list += valList; - return buffer; -} - -QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr) -{ - QList list; - ISC_ARRAY_DESC desc; - - if (!arr) - return list; - - QByteArray relname(sqlda->sqlvar[pos].relname, sqlda->sqlvar[pos].relname_length); - QByteArray sqlname(sqlda->sqlvar[pos].aliasname, sqlda->sqlvar[pos].aliasname_length); - - isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"), - QSqlError::StatementError)) - return list; - - - int arraySize = 1, subArraySize; - short dimensions = desc.array_desc_dimensions; - QVarLengthArray numElements(dimensions); - - for(int i = 0; i < dimensions; ++i) { - subArraySize = (desc.array_desc_bounds[i].array_bound_upper - - desc.array_desc_bounds[i].array_bound_lower + 1); - numElements[i] = subArraySize; - arraySize = subArraySize * arraySize; - } - - ISC_LONG bufLen; - QByteArray ba; - /* varying arrayelements are stored with 2 trailing null bytes - indicating the length of the string - */ - if (desc.array_desc_dtype == blr_varying - || desc.array_desc_dtype == blr_varying2) { - desc.array_desc_length += 2; - bufLen = desc.array_desc_length * arraySize * sizeof(short); - } else { - bufLen = desc.array_desc_length * arraySize; - } - - - ba.resize(int(bufLen)); - isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get array data"), - QSqlError::StatementError)) - return list; - - readArrayBuffer(list, ba.data(), 0, numElements.data(), &desc, tc); - - return QVariant(list); -} - -template -static char* fillList(char *buffer, const QList &list, T* = 0) -{ - for (int i = 0; i < list.size(); ++i) { - T val; - val = qvariant_cast(list.at(i)); - memcpy(buffer, &val, sizeof(T)); - buffer += sizeof(T); - } - return buffer; -} - -template<> -char* fillList(char *buffer, const QList &list, float*) -{ - for (int i = 0; i < list.size(); ++i) { - double val; - float val2 = 0; - val = qvariant_cast(list.at(i)); - val2 = (float)val; - memcpy(buffer, &val2, sizeof(float)); - buffer += sizeof(float); - } - return buffer; -} - -static char* qFillBufferWithString(char *buffer, const QString& string, - short buflen, bool varying, bool array, - QTextCodec *tc) -{ - QByteArray str = encodeString(tc, string); // keep a copy of the string alive in this scope - if (varying) { - short tmpBuflen = buflen; - if (str.length() < buflen) - buflen = str.length(); - if (array) { // interbase stores varying arrayelements different than normal varying elements - memcpy(buffer, str.data(), buflen); - memset(buffer + buflen, 0, tmpBuflen - buflen); - } else { - *(short*)buffer = buflen; // first two bytes is the length - memcpy(buffer + sizeof(short), str.data(), buflen); - } - buffer += tmpBuflen; - } else { - str = str.leftJustified(buflen, ' ', true); - memcpy(buffer, str.data(), buflen); - buffer += buflen; - } - return buffer; -} - -static char* createArrayBuffer(char *buffer, const QList &list, - QVariant::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc, - QString& error, QTextCodec *tc) -{ - int i; - ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds; - short dim = arrayDesc->array_desc_dimensions - 1; - - int elements = (bounds[curDim].array_bound_upper - - bounds[curDim].array_bound_lower + 1); - - if (list.size() != elements) { // size mismatch - error = QLatin1String("Expected size: %1. Supplied size: %2"); - error = QLatin1String("Array size mismatch. Fieldname: %1 ") - + error.arg(elements).arg(list.size()); - return 0; - } - - if (curDim != dim) { - for(i = 0; i < list.size(); ++i) { - - if (list.at(i).type() != QVariant::List) { // dimensions mismatch - error = QLatin1String("Array dimensons mismatch. Fieldname: %1"); - return 0; - } - - buffer = createArrayBuffer(buffer, list.at(i).toList(), type, curDim + 1, - arrayDesc, error, tc); - if (!buffer) - return 0; - } - } else { - switch(type) { - case QVariant::Int: - case QVariant::UInt: - if (arrayDesc->array_desc_dtype == blr_short) - buffer = fillList(buffer, list); - else - buffer = fillList(buffer, list); - break; - case QVariant::Double: - if (arrayDesc->array_desc_dtype == blr_float) - buffer = fillList(buffer, list, static_cast(0)); - else - buffer = fillList(buffer, list); - break; - case QVariant::LongLong: - buffer = fillList(buffer, list); - break; - case QVariant::ULongLong: - buffer = fillList(buffer, list); - break; - case QVariant::String: - for (i = 0; i < list.size(); ++i) - buffer = qFillBufferWithString(buffer, list.at(i).toString(), - arrayDesc->array_desc_length, - arrayDesc->array_desc_dtype == blr_varying, - true, tc); - break; - case QVariant::Date: - for (i = 0; i < list.size(); ++i) { - *((ISC_DATE*)buffer) = toDate(list.at(i).toDate()); - buffer += sizeof(ISC_DATE); - } - break; - case QVariant::Time: - for (i = 0; i < list.size(); ++i) { - *((ISC_TIME*)buffer) = toTime(list.at(i).toTime()); - buffer += sizeof(ISC_TIME); - } - break; - - case QVariant::DateTime: - for (i = 0; i < list.size(); ++i) { - *((ISC_TIMESTAMP*)buffer) = toTimeStamp(list.at(i).toDateTime()); - buffer += sizeof(ISC_TIMESTAMP); - } - break; - default: - break; - } - } - return buffer; -} - -bool QIBaseResultPrivate::writeArray(int column, const QList &list) -{ - Q_Q(QIBaseResult); - QString error; - ISC_QUAD *arrayId = (ISC_QUAD*) inda->sqlvar[column].sqldata; - ISC_ARRAY_DESC desc; - - QByteArray relname(inda->sqlvar[column].relname, inda->sqlvar[column].relname_length); - QByteArray sqlname(inda->sqlvar[column].aliasname, inda->sqlvar[column].aliasname_length); - - isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"), - QSqlError::StatementError)) - return false; - - short arraySize = 1; - ISC_LONG bufLen; - QList subList = list; - - short dimensions = desc.array_desc_dimensions; - for(int i = 0; i < dimensions; ++i) { - arraySize *= (desc.array_desc_bounds[i].array_bound_upper - - desc.array_desc_bounds[i].array_bound_lower + 1); - } - - /* varying arrayelements are stored with 2 trailing null bytes - indicating the length of the string - */ - if (desc.array_desc_dtype == blr_varying || - desc.array_desc_dtype == blr_varying2) - desc.array_desc_length += 2; - - bufLen = desc.array_desc_length * arraySize; - QByteArray ba; - ba.resize(int(bufLen)); - - if (list.size() > arraySize) { - error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3"); - error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size()); - q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError)); - return false; - } - - if (!createArrayBuffer(ba.data(), list, - qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0), - 0, &desc, error, tc)) { - q->setLastError(QSqlError(error.arg(QLatin1String(sqlname)), QLatin1String(""), - QSqlError::StatementError)); - return false; - } - - /* readjust the buffer size*/ - if (desc.array_desc_dtype == blr_varying - || desc.array_desc_dtype == blr_varying2) - desc.array_desc_length -= 2; - - isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen); - return true; -} - - -bool QIBaseResultPrivate::isSelect() -{ - char acBuffer[9]; - char qType = isc_info_sql_stmt_type; - isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get query info"), - QSqlError::StatementError)) - return false; - int iLength = isc_vax_integer(&acBuffer[1], 2); - queryType = isc_vax_integer(&acBuffer[3], iLength); - return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure); -} - -bool QIBaseResultPrivate::transaction() -{ - if (trans) - return true; - if (drv_d_func()->trans) { - localTransaction = false; - trans = drv_d_func()->trans; - return true; - } - localTransaction = true; - - isc_start_transaction(status, &trans, 1, &ibase, 0, NULL); - if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not start transaction"), - QSqlError::TransactionError)) - return false; - - return true; -} - -// does nothing if the transaction is on the -// driver level -bool QIBaseResultPrivate::commit() -{ - if (!trans) - return false; - // don't commit driver's transaction, the driver will do it for us - if (!localTransaction) - return true; - - isc_commit_transaction(status, &trans); - trans = 0; - return !isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to commit transaction"), - QSqlError::TransactionError); -} - -////////// - -QIBaseResult::QIBaseResult(const QIBaseDriver *db) - : QSqlCachedResult(*new QIBaseResultPrivate(this, db)) -{ -} - -bool QIBaseResult::prepare(const QString& query) -{ - Q_D(QIBaseResult); -// qDebug("prepare: %s", qPrintable(query)); - if (!driver() || !driver()->isOpen() || driver()->isOpenError()) - return false; - d->cleanup(); - setActive(false); - setAt(QSql::BeforeFirstRow); - - createDA(d->sqlda); - if (d->sqlda == (XSQLDA*)0) { - qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory"; - return false; - } - - createDA(d->inda); - if (d->inda == (XSQLDA*)0){ - qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory"; - return false; - } - - if (!d->transaction()) - return false; - - isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not allocate statement"), - QSqlError::StatementError)) - return false; - isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, - const_cast(encodeString(d->tc, query).constData()), FBVERSION, d->sqlda); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not prepare statement"), - QSqlError::StatementError)) - return false; - - isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", - "Could not describe input statement"), QSqlError::StatementError)) - return false; - if (d->inda->sqld > d->inda->sqln) { - enlargeDA(d->inda, d->inda->sqld); - if (d->inda == (XSQLDA*)0) { - qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory"; - return false; - } - - isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", - "Could not describe input statement"), QSqlError::StatementError)) - return false; - } - initDA(d->inda); - if (d->sqlda->sqld > d->sqlda->sqln) { - // need more field descriptors - enlargeDA(d->sqlda, d->sqlda->sqld); - if (d->sqlda == (XSQLDA*)0) { - qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory"; - return false; - } - - isc_dsql_describe(d->status, &d->stmt, FBVERSION, d->sqlda); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not describe statement"), - QSqlError::StatementError)) - return false; - } - initDA(d->sqlda); - - setSelect(d->isSelect()); - if (!isSelect()) { - free(d->sqlda); - d->sqlda = 0; - } - - return true; -} - - -bool QIBaseResult::exec() -{ - Q_D(QIBaseResult); - bool ok = true; - - if (!d->trans) - d->transaction(); - - if (!driver() || !driver()->isOpen() || driver()->isOpenError()) - return false; - setActive(false); - setAt(QSql::BeforeFirstRow); - - if (d->inda) { - QVector& values = boundValues(); - int i; - if (values.count() > d->inda->sqld) { - qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", - d->inda->sqld, values.count()); - return false; - } - int para = 0; - for (i = 0; i < values.count(); ++i) { - para = i; - if (!d->inda->sqlvar[para].sqldata) - // skip unknown datatypes - continue; - const QVariant val(values[i]); - if (d->inda->sqlvar[para].sqltype & 1) { - if (val.isNull()) { - // set null indicator - *(d->inda->sqlvar[para].sqlind) = -1; - // and set the value to 0, otherwise it would count as empty string. - // it seems to be working with just setting sqlind to -1 - //*((char*)d->inda->sqlvar[para].sqldata) = 0; - continue; - } - // a value of 0 means non-null. - *(d->inda->sqlvar[para].sqlind) = 0; - } - switch(d->inda->sqlvar[para].sqltype & ~1) { - case SQL_INT64: - if (d->inda->sqlvar[para].sqlscale < 0) - *((qint64*)d->inda->sqlvar[para].sqldata) = - (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); - break; - case SQL_LONG: - if (d->inda->sqlvar[para].sqllen == 4) { - if (d->inda->sqlvar[para].sqlscale < 0) - *((qint32*)d->inda->sqlvar[para].sqldata) = - (qint32)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((qint32*)d->inda->sqlvar[para].sqldata) = (qint32)val.toInt(); - } else { - *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); - } - break; - case SQL_SHORT: - if (d->inda->sqlvar[para].sqlscale < 0) - *((short*)d->inda->sqlvar[para].sqldata) = - (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt(); - break; - case SQL_FLOAT: - *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble(); - break; - case SQL_DOUBLE: - *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble(); - break; - case SQL_TIMESTAMP: - *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime()); - break; - case SQL_TYPE_TIME: - *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime()); - break; - case SQL_TYPE_DATE: - *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate()); - break; - case SQL_VARYING: - case SQL_TEXT: - qFillBufferWithString(d->inda->sqlvar[para].sqldata, val.toString(), - d->inda->sqlvar[para].sqllen, - (d->inda->sqlvar[para].sqltype & ~1) == SQL_VARYING, false, d->tc); - break; - case SQL_BLOB: - ok &= d->writeBlob(para, val.toByteArray()); - break; - case SQL_ARRAY: - ok &= d->writeArray(para, val.toList()); - break; - default: - qWarning("QIBaseResult::exec: Unknown datatype %d", - d->inda->sqlvar[para].sqltype & ~1); - break; - } - } - } - - if (ok) { - if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) { - isc_dsql_free_statement(d->status, &d->stmt, DSQL_close); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to close statement"))) - return false; - cleanup(); - } - if (d->queryType == isc_info_sql_stmt_exec_procedure) - isc_dsql_execute2(d->status, &d->trans, &d->stmt, FBVERSION, d->inda, d->sqlda); - else - isc_dsql_execute(d->status, &d->trans, &d->stmt, FBVERSION, d->inda); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to execute query"))) - return false; - - // Not all stored procedures necessarily return values. - if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0) - delDA(d->sqlda); - - if (d->sqlda) - init(d->sqlda->sqld); - - if (!isSelect()) - d->commit(); - - setActive(true); - return true; - } - return false; -} - -bool QIBaseResult::reset (const QString& query) -{ - if (!prepare(query)) - return false; - return exec(); -} - -bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) -{ - Q_D(QIBaseResult); - ISC_STATUS stat = 0; - - // Stored Procedures are special - they populate our d->sqlda when executing, - // so we don't have to call isc_dsql_fetch - if (d->queryType == isc_info_sql_stmt_exec_procedure) { - // the first "fetch" shall succeed, all consecutive ones will fail since - // we only have one row to fetch for stored procedures - if (rowIdx != 0) - stat = 100; - } else { - stat = isc_dsql_fetch(d->status, &d->stmt, FBVERSION, d->sqlda); - } - - if (stat == 100) { - // no more rows - setAt(QSql::AfterLastRow); - return false; - } - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not fetch next item"), - QSqlError::StatementError)) - return false; - if (rowIdx < 0) // not interested in actual values - return true; - - for (int i = 0; i < d->sqlda->sqld; ++i) { - int idx = rowIdx + i; - char *buf = d->sqlda->sqlvar[i].sqldata; - int size = d->sqlda->sqlvar[i].sqllen; - Q_ASSERT(buf); - - if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) { - // null value - QVariant v; - v.convert(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype, d->sqlda->sqlvar[i].sqlscale < 0)); - if(v.type() == QVariant::Double) { - switch(numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - v.convert(QVariant::Int); - break; - case QSql::LowPrecisionInt64: - v.convert(QVariant::LongLong); - break; - case QSql::HighPrecision: - v.convert(QVariant::String); - break; - case QSql::LowPrecisionDouble: - // no conversion - break; - } - } - row[idx] = v; - continue; - } - - switch(d->sqlda->sqlvar[i].sqltype & ~1) { - case SQL_VARYING: - // pascal strings - a short with a length information followed by the data - if (d->tc) - row[idx] = d->tc->toUnicode(buf + sizeof(short), *(short*)buf); - else - row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf); - break; - case SQL_INT64: - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = *(qint64*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale); - else - row[idx] = QVariant(*(qint64*)buf); - break; - case SQL_LONG: - if (d->sqlda->sqlvar[i].sqllen == 4) - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = QVariant(*(qint32*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale)); - else - row[idx] = QVariant(*(qint32*)buf); - else - row[idx] = QVariant(*(qint64*)buf); - break; - case SQL_SHORT: - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = QVariant(long((*(short*)buf)) * pow(10.0, d->sqlda->sqlvar[i].sqlscale)); - else - row[idx] = QVariant(int((*(short*)buf))); - break; - case SQL_FLOAT: - row[idx] = QVariant(double((*(float*)buf))); - break; - case SQL_DOUBLE: - row[idx] = QVariant(*(double*)buf); - break; - case SQL_TIMESTAMP: - row[idx] = fromTimeStamp(buf); - break; - case SQL_TYPE_TIME: - row[idx] = fromTime(buf); - break; - case SQL_TYPE_DATE: - row[idx] = fromDate(buf); - break; - case SQL_TEXT: - if (d->tc) - row[idx] = d->tc->toUnicode(buf, size); - else - row[idx] = QString::fromUtf8(buf, size); - break; - case SQL_BLOB: - row[idx] = d->fetchBlob((ISC_QUAD*)buf); - break; - case SQL_ARRAY: - row[idx] = d->fetchArray(i, (ISC_QUAD*)buf); - break; - default: - // unknown type - don't even try to fetch - row[idx] = QVariant(); - break; - } - if (d->sqlda->sqlvar[i].sqlscale < 0) { - QVariant v = row[idx]; - switch(numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - if(v.convert(QVariant::Int)) - row[idx]=v; - break; - case QSql::LowPrecisionInt64: - if(v.convert(QVariant::LongLong)) - row[idx]=v; - break; - case QSql::LowPrecisionDouble: - if(v.convert(QVariant::Double)) - row[idx]=v; - break; - case QSql::HighPrecision: - if(v.convert(QVariant::String)) - row[idx]=v; - break; - } - } - } - - return true; -} - -int QIBaseResult::size() -{ - return -1; - -#if 0 /// ### FIXME - static char sizeInfo[] = {isc_info_sql_records}; - char buf[64]; - - //qDebug() << sizeInfo; - if (!isActive() || !isSelect()) - return -1; - - char ct; - short len; - int val = 0; -// while(val == 0) { - isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf); -// isc_database_info(d->status, &d->ibase, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf); - - for(int i = 0; i < 66; ++i) - qDebug() << QString::number(buf[i]); - - for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) { - ct = *(c++); - len = isc_vax_integer(c, 2); - c += 2; - val = isc_vax_integer(c, len); - c += len; - qDebug() << "size" << val; - if (ct == isc_info_req_select_count) - return val; - } - //qDebug("size -1"); - return -1; - - unsigned int i, result_size; - if (buf[0] == isc_info_sql_records) { - i = 3; - result_size = isc_vax_integer(&buf[1],2); - while (buf[i] != isc_info_end && i < result_size) { - len = (short)isc_vax_integer(&buf[i+1],2); - if (buf[i] == isc_info_req_select_count) - return (isc_vax_integer(&buf[i+3],len)); - i += len+3; - } - } -// } - return -1; -#endif -} - -int QIBaseResult::numRowsAffected() -{ - Q_D(QIBaseResult); - static char acCountInfo[] = {isc_info_sql_records}; - char cCountType; - bool bIsProcedure = false; - - switch (d->queryType) { - case isc_info_sql_stmt_select: - cCountType = isc_info_req_select_count; - break; - case isc_info_sql_stmt_update: - cCountType = isc_info_req_update_count; - break; - case isc_info_sql_stmt_delete: - cCountType = isc_info_req_delete_count; - break; - case isc_info_sql_stmt_insert: - cCountType = isc_info_req_insert_count; - break; - case isc_info_sql_stmt_exec_procedure: - bIsProcedure = true; // will sum all changes - break; - default: - qWarning() << "numRowsAffected: Unknown statement type (" << d->queryType << ")"; - return -1; - } - - char acBuffer[33]; - int iResult = -1; - isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get statement info"), - QSqlError::StatementError)) - return -1; - for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) { - char cType = *pcBuf++; - short sLength = isc_vax_integer (pcBuf, 2); - pcBuf += 2; - int iValue = isc_vax_integer (pcBuf, sLength); - pcBuf += sLength; - if (bIsProcedure) { - if (cType == isc_info_req_insert_count || cType == isc_info_req_update_count - || cType == isc_info_req_delete_count) { - if (iResult == -1) - iResult = 0; - iResult += iValue; - } - } else if (cType == cCountType) { - iResult = iValue; - break; - } - } - return iResult; -} - -QSqlRecord QIBaseResult::record() const -{ - Q_D(const QIBaseResult); - QSqlRecord rec; - if (!isActive() || !d->sqlda) - return rec; - - XSQLVAR v; - for (int i = 0; i < d->sqlda->sqld; ++i) { - v = d->sqlda->sqlvar[i]; - QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(), - qIBaseTypeName2(v.sqltype, v.sqlscale < 0)); - f.setLength(v.sqllen); - f.setPrecision(qAbs(v.sqlscale)); - f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional); - if(v.sqlscale < 0) { - QSqlQuery q(driver()->createResult()); - q.setForwardOnly(true); - q.exec(QLatin1String("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG " - "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b " - "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE " - "AND a.RDB$RELATION_NAME = '") + QString::fromLatin1(v.relname, v.relname_length).toUpper() + QLatin1String("' " - "AND a.RDB$FIELD_NAME = '") + QString::fromLatin1(v.sqlname, v.sqlname_length).toUpper() + QLatin1String("' ")); - if(q.first()) { - if(v.sqlscale < 0) { - f.setLength(q.value(0).toInt()); - f.setPrecision(qAbs(q.value(1).toInt())); - } else { - f.setLength(q.value(2).toInt()); - f.setPrecision(0); - } - f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional); - } - } - f.setSqlType(v.sqltype); - rec.append(f); - } - return rec; -} - -QVariant QIBaseResult::handle() const -{ - Q_D(const QIBaseResult); - return QVariant(qRegisterMetaType("isc_stmt_handle"), &d->stmt); -} - -/*********************************/ - -QIBaseDriver::QIBaseDriver(QObject * parent) - : QSqlDriver(*new QIBaseDriverPrivate, parent) -{ -} - -QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent) - : QSqlDriver(*new QIBaseDriverPrivate, parent) -{ - Q_D(QIBaseDriver); - d->ibase = connection; - setOpen(true); - setOpenError(false); -} - -QIBaseDriver::~QIBaseDriver() -{ -} - -bool QIBaseDriver::hasFeature(DriverFeature f) const -{ - switch (f) { - case QuerySize: - case NamedPlaceholders: - case LastInsertId: - case BatchOperations: - case SimpleLocking: - case FinishQuery: - case MultipleResultSets: - case CancelQuery: - return false; - case Transactions: - case PreparedQueries: - case PositionalPlaceholders: - case Unicode: - case BLOB: - case EventNotifications: - case LowPrecisionNumbers: - return true; - } - return false; -} - -bool QIBaseDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString & connOpts) -{ - Q_D(QIBaseDriver); - if (isOpen()) - close(); - - const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts)); - - QString encString; - QByteArray role; - 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.toUpper() == QLatin1String("ISC_DPB_LC_CTYPE")) - encString = val; - else if (opt.toUpper() == QLatin1String("ISC_DPB_SQL_ROLE_NAME")) { - role = val.toLocal8Bit(); - role.truncate(255); - } - } - } - - // Use UNICODE_FSS when no ISC_DPB_LC_CTYPE is provided - if (encString.isEmpty()) - encString = QLatin1String("UNICODE_FSS"); - else { - d->tc = QTextCodec::codecForName(encString.toLocal8Bit()); - if (!d->tc) { - qWarning("Unsupported encoding: %s. Using UNICODE_FFS for ISC_DPB_LC_CTYPE.", encString.toLocal8Bit().constData()); - encString = QLatin1String("UNICODE_FSS"); // Fallback to UNICODE_FSS - } - } - - QByteArray enc = encString.toLocal8Bit(); - QByteArray usr = user.toLocal8Bit(); - QByteArray pass = password.toLocal8Bit(); - enc.truncate(255); - usr.truncate(255); - pass.truncate(255); - - QByteArray ba; - ba.reserve(usr.length() + pass.length() + enc.length() + role.length() + 9); - ba.append(char(isc_dpb_version1)); - ba.append(char(isc_dpb_user_name)); - ba.append(char(usr.length())); - ba.append(usr.data(), usr.length()); - ba.append(char(isc_dpb_password)); - ba.append(char(pass.length())); - ba.append(pass.data(), pass.length()); - ba.append(char(isc_dpb_lc_ctype)); - ba.append(char(enc.length())); - ba.append(enc.data(), enc.length()); - - if (!role.isEmpty()) { - ba.append(char(isc_dpb_sql_role_name)); - ba.append(char(role.length())); - ba.append(role.data(), role.length()); - } - - QString portString; - if (port != -1) - portString = QStringLiteral("/%1").arg(port); - - QString ldb; - if (!host.isEmpty()) - ldb += host + portString + QLatin1Char(':'); - ldb += db; - isc_attach_database(d->status, 0, const_cast(ldb.toLocal8Bit().constData()), - &d->ibase, ba.size(), ba.data()); - if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"), - QSqlError::ConnectionError)) { - setOpenError(true); - return false; - } - - setOpen(true); - setOpenError(false); - return true; -} - -void QIBaseDriver::close() -{ - Q_D(QIBaseDriver); - if (isOpen()) { - - if (d->eventBuffers.size()) { - ISC_STATUS status[20]; - QMap::const_iterator i; - for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) { - QIBaseEventBuffer *eBuffer = i.value(); - eBuffer->subscriptionState = QIBaseEventBuffer::Finished; - isc_cancel_events(status, &d->ibase, &eBuffer->eventId); - qFreeEventBuffer(eBuffer); - } - d->eventBuffers.clear(); - -#if defined(FB_API_VER) - // Workaround for Firebird crash - QTime timer; - timer.start(); - while (timer.elapsed() < 500) - QCoreApplication::processEvents(); -#endif - } - - isc_detach_database(d->status, &d->ibase); - d->ibase = 0; - setOpen(false); - setOpenError(false); - } -} - -QSqlResult *QIBaseDriver::createResult() const -{ - return new QIBaseResult(this); -} - -bool QIBaseDriver::beginTransaction() -{ - Q_D(QIBaseDriver); - if (!isOpen() || isOpenError()) - return false; - if (d->trans) - return false; - - isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL); - return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Could not start transaction"), - QSqlError::TransactionError); -} - -bool QIBaseDriver::commitTransaction() -{ - Q_D(QIBaseDriver); - if (!isOpen() || isOpenError()) - return false; - if (!d->trans) - return false; - - isc_commit_transaction(d->status, &d->trans); - d->trans = 0; - return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to commit transaction"), - QSqlError::TransactionError); -} - -bool QIBaseDriver::rollbackTransaction() -{ - Q_D(QIBaseDriver); - if (!isOpen() || isOpenError()) - return false; - if (!d->trans) - return false; - - isc_rollback_transaction(d->status, &d->trans); - d->trans = 0; - return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to rollback transaction"), - QSqlError::TransactionError); -} - -QStringList QIBaseDriver::tables(QSql::TableType type) const -{ - QStringList res; - if (!isOpen()) - return res; - - QString typeFilter; - - if (type == QSql::SystemTables) { - typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0"); - } else if (type == (QSql::SystemTables | QSql::Views)) { - typeFilter += QLatin1String("RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"); - } else { - if (!(type & QSql::SystemTables)) - typeFilter += QLatin1String("RDB$SYSTEM_FLAG = 0 AND "); - if (!(type & QSql::Views)) - typeFilter += QLatin1String("RDB$VIEW_BLR IS NULL AND "); - if (!(type & QSql::Tables)) - typeFilter += QLatin1String("RDB$VIEW_BLR IS NOT NULL AND "); - if (!typeFilter.isEmpty()) - typeFilter.chop(5); - } - if (!typeFilter.isEmpty()) - typeFilter.prepend(QLatin1String("where ")); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - if (!q.exec(QLatin1String("select rdb$relation_name from rdb$relations ") + typeFilter)) - return res; - while(q.next()) - res << q.value(0).toString().simplified(); - - return res; -} - -QSqlRecord QIBaseDriver::record(const QString& tablename) const -{ - QSqlRecord rec; - if (!isOpen()) - return rec; - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - QString table = tablename; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - q.exec(QLatin1String("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, " - "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG " - "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b " - "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE " - "AND a.RDB$RELATION_NAME = '") + table + QLatin1String("' " - "ORDER BY a.RDB$FIELD_POSITION")); - - while (q.next()) { - int type = q.value(1).toInt(); - bool hasScale = q.value(3).toInt() < 0; - QSqlField f(q.value(0).toString().simplified(), qIBaseTypeName(type, hasScale)); - if(hasScale) { - f.setLength(q.value(4).toInt()); - f.setPrecision(qAbs(q.value(3).toInt())); - } else { - f.setLength(q.value(2).toInt()); - f.setPrecision(0); - } - f.setRequired(q.value(5).toInt() > 0); - f.setSqlType(type); - - rec.append(f); - } - return rec; -} - -QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const -{ - QSqlIndex index(table); - if (!isOpen()) - return index; - - QString tablename = table; - if (isIdentifierEscaped(tablename, QSqlDriver::TableName)) - tablename = stripDelimiters(tablename, QSqlDriver::TableName); - else - tablename = tablename.toUpper(); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - q.exec(QLatin1String("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE " - "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d " - "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' " - "AND a.RDB$RELATION_NAME = '") + tablename + - QLatin1String(" 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME " - "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME " - "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME " - "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE " - "ORDER BY b.RDB$FIELD_POSITION")); - - while (q.next()) { - QSqlField field(q.value(1).toString().simplified(), qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0)); - index.append(field); //TODO: asc? desc? - index.setName(q.value(0).toString()); - } - - return index; -} - -QString QIBaseDriver::formatValue(const QSqlField &field, bool trimStrings) const -{ - switch (field.type()) { - case QVariant::DateTime: { - QDateTime datetime = field.value().toDateTime(); - if (datetime.isValid()) - return QLatin1Char('\'') + QString::number(datetime.date().year()) + QLatin1Char('-') + - QString::number(datetime.date().month()) + QLatin1Char('-') + - QString::number(datetime.date().day()) + QLatin1Char(' ') + - QString::number(datetime.time().hour()) + QLatin1Char(':') + - QString::number(datetime.time().minute()) + QLatin1Char(':') + - QString::number(datetime.time().second()) + QLatin1Char('.') + - QString::number(datetime.time().msec()).rightJustified(3, QLatin1Char('0'), true) + - QLatin1Char('\''); - else - return QLatin1String("NULL"); - } - case QVariant::Time: { - QTime time = field.value().toTime(); - if (time.isValid()) - return QLatin1Char('\'') + QString::number(time.hour()) + QLatin1Char(':') + - QString::number(time.minute()) + QLatin1Char(':') + - QString::number(time.second()) + QLatin1Char('.') + - QString::number(time.msec()).rightJustified(3, QLatin1Char('0'), true) + - QLatin1Char('\''); - else - return QLatin1String("NULL"); - } - case QVariant::Date: { - QDate date = field.value().toDate(); - if (date.isValid()) - return QLatin1Char('\'') + QString::number(date.year()) + QLatin1Char('-') + - QString::number(date.month()) + QLatin1Char('-') + - QString::number(date.day()) + QLatin1Char('\''); - else - return QLatin1String("NULL"); - } - default: - return QSqlDriver::formatValue(field, trimStrings); - } -} - -QVariant QIBaseDriver::handle() const -{ - Q_D(const QIBaseDriver); - return QVariant(qRegisterMetaType("isc_db_handle"), &d->ibase); -} - -#if defined(FB_API_VER) && FB_API_VER >= 20 -static ISC_EVENT_CALLBACK qEventCallback(char *result, ISC_USHORT length, const ISC_UCHAR *updated) -#else -static isc_callback qEventCallback(char *result, short length, char *updated) -#endif -{ - if (!updated) - return 0; - - - memcpy(result, updated, length); - qMutex()->lock(); - QIBaseDriver *driver = qBufferDriverMap()->value(result); - qMutex()->unlock(); - - // We use an asynchronous call (i.e., queued connection) because the event callback - // is executed in a different thread than the one in which the driver lives. - if (driver) - QMetaObject::invokeMethod(driver, "qHandleEventNotification", Qt::QueuedConnection, Q_ARG(void *, reinterpret_cast(result))); - - return 0; -} - -bool QIBaseDriver::subscribeToNotification(const QString &name) -{ - Q_D(QIBaseDriver); - if (!isOpen()) { - qWarning("QIBaseDriver::subscribeFromNotificationImplementation: database not open."); - return false; - } - - if (d->eventBuffers.contains(name)) { - qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%s'.", - qPrintable(name)); - return false; - } - - QIBaseEventBuffer *eBuffer = new QIBaseEventBuffer; - eBuffer->subscriptionState = QIBaseEventBuffer::Starting; - eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer, - &eBuffer->resultBuffer, - 1, - name.toLocal8Bit().constData()); - - qMutex()->lock(); - qBufferDriverMap()->insert(eBuffer->resultBuffer, this); - qMutex()->unlock(); - - d->eventBuffers.insert(name, eBuffer); - - ISC_STATUS status[20]; - isc_que_events(status, - &d->ibase, - &eBuffer->eventId, - eBuffer->bufferLength, - eBuffer->eventBuffer, -#if defined (FB_API_VER) && FB_API_VER >= 20 - (ISC_EVENT_CALLBACK)qEventCallback, -#else - (isc_callback)qEventCallback, -#endif - eBuffer->resultBuffer); - - if (status[0] == 1 && status[1]) { - setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name))); - d->eventBuffers.remove(name); - qFreeEventBuffer(eBuffer); - return false; - } - - return true; -} - -bool QIBaseDriver::unsubscribeFromNotification(const QString &name) -{ - Q_D(QIBaseDriver); - if (!isOpen()) { - qWarning("QIBaseDriver::unsubscribeFromNotificationImplementation: database not open."); - return false; - } - - if (!d->eventBuffers.contains(name)) { - qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%s'.", - qPrintable(name)); - return false; - } - - QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name); - ISC_STATUS status[20]; - eBuffer->subscriptionState = QIBaseEventBuffer::Finished; - isc_cancel_events(status, &d->ibase, &eBuffer->eventId); - - if (status[0] == 1 && status[1]) { - setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name))); - return false; - } - - d->eventBuffers.remove(name); - qFreeEventBuffer(eBuffer); - - return true; -} - -QStringList QIBaseDriver::subscribedToNotifications() const -{ - Q_D(const QIBaseDriver); - return QStringList(d->eventBuffers.keys()); -} - -void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer) -{ - Q_D(QIBaseDriver); - QMap::const_iterator i; - for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) { - QIBaseEventBuffer* eBuffer = i.value(); - if (reinterpret_cast(eBuffer->resultBuffer) != updatedResultBuffer) - continue; - - ISC_ULONG counts[20]; - memset(counts, 0, sizeof(counts)); - isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer); - if (counts[0]) { - - if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed) { - emit notification(i.key()); - emit notification(i.key(), QSqlDriver::UnknownSource, QVariant()); - } - else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting) - eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed; - - ISC_STATUS status[20]; - isc_que_events(status, - &d->ibase, - &eBuffer->eventId, - eBuffer->bufferLength, - eBuffer->eventBuffer, -#if defined (FB_API_VER) && FB_API_VER >= 20 - (ISC_EVENT_CALLBACK)qEventCallback, -#else - (isc_callback)qEventCallback, -#endif - eBuffer->resultBuffer); - if (Q_UNLIKELY(status[0] == 1 && status[1])) { - qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%s'", - qPrintable(i.key())); - } - - return; - } - } -} - -QString QIBaseDriver::escapeIdentifier(const QString &identifier, IdentifierType) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/ibase/qsql_ibase.pri b/src/sql/drivers/ibase/qsql_ibase.pri deleted file mode 100644 index ef3b68d34e..0000000000 --- a/src/sql/drivers/ibase/qsql_ibase.pri +++ /dev/null @@ -1,10 +0,0 @@ -HEADERS += $$PWD/qsql_ibase_p.h -SOURCES += $$PWD/qsql_ibase.cpp - -unix { - !contains(LIBS, .*gds.*):!contains(LIBS, .*libfb.*):LIBS += -lgds -} else { - !contains(LIBS, .*gds.*):!contains(LIBS, .*fbclient.*) { - LIBS += -lgds32_ms - } -} diff --git a/src/sql/drivers/ibase/qsql_ibase_p.h b/src/sql/drivers/ibase/qsql_ibase_p.h deleted file mode 100644 index c7cee41462..0000000000 --- a/src/sql/drivers/ibase/qsql_ibase_p.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** 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_IBASE_H -#define QSQL_IBASE_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 -#include - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_IBASE -#else -#define Q_EXPORT_SQLDRIVER_IBASE Q_SQL_EXPORT -#endif - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QIBaseDriverPrivate; - -class Q_EXPORT_SQLDRIVER_IBASE QIBaseDriver : public QSqlDriver -{ - friend class QIBaseResultPrivate; - Q_DECLARE_PRIVATE(QIBaseDriver) - Q_OBJECT -public: - explicit QIBaseDriver(QObject *parent = 0); - explicit QIBaseDriver(isc_db_handle connection, QObject *parent = 0); - virtual ~QIBaseDriver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port) { return open(db, user, password, host, port, QString()); } - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - - QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE; - - QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - - bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE; - bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE; - QStringList subscribedToNotifications() const Q_DECL_OVERRIDE; - -private Q_SLOTS: - void qHandleEventNotification(void* updatedResultBuffer); -}; - -QT_END_NAMESPACE - -#endif // QSQL_IBASE_H diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp deleted file mode 100644 index 56caf52dcb..0000000000 --- a/src/sql/drivers/mysql/qsql_mysql.cpp +++ /dev/null @@ -1,1665 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#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 - -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 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((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 : - 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(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; - 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(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 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 -} - -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(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 >= 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(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 >= 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 diff --git a/src/sql/drivers/mysql/qsql_mysql.pri b/src/sql/drivers/mysql/qsql_mysql.pri deleted file mode 100644 index 3cfb6144a1..0000000000 --- a/src/sql/drivers/mysql/qsql_mysql.pri +++ /dev/null @@ -1,16 +0,0 @@ -HEADERS += $$PWD/qsql_mysql_p.h -SOURCES += $$PWD/qsql_mysql.cpp - -QMAKE_CXXFLAGS *= $$QT_CFLAGS_MYSQL -LIBS += $$QT_LFLAGS_MYSQL - -unix { - isEmpty(QT_LFLAGS_MYSQL) { - !contains(LIBS, .*mysqlclient.*):!contains(LIBS, .*mysqld.*) { - use_libmysqlclient_r:LIBS += -lmysqlclient_r - else:LIBS += -lmysqlclient - } - } -} else { - !contains(LIBS, .*mysql.*):!contains(LIBS, .*mysqld.*):LIBS += -llibmysql -} diff --git a/src/sql/drivers/mysql/qsql_mysql_p.h b/src/sql/drivers/mysql/qsql_mysql_p.h deleted file mode 100644 index 7641f9aa34..0000000000 --- a/src/sql/drivers/mysql/qsql_mysql_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** 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 Q_DECL_OVERRIDE; - bool open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString& connOpts) Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE; - QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, - bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - - bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; -private: - void init(); -}; - -QT_END_NAMESPACE - -#endif // QSQL_MYSQL_H diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp deleted file mode 100644 index 47d6db7ea4..0000000000 --- a/src/sql/drivers/oci/qsql_oci.cpp +++ /dev/null @@ -1,2725 +0,0 @@ -/**************************************************************************** -** -** 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_oci_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// This is needed for oracle oci when compiling with mingw-w64 headers -#if defined(__MINGW64_VERSION_MAJOR) && defined(_WIN64) -#define _int64 __int64 -#endif - - -#include -#ifdef max -#undef max -#endif -#ifdef min -#undef min -#endif - -#include - -#define QOCI_DYNAMIC_CHUNK_SIZE 65535 -#define QOCI_PREFETCH_MEM 10240 - -// setting this define will allow using a query from a different -// thread than its database connection. -// warning - this is not fully tested and can lead to race conditions -#define QOCI_THREADED - -//#define QOCI_DEBUG - -Q_DECLARE_OPAQUE_POINTER(OCIEnv*); -Q_DECLARE_METATYPE(OCIEnv*) -Q_DECLARE_OPAQUE_POINTER(OCIStmt*); -Q_DECLARE_METATYPE(OCIStmt*) - -QT_BEGIN_NAMESPACE - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN -enum { QOCIEncoding = 2002 }; // AL16UTF16LE -#else -enum { QOCIEncoding = 2000 }; // AL16UTF16 -#endif - -#ifdef OCI_ATTR_CHARSET_FORM -// Always set the OCI_ATTR_CHARSET_FORM to SQLCS_NCHAR is safe -// because Oracle server will deal with the implicit Conversion -// Between CHAR and NCHAR. -// see: http://download.oracle.com/docs/cd/A91202_01/901_doc/appdev.901/a89857/oci05bnd.htm#422705 -static const ub1 qOraCharsetForm = SQLCS_NCHAR; -#endif - -#if defined (OCI_UTF16ID) -static const ub2 qOraCharset = OCI_UTF16ID; -#else -static const ub2 qOraCharset = OCI_UCS2ID; -#endif - -typedef QVarLengthArray IndicatorArray; -typedef QVarLengthArray SizeArray; - -static QByteArray qMakeOraDate(const QDateTime& dt); -static QDateTime qMakeDate(const char* oraDate); - -static QByteArray qMakeOCINumber(const qlonglong &ll, OCIError *err); -static QByteArray qMakeOCINumber(const qulonglong& ull, OCIError* err); - -static qlonglong qMakeLongLong(const char* ociNumber, OCIError* err); -static qulonglong qMakeULongLong(const char* ociNumber, OCIError* err); - -static QString qOraWarn(OCIError *err, int *errorCode = 0); - -#ifndef Q_CC_SUN -static // for some reason, Sun CC can't use qOraWarning when it's declared static -#endif -void qOraWarning(const char* msg, OCIError *err); -static QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err); - - - -class QOCIRowId: public QSharedData -{ -public: - QOCIRowId(OCIEnv *env); - ~QOCIRowId(); - - OCIRowid *id; - -private: - QOCIRowId(const QOCIRowId &other): QSharedData(other) { Q_ASSERT(false); } -}; - -QOCIRowId::QOCIRowId(OCIEnv *env) - : id(0) -{ - OCIDescriptorAlloc (env, reinterpret_cast(&id), - OCI_DTYPE_ROWID, 0, 0); -} - -QOCIRowId::~QOCIRowId() -{ - if (id) - OCIDescriptorFree(id, OCI_DTYPE_ROWID); -} - -typedef QSharedDataPointer QOCIRowIdPointer; -QT_BEGIN_INCLUDE_NAMESPACE -Q_DECLARE_METATYPE(QOCIRowIdPointer) -QT_END_INCLUDE_NAMESPACE - -class QOCIDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QOCIDriver) - -public: - QOCIDriverPrivate(); - - OCIEnv *env; - OCISvcCtx *svc; - OCIServer *srvhp; - OCISession *authp; - OCIError *err; - bool transaction; - int serverVersion; - int prefetchRows; - int prefetchMem; - QString user; - - void allocErrorHandle(); -}; - -class QOCICols; -class QOCIResultPrivate; - -class QOCIResult: public QSqlCachedResult -{ - Q_DECLARE_PRIVATE(QOCIResult) - friend class QOCIDriver; - friend class QOCICols; -public: - QOCIResult(const QOCIDriver *db); - ~QOCIResult(); - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool gotoNext(ValueCache &values, int index) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; - QVariant lastInsertId() const Q_DECL_OVERRIDE; - bool execBatch(bool arrayBind = false) Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; -}; - -class QOCIResultPrivate: public QSqlCachedResultPrivate -{ -public: - Q_DECLARE_PUBLIC(QOCIResult) - Q_DECLARE_SQLDRIVER_PRIVATE(QOCIDriver) - QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv); - ~QOCIResultPrivate(); - - QOCICols *cols; - OCIEnv *env; - OCIError *err; - OCISvcCtx *&svc; - OCIStmt *sql; - bool transaction; - int serverVersion; - int prefetchRows, prefetchMem; - - void setStatementAttributes(); - int bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos, - const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList &tmpStorage); - int bindValues(QVector &values, IndicatorArray &indicators, SizeArray &tmpSizes, - QList &tmpStorage); - void outValues(QVector &values, IndicatorArray &indicators, - QList &tmpStorage); - inline bool isOutValue(int i) const - { Q_Q(const QOCIResult); return q->bindValueType(i) & QSql::Out; } - inline bool isBinaryValue(int i) const - { Q_Q(const QOCIResult); return q->bindValueType(i) & QSql::Binary; } - - void setCharset(dvoid* handle, ub4 type) const - { - int r = 0; - Q_ASSERT(handle); - -#ifdef OCI_ATTR_CHARSET_FORM - r = OCIAttrSet(handle, - type, - // this const cast is safe since OCI doesn't touch - // the charset. - const_cast(static_cast(&qOraCharsetForm)), - 0, - OCI_ATTR_CHARSET_FORM, - //Strange Oracle bug: some Oracle servers crash the server process with non-zero error handle (mostly for 10g). - //So ignore the error message here. - 0); - #ifdef QOCI_DEBUG - if (r != 0) - qWarning("QOCIResultPrivate::setCharset: Couldn't set OCI_ATTR_CHARSET_FORM."); - #endif -#endif - - r = OCIAttrSet(handle, - type, - // this const cast is safe since OCI doesn't touch - // the charset. - const_cast(static_cast(&qOraCharset)), - 0, - OCI_ATTR_CHARSET_ID, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setCharsetI Couldn't set OCI_ATTR_CHARSET_ID: ", err); - - } -}; - -void QOCIResultPrivate::setStatementAttributes() -{ - Q_ASSERT(sql); - - int r = 0; - - if (prefetchRows >= 0) { - r = OCIAttrSet(sql, - OCI_HTYPE_STMT, - &prefetchRows, - 0, - OCI_ATTR_PREFETCH_ROWS, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setStatementAttributes:" - " Couldn't set OCI_ATTR_PREFETCH_ROWS: ", err); - } - if (prefetchMem >= 0) { - r = OCIAttrSet(sql, - OCI_HTYPE_STMT, - &prefetchMem, - 0, - OCI_ATTR_PREFETCH_MEMORY, - err); - if (r != 0) - qOraWarning("QOCIResultPrivate::setStatementAttributes:" - " Couldn't set OCI_ATTR_PREFETCH_MEMORY: ", err); - } -} - -int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos, - const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList &tmpStorage) -{ - int r = OCI_SUCCESS; - void *data = const_cast(val.constData()); - - switch (val.type()) { - case QVariant::ByteArray: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - isOutValue(pos) - ? const_cast(reinterpret_cast(data)->constData()) - : reinterpret_cast(data)->data(), - reinterpret_cast(data)->size(), - SQLT_BIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime: { - QByteArray ba = qMakeOraDate(val.toDateTime()); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_DAT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; } - case QVariant::Int: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast(data), - sizeof(int), - SQLT_INT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::UInt: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast(data), - sizeof(uint), - SQLT_UIN, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::LongLong: - { - QByteArray ba = qMakeOCINumber(val.toLongLong(), err); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; - } - case QVariant::ULongLong: - { - QByteArray ba = qMakeOCINumber(val.toULongLong(), err); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - tmpStorage.append(ba); - break; - } - case QVariant::Double: - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // if it's an out value, the data is already detached - // so the const cast is safe. - const_cast(data), - sizeof(double), - SQLT_FLT, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - case QVariant::UserType: - if (val.canConvert() && !isOutValue(pos)) { - // use a const pointer to prevent a detach - const QOCIRowIdPointer rptr = qvariant_cast(val); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // it's an IN value, so const_cast is ok - const_cast(&rptr->id), - -1, - SQLT_RDD, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - } else { - qWarning("Unknown bind variable"); - r = OCI_ERROR; - } - break; - case QVariant::String: { - const QString s = val.toString(); - if (isBinaryValue(pos)) { - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - const_cast(s.utf16()), - s.length() * sizeof(QChar), - SQLT_LNG, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - break; - } else if (!isOutValue(pos)) { - // don't detach the string - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - // safe since oracle doesn't touch OUT values - const_cast(s.utf16()), - (s.length() + 1) * sizeof(QChar), - SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - if (r == OCI_SUCCESS) - setCharset(*hbnd, OCI_HTYPE_BIND); - break; - } - } // fall through for OUT values - default: { - const QString s = val.toString(); - // create a deep-copy - QByteArray ba(reinterpret_cast(s.utf16()), (s.length() + 1) * sizeof(QChar)); - if (isOutValue(pos)) { - ba.reserve((s.capacity() + 1) * sizeof(QChar)); - *tmpSize = ba.size(); - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.capacity(), - SQLT_STR, indPtr, tmpSize, 0, 0, 0, OCI_DEFAULT); - } else { - r = OCIBindByPos(sql, hbnd, err, - pos + 1, - ba.data(), - ba.size(), - SQLT_STR, indPtr, 0, 0, 0, 0, OCI_DEFAULT); - } - if (r == OCI_SUCCESS) - setCharset(*hbnd, OCI_HTYPE_BIND); - tmpStorage.append(ba); - break; - } // default case - } // switch - if (r != OCI_SUCCESS) - qOraWarning("QOCIResultPrivate::bindValue:", err); - return r; -} - -int QOCIResultPrivate::bindValues(QVector &values, IndicatorArray &indicators, - SizeArray &tmpSizes, QList &tmpStorage) -{ - int r = OCI_SUCCESS; - for (int i = 0; i < values.count(); ++i) { - if (isOutValue(i)) - values[i].detach(); - const QVariant &val = values.at(i); - - OCIBind * hbnd = 0; // Oracle handles these automatically - sb2 *indPtr = &indicators[i]; - *indPtr = val.isNull() ? -1 : 0; - - bindValue(sql, &hbnd, err, i, val, indPtr, &tmpSizes[i], tmpStorage); - } - return r; -} - -// will assign out value and remove its temp storage. -static void qOraOutValue(QVariant &value, QList &storage, OCIError* err) -{ - switch (value.type()) { - case QVariant::Time: - value = qMakeDate(storage.takeFirst()).time(); - break; - case QVariant::Date: - value = qMakeDate(storage.takeFirst()).date(); - break; - case QVariant::DateTime: - value = qMakeDate(storage.takeFirst()); - break; - case QVariant::LongLong: - value = qMakeLongLong(storage.takeFirst(), err); - break; - case QVariant::ULongLong: - value = qMakeULongLong(storage.takeFirst(), err); - break; - case QVariant::String: - value = QString( - reinterpret_cast(storage.takeFirst().constData())); - break; - default: - break; //nothing - } -} - -void QOCIResultPrivate::outValues(QVector &values, IndicatorArray &indicators, - QList &tmpStorage) -{ - for (int i = 0; i < values.count(); ++i) { - - if (!isOutValue(i)) - continue; - - qOraOutValue(values[i], tmpStorage, err); - - QVariant::Type typ = values.at(i).type(); - if (indicators[i] == -1) // NULL - values[i] = QVariant(typ); - else - values[i] = QVariant(typ, values.at(i).constData()); - } -} - - -QOCIDriverPrivate::QOCIDriverPrivate() - : QSqlDriverPrivate(), env(0), svc(0), srvhp(0), authp(0), err(0), transaction(false), - serverVersion(-1), prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM) -{ - dbmsType = QSqlDriver::Oracle; -} - -void QOCIDriverPrivate::allocErrorHandle() -{ - int r = OCIHandleAlloc(env, - reinterpret_cast(&err), - OCI_HTYPE_ERROR, - 0, - 0); - if (r != 0) - qWarning("QOCIDriver: unable to allocate error handle"); -} - -struct OraFieldInfo -{ - QString name; - QVariant::Type type; - ub1 oraIsNull; - ub4 oraType; - sb1 oraScale; - ub4 oraLength; // size in bytes - ub4 oraFieldLength; // amount of characters - sb2 oraPrecision; -}; - -QString qOraWarn(OCIError *err, int *errorCode) -{ - sb4 errcode; - text errbuf[1024]; - errbuf[0] = 0; - errbuf[1] = 0; - - OCIErrorGet(err, - 1, - 0, - &errcode, - errbuf, - sizeof(errbuf), - OCI_HTYPE_ERROR); - if (errorCode) - *errorCode = errcode; - return QString(reinterpret_cast(errbuf)); -} - -void qOraWarning(const char* msg, OCIError *err) -{ -#ifdef QOCI_DEBUG - qWarning("%s %s", msg, qPrintable(qOraWarn(err))); -#else - Q_UNUSED(msg); - Q_UNUSED(err); -#endif -} - -static int qOraErrorNumber(OCIError *err) -{ - sb4 errcode; - OCIErrorGet(err, - 1, - 0, - &errcode, - 0, - 0, - OCI_HTYPE_ERROR); - return errcode; -} - -QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIError *err) -{ - int errorCode = 0; - const QString oraErrorString = qOraWarn(err, &errorCode); - return QSqlError(errString, oraErrorString, type, errorCode); -} - -QVariant::Type qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy precisionPolicy) -{ - QVariant::Type type = QVariant::Invalid; - if (ocitype == QLatin1String("VARCHAR2") || ocitype == QLatin1String("VARCHAR") - || ocitype.startsWith(QLatin1String("INTERVAL")) - || ocitype == QLatin1String("CHAR") || ocitype == QLatin1String("NVARCHAR2") - || ocitype == QLatin1String("NCHAR")) - type = QVariant::String; - else if (ocitype == QLatin1String("NUMBER") - || ocitype == QLatin1String("FLOAT") - || ocitype == QLatin1String("BINARY_FLOAT") - || ocitype == QLatin1String("BINARY_DOUBLE")) { - switch(precisionPolicy) { - case QSql::LowPrecisionInt32: - type = QVariant::Int; - break; - case QSql::LowPrecisionInt64: - type = QVariant::LongLong; - break; - case QSql::LowPrecisionDouble: - type = QVariant::Double; - break; - case QSql::HighPrecision: - default: - type = QVariant::String; - break; - } - } - else if (ocitype == QLatin1String("LONG") || ocitype == QLatin1String("NCLOB") - || ocitype == QLatin1String("CLOB")) - type = QVariant::ByteArray; - else if (ocitype == QLatin1String("RAW") || ocitype == QLatin1String("LONG RAW") - || ocitype == QLatin1String("ROWID") || ocitype == QLatin1String("BLOB") - || ocitype == QLatin1String("CFILE") || ocitype == QLatin1String("BFILE")) - type = QVariant::ByteArray; - else if (ocitype == QLatin1String("DATE") || ocitype.startsWith(QLatin1String("TIME"))) - type = QVariant::DateTime; - else if (ocitype == QLatin1String("UNDEFINED")) - type = QVariant::Invalid; - if (type == QVariant::Invalid) - qWarning("qDecodeOCIType: unknown type: %s", ocitype.toLocal8Bit().constData()); - return type; -} - -QVariant::Type qDecodeOCIType(int ocitype, QSql::NumericalPrecisionPolicy precisionPolicy) -{ - QVariant::Type type = QVariant::Invalid; - switch (ocitype) { - case SQLT_STR: - case SQLT_VST: - case SQLT_CHR: - case SQLT_AFC: - case SQLT_VCS: - case SQLT_AVC: - case SQLT_RDD: - case SQLT_LNG: -#ifdef SQLT_INTERVAL_YM - case SQLT_INTERVAL_YM: -#endif -#ifdef SQLT_INTERVAL_DS - case SQLT_INTERVAL_DS: -#endif - type = QVariant::String; - break; - case SQLT_INT: - type = QVariant::Int; - break; - case SQLT_FLT: - case SQLT_NUM: - case SQLT_VNU: - case SQLT_UIN: - switch(precisionPolicy) { - case QSql::LowPrecisionInt32: - type = QVariant::Int; - break; - case QSql::LowPrecisionInt64: - type = QVariant::LongLong; - break; - case QSql::LowPrecisionDouble: - type = QVariant::Double; - break; - case QSql::HighPrecision: - default: - type = QVariant::String; - break; - } - break; - case SQLT_VBI: - case SQLT_BIN: - case SQLT_LBI: - case SQLT_LVC: - case SQLT_LVB: - case SQLT_BLOB: - case SQLT_CLOB: - case SQLT_FILE: - case SQLT_NTY: - case SQLT_REF: - case SQLT_RID: - type = QVariant::ByteArray; - break; - case SQLT_DAT: - case SQLT_ODT: -#ifdef SQLT_TIMESTAMP - case SQLT_TIMESTAMP: - case SQLT_TIMESTAMP_TZ: - case SQLT_TIMESTAMP_LTZ: -#endif - type = QVariant::DateTime; - break; - default: - type = QVariant::Invalid; - qWarning("qDecodeOCIType: unknown OCI datatype: %d", ocitype); - break; - } - return type; -} - -static QSqlField qFromOraInf(const OraFieldInfo &ofi) -{ - QSqlField f(ofi.name, ofi.type); - f.setRequired(ofi.oraIsNull == 0); - - if (ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU) - f.setLength(ofi.oraFieldLength); - else - f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision)); - - f.setPrecision(ofi.oraScale); - f.setSqlType(int(ofi.oraType)); - return f; -} - -/*! - \internal - - Convert QDateTime to the internal Oracle DATE format NB! - It does not handle BCE dates. -*/ -QByteArray qMakeOraDate(const QDateTime& dt) -{ - QByteArray ba; - ba.resize(7); - int year = dt.date().year(); - ba[0]= (year / 100) + 100; // century - ba[1]= (year % 100) + 100; // year - ba[2]= dt.date().month(); - ba[3]= dt.date().day(); - ba[4]= dt.time().hour() + 1; - ba[5]= dt.time().minute() + 1; - ba[6]= dt.time().second() + 1; - return ba; -} - -/*! - \internal - - Convert qlonglong to the internal Oracle OCINumber format. - */ -QByteArray qMakeOCINumber(const qlonglong& ll, OCIError* err) -{ - QByteArray ba(sizeof(OCINumber), 0); - - OCINumberFromInt(err, - &ll, - sizeof(qlonglong), - OCI_NUMBER_SIGNED, - reinterpret_cast(ba.data())); - return ba; -} - -/*! - \internal - - Convert qulonglong to the internal Oracle OCINumber format. - */ -QByteArray qMakeOCINumber(const qulonglong& ull, OCIError* err) -{ - QByteArray ba(sizeof(OCINumber), 0); - - OCINumberFromInt(err, - &ull, - sizeof(qlonglong), - OCI_NUMBER_UNSIGNED, - reinterpret_cast(ba.data())); - return ba; -} - -qlonglong qMakeLongLong(const char* ociNumber, OCIError* err) -{ - qlonglong qll = 0; - OCINumberToInt(err, reinterpret_cast(ociNumber), sizeof(qlonglong), - OCI_NUMBER_SIGNED, &qll); - return qll; -} - -qulonglong qMakeULongLong(const char* ociNumber, OCIError* err) -{ - qulonglong qull = 0; - OCINumberToInt(err, reinterpret_cast(ociNumber), sizeof(qulonglong), - OCI_NUMBER_UNSIGNED, &qull); - return qull; -} - -QDateTime qMakeDate(const char* oraDate) -{ - int century = uchar(oraDate[0]); - if(century >= 100){ - int year = uchar(oraDate[1]); - year = ((century-100)*100) + (year-100); - int month = oraDate[2]; - int day = oraDate[3]; - int hour = oraDate[4] - 1; - int min = oraDate[5] - 1; - int sec = oraDate[6] - 1; - return QDateTime(QDate(year,month,day), QTime(hour,min,sec)); - } - return QDateTime(); -} - -class QOCICols -{ -public: - QOCICols(int size, QOCIResultPrivate* dp); - ~QOCICols(); - int readPiecewise(QVector &values, int index = 0); - int readLOBs(QVector &values, int index = 0); - int fieldFromDefine(OCIDefine* d); - void getValues(QVector &v, int index); - inline int size() { return fieldInf.size(); } - static bool execBatch(QOCIResultPrivate *d, QVector &boundValues, bool arrayBind); - - QSqlRecord rec; - -private: - char* create(int position, int size); - OCILobLocator ** createLobLocator(int position, OCIEnv* env); - OraFieldInfo qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const; - - class OraFieldInf - { - public: - OraFieldInf(): data(0), len(0), ind(0), typ(QVariant::Invalid), oraType(0), def(0), lob(0) - {} - ~OraFieldInf(); - char *data; - int len; - sb2 ind; - QVariant::Type typ; - ub4 oraType; - OCIDefine *def; - OCILobLocator *lob; - }; - - QVector fieldInf; - const QOCIResultPrivate *const d; -}; - -QOCICols::OraFieldInf::~OraFieldInf() -{ - delete [] data; - if (lob) { - int r = OCIDescriptorFree(lob, OCI_DTYPE_LOB); - if (r != 0) - qWarning("QOCICols: Cannot free LOB descriptor"); - } -} - -QOCICols::QOCICols(int size, QOCIResultPrivate* dp) - : fieldInf(size), d(dp) -{ - ub4 dataSize = 0; - OCIDefine* dfn = 0; - int r; - - OCIParam* param = 0; - sb4 parmStatus = 0; - ub4 count = 1; - int idx = 0; - parmStatus = OCIParamGet(d->sql, - OCI_HTYPE_STMT, - d->err, - reinterpret_cast(¶m), - count); - - while (parmStatus == OCI_SUCCESS) { - OraFieldInfo ofi = qMakeOraField(d, param); - if (ofi.oraType == SQLT_RDD) - dataSize = 50; -#ifdef SQLT_INTERVAL_YM -#ifdef SQLT_INTERVAL_DS - else if (ofi.oraType == SQLT_INTERVAL_YM || ofi.oraType == SQLT_INTERVAL_DS) - // since we are binding interval datatype as string, - // we are not interested in the number of bytes but characters. - dataSize = 50; // magic number -#endif //SQLT_INTERVAL_DS -#endif //SQLT_INTERVAL_YM - else if (ofi.oraType == SQLT_NUM || ofi.oraType == SQLT_VNU){ - if (ofi.oraPrecision > 0) - dataSize = (ofi.oraPrecision + 1) * sizeof(utext); - else - dataSize = (38 + 1) * sizeof(utext); - } - else - dataSize = ofi.oraLength; - - fieldInf[idx].typ = ofi.type; - fieldInf[idx].oraType = ofi.oraType; - rec.append(qFromOraInf(ofi)); - - switch (ofi.type) { - case QVariant::DateTime: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize+1), - dataSize+1, - SQLT_DAT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::Double: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(double) - 1), - sizeof(double), - SQLT_FLT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::Int: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(qint32) - 1), - sizeof(qint32), - SQLT_INT, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::LongLong: - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, sizeof(OCINumber)), - sizeof(OCINumber), - SQLT_VNU, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - case QVariant::ByteArray: - // RAW and LONG RAW fields can't be bound to LOB locators - if (ofi.oraType == SQLT_BIN) { -// qDebug("binding SQLT_BIN"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize, - SQLT_BIN, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else if (ofi.oraType == SQLT_LBI) { -// qDebug("binding SQLT_LBI"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - 0, - SB4MAXVAL, - SQLT_LBI, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else if (ofi.oraType == SQLT_CLOB) { - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - createLobLocator(idx, d->env), - -1, - SQLT_CLOB, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - } else { -// qDebug("binding SQLT_BLOB"); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - createLobLocator(idx, d->env), - -1, - SQLT_BLOB, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - } - break; - case QVariant::String: - if (ofi.oraType == SQLT_LNG) { - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - 0, - SB4MAXVAL, - SQLT_LNG, - &(fieldInf[idx].ind), - 0, 0, OCI_DYNAMIC_FETCH); - } else { - dataSize += dataSize + sizeof(QChar); - //qDebug("OCIDefineByPosStr(%d): %d", count, dataSize); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize, - SQLT_STR, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - if (r == 0) - d->setCharset(dfn, OCI_HTYPE_DEFINE); - } - break; - default: - // this should make enough space even with character encoding - dataSize = (dataSize + 1) * sizeof(utext) ; - //qDebug("OCIDefineByPosDef(%d): %d", count, dataSize); - r = OCIDefineByPos(d->sql, - &dfn, - d->err, - count, - create(idx, dataSize), - dataSize+1, - SQLT_STR, - &(fieldInf[idx].ind), - 0, 0, OCI_DEFAULT); - break; - } - if (r != 0) - qOraWarning("QOCICols::bind:", d->err); - fieldInf[idx].def = dfn; - ++count; - ++idx; - parmStatus = OCIParamGet(d->sql, - OCI_HTYPE_STMT, - d->err, - reinterpret_cast(¶m), - count); - } -} - -QOCICols::~QOCICols() -{ -} - -char* QOCICols::create(int position, int size) -{ - char* c = new char[size+1]; - // Oracle may not fill fixed width fields - memset(c, 0, size+1); - fieldInf[position].data = c; - fieldInf[position].len = size; - return c; -} - -OCILobLocator **QOCICols::createLobLocator(int position, OCIEnv* env) -{ - OCILobLocator *& lob = fieldInf[position].lob; - int r = OCIDescriptorAlloc(env, - reinterpret_cast(&lob), - OCI_DTYPE_LOB, - 0, - 0); - if (r != 0) { - qWarning("QOCICols: Cannot create LOB locator"); - lob = 0; - } - return &lob; -} - -int QOCICols::readPiecewise(QVector &values, int index) -{ - OCIDefine* dfn; - ub4 typep; - ub1 in_outp; - ub4 iterp; - ub4 idxp; - ub1 piecep; - sword status; - text col [QOCI_DYNAMIC_CHUNK_SIZE+1]; - int fieldNum = -1; - int r = 0; - bool nullField; - - do { - r = OCIStmtGetPieceInfo(d->sql, d->err, reinterpret_cast(&dfn), &typep, - &in_outp, &iterp, &idxp, &piecep); - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readPiecewise: unable to get piece info:", d->err); - fieldNum = fieldFromDefine(dfn); - bool isStringField = fieldInf.at(fieldNum).oraType == SQLT_LNG; - ub4 chunkSize = QOCI_DYNAMIC_CHUNK_SIZE; - nullField = false; - r = OCIStmtSetPieceInfo(dfn, OCI_HTYPE_DEFINE, - d->err, col, - &chunkSize, piecep, NULL, NULL); - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readPiecewise: unable to set piece info:", d->err); - status = OCIStmtFetch (d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); - if (status == -1) { - sb4 errcode; - OCIErrorGet(d->err, 1, 0, &errcode, 0, 0,OCI_HTYPE_ERROR); - switch (errcode) { - case 1405: /* NULL */ - nullField = true; - break; - default: - qOraWarning("OCIResultPrivate::readPiecewise: unable to fetch next:", d->err); - break; - } - } - if (status == OCI_NO_DATA) - break; - if (nullField || !chunkSize) { - fieldInf[fieldNum].ind = -1; - } else { - if (isStringField) { - QString str = values.at(fieldNum + index).toString(); - str += QString(reinterpret_cast(col), chunkSize / 2); - values[fieldNum + index] = str; - fieldInf[fieldNum].ind = 0; - } else { - QByteArray ba = values.at(fieldNum + index).toByteArray(); - int sz = ba.size(); - ba.resize(sz + chunkSize); - memcpy(ba.data() + sz, reinterpret_cast(col), chunkSize); - values[fieldNum + index] = ba; - fieldInf[fieldNum].ind = 0; - } - } - } while (status == OCI_SUCCESS_WITH_INFO || status == OCI_NEED_DATA); - return r; -} - -OraFieldInfo QOCICols::qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const -{ - OraFieldInfo ofi; - ub2 colType(0); - text *colName = 0; - ub4 colNameLen(0); - sb1 colScale(0); - ub2 colLength(0); - ub2 colFieldLength(0); - sb2 colPrecision(0); - ub1 colIsNull(0); - int r(0); - QVariant::Type type(QVariant::Invalid); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colType, - 0, - OCI_ATTR_DATA_TYPE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colName, - &colNameLen, - OCI_ATTR_NAME, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colLength, - 0, - OCI_ATTR_DATA_SIZE, /* in bytes */ - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - -#ifdef OCI_ATTR_CHAR_SIZE - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colFieldLength, - 0, - OCI_ATTR_CHAR_SIZE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); -#else - // for Oracle8. - colFieldLength = colLength; -#endif - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colPrecision, - 0, - OCI_ATTR_PRECISION, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colScale, - 0, - OCI_ATTR_SCALE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colType, - 0, - OCI_ATTR_DATA_TYPE, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - r = OCIAttrGet(param, - OCI_DTYPE_PARAM, - &colIsNull, - 0, - OCI_ATTR_IS_NULL, - p->err); - if (r != 0) - qOraWarning("qMakeOraField:", p->err); - - type = qDecodeOCIType(colType, p->q_func()->numericalPrecisionPolicy()); - - if (type == QVariant::Int) { - if (colLength == 22 && colPrecision == 0 && colScale == 0) - type = QVariant::String; - if (colScale > 0) - type = QVariant::String; - } - - // bind as double if the precision policy asks for it - if (((colType == SQLT_FLT) || (colType == SQLT_NUM)) - && (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)) { - type = QVariant::Double; - } - - // bind as int32 or int64 if the precision policy asks for it - if ((colType == SQLT_NUM) || (colType == SQLT_VNU) || (colType == SQLT_UIN) - || (colType == SQLT_INT)) { - if (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64) - type = QVariant::LongLong; - else if (p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32) - type = QVariant::Int; - } - - if (colType == SQLT_BLOB) - colLength = 0; - - // colNameLen is length in bytes - ofi.name = QString(reinterpret_cast(colName), colNameLen / 2); - ofi.type = type; - ofi.oraType = colType; - ofi.oraFieldLength = colFieldLength; - ofi.oraLength = colLength; - ofi.oraScale = colScale; - ofi.oraPrecision = colPrecision; - ofi.oraIsNull = colIsNull; - - return ofi; -} - -struct QOCIBatchColumn -{ - inline QOCIBatchColumn() - : bindh(0), bindAs(0), maxLen(0), recordCount(0), - data(0), lengths(0), indicators(0), maxarr_len(0), curelep(0) {} - - OCIBind* bindh; - ub2 bindAs; - ub4 maxLen; - ub4 recordCount; - char* data; - ub2* lengths; - sb2* indicators; - ub4 maxarr_len; - ub4 curelep; -}; - -struct QOCIBatchCleanupHandler -{ - inline QOCIBatchCleanupHandler(QVector &columns) - : col(columns) {} - - ~QOCIBatchCleanupHandler() - { - // deleting storage, length and indicator arrays - for ( int j = 0; j < col.count(); ++j){ - delete[] col[j].lengths; - delete[] col[j].indicators; - delete[] col[j].data; - } - } - - QVector &col; -}; - -bool QOCICols::execBatch(QOCIResultPrivate *d, QVector &boundValues, bool arrayBind) -{ - int columnCount = boundValues.count(); - if (boundValues.isEmpty() || columnCount == 0) - return false; - -#ifdef QOCI_DEBUG - qDebug() << "columnCount:" << columnCount << boundValues; -#endif - - int i; - sword r; - - QVarLengthArray fieldTypes; - for (i = 0; i < columnCount; ++i) { - QVariant::Type tp = boundValues.at(i).type(); - fieldTypes.append(tp == QVariant::List ? boundValues.at(i).toList().value(0).type() - : tp); - } - - QList tmpStorage; - SizeArray tmpSizes(columnCount); - QVector columns(columnCount); - QOCIBatchCleanupHandler cleaner(columns); - - // figuring out buffer sizes - for (i = 0; i < columnCount; ++i) { - - if (boundValues.at(i).type() != QVariant::List) { - - // not a list - create a deep-copy of the single value - QOCIBatchColumn &singleCol = columns[i]; - singleCol.indicators = new sb2[1]; - *singleCol.indicators = boundValues.at(i).isNull() ? -1 : 0; - - r = d->bindValue(d->sql, &singleCol.bindh, d->err, i, - boundValues.at(i), singleCol.indicators, &tmpSizes[i], tmpStorage); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - continue; - } - - QOCIBatchColumn &col = columns[i]; - col.recordCount = boundValues.at(i).toList().count(); - - col.lengths = new ub2[col.recordCount]; - col.indicators = new sb2[col.recordCount]; - col.maxarr_len = col.recordCount; - col.curelep = col.recordCount; - - switch (fieldTypes[i]) { - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime: - col.bindAs = SQLT_DAT; - col.maxLen = 7; - break; - - case QVariant::Int: - col.bindAs = SQLT_INT; - col.maxLen = sizeof(int); - break; - - case QVariant::UInt: - col.bindAs = SQLT_UIN; - col.maxLen = sizeof(uint); - break; - - case QVariant::LongLong: - col.bindAs = SQLT_VNU; - col.maxLen = sizeof(OCINumber); - break; - - case QVariant::ULongLong: - col.bindAs = SQLT_VNU; - col.maxLen = sizeof(OCINumber); - break; - - case QVariant::Double: - col.bindAs = SQLT_FLT; - col.maxLen = sizeof(double); - break; - - case QVariant::UserType: - col.bindAs = SQLT_RDD; - col.maxLen = sizeof(OCIRowid*); - break; - - case QVariant::String: { - col.bindAs = SQLT_STR; - for (uint j = 0; j < col.recordCount; ++j) { - uint len; - if(d->isOutValue(i)) - len = boundValues.at(i).toList().at(j).toString().capacity() + 1; - else - len = boundValues.at(i).toList().at(j).toString().length() + 1; - if (len > col.maxLen) - col.maxLen = len; - } - col.maxLen *= sizeof(QChar); - break; } - - case QVariant::ByteArray: - default: { - col.bindAs = SQLT_LBI; - for (uint j = 0; j < col.recordCount; ++j) { - if(d->isOutValue(i)) - col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().capacity(); - else - col.lengths[j] = boundValues.at(i).toList().at(j).toByteArray().size(); - if (col.lengths[j] > col.maxLen) - col.maxLen = col.lengths[j]; - } - break; } - } - - col.data = new char[col.maxLen * col.recordCount]; - memset(col.data, 0, col.maxLen * col.recordCount); - - // we may now populate column with data - for (uint row = 0; row < col.recordCount; ++row) { - const QVariant &val = boundValues.at(i).toList().at(row); - - if (val.isNull()){ - columns[i].indicators[row] = -1; - columns[i].lengths[row] = 0; - } else { - columns[i].indicators[row] = 0; - char *dataPtr = columns[i].data + (columns[i].maxLen * row); - switch (fieldTypes[i]) { - case QVariant::Time: - case QVariant::Date: - case QVariant::DateTime:{ - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOraDate(val.toDateTime()); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::Int: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast(dataPtr) = val.toInt(); - break; - - case QVariant::UInt: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast(dataPtr) = val.toUInt(); - break; - - case QVariant::LongLong: - { - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOCINumber(val.toLongLong(), d->err); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::ULongLong: - { - columns[i].lengths[row] = columns[i].maxLen; - const QByteArray ba = qMakeOCINumber(val.toULongLong(), d->err); - Q_ASSERT(ba.size() == int(columns[i].maxLen)); - memcpy(dataPtr, ba.constData(), columns[i].maxLen); - break; - } - case QVariant::Double: - columns[i].lengths[row] = columns[i].maxLen; - *reinterpret_cast(dataPtr) = val.toDouble(); - break; - - case QVariant::String: { - const QString s = val.toString(); - columns[i].lengths[row] = (s.length() + 1) * sizeof(QChar); - memcpy(dataPtr, s.utf16(), columns[i].lengths[row]); - break; - } - case QVariant::UserType: - if (val.canConvert()) { - const QOCIRowIdPointer rptr = qvariant_cast(val); - *reinterpret_cast(dataPtr) = rptr->id; - columns[i].lengths[row] = 0; - break; - } - case QVariant::ByteArray: - default: { - const QByteArray ba = val.toByteArray(); - columns[i].lengths[row] = ba.size(); - memcpy(dataPtr, ba.constData(), ba.size()); - break; - } - } - } - } - - QOCIBatchColumn &bindColumn = columns[i]; - -#ifdef QOCI_DEBUG - qDebug("OCIBindByPos(%p, %p, %p, %d, %p, %d, %d, %p, %p, 0, %d, %p, OCI_DEFAULT)", - d->sql, &bindColumn.bindh, d->err, i + 1, bindColumn.data, - bindColumn.maxLen, bindColumn.bindAs, bindColumn.indicators, bindColumn.lengths, - arrayBind ? bindColumn.maxarr_len : 0, arrayBind ? &bindColumn.curelep : 0); - - for (int ii = 0; ii < (int)bindColumn.recordCount; ++ii) { - qDebug(" record %d: indicator %d, length %d", ii, bindColumn.indicators[ii], - bindColumn.lengths[ii]); - } -#endif - - - // binding the column - r = OCIBindByPos( - d->sql, &bindColumn.bindh, d->err, i + 1, - bindColumn.data, - bindColumn.maxLen, - bindColumn.bindAs, - bindColumn.indicators, - bindColumn.lengths, - 0, - arrayBind ? bindColumn.maxarr_len : 0, - arrayBind ? &bindColumn.curelep : 0, - OCI_DEFAULT); - -#ifdef QOCI_DEBUG - qDebug("After OCIBindByPos: r = %d, bindh = %p", r, bindColumn.bindh); -#endif - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - - r = OCIBindArrayOfStruct ( - columns[i].bindh, d->err, - columns[i].maxLen, - sizeof(columns[i].indicators[0]), - sizeof(columns[i].lengths[0]), - 0); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to bind column:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to bind column for batch execute"), - QSqlError::StatementError, d->err)); - return false; - } - } - - //finaly we can execute - r = OCIStmtExecute(d->svc, d->sql, d->err, - arrayBind ? 1 : columns[0].recordCount, - 0, NULL, NULL, - d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIPrivate::execBatch: unable to execute batch statement:", d->err); - d->q_func()->setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to execute batch statement"), - QSqlError::StatementError, d->err)); - return false; - } - - // for out parameters we copy data back to value vector - for (i = 0; i < columnCount; ++i) { - - if (!d->isOutValue(i)) - continue; - - QVariant::Type tp = boundValues.at(i).type(); - if (tp != QVariant::List) { - qOraOutValue(boundValues[i], tmpStorage, d->err); - if (*columns[i].indicators == -1) - boundValues[i] = QVariant(tp); - continue; - } - - QVariantList *list = static_cast(const_cast(boundValues.at(i).data())); - - char* data = columns[i].data; - for (uint r = 0; r < columns[i].recordCount; ++r){ - - if (columns[i].indicators[r] == -1) { - (*list)[r] = QVariant(); - continue; - } - - switch(columns[i].bindAs) { - - case SQLT_DAT: - (*list)[r] = qMakeDate(data + r * columns[i].maxLen); - break; - - case SQLT_INT: - (*list)[r] = *reinterpret_cast(data + r * columns[i].maxLen); - break; - - case SQLT_UIN: - (*list)[r] = *reinterpret_cast(data + r * columns[i].maxLen); - break; - - case SQLT_VNU: - { - switch (boundValues.at(i).type()) { - case QVariant::LongLong: - (*list)[r] = qMakeLongLong(data + r * columns[i].maxLen, d->err); - break; - case QVariant::ULongLong: - (*list)[r] = qMakeULongLong(data + r * columns[i].maxLen, d->err); - break; - default: - break; - } - break; - } - - case SQLT_FLT: - (*list)[r] = *reinterpret_cast(data + r * columns[i].maxLen); - break; - - case SQLT_STR: - (*list)[r] = QString(reinterpret_cast(data - + r * columns[i].maxLen)); - break; - - default: - (*list)[r] = QByteArray(data + r * columns[i].maxLen, columns[i].maxLen); - break; - } - } - } - - d->q_func()->setSelect(false); - d->q_func()->setAt(QSql::BeforeFirstRow); - d->q_func()->setActive(true); - - return true; -} - -template -int qReadLob(T &buf, const QOCIResultPrivate *d, OCILobLocator *lob) -{ - ub1 csfrm; - ub4 amount; - int r; - - // Read this from the database, don't assume we know what it is set to - r = OCILobCharSetForm(d->env, d->err, lob, &csfrm); - if (r != OCI_SUCCESS) { - qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB char set form: ", d->err); - csfrm = 0; - } - - // Get the length of the LOB (this is in characters) - r = OCILobGetLength(d->svc, d->err, lob, &amount); - if (r == OCI_SUCCESS) { - if (amount == 0) { - // Short cut for null LOBs - buf.resize(0); - return OCI_SUCCESS; - } - } else { - qOraWarning("OCIResultPrivate::readLobs: Couldn't get LOB length: ", d->err); - return r; - } - - // Resize the buffer to hold the LOB contents - buf.resize(amount); - - // Read the LOB into the buffer - r = OCILobRead(d->svc, - d->err, - lob, - &amount, - 1, - buf.data(), - buf.size() * sz, // this argument is in bytes, not characters - 0, - 0, - // Extract the data from a CLOB in UTF-16 (ie. what QString uses internally) - sz == 1 ? ub2(0) : ub2(QOCIEncoding), - csfrm); - - if (r != OCI_SUCCESS) - qOraWarning("OCIResultPrivate::readLOBs: Cannot read LOB: ", d->err); - - return r; -} - -int QOCICols::readLOBs(QVector &values, int index) -{ - OCILobLocator *lob; - int r = OCI_SUCCESS; - - for (int i = 0; i < size(); ++i) { - const OraFieldInf &fi = fieldInf.at(i); - if (fi.ind == -1 || !(lob = fi.lob)) - continue; - - bool isClob = fi.oraType == SQLT_CLOB; - QVariant var; - - if (isClob) { - QString str; - r = qReadLob(str, d, lob); - var = str; - } else { - QByteArray buf; - r = qReadLob(buf, d, lob); - var = buf; - } - if (r == OCI_SUCCESS) - values[index + i] = var; - else - break; - } - return r; -} - -int QOCICols::fieldFromDefine(OCIDefine* d) -{ - for (int i = 0; i < fieldInf.count(); ++i) { - if (fieldInf.at(i).def == d) - return i; - } - return -1; -} - -void QOCICols::getValues(QVector &v, int index) -{ - for (int i = 0; i < fieldInf.size(); ++i) { - const OraFieldInf &fld = fieldInf.at(i); - - if (fld.ind == -1) { - // got a NULL value - v[index + i] = QVariant(fld.typ); - continue; - } - - if (fld.oraType == SQLT_BIN || fld.oraType == SQLT_LBI || fld.oraType == SQLT_LNG) - continue; // already fetched piecewise - - switch (fld.typ) { - case QVariant::DateTime: - v[index + i] = QVariant(qMakeDate(fld.data)); - break; - case QVariant::Double: - case QVariant::Int: - case QVariant::LongLong: - if (d->q_func()->numericalPrecisionPolicy() != QSql::HighPrecision) { - if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble) - && (fld.typ == QVariant::Double)) { - v[index + i] = *reinterpret_cast(fld.data); - break; - } else if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64) - && (fld.typ == QVariant::LongLong)) { - qint64 qll = 0; - int r = OCINumberToInt(d->err, reinterpret_cast(fld.data), sizeof(qint64), - OCI_NUMBER_SIGNED, &qll); - if(r == OCI_SUCCESS) - v[index + i] = qll; - else - v[index + i] = QVariant(); - break; - } else if ((d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32) - && (fld.typ == QVariant::Int)) { - v[index + i] = *reinterpret_cast(fld.data); - break; - } - } - // else fall through - case QVariant::String: - v[index + i] = QString(reinterpret_cast(fld.data)); - break; - case QVariant::ByteArray: - if (fld.len > 0) - v[index + i] = QByteArray(fld.data, fld.len); - else - v[index + i] = QVariant(QVariant::ByteArray); - break; - default: - qWarning("QOCICols::value: unknown data type"); - break; - } - } -} - -QOCIResultPrivate::QOCIResultPrivate(QOCIResult *q, const QOCIDriver *drv) - : QSqlCachedResultPrivate(q, drv), - cols(0), - env(drv_d_func()->env), - err(0), - svc(const_cast(drv_d_func()->svc)), - sql(0), - transaction(drv_d_func()->transaction), - serverVersion(drv_d_func()->serverVersion), - prefetchRows(drv_d_func()->prefetchRows), - prefetchMem(drv_d_func()->prefetchMem) -{ - int r = OCIHandleAlloc(env, - reinterpret_cast(&err), - OCI_HTYPE_ERROR, - 0, - 0); - if (r != 0) - qWarning("QOCIResult: unable to alloc error handle"); -} - -QOCIResultPrivate::~QOCIResultPrivate() -{ - delete cols; - - int r = OCIHandleFree(err, OCI_HTYPE_ERROR); - if (r != 0) - qWarning("~QOCIResult: unable to free statement handle"); -} - - -//////////////////////////////////////////////////////////////////////////// - -QOCIResult::QOCIResult(const QOCIDriver *db) - : QSqlCachedResult(*new QOCIResultPrivate(this, db)) -{ -} - -QOCIResult::~QOCIResult() -{ - Q_D(QOCIResult); - if (d->sql) { - int r = OCIHandleFree(d->sql, OCI_HTYPE_STMT); - if (r != 0) - qWarning("~QOCIResult: unable to free statement handle"); - } -} - -QVariant QOCIResult::handle() const -{ - Q_D(const QOCIResult); - return QVariant::fromValue(d->sql); -} - -bool QOCIResult::reset (const QString& query) -{ - if (!prepare(query)) - return false; - return exec(); -} - -bool QOCIResult::gotoNext(QSqlCachedResult::ValueCache &values, int index) -{ - Q_D(QOCIResult); - if (at() == QSql::AfterLastRow) - return false; - - bool piecewise = false; - int r = OCI_SUCCESS; - r = OCIStmtFetch(d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); - - if (index < 0) //not interested in values - return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO; - - switch (r) { - case OCI_SUCCESS: - break; - case OCI_SUCCESS_WITH_INFO: - qOraWarning("QOCIResult::gotoNext: SuccessWithInfo: ", d->err); - r = OCI_SUCCESS; //ignore it - break; - case OCI_NO_DATA: - // end of rowset - return false; - case OCI_NEED_DATA: - piecewise = true; - r = OCI_SUCCESS; - break; - case OCI_ERROR: - if (qOraErrorNumber(d->err) == 1406) { - qWarning("QOCI Warning: data truncated for %s", lastQuery().toLocal8Bit().constData()); - r = OCI_SUCCESS; /* ignore it */ - break; - } - // fall through - default: - qOraWarning("QOCIResult::gotoNext: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to goto next"), - QSqlError::StatementError, d->err)); - break; - } - - // need to read piecewise before assigning values - if (r == OCI_SUCCESS && piecewise) - r = d->cols->readPiecewise(values, index); - - if (r == OCI_SUCCESS) - d->cols->getValues(values, index); - if (r == OCI_SUCCESS) - r = d->cols->readLOBs(values, index); - if (r != OCI_SUCCESS) - setAt(QSql::AfterLastRow); - return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO; -} - -int QOCIResult::size() -{ - return -1; -} - -int QOCIResult::numRowsAffected() -{ - Q_D(QOCIResult); - int rowCount; - OCIAttrGet(d->sql, - OCI_HTYPE_STMT, - &rowCount, - NULL, - OCI_ATTR_ROW_COUNT, - d->err); - return rowCount; -} - -bool QOCIResult::prepare(const QString& query) -{ - Q_D(QOCIResult); - int r = 0; - QSqlResult::prepare(query); - - delete d->cols; - d->cols = 0; - QSqlCachedResult::cleanup(); - - if (d->sql) { - r = OCIHandleFree(d->sql, OCI_HTYPE_STMT); - if (r != OCI_SUCCESS) - qOraWarning("QOCIResult::prepare: unable to free statement handle:", d->err); - } - if (query.isEmpty()) - return false; - r = OCIHandleAlloc(d->env, - reinterpret_cast(&d->sql), - OCI_HTYPE_STMT, - 0, - 0); - if (r != OCI_SUCCESS) { - qOraWarning("QOCIResult::prepare: unable to alloc statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to alloc statement"), QSqlError::StatementError, d->err)); - return false; - } - d->setStatementAttributes(); - const OraText *txt = reinterpret_cast(query.utf16()); - const int len = query.length() * sizeof(QChar); - r = OCIStmtPrepare(d->sql, - d->err, - txt, - len, - OCI_NTV_SYNTAX, - OCI_DEFAULT); - if (r != OCI_SUCCESS) { - qOraWarning("QOCIResult::prepare: unable to prepare statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to prepare statement"), QSqlError::StatementError, d->err)); - return false; - } - return true; -} - -bool QOCIResult::exec() -{ - Q_D(QOCIResult); - int r = 0; - ub2 stmtType=0; - ub4 iters; - ub4 mode; - QList tmpStorage; - IndicatorArray indicators(boundValueCount()); - SizeArray tmpSizes(boundValueCount()); - - r = OCIAttrGet(d->sql, - OCI_HTYPE_STMT, - &stmtType, - NULL, - OCI_ATTR_STMT_TYPE, - d->err); - - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIResult::exec: Unable to get statement type:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to get statement type"), QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - iters = stmtType == OCI_STMT_SELECT ? 0 : 1; - mode = d->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS; - - // bind placeholders - if (boundValueCount() > 0 - && d->bindValues(boundValues(), indicators, tmpSizes, tmpStorage) != OCI_SUCCESS) { - qOraWarning("QOCIResult::exec: unable to bind value: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", "Unable to bind value"), - QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - // execute - r = OCIStmtExecute(d->svc, - d->sql, - d->err, - iters, - 0, - 0, - 0, - mode); - if (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO) { - qOraWarning("QOCIResult::exec: unable to execute statement:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIResult", - "Unable to execute statement"), QSqlError::StatementError, d->err)); -#ifdef QOCI_DEBUG - qDebug() << "lastQuery()" << lastQuery(); -#endif - return false; - } - - if (stmtType == OCI_STMT_SELECT) { - ub4 parmCount = 0; - int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, reinterpret_cast(&parmCount), - 0, OCI_ATTR_PARAM_COUNT, d->err); - if (r == 0 && !d->cols) - d->cols = new QOCICols(parmCount, d); - setSelect(true); - QSqlCachedResult::init(parmCount); - } else { /* non-SELECT */ - setSelect(false); - } - setAt(QSql::BeforeFirstRow); - setActive(true); - - if (hasOutValues()) - d->outValues(boundValues(), indicators, tmpStorage); - - return true; -} - -QSqlRecord QOCIResult::record() const -{ - Q_D(const QOCIResult); - QSqlRecord inf; - if (!isActive() || !isSelect() || !d->cols) - return inf; - return d->cols->rec; -} - -QVariant QOCIResult::lastInsertId() const -{ - Q_D(const QOCIResult); - if (isActive()) { - QOCIRowIdPointer ptr(new QOCIRowId(d->env)); - - int r = OCIAttrGet(d->sql, OCI_HTYPE_STMT, ptr.constData()->id, - 0, OCI_ATTR_ROWID, d->err); - if (r == OCI_SUCCESS) - return QVariant::fromValue(ptr); - } - return QVariant(); -} - -bool QOCIResult::execBatch(bool arrayBind) -{ - Q_D(QOCIResult); - QOCICols::execBatch(d, boundValues(), arrayBind); - resetBindCount(); - return lastError().type() == QSqlError::NoError; -} - -void QOCIResult::virtual_hook(int id, void *data) -{ - Q_ASSERT(data); - - QSqlCachedResult::virtual_hook(id, data); -} - -//////////////////////////////////////////////////////////////////////////// - - -QOCIDriver::QOCIDriver(QObject* parent) - : QSqlDriver(*new QOCIDriverPrivate, parent) -{ - Q_D(QOCIDriver); -#ifdef QOCI_THREADED - const ub4 mode = OCI_UTF16 | OCI_OBJECT | OCI_THREADED; -#else - const ub4 mode = OCI_UTF16 | OCI_OBJECT; -#endif - int r = OCIEnvCreate(&d->env, - mode, - NULL, - NULL, - NULL, - NULL, - 0, - NULL); - if (r != 0) { - qWarning("QOCIDriver: unable to create environment"); - setLastError(qMakeError(tr("Unable to initialize", "QOCIDriver"), - QSqlError::ConnectionError, d->err)); - return; - } - - d->allocErrorHandle(); -} - -QOCIDriver::QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent) - : QSqlDriver(*new QOCIDriverPrivate, parent) -{ - Q_D(QOCIDriver); - d->env = env; - d->svc = ctx; - - d->allocErrorHandle(); - - if (env && ctx) { - setOpen(true); - setOpenError(false); - } -} - -QOCIDriver::~QOCIDriver() -{ - Q_D(QOCIDriver); - if (isOpen()) - close(); - int r = OCIHandleFree(d->err, OCI_HTYPE_ERROR); - if (r != OCI_SUCCESS) - qWarning("Unable to free Error handle: %d", r); - r = OCIHandleFree(d->env, OCI_HTYPE_ENV); - if (r != OCI_SUCCESS) - qWarning("Unable to free Environment handle: %d", r); -} - -bool QOCIDriver::hasFeature(DriverFeature f) const -{ - Q_D(const QOCIDriver); - switch (f) { - case Transactions: - case LastInsertId: - case BLOB: - case PreparedQueries: - case NamedPlaceholders: - case BatchOperations: - case LowPrecisionNumbers: - return true; - case QuerySize: - case PositionalPlaceholders: - case SimpleLocking: - case EventNotifications: - case FinishQuery: - case CancelQuery: - case MultipleResultSets: - return false; - case Unicode: - return d->serverVersion >= 9; - } - return false; -} - -static void qParseOpts(const QString &options, QOCIDriverPrivate *d) -{ - const QStringList opts(options.split(QLatin1Char(';'), QString::SkipEmptyParts)); - for (int i = 0; i < opts.count(); ++i) { - const QString tmp(opts.at(i)); - int idx; - if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) { - qWarning("QOCIDriver::parseArgs: Invalid parameter: '%s'", - tmp.toLocal8Bit().constData()); - continue; - } - const QString opt = tmp.left(idx); - const QString val = tmp.mid(idx + 1).simplified(); - bool ok; - if (opt == QLatin1String("OCI_ATTR_PREFETCH_ROWS")) { - d->prefetchRows = val.toInt(&ok); - if (!ok) - d->prefetchRows = -1; - } else if (opt == QLatin1String("OCI_ATTR_PREFETCH_MEMORY")) { - d->prefetchMem = val.toInt(&ok); - if (!ok) - d->prefetchMem = -1; - } else { - qWarning ("QOCIDriver::parseArgs: Invalid parameter: '%s'", - opt.toLocal8Bit().constData()); - } - } -} - -bool QOCIDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString & hostname, - int port, - const QString &opts) -{ - Q_D(QOCIDriver); - int r; - - if (isOpen()) - close(); - - qParseOpts(opts, d); - - // Connect without tnsnames.ora if a hostname is given - QString connectionString = db; - if (!hostname.isEmpty()) - connectionString = - QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))" - "(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db); - - r = OCIHandleAlloc(d->env, reinterpret_cast(&d->srvhp), OCI_HTYPE_SERVER, 0, 0); - if (r == OCI_SUCCESS) - r = OCIServerAttach(d->srvhp, d->err, reinterpret_cast(connectionString.utf16()), - connectionString.length() * sizeof(QChar), OCI_DEFAULT); - if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO) - r = OCIHandleAlloc(d->env, reinterpret_cast(&d->svc), OCI_HTYPE_SVCCTX, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err); - if (r == OCI_SUCCESS) - r = OCIHandleAlloc(d->env, reinterpret_cast(&d->authp), OCI_HTYPE_SESSION, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast(user.utf16()), - user.length() * sizeof(QChar), OCI_ATTR_USERNAME, d->err); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->authp, OCI_HTYPE_SESSION, const_cast(password.utf16()), - password.length() * sizeof(QChar), OCI_ATTR_PASSWORD, d->err); - - OCITrans* trans; - if (r == OCI_SUCCESS) - r = OCIHandleAlloc(d->env, reinterpret_cast(&trans), OCI_HTYPE_TRANS, 0, 0); - if (r == OCI_SUCCESS) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err); - - if (r == OCI_SUCCESS) { - if (user.isEmpty() && password.isEmpty()) - r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT); - else - r = OCISessionBegin(d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT); - } - if (r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO) - r = OCIAttrSet(d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err); - - if (r != OCI_SUCCESS) { - setLastError(qMakeError(tr("Unable to logon"), QSqlError::ConnectionError, d->err)); - setOpenError(true); - if (d->authp) - OCIHandleFree(d->authp, OCI_HTYPE_SESSION); - d->authp = 0; - if (d->srvhp) - OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER); - d->srvhp = 0; - return false; - } - - // get server version - char vertxt[512]; - r = OCIServerVersion(d->svc, - d->err, - reinterpret_cast(vertxt), - sizeof(vertxt), - OCI_HTYPE_SVCCTX); - if (r != 0) { - qWarning("QOCIDriver::open: could not get Oracle server version."); - } else { - QString versionStr; - versionStr = QString(reinterpret_cast(vertxt)); - QRegExp vers(QLatin1String("([0-9]+)\\.[0-9\\.]+[0-9]")); - if (vers.indexIn(versionStr) >= 0) - d->serverVersion = vers.cap(1).toInt(); - if (d->serverVersion == 0) - d->serverVersion = -1; - } - - setOpen(true); - setOpenError(false); - d->user = user; - - return true; -} - -void QOCIDriver::close() -{ - Q_D(QOCIDriver); - if (!isOpen()) - return; - - OCISessionEnd(d->svc, d->err, d->authp, OCI_DEFAULT); - OCIServerDetach(d->srvhp, d->err, OCI_DEFAULT); - OCIHandleFree(d->authp, OCI_HTYPE_SESSION); - d->authp = 0; - OCIHandleFree(d->srvhp, OCI_HTYPE_SERVER); - d->srvhp = 0; - OCIHandleFree(d->svc, OCI_HTYPE_SVCCTX); - d->svc = 0; - setOpen(false); - setOpenError(false); -} - -QSqlResult *QOCIDriver::createResult() const -{ - return new QOCIResult(this); -} - -bool QOCIDriver::beginTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::beginTransaction: Database not open"); - return false; - } - int r = OCITransStart(d->svc, - d->err, - 2, - OCI_TRANS_READWRITE); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::beginTransaction: ", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to begin transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = true; - return true; -} - -bool QOCIDriver::commitTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::commitTransaction: Database not open"); - return false; - } - int r = OCITransCommit(d->svc, - d->err, - 0); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::commitTransaction:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to commit transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = false; - return true; -} - -bool QOCIDriver::rollbackTransaction() -{ - Q_D(QOCIDriver); - if (!isOpen()) { - qWarning("QOCIDriver::rollbackTransaction: Database not open"); - return false; - } - int r = OCITransRollback(d->svc, - d->err, - 0); - if (r == OCI_ERROR) { - qOraWarning("QOCIDriver::rollbackTransaction:", d->err); - setLastError(qMakeError(QCoreApplication::translate("QOCIDriver", - "Unable to rollback transaction"), QSqlError::TransactionError, d->err)); - return false; - } - d->transaction = false; - return true; -} - -enum Expression { - OrExpression, - AndExpression -}; - -static QString make_where_clause(const QString &user, Expression e) -{ - static const char sysUsers[][8] = { - "MDSYS", - "LBACSYS", - "SYS", - "SYSTEM", - "WKSYS", - "CTXSYS", - "WMSYS", - }; - static const char joinC[][4] = { "or" , "and" }; - static Q_CONSTEXPR QLatin1Char bang[] = { QLatin1Char(' '), QLatin1Char('!') }; - - const QLatin1String join(joinC[e], -1); // -1: force strlen call - - QString result; - result.reserve(sizeof sysUsers / sizeof *sysUsers * - // max-sizeof(owner != and ) - (9 + sizeof *sysUsers + 5)); - for (const auto &sysUser : sysUsers) { - const QLatin1String l1(sysUser, -1); // -1: force strlen call - if (l1 != user) - result += QLatin1String("owner ") + bang[e] + QLatin1String("= '") + l1 + QLatin1Char(' ') + join + QLatin1Char(' '); - } - - result.chop(join.size() + 2); // remove final " " - - return result; -} - -QStringList QOCIDriver::tables(QSql::TableType type) const -{ - Q_D(const QOCIDriver); - QStringList tl; - - QString user = d->user; - if ( isIdentifierEscaped(user, QSqlDriver::TableName)) - user = stripDelimiters(user, QSqlDriver::TableName); - else - user = user.toUpper(); - - if (!isOpen()) - return tl; - - QSqlQuery t(createResult()); - t.setForwardOnly(true); - if (type & QSql::Tables) { - const QLatin1String tableQuery("select owner, table_name from all_tables where "); - const QString where = make_where_clause(user, AndExpression); - t.exec(tableQuery + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - - // list all table synonyms as well - const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); - t.exec(synonymQuery + where); - while (t.next()) { - if (t.value(0).toString() != d->user) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - if (type & QSql::Views) { - const QLatin1String query("select owner, view_name from all_views where "); - const QString where = make_where_clause(user, AndExpression); - t.exec(query + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != d->user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - if (type & QSql::SystemTables) { - t.exec(QLatin1String("select table_name from dictionary")); - while (t.next()) { - tl.append(t.value(0).toString()); - } - const QLatin1String tableQuery("select owner, table_name from all_tables where "); - const QString where = make_where_clause(user, OrExpression); - t.exec(tableQuery + where); - while (t.next()) { - if (t.value(0).toString().toUpper() != user.toUpper()) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - - // list all table synonyms as well - const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); - t.exec(synonymQuery + where); - while (t.next()) { - if (t.value(0).toString() != d->user) - tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); - else - tl.append(t.value(1).toString()); - } - } - return tl; -} - -void qSplitTableAndOwner(const QString & tname, QString * tbl, - QString * owner) -{ - int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner? - if (i != -1) { - *tbl = tname.right(tname.length() - i - 1); - *owner = tname.left(i); - } else { - *tbl = tname; - } -} - -QSqlRecord QOCIDriver::record(const QString& tablename) const -{ - Q_D(const QOCIDriver); - QSqlRecord fil; - if (!isOpen()) - return fil; - - QSqlQuery t(createResult()); - // using two separate queries for this is A LOT faster than using - // eg. a sub-query on the sys.synonyms table - QString stmt(QLatin1String("select column_name, data_type, data_length, " - "data_precision, data_scale, nullable, data_default%1" - "from all_tab_columns a " - "where a.table_name=%2")); - if (d->serverVersion >= 9) - stmt = stmt.arg(QLatin1String(", char_length ")); - else - stmt = stmt.arg(QLatin1String(" ")); - bool buildRecordInfo = false; - QString table, owner, tmpStmt; - qSplitTableAndOwner(tablename, &table, &owner); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\'')); - if (owner.isEmpty()) { - owner = d->user; - } - - if (isIdentifierEscaped(owner, QSqlDriver::TableName)) - owner = stripDelimiters(owner, QSqlDriver::TableName); - else - owner = owner.toUpper(); - - tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(tmpStmt); - if (!t.next()) { // try and see if the tablename is a synonym - stmt = stmt + QLatin1String(" join all_synonyms b " - "on a.owner=b.table_owner and a.table_name=b.table_name " - "where b.owner='") + owner + - QLatin1String("' and b.synonym_name='") + table + - QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(stmt); - if (t.next()) - buildRecordInfo = true; - } else { - buildRecordInfo = true; - } - QStringList keywords = QStringList() << QLatin1String("NUMBER") << QLatin1String("FLOAT") << QLatin1String("BINARY_FLOAT") - << QLatin1String("BINARY_DOUBLE"); - if (buildRecordInfo) { - do { - QVariant::Type ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy()); - QSqlField f(t.value(0).toString(), ty); - f.setRequired(t.value(5).toString() == QLatin1String("N")); - f.setPrecision(t.value(4).toInt()); - if (d->serverVersion >= 9 && (ty == QVariant::String) && !t.isNull(3) && !keywords.contains(t.value(1).toString())) { - // Oracle9: data_length == size in bytes, char_length == amount of characters - f.setLength(t.value(7).toInt()); - } else { - f.setLength(t.value(t.isNull(3) ? 2 : 3).toInt()); - } - f.setDefaultValue(t.value(6)); - fil.append(f); - } while (t.next()); - } - return fil; -} - -QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const -{ - Q_D(const QOCIDriver); - QSqlIndex idx(tablename); - if (!isOpen()) - return idx; - QSqlQuery t(createResult()); - QString stmt(QLatin1String("select b.column_name, b.index_name, a.table_name, a.owner " - "from all_constraints a, all_ind_columns b " - "where a.constraint_type='P' " - "and b.index_name = a.constraint_name " - "and b.index_owner = a.owner")); - - bool buildIndex = false; - QString table, owner, tmpStmt; - qSplitTableAndOwner(tablename, &table, &owner); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = table.toUpper(); - - tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\''); - if (owner.isEmpty()) { - owner = d->user; - } - - if (isIdentifierEscaped(owner, QSqlDriver::TableName)) - owner = stripDelimiters(owner, QSqlDriver::TableName); - else - owner = owner.toUpper(); - - tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\''); - t.setForwardOnly(true); - t.exec(tmpStmt); - - if (!t.next()) { - stmt += QLatin1String(" and a.table_name=(select tname from sys.synonyms " - "where sname='") + table + QLatin1String("' and creator=a.owner)"); - t.setForwardOnly(true); - t.exec(stmt); - if (t.next()) { - owner = t.value(3).toString(); - buildIndex = true; - } - } else { - buildIndex = true; - } - if (buildIndex) { - QSqlQuery tt(createResult()); - tt.setForwardOnly(true); - idx.setName(t.value(1).toString()); - do { - tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") + - t.value(2).toString() + QLatin1String("' and column_name='") + - t.value(0).toString() + QLatin1String("' and owner='") + - owner + QLatin1Char('\'')); - if (!tt.next()) { - return QSqlIndex(); - } - QSqlField f(t.value(0).toString(), qDecodeOCIType(tt.value(0).toString(), t.numericalPrecisionPolicy())); - idx.append(f); - } while (t.next()); - return idx; - } - return QSqlIndex(); -} - -QString QOCIDriver::formatValue(const QSqlField &field, bool trimStrings) const -{ - switch (field.type()) { - case QVariant::DateTime: { - QDateTime datetime = field.value().toDateTime(); - QString datestring; - if (datetime.isValid()) { - datestring = QLatin1String("TO_DATE('") + QString::number(datetime.date().year()) - + QLatin1Char('-') - + QString::number(datetime.date().month()) + QLatin1Char('-') - + QString::number(datetime.date().day()) + QLatin1Char(' ') - + QString::number(datetime.time().hour()) + QLatin1Char(':') - + QString::number(datetime.time().minute()) + QLatin1Char(':') - + QString::number(datetime.time().second()) - + QLatin1String("','YYYY-MM-DD HH24:MI:SS')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - case QVariant::Time: { - QDateTime datetime = field.value().toDateTime(); - QString datestring; - if (datetime.isValid()) { - datestring = QLatin1String("TO_DATE('") - + QString::number(datetime.time().hour()) + QLatin1Char(':') - + QString::number(datetime.time().minute()) + QLatin1Char(':') - + QString::number(datetime.time().second()) - + QLatin1String("','HH24:MI:SS')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - case QVariant::Date: { - QDate date = field.value().toDate(); - QString datestring; - if (date.isValid()) { - datestring = QLatin1String("TO_DATE('") + QString::number(date.year()) + - QLatin1Char('-') + - QString::number(date.month()) + QLatin1Char('-') + - QString::number(date.day()) + QLatin1String("','YYYY-MM-DD')"); - } else { - datestring = QLatin1String("NULL"); - } - return datestring; - } - default: - break; - } - return QSqlDriver::formatValue(field, trimStrings); -} - -QVariant QOCIDriver::handle() const -{ - Q_D(const QOCIDriver); - return QVariant::fromValue(d->env); -} - -QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/oci/qsql_oci.pri b/src/sql/drivers/oci/qsql_oci.pri deleted file mode 100644 index 66ccdb1abb..0000000000 --- a/src/sql/drivers/oci/qsql_oci.pri +++ /dev/null @@ -1,9 +0,0 @@ -HEADERS += $$PWD/qsql_oci_p.h -SOURCES += $$PWD/qsql_oci.cpp - -unix { - !contains(LIBS, .*clnts.*):LIBS += -lclntsh -} else { - LIBS *= -loci -} -mac:QMAKE_LFLAGS += -Wl,-flat_namespace,-U,_environ diff --git a/src/sql/drivers/oci/qsql_oci_p.h b/src/sql/drivers/oci/qsql_oci_p.h deleted file mode 100644 index 69911f4bee..0000000000 --- a/src/sql/drivers/oci/qsql_oci_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** 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_OCI_H -#define QSQL_OCI_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 - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_OCI -#else -#define Q_EXPORT_SQLDRIVER_OCI Q_SQL_EXPORT -#endif - -typedef struct OCIEnv OCIEnv; -typedef struct OCISvcCtx OCISvcCtx; - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QOCIDriverPrivate; - -class Q_EXPORT_SQLDRIVER_OCI QOCIDriver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QOCIDriver) - Q_OBJECT - friend class QOCICols; - friend class QOCIResultPrivate; - -public: - explicit QOCIDriver(QObject* parent = 0); - QOCIDriver(OCIEnv* env, OCISvcCtx* ctx, QObject* parent = 0); - ~QOCIDriver(); - bool hasFeature(DriverFeature f) const; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, - bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; -}; - -QT_END_NAMESPACE - -#endif // QSQL_OCI_H diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp deleted file mode 100644 index 59ef42d609..0000000000 --- a/src/sql/drivers/odbc/qsql_odbc.cpp +++ /dev/null @@ -1,2639 +0,0 @@ -/**************************************************************************** -** -** 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_odbc_p.h" -#include - -#if defined (Q_OS_WIN32) -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -// undefine this to prevent initial check of the ODBC driver -#define ODBC_CHECK_DRIVER - -static const int COLNAMESIZE = 256; -//Map Qt parameter types to ODBC types -static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT }; - -inline static QString fromSQLTCHAR(const QVarLengthArray& input, int size=-1) -{ - QString result; - - int realsize = qMin(size, input.size()); - if(realsize > 0 && input[realsize-1] == 0) - realsize--; - switch(sizeof(SQLTCHAR)) { - case 1: - result=QString::fromUtf8((const char *)input.constData(), realsize); - break; - case 2: - result=QString::fromUtf16((const ushort *)input.constData(), realsize); - break; - case 4: - result=QString::fromUcs4((const uint *)input.constData(), realsize); - break; - default: - qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR))); - } - return result; -} - -inline static QVarLengthArray toSQLTCHAR(const QString &input) -{ - QVarLengthArray result; - result.resize(input.size()); - switch(sizeof(SQLTCHAR)) { - case 1: - memcpy(result.data(), input.toUtf8().data(), input.size()); - break; - case 2: - memcpy(result.data(), input.unicode(), input.size() * 2); - break; - case 4: - memcpy(result.data(), input.toUcs4().data(), input.size() * 4); - break; - default: - qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR))); - } - result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't. - return result; -} - -class QODBCDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QODBCDriver) - -public: - enum DefaultCase{Lower, Mixed, Upper, Sensitive}; - QODBCDriverPrivate() - : QSqlDriverPrivate(), hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), datetime_precision(19), - isFreeTDSDriver(false), hasSQLFetchScroll(true), hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"')) - { - } - - SQLHANDLE hEnv; - SQLHANDLE hDbc; - - bool unicode; - bool useSchema; - int disconnectCount; - int datetime_precision; - bool isFreeTDSDriver; - bool hasSQLFetchScroll; - bool hasMultiResultSets; - - bool checkDriver() const; - void checkUnicode(); - void checkDBMS(); - void checkHasSQLFetchScroll(); - void checkHasMultiResults(); - void checkSchemaUsage(); - void checkDateTimePrecision(); - bool setConnectionOptions(const QString& connOpts); - void splitTableQualifier(const QString &qualifier, QString &catalog, - QString &schema, QString &table); - DefaultCase defaultCase() const; - QString adjustCase(const QString&) const; - QChar quoteChar(); -private: - bool isQuoteInitialized; - QChar quote; -}; - -class QODBCResultPrivate; - -class QODBCResult: public QSqlResult -{ - Q_DECLARE_PRIVATE(QODBCResult) - -public: - QODBCResult(const QODBCDriver *db); - virtual ~QODBCResult(); - - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; - - QVariant lastInsertId() const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool fetchNext() Q_DECL_OVERRIDE; - bool fetchFirst() Q_DECL_OVERRIDE; - bool fetchLast() Q_DECL_OVERRIDE; - bool fetchPrevious() Q_DECL_OVERRIDE; - bool fetch(int i) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - QVariant data(int field) Q_DECL_OVERRIDE; - bool isNull(int field) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; - void detachFromResultSet() Q_DECL_OVERRIDE; - bool nextResult() Q_DECL_OVERRIDE; -}; - -class QODBCResultPrivate: public QSqlResultPrivate -{ - Q_DECLARE_PUBLIC(QODBCResult) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QODBCDriver) - QODBCResultPrivate(QODBCResult *q, const QODBCDriver *db) - : QSqlResultPrivate(q, db), - hStmt(0), - useSchema(false), - hasSQLFetchScroll(true) - { - unicode = drv_d_func()->unicode; - useSchema = drv_d_func()->useSchema; - disconnectCount = drv_d_func()->disconnectCount; - hasSQLFetchScroll = drv_d_func()->hasSQLFetchScroll; - } - - inline void clearValues() - { fieldCache.fill(QVariant()); fieldCacheIdx = 0; } - - SQLHANDLE dpEnv() const { return drv_d_func() ? drv_d_func()->hEnv : 0;} - SQLHANDLE dpDbc() const { return drv_d_func() ? drv_d_func()->hDbc : 0;} - SQLHANDLE hStmt; - - bool unicode; - bool useSchema; - - QSqlRecord rInf; - QVector fieldCache; - int fieldCacheIdx; - int disconnectCount; - bool hasSQLFetchScroll; - - bool isStmtHandleValid(); - void updateStmtHandleState(); -}; - -bool QODBCResultPrivate::isStmtHandleValid() -{ - return disconnectCount == drv_d_func()->disconnectCount; -} - -void QODBCResultPrivate::updateStmtHandleState() -{ - disconnectCount = drv_d_func()->disconnectCount; -} - -static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0) -{ - SQLINTEGER nativeCode_ = 0; - SQLSMALLINT msgLen = 0; - SQLRETURN r = SQL_NO_DATA; - SQLTCHAR state_[SQL_SQLSTATE_SIZE+1]; - QVarLengthArray description_(SQL_MAX_MESSAGE_LENGTH); - QString result; - int i = 1; - - description_[0] = 0; - do { - r = SQLGetDiagRec(handleType, - handle, - i, - state_, - &nativeCode_, - 0, - 0, - &msgLen); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0) - description_.resize(msgLen+1); - r = SQLGetDiagRec(handleType, - handle, - i, - state_, - &nativeCode_, - description_.data(), - description_.size(), - &msgLen); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - if (nativeCode) - *nativeCode = nativeCode_; - const QString tmpstore = fromSQLTCHAR(description_, msgLen); - if(result != tmpstore) { - if(!result.isEmpty()) - result += QLatin1Char(' '); - result += tmpstore; - } - } else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) { - return result; - } - ++i; - } while (r != SQL_NO_DATA); - return result; -} - -static QString qODBCWarn(const SQLHANDLE hStmt, const SQLHANDLE envHandle = 0, - const SQLHANDLE pDbC = 0, int *nativeCode = 0) -{ - QString result; - if (envHandle) - result += qWarnODBCHandle(SQL_HANDLE_ENV, envHandle, nativeCode); - if (pDbC) { - const QString dMessage = qWarnODBCHandle(SQL_HANDLE_DBC, pDbC, nativeCode); - if (!dMessage.isEmpty()) { - if (!result.isEmpty()) - result += QLatin1Char(' '); - result += dMessage; - } - } - if (hStmt) { - const QString hMessage = qWarnODBCHandle(SQL_HANDLE_STMT, hStmt, nativeCode); - if (!hMessage.isEmpty()) { - if (!result.isEmpty()) - result += QLatin1Char(' '); - result += hMessage; - } - } - return result; -} - -static QString qODBCWarn(const QODBCResultPrivate* odbc, int *nativeCode = 0) -{ - return qODBCWarn(odbc->hStmt, odbc->dpEnv(), odbc->dpDbc(), nativeCode); -} - -static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0) -{ - return qODBCWarn(0, odbc->hEnv, odbc->hDbc, nativeCode); -} - -static void qSqlWarning(const QString& message, const QODBCResultPrivate* odbc) -{ - qWarning() << message << "\tError:" << qODBCWarn(odbc); -} - -static void qSqlWarning(const QString &message, const QODBCDriverPrivate *odbc) -{ - qWarning() << message << "\tError:" << qODBCWarn(odbc); -} - -static void qSqlWarning(const QString &message, const SQLHANDLE hStmt) -{ - qWarning() << message << "\tError:" << qODBCWarn(hStmt); -} - -static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QODBCResultPrivate* p) -{ - int nativeCode = -1; - QString message = qODBCWarn(p, &nativeCode); - return QSqlError(QLatin1String("QODBC3: ") + err, message, type, nativeCode); -} - -static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, - const QODBCDriverPrivate* p) -{ - int nativeCode = -1; - QString message = qODBCWarn(p, &nativeCode); - return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type, nativeCode); -} - -static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true) -{ - QVariant::Type type = QVariant::Invalid; - switch (sqltype) { - case SQL_DECIMAL: - case SQL_NUMERIC: - case SQL_REAL: - case SQL_FLOAT: - case SQL_DOUBLE: - type = QVariant::Double; - break; - case SQL_SMALLINT: - case SQL_INTEGER: - case SQL_BIT: - type = isSigned ? QVariant::Int : QVariant::UInt; - break; - case SQL_TINYINT: - type = QVariant::UInt; - break; - case SQL_BIGINT: - type = isSigned ? QVariant::LongLong : QVariant::ULongLong; - break; - case SQL_BINARY: - case SQL_VARBINARY: - case SQL_LONGVARBINARY: - type = QVariant::ByteArray; - break; - case SQL_DATE: - case SQL_TYPE_DATE: - type = QVariant::Date; - break; - case SQL_TIME: - case SQL_TYPE_TIME: - type = QVariant::Time; - break; - case SQL_TIMESTAMP: - case SQL_TYPE_TIMESTAMP: - type = QVariant::DateTime; - break; - case SQL_WCHAR: - case SQL_WVARCHAR: - case SQL_WLONGVARCHAR: - type = QVariant::String; - break; - case SQL_CHAR: - case SQL_VARCHAR: -#if (ODBCVER >= 0x0350) - case SQL_GUID: -#endif - case SQL_LONGVARCHAR: - type = QVariant::String; - break; - default: - type = QVariant::ByteArray; - break; - } - return type; -} - -static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode = false) -{ - QString fieldVal; - SQLRETURN r = SQL_ERROR; - SQLLEN lengthIndicator = 0; - - // NB! colSize must be a multiple of 2 for unicode enabled DBs - if (colSize <= 0) { - colSize = 256; - } else if (colSize > 65536) { // limit buffer size to 64 KB - colSize = 65536; - } else { - colSize++; // make sure there is room for more than the 0 termination - } - if(unicode) { - r = SQLGetData(hStmt, - column+1, - SQL_C_TCHAR, - NULL, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0) - colSize = int(lengthIndicator / sizeof(SQLTCHAR) + 1); - QVarLengthArray buf(colSize); - memset(buf.data(), 0, colSize*sizeof(SQLTCHAR)); - while (true) { - r = SQLGetData(hStmt, - column+1, - SQL_C_TCHAR, - (SQLPOINTER)buf.data(), - colSize*sizeof(SQLTCHAR), - &lengthIndicator); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - if (lengthIndicator == SQL_NULL_DATA) { - fieldVal.clear(); - break; - } - // starting with ODBC Native Client 2012, SQL_NO_TOTAL is returned - // instead of the length (which sometimes was wrong in older versions) - // see link for more info: http://msdn.microsoft.com/en-us/library/jj219209.aspx - // if length indicator equals SQL_NO_TOTAL, indicating that - // more data can be fetched, but size not known, collect data - // and fetch next block - if (lengthIndicator == SQL_NO_TOTAL) { - fieldVal += fromSQLTCHAR(buf, colSize); - continue; - } - // if SQL_SUCCESS_WITH_INFO is returned, indicating that - // more data can be fetched, the length indicator does NOT - // contain the number of bytes returned - it contains the - // total number of bytes that CAN be fetched - // colSize-1: remove 0 termination when there is more data to fetch - int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : int(lengthIndicator / sizeof(SQLTCHAR)); - fieldVal += fromSQLTCHAR(buf, rSize); - if (lengthIndicator < SQLLEN(colSize*sizeof(SQLTCHAR))) { - // workaround for Drivermanagers that don't return SQL_NO_DATA - break; - } - } else if (r == SQL_NO_DATA) { - break; - } else { - qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')'; - fieldVal.clear(); - break; - } - } - } else { - r = SQLGetData(hStmt, - column+1, - SQL_C_CHAR, - NULL, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0) - colSize = lengthIndicator + 1; - QVarLengthArray buf(colSize); - while (true) { - r = SQLGetData(hStmt, - column+1, - SQL_C_CHAR, - (SQLPOINTER)buf.data(), - colSize, - &lengthIndicator); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) { - fieldVal.clear(); - break; - } - // if SQL_SUCCESS_WITH_INFO is returned, indicating that - // more data can be fetched, the length indicator does NOT - // contain the number of bytes returned - it contains the - // total number of bytes that CAN be fetched - // colSize-1: remove 0 termination when there is more data to fetch - int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator; - fieldVal += QString::fromUtf8((const char *)buf.constData(), rSize); - if (lengthIndicator < SQLLEN(colSize)) { - // workaround for Drivermanagers that don't return SQL_NO_DATA - break; - } - } else if (r == SQL_NO_DATA) { - break; - } else { - qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')'; - fieldVal.clear(); - break; - } - } - } - return fieldVal; -} - -static QVariant qGetBinaryData(SQLHANDLE hStmt, int column) -{ - QByteArray fieldVal; - SQLSMALLINT colNameLen; - SQLSMALLINT colType; - SQLULEN colSize; - SQLSMALLINT colScale; - SQLSMALLINT nullable; - SQLLEN lengthIndicator = 0; - SQLRETURN r = SQL_ERROR; - - QVarLengthArray colName(COLNAMESIZE); - - r = SQLDescribeCol(hStmt, - column + 1, - colName.data(), - COLNAMESIZE, - &colNameLen, - &colType, - &colSize, - &colScale, - &nullable); - if (r != SQL_SUCCESS) - qWarning() << "qGetBinaryData: Unable to describe column" << column; - // SQLDescribeCol may return 0 if size cannot be determined - if (!colSize) - colSize = 255; - else if (colSize > 65536) // read the field in 64 KB chunks - colSize = 65536; - fieldVal.resize(colSize); - ulong read = 0; - while (true) { - r = SQLGetData(hStmt, - column+1, - SQL_C_BINARY, - const_cast(fieldVal.constData() + read), - colSize, - &lengthIndicator); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - break; - if (lengthIndicator == SQL_NULL_DATA) - return QVariant(QVariant::ByteArray); - if (lengthIndicator > SQLLEN(colSize) || lengthIndicator == SQL_NO_TOTAL) { - read += colSize; - colSize = 65536; - } else { - read += lengthIndicator; - } - if (r == SQL_SUCCESS) { // the whole field was read in one chunk - fieldVal.resize(read); - break; - } - fieldVal.resize(fieldVal.size() + colSize); - } - return fieldVal; -} - -static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true) -{ - SQLINTEGER intbuf = 0; - SQLLEN lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column+1, - isSigned ? SQL_C_SLONG : SQL_C_ULONG, - (SQLPOINTER)&intbuf, - sizeof(intbuf), - &lengthIndicator); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - return QVariant(QVariant::Invalid); - if (lengthIndicator == SQL_NULL_DATA) - return QVariant(QVariant::Int); - if (isSigned) - return int(intbuf); - else - return uint(intbuf); -} - -static QVariant qGetDoubleData(SQLHANDLE hStmt, int column) -{ - SQLDOUBLE dblbuf; - SQLLEN lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column+1, - SQL_C_DOUBLE, - (SQLPOINTER) &dblbuf, - 0, - &lengthIndicator); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - return QVariant(QVariant::Invalid); - } - if(lengthIndicator == SQL_NULL_DATA) - return QVariant(QVariant::Double); - - return (double) dblbuf; -} - - -static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true) -{ - SQLBIGINT lngbuf = 0; - SQLLEN lengthIndicator = 0; - SQLRETURN r = SQLGetData(hStmt, - column+1, - isSigned ? SQL_C_SBIGINT : SQL_C_UBIGINT, - (SQLPOINTER) &lngbuf, - sizeof(lngbuf), - &lengthIndicator); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - return QVariant(QVariant::Invalid); - if (lengthIndicator == SQL_NULL_DATA) - return QVariant(QVariant::LongLong); - - if (isSigned) - return qint64(lngbuf); - else - return quint64(lngbuf); -} - -static bool isAutoValue(const SQLHANDLE hStmt, int column) -{ - SQLLEN nNumericAttribute = 0; // Check for auto-increment - const SQLRETURN r = ::SQLColAttribute(hStmt, column + 1, SQL_DESC_AUTO_UNIQUE_VALUE, - 0, 0, 0, &nNumericAttribute); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QStringLiteral("qMakeField: Unable to get autovalue attribute for column ") - + QString::number(column), hStmt); - return false; - } - return nNumericAttribute != SQL_FALSE; -} - -static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMessage); - -// creates a QSqlField from a valid hStmt generated -// by SQLColumns. The hStmt has to point to a valid position. -static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p) -{ - QString fname = qGetStringData(hStmt, 3, -1, p->unicode); - int type = qGetIntData(hStmt, 4).toInt(); // column type - QSqlField f(fname, qDecodeODBCType(type, p)); - QVariant var = qGetIntData(hStmt, 6); - f.setLength(var.isNull() ? -1 : var.toInt()); // column size - var = qGetIntData(hStmt, 8).toInt(); - f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision - f.setSqlType(type); - int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag - // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN - if (required == SQL_NO_NULLS) - f.setRequired(true); - else if (required == SQL_NULLABLE) - f.setRequired(false); - // else we don't know - return f; -} - -static QSqlField qMakeFieldInfo(const QODBCResultPrivate* p, int i ) -{ - QString errorMessage; - const QSqlField result = qMakeFieldInfo(p->hStmt, i, &errorMessage); - if (!errorMessage.isEmpty()) - qSqlWarning(errorMessage, p); - return result; -} - -static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMessage) -{ - SQLSMALLINT colNameLen; - SQLSMALLINT colType; - SQLULEN colSize; - SQLSMALLINT colScale; - SQLSMALLINT nullable; - SQLRETURN r = SQL_ERROR; - QVarLengthArray colName(COLNAMESIZE); - errorMessage->clear(); - r = SQLDescribeCol(hStmt, - i+1, - colName.data(), - (SQLSMALLINT)COLNAMESIZE, - &colNameLen, - &colType, - &colSize, - &colScale, - &nullable); - - if (r != SQL_SUCCESS) { - *errorMessage = QStringLiteral("qMakeField: Unable to describe column ") + QString::number(i); - return QSqlField(); - } - - SQLLEN unsignedFlag = SQL_FALSE; - r = SQLColAttribute (hStmt, - i + 1, - SQL_DESC_UNSIGNED, - 0, - 0, - 0, - &unsignedFlag); - if (r != SQL_SUCCESS) { - qSqlWarning(QStringLiteral("qMakeField: Unable to get column attributes for column ") - + QString::number(i), hStmt); - } - - const QString qColName(fromSQLTCHAR(colName, colNameLen)); - // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN - QVariant::Type type = qDecodeODBCType(colType, unsignedFlag == SQL_FALSE); - QSqlField f(qColName, type); - f.setSqlType(colType); - f.setLength(colSize == 0 ? -1 : int(colSize)); - f.setPrecision(colScale == 0 ? -1 : int(colScale)); - if (nullable == SQL_NO_NULLS) - f.setRequired(true); - else if (nullable == SQL_NULLABLE) - f.setRequired(false); - // else we don't know - f.setAutoValue(isAutoValue(hStmt, i)); - return f; -} - -static size_t qGetODBCVersion(const QString &connOpts) -{ - if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive)) - return SQL_OV_ODBC3; - return SQL_OV_ODBC2; -} - -QChar QODBCDriverPrivate::quoteChar() -{ - if (!isQuoteInitialized) { - SQLTCHAR driverResponse[4]; - SQLSMALLINT length; - int r = SQLGetInfo(hDbc, - SQL_IDENTIFIER_QUOTE_CHAR, - &driverResponse, - sizeof(driverResponse), - &length); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - quote = QChar(driverResponse[0]); - else - quote = QLatin1Char('"'); - isQuoteInitialized = true; - } - return quote; -} - - -bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) -{ - // Set any connection attributes - const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts)); - SQLRETURN r = SQL_SUCCESS; - for (int i = 0; i < opts.count(); ++i) { - const QString tmp(opts.at(i)); - int idx; - if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) { - qWarning() << "QODBCDriver::open: Illegal connect option value '" << tmp << '\''; - continue; - } - const QString opt(tmp.left(idx)); - const QString val(tmp.mid(idx + 1).simplified()); - SQLUINTEGER v = 0; - - r = SQL_SUCCESS; - if (opt.toUpper() == QLatin1String("SQL_ATTR_ACCESS_MODE")) { - if (val.toUpper() == QLatin1String("SQL_MODE_READ_ONLY")) { - v = SQL_MODE_READ_ONLY; - } else if (val.toUpper() == QLatin1String("SQL_MODE_READ_WRITE")) { - v = SQL_MODE_READ_WRITE; - } else { - qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\''; - continue; - } - r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_TIMEOUT")) { - v = val.toUInt(); - r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) { - v = val.toUInt(); - r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) { - val.utf16(); // 0 terminate - r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, - toSQLTCHAR(val).data(), - val.length()*sizeof(SQLTCHAR)); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) { - if (val.toUpper() == QLatin1String("SQL_TRUE")) { - v = SQL_TRUE; - } else if (val.toUpper() == QLatin1String("SQL_FALSE")) { - v = SQL_FALSE; - } else { - qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\''; - continue; - } - r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_PACKET_SIZE")) { - v = val.toUInt(); - r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) { - val.utf16(); // 0 terminate - r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, - toSQLTCHAR(val).data(), - val.length()*sizeof(SQLTCHAR)); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) { - if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) { - v = SQL_OPT_TRACE_OFF; - } else if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_ON")) { - v = SQL_OPT_TRACE_ON; - } else { - qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\''; - continue; - } - r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) { - if (val == QLatin1String("SQL_CP_OFF")) - v = SQL_CP_OFF; - else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_DRIVER")) - v = SQL_CP_ONE_PER_DRIVER; - else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_HENV")) - v = SQL_CP_ONE_PER_HENV; - else if (val.toUpper() == QLatin1String("SQL_CP_DEFAULT")) - v = SQL_CP_DEFAULT; - else { - qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\''; - continue; - } - r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CP_MATCH")) { - if (val.toUpper() == QLatin1String("SQL_CP_STRICT_MATCH")) - v = SQL_CP_STRICT_MATCH; - else if (val.toUpper() == QLatin1String("SQL_CP_RELAXED_MATCH")) - v = SQL_CP_RELAXED_MATCH; - else if (val.toUpper() == QLatin1String("SQL_CP_MATCH_DEFAULT")) - v = SQL_CP_MATCH_DEFAULT; - else { - qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\''; - continue; - } - r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER) size_t(v), 0); - } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) { - // Already handled in QODBCDriver::open() - continue; - } else { - qWarning() << "QODBCDriver::open: Unknown connection attribute '" << opt << '\''; - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) - qSqlWarning(QString::fromLatin1("QODBCDriver::open: Unable to set connection attribute'%1'").arg( - opt), this); - } - return true; -} - -void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString &catalog, - QString &schema, QString &table) -{ - if (!useSchema) { - table = qualifier; - return; - } - QStringList l = qualifier.split(QLatin1Char('.')); - if (l.count() > 3) - return; // can't possibly be a valid table qualifier - int i = 0, n = l.count(); - if (n == 1) { - table = qualifier; - } else { - for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { - if (n == 3) { - if (i == 0) { - catalog = *it; - } else if (i == 1) { - schema = *it; - } else if (i == 2) { - table = *it; - } - } else if (n == 2) { - if (i == 0) { - schema = *it; - } else if (i == 1) { - table = *it; - } - } - i++; - } - } -} - -QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const -{ - DefaultCase ret; - SQLUSMALLINT casing; - int r = SQLGetInfo(hDbc, - SQL_IDENTIFIER_CASE, - &casing, - sizeof(casing), - NULL); - if ( r != SQL_SUCCESS) - ret = Mixed;//arbitrary case if driver cannot be queried - else { - switch (casing) { - case (SQL_IC_UPPER): - ret = Upper; - break; - case (SQL_IC_LOWER): - ret = Lower; - break; - case (SQL_IC_SENSITIVE): - ret = Sensitive; - break; - case (SQL_IC_MIXED): - default: - ret = Mixed; - break; - } - } - return ret; -} - -/* - Adjust the casing of an identifier to match what the - database engine would have done to it. -*/ -QString QODBCDriverPrivate::adjustCase(const QString &identifier) const -{ - QString ret = identifier; - switch(defaultCase()) { - case (Lower): - ret = identifier.toLower(); - break; - case (Upper): - ret = identifier.toUpper(); - break; - case(Mixed): - case(Sensitive): - default: - ret = identifier; - } - return ret; -} - -//////////////////////////////////////////////////////////////////////////// - -QODBCResult::QODBCResult(const QODBCDriver *db) - : QSqlResult(*new QODBCResultPrivate(this, db)) -{ -} - -QODBCResult::~QODBCResult() -{ - Q_D(QODBCResult); - if (d->hStmt && d->isStmtHandleValid() && driver()->isOpen()) { - SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") - + QString::number(r), d); - } -} - -bool QODBCResult::reset (const QString& query) -{ - Q_D(QODBCResult); - setActive(false); - setAt(QSql::BeforeFirstRow); - d->rInf.clear(); - d->fieldCache.clear(); - d->fieldCacheIdx = 0; - - // Always reallocate the statement handle - the statement attributes - // are not reset if SQLFreeStmt() is called which causes some problems. - SQLRETURN r; - if (d->hStmt && d->isStmtHandleValid()) { - r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCResult::reset: Unable to free statement handle"), d); - return false; - } - } - r = SQLAllocHandle(SQL_HANDLE_STMT, - d->dpDbc(), - &d->hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCResult::reset: Unable to allocate statement handle"), d); - return false; - } - - d->updateStmtHandleState(); - - if (isForwardOnly()) { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - } else { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_STATIC, - SQL_IS_UINTEGER); - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. " - "Please check your ODBC driver configuration"), QSqlError::StatementError, d)); - return false; - } - - r = SQLExecDirect(d->hStmt, - toSQLTCHAR(query).data(), - (SQLINTEGER) query.length()); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) { - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to execute statement"), QSqlError::StatementError, d)); - return false; - } - - SQLULEN isScrollable = 0; - r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0); - if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - setForwardOnly(isScrollable == SQL_NONSCROLLABLE); - - SQLSMALLINT count = 0; - SQLNumResultCols(d->hStmt, &count); - if (count) { - setSelect(true); - for (int i = 0; i < count; ++i) { - d->rInf.append(qMakeFieldInfo(d, i)); - } - d->fieldCache.resize(count); - } else { - setSelect(false); - } - setActive(true); - - return true; -} - -bool QODBCResult::fetch(int i) -{ - Q_D(QODBCResult); - if (!driver()->isOpen()) - return false; - - if (isForwardOnly() && i < at()) - return false; - if (i == at()) - return true; - d->clearValues(); - int actualIdx = i + 1; - if (actualIdx <= 0) { - setAt(QSql::BeforeFirstRow); - return false; - } - SQLRETURN r; - if (isForwardOnly()) { - bool ok = true; - while (ok && i > at()) - ok = fetchNext(); - return ok; - } else { - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_ABSOLUTE, - actualIdx); - } - if (r != SQL_SUCCESS) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch"), QSqlError::ConnectionError, d)); - return false; - } - setAt(i); - return true; -} - -bool QODBCResult::fetchNext() -{ - Q_D(QODBCResult); - SQLRETURN r; - d->clearValues(); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(d->hStmt); - - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch next"), QSqlError::ConnectionError, d)); - return false; - } - setAt(at() + 1); - return true; -} - -bool QODBCResult::fetchFirst() -{ - Q_D(QODBCResult); - if (isForwardOnly() && at() != QSql::BeforeFirstRow) - return false; - SQLRETURN r; - d->clearValues(); - if (isForwardOnly()) { - return fetchNext(); - } - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_FIRST, - 0); - if (r != SQL_SUCCESS) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch first"), QSqlError::ConnectionError, d)); - return false; - } - setAt(0); - return true; -} - -bool QODBCResult::fetchPrevious() -{ - Q_D(QODBCResult); - if (isForwardOnly()) - return false; - SQLRETURN r; - d->clearValues(); - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_PRIOR, - 0); - if (r != SQL_SUCCESS) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch previous"), QSqlError::ConnectionError, d)); - return false; - } - setAt(at() - 1); - return true; -} - -bool QODBCResult::fetchLast() -{ - Q_D(QODBCResult); - SQLRETURN r; - d->clearValues(); - - if (isForwardOnly()) { - // cannot seek to last row in forwardOnly mode, so we have to use brute force - int i = at(); - if (i == QSql::AfterLastRow) - return false; - if (i == QSql::BeforeFirstRow) - i = 0; - while (fetchNext()) - ++i; - setAt(i); - return true; - } - - r = SQLFetchScroll(d->hStmt, - SQL_FETCH_LAST, - 0); - if (r != SQL_SUCCESS) { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch last"), QSqlError::ConnectionError, d)); - return false; - } - SQLULEN currRow = 0; - r = SQLGetStmtAttr(d->hStmt, - SQL_ROW_NUMBER, - &currRow, - SQL_IS_INTEGER, - 0); - if (r != SQL_SUCCESS) - return false; - setAt(currRow-1); - return true; -} - -QVariant QODBCResult::data(int field) -{ - Q_D(QODBCResult); - if (field >= d->rInf.count() || field < 0) { - qWarning() << "QODBCResult::data: column" << field << "out of range"; - return QVariant(); - } - if (field < d->fieldCacheIdx) - return d->fieldCache.at(field); - - SQLRETURN r(0); - SQLLEN lengthIndicator = 0; - - for (int i = d->fieldCacheIdx; i <= field; ++i) { - // some servers do not support fetching column n after we already - // fetched column n+1, so cache all previous columns here - const QSqlField info = d->rInf.field(i); - switch (info.type()) { - case QVariant::LongLong: - d->fieldCache[i] = qGetBigIntData(d->hStmt, i); - break; - case QVariant::ULongLong: - d->fieldCache[i] = qGetBigIntData(d->hStmt, i, false); - break; - case QVariant::Int: - d->fieldCache[i] = qGetIntData(d->hStmt, i); - break; - case QVariant::UInt: - d->fieldCache[i] = qGetIntData(d->hStmt, i, false); - break; - case QVariant::Date: - DATE_STRUCT dbuf; - r = SQLGetData(d->hStmt, - i + 1, - SQL_C_DATE, - (SQLPOINTER)&dbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) - d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day)); - else - d->fieldCache[i] = QVariant(QVariant::Date); - break; - case QVariant::Time: - TIME_STRUCT tbuf; - r = SQLGetData(d->hStmt, - i + 1, - SQL_C_TIME, - (SQLPOINTER)&tbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) - d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second)); - else - d->fieldCache[i] = QVariant(QVariant::Time); - break; - case QVariant::DateTime: - TIMESTAMP_STRUCT dtbuf; - r = SQLGetData(d->hStmt, - i + 1, - SQL_C_TIMESTAMP, - (SQLPOINTER)&dtbuf, - 0, - &lengthIndicator); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) - d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day), - QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000))); - else - d->fieldCache[i] = QVariant(QVariant::DateTime); - break; - case QVariant::ByteArray: - d->fieldCache[i] = qGetBinaryData(d->hStmt, i); - break; - case QVariant::String: - d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode); - break; - case QVariant::Double: - switch(numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - d->fieldCache[i] = qGetIntData(d->hStmt, i); - break; - case QSql::LowPrecisionInt64: - d->fieldCache[i] = qGetBigIntData(d->hStmt, i); - break; - case QSql::LowPrecisionDouble: - d->fieldCache[i] = qGetDoubleData(d->hStmt, i); - break; - case QSql::HighPrecision: - d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false); - break; - } - break; - default: - d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false)); - break; - } - d->fieldCacheIdx = field + 1; - } - return d->fieldCache[field]; -} - -bool QODBCResult::isNull(int field) -{ - Q_D(const QODBCResult); - if (field < 0 || field > d->fieldCache.size()) - return true; - if (field <= d->fieldCacheIdx) { - // since there is no good way to find out whether the value is NULL - // without fetching the field we'll fetch it here. - // (data() also sets the NULL flag) - data(field); - } - return d->fieldCache.at(field).isNull(); -} - -int QODBCResult::size() -{ - return -1; -} - -int QODBCResult::numRowsAffected() -{ - Q_D(QODBCResult); - SQLLEN affectedRowCount = 0; - SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount); - if (r == SQL_SUCCESS) - return affectedRowCount; - else - qSqlWarning(QLatin1String("QODBCResult::numRowsAffected: Unable to count affected rows"), d); - return -1; -} - -bool QODBCResult::prepare(const QString& query) -{ - Q_D(QODBCResult); - setActive(false); - setAt(QSql::BeforeFirstRow); - SQLRETURN r; - - d->rInf.clear(); - if (d->hStmt && d->isStmtHandleValid()) { - r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to close statement"), d); - return false; - } - } - r = SQLAllocHandle(SQL_HANDLE_STMT, - d->dpDbc(), - &d->hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to allocate statement handle"), d); - return false; - } - - d->updateStmtHandleState(); - - if (isForwardOnly()) { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - } else { - r = SQLSetStmtAttr(d->hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_STATIC, - SQL_IS_UINTEGER); - } - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. " - "Please check your ODBC driver configuration"), QSqlError::StatementError, d)); - return false; - } - - r = SQLPrepare(d->hStmt, - toSQLTCHAR(query).data(), - (SQLINTEGER) query.length()); - - if (r != SQL_SUCCESS) { - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to prepare statement"), QSqlError::StatementError, d)); - return false; - } - return true; -} - -bool QODBCResult::exec() -{ - Q_D(QODBCResult); - setActive(false); - setAt(QSql::BeforeFirstRow); - d->rInf.clear(); - d->fieldCache.clear(); - d->fieldCacheIdx = 0; - - if (!d->hStmt) { - qSqlWarning(QLatin1String("QODBCResult::exec: No statement handle available"), d); - return false; - } - - if (isSelect()) - SQLCloseCursor(d->hStmt); - - QVector& values = boundValues(); - QVector tmpStorage(values.count(), QByteArray()); // holds temporary buffers - QVarLengthArray indicators(values.count()); - memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN)); - - // bind parameters - only positional binding allowed - int i; - SQLRETURN r; - for (i = 0; i < values.count(); ++i) { - if (bindValueType(i) & QSql::Out) - values[i].detach(); - const QVariant &val = values.at(i); - SQLLEN *ind = &indicators[i]; - if (val.isNull()) - *ind = SQL_NULL_DATA; - switch (val.type()) { - case QVariant::Date: { - QByteArray &ba = tmpStorage[i]; - ba.resize(sizeof(DATE_STRUCT)); - DATE_STRUCT *dt = (DATE_STRUCT *)const_cast(ba.constData()); - QDate qdt = val.toDate(); - dt->year = qdt.year(); - dt->month = qdt.month(); - dt->day = qdt.day(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_DATE, - SQL_DATE, - 0, - 0, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; } - case QVariant::Time: { - QByteArray &ba = tmpStorage[i]; - ba.resize(sizeof(TIME_STRUCT)); - TIME_STRUCT *dt = (TIME_STRUCT *)const_cast(ba.constData()); - QTime qdt = val.toTime(); - dt->hour = qdt.hour(); - dt->minute = qdt.minute(); - dt->second = qdt.second(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_TIME, - SQL_TIME, - 0, - 0, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; } - case QVariant::DateTime: { - QByteArray &ba = tmpStorage[i]; - ba.resize(sizeof(TIMESTAMP_STRUCT)); - TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)const_cast(ba.constData()); - QDateTime qdt = val.toDateTime(); - dt->year = qdt.date().year(); - dt->month = qdt.date().month(); - dt->day = qdt.date().day(); - dt->hour = qdt.time().hour(); - dt->minute = qdt.time().minute(); - dt->second = qdt.time().second(); - - int precision = d->drv_d_func()->datetime_precision - 20; // (20 includes a separating period) - if (precision <= 0) { - dt->fraction = 0; - } else { - dt->fraction = qdt.time().msec() * 1000000; - - // (How many leading digits do we want to keep? With SQL Server 2005, this should be 3: 123000000) - int keep = (int)qPow(10.0, 9 - qMin(9, precision)); - dt->fraction = (dt->fraction / keep) * keep; - } - - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_TIMESTAMP, - SQL_TIMESTAMP, - d->drv_d_func()->datetime_precision, - precision, - (void *) dt, - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; } - case QVariant::Int: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_SLONG, - SQL_INTEGER, - 0, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::UInt: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_ULONG, - SQL_NUMERIC, - 15, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::Double: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_DOUBLE, - SQL_DOUBLE, - 0, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::LongLong: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_SBIGINT, - SQL_BIGINT, - 0, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::ULongLong: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_UBIGINT, - SQL_BIGINT, - 0, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::ByteArray: - if (*ind != SQL_NULL_DATA) { - *ind = val.toByteArray().size(); - } - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_BINARY, - SQL_LONGVARBINARY, - val.toByteArray().size(), - 0, - const_cast(val.toByteArray().constData()), - val.toByteArray().size(), - ind); - break; - case QVariant::Bool: - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_BIT, - SQL_BIT, - 0, - 0, - const_cast(val.constData()), - 0, - *ind == SQL_NULL_DATA ? ind : NULL); - break; - case QVariant::String: - if (d->unicode) { - QByteArray &ba = tmpStorage[i]; - QString str = val.toString(); - if (*ind != SQL_NULL_DATA) - *ind = str.length() * sizeof(SQLTCHAR); - int strSize = str.length() * sizeof(SQLTCHAR); - - if (bindValueType(i) & QSql::Out) { - const QVarLengthArray a(toSQLTCHAR(str)); - ba = QByteArray((const char *)a.constData(), a.size() * sizeof(SQLTCHAR)); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_TCHAR, - strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, - 0, // god knows... don't change this! - 0, - ba.data(), - ba.size(), - ind); - break; - } - ba = QByteArray ((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR)); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_TCHAR, - strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, - strSize, - 0, - const_cast(ba.constData()), - ba.size(), - ind); - break; - } - else - { - QByteArray &str = tmpStorage[i]; - str = val.toString().toUtf8(); - if (*ind != SQL_NULL_DATA) - *ind = str.length(); - int strSize = str.length(); - - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_CHAR, - strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR, - strSize, - 0, - const_cast(str.constData()), - strSize, - ind); - break; - } - // fall through - default: { - QByteArray &ba = tmpStorage[i]; - if (*ind != SQL_NULL_DATA) - *ind = ba.size(); - r = SQLBindParameter(d->hStmt, - i + 1, - qParamType[bindValueType(i) & QSql::InOut], - SQL_C_BINARY, - SQL_VARBINARY, - ba.length() + 1, - 0, - const_cast(ba.constData()), - ba.length() + 1, - ind); - break; } - } - if (r != SQL_SUCCESS) { - qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d); - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to bind variable"), QSqlError::StatementError, d)); - return false; - } - } - r = SQLExecute(d->hStmt); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) { - qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d); - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to execute statement"), QSqlError::StatementError, d)); - return false; - } - - SQLULEN isScrollable = 0; - r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, 0); - if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - setForwardOnly(isScrollable == SQL_NONSCROLLABLE); - - SQLSMALLINT count = 0; - SQLNumResultCols(d->hStmt, &count); - if (count) { - setSelect(true); - for (int i = 0; i < count; ++i) { - d->rInf.append(qMakeFieldInfo(d, i)); - } - d->fieldCache.resize(count); - } else { - setSelect(false); - } - setActive(true); - - - //get out parameters - if (!hasOutValues()) - return true; - - for (i = 0; i < values.count(); ++i) { - switch (values.at(i).type()) { - case QVariant::Date: { - DATE_STRUCT ds = *((DATE_STRUCT *)const_cast(tmpStorage.at(i).constData())); - values[i] = QVariant(QDate(ds.year, ds.month, ds.day)); - break; } - case QVariant::Time: { - TIME_STRUCT dt = *((TIME_STRUCT *)const_cast(tmpStorage.at(i).constData())); - values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second)); - break; } - case QVariant::DateTime: { - TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*) - const_cast(tmpStorage.at(i).constData())); - values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day), - QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000))); - break; } - case QVariant::Bool: - case QVariant::Int: - case QVariant::UInt: - case QVariant::Double: - case QVariant::ByteArray: - case QVariant::LongLong: - case QVariant::ULongLong: - //nothing to do - break; - case QVariant::String: - if (d->unicode) { - if (bindValueType(i) & QSql::Out) { - const QByteArray &first = tmpStorage.at(i); - QVarLengthArray array; - array.append((const SQLTCHAR *)first.constData(), first.size()); - values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR)); - } - break; - } - // fall through - default: { - if (bindValueType(i) & QSql::Out) - values[i] = tmpStorage.at(i); - break; } - } - if (indicators[i] == SQL_NULL_DATA) - values[i] = QVariant(values[i].type()); - } - return true; -} - -QSqlRecord QODBCResult::record() const -{ - Q_D(const QODBCResult); - if (!isActive() || !isSelect()) - return QSqlRecord(); - return d->rInf; -} - -QVariant QODBCResult::lastInsertId() const -{ - Q_D(const QODBCResult); - QString sql; - - switch (driver()->dbmsType()) { - case QSqlDriver::MSSqlServer: - case QSqlDriver::Sybase: - sql = QLatin1String("SELECT @@IDENTITY;"); - break; - case QSqlDriver::MySqlServer: - sql = QLatin1String("SELECT LAST_INSERT_ID();"); - break; - case QSqlDriver::PostgreSQL: - sql = QLatin1String("SELECT lastval();"); - break; - default: - break; - } - - if (!sql.isEmpty()) { - QSqlQuery qry(driver()->createResult()); - if (qry.exec(sql) && qry.next()) - return qry.value(0); - - qSqlWarning(QLatin1String("QODBCResult::lastInsertId: Unable to get lastInsertId"), d); - } else { - qSqlWarning(QLatin1String("QODBCResult::lastInsertId: not implemented for this DBMS"), d); - } - - return QVariant(); -} - -QVariant QODBCResult::handle() const -{ - Q_D(const QODBCResult); - return QVariant(qRegisterMetaType("SQLHANDLE"), &d->hStmt); -} - -bool QODBCResult::nextResult() -{ - Q_D(QODBCResult); - setActive(false); - setAt(QSql::BeforeFirstRow); - d->rInf.clear(); - d->fieldCache.clear(); - d->fieldCacheIdx = 0; - setSelect(false); - - SQLRETURN r = SQLMoreResults(d->hStmt); - if (r != SQL_SUCCESS) { - if (r == SQL_SUCCESS_WITH_INFO) { - int nativeCode = -1; - QString message = qODBCWarn(d, &nativeCode); - qWarning() << "QODBCResult::nextResult():" << message; - } else { - if (r != SQL_NO_DATA) - setLastError(qMakeError(QCoreApplication::translate("QODBCResult", - "Unable to fetch last"), QSqlError::ConnectionError, d)); - return false; - } - } - - SQLSMALLINT count = 0; - SQLNumResultCols(d->hStmt, &count); - if (count) { - setSelect(true); - for (int i = 0; i < count; ++i) { - d->rInf.append(qMakeFieldInfo(d, i)); - } - d->fieldCache.resize(count); - } else { - setSelect(false); - } - setActive(true); - - return true; -} - -void QODBCResult::virtual_hook(int id, void *data) -{ - QSqlResult::virtual_hook(id, data); -} - -void QODBCResult::detachFromResultSet() -{ - Q_D(QODBCResult); - if (d->hStmt) - SQLCloseCursor(d->hStmt); -} - -//////////////////////////////////////// - - -QODBCDriver::QODBCDriver(QObject *parent) - : QSqlDriver(*new QODBCDriverPrivate, parent) -{ -} - -QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject *parent) - : QSqlDriver(*new QODBCDriverPrivate, parent) -{ - Q_D(QODBCDriver); - d->hEnv = env; - d->hDbc = con; - if (env && con) { - setOpen(true); - setOpenError(false); - } -} - -QODBCDriver::~QODBCDriver() -{ - cleanup(); -} - -bool QODBCDriver::hasFeature(DriverFeature f) const -{ - Q_D(const QODBCDriver); - switch (f) { - case Transactions: { - if (!d->hDbc) - return false; - SQLUSMALLINT txn; - SQLSMALLINT t; - int r = SQLGetInfo(d->hDbc, - (SQLUSMALLINT)SQL_TXN_CAPABLE, - &txn, - sizeof(txn), - &t); - if (r != SQL_SUCCESS || txn == SQL_TC_NONE) - return false; - else - return true; - } - case Unicode: - return d->unicode; - case PreparedQueries: - case PositionalPlaceholders: - case FinishQuery: - case LowPrecisionNumbers: - return true; - case QuerySize: - case NamedPlaceholders: - case BatchOperations: - case SimpleLocking: - case EventNotifications: - case CancelQuery: - return false; - case LastInsertId: - return (d->dbmsType == MSSqlServer) - || (d->dbmsType == Sybase) - || (d->dbmsType == MySqlServer) - || (d->dbmsType == PostgreSQL); - case MultipleResultSets: - return d->hasMultiResultSets; - case BLOB: { - if (d->dbmsType == MySqlServer) - return true; - else - return false; - } - } - return false; -} - -bool QODBCDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString &, - int, - const QString& connOpts) -{ - Q_D(QODBCDriver); - if (isOpen()) - close(); - SQLRETURN r; - r = SQLAllocHandle(SQL_HANDLE_ENV, - SQL_NULL_HANDLE, - &d->hEnv); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d); - setOpenError(true); - return false; - } - r = SQLSetEnvAttr(d->hEnv, - SQL_ATTR_ODBC_VERSION, - (SQLPOINTER)qGetODBCVersion(connOpts), - SQL_IS_UINTEGER); - r = SQLAllocHandle(SQL_HANDLE_DBC, - d->hEnv, - &d->hDbc); - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d); - setOpenError(true); - cleanup(); - return false; - } - - if (!d->setConnectionOptions(connOpts)) { - cleanup(); - return false; - } - - // Create the connection string - QString connQStr; - // support the "DRIVER={SQL SERVER};SERVER=blah" syntax - if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive)) - connQStr = QLatin1String("FILEDSN=") + db; - else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive) - || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive)) - connQStr = db; - else - connQStr = QLatin1String("DSN=") + db; - - if (!user.isEmpty()) - connQStr += QLatin1String(";UID=") + user; - if (!password.isEmpty()) - connQStr += QLatin1String(";PWD=") + password; - - SQLSMALLINT cb; - QVarLengthArray connOut(1024); - memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR)); - r = SQLDriverConnect(d->hDbc, - NULL, - toSQLTCHAR(connQStr).data(), - (SQLSMALLINT)connQStr.length(), - connOut.data(), - 1024, - &cb, - /*SQL_DRIVER_NOPROMPT*/0); - - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { - setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); - setOpenError(true); - cleanup(); - return false; - } - - if (!d->checkDriver()) { - setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all " - "functionality required"), QSqlError::ConnectionError, d)); - setOpenError(true); - cleanup(); - return false; - } - - d->checkUnicode(); - d->checkSchemaUsage(); - d->checkDBMS(); - d->checkHasSQLFetchScroll(); - d->checkHasMultiResults(); - d->checkDateTimePrecision(); - setOpen(true); - setOpenError(false); - if (d->dbmsType == MSSqlServer) { - QSqlQuery i(createResult()); - i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON")); - } - return true; -} - -void QODBCDriver::close() -{ - cleanup(); - setOpen(false); - setOpenError(false); -} - -void QODBCDriver::cleanup() -{ - Q_D(QODBCDriver); - SQLRETURN r; - - if(d->hDbc) { - // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect - if (isOpen()) { - r = SQLDisconnect(d->hDbc); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d); - else - d->disconnectCount++; - } - - r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d); - d->hDbc = 0; - } - - if (d->hEnv) { - r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d); - d->hEnv = 0; - } -} - -// checks whether the server can return char, varchar and longvarchar -// as two byte unicode characters -void QODBCDriverPrivate::checkUnicode() -{ - SQLRETURN r; - SQLUINTEGER fFunc; - - unicode = false; - r = SQLGetInfo(hDbc, - SQL_CONVERT_CHAR, - (SQLPOINTER)&fFunc, - sizeof(fFunc), - NULL); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) { - unicode = true; - return; - } - - r = SQLGetInfo(hDbc, - SQL_CONVERT_VARCHAR, - (SQLPOINTER)&fFunc, - sizeof(fFunc), - NULL); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) { - unicode = true; - return; - } - - r = SQLGetInfo(hDbc, - SQL_CONVERT_LONGVARCHAR, - (SQLPOINTER)&fFunc, - sizeof(fFunc), - NULL); - if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) { - unicode = true; - return; - } - SQLHANDLE hStmt; - r = SQLAllocHandle(SQL_HANDLE_STMT, - hDbc, - &hStmt); - - r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS); - if(r == SQL_SUCCESS) { - r = SQLFetch(hStmt); - if(r == SQL_SUCCESS) { - QVarLengthArray buffer(10); - r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL); - if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) { - unicode = true; - } - } - } - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); -} - -bool QODBCDriverPrivate::checkDriver() const -{ -#ifdef ODBC_CHECK_DRIVER - static const SQLUSMALLINT reqFunc[] = { - SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS, - SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT, - SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0 - }; - - // these functions are optional - static const SQLUSMALLINT optFunc[] = { - SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0 - }; - - SQLRETURN r; - SQLUSMALLINT sup; - - int i; - // check the required functions - for (i = 0; reqFunc[i] != 0; ++i) { - - r = SQLGetFunctions(hDbc, reqFunc[i], &sup); - - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this); - return false; - } - if (sup == SQL_FALSE) { - qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] << - ").\nPlease look at the Qt SQL Module Driver documentation for more information."; - return false; - } - } - - // these functions are optional and just generate a warning - for (i = 0; optFunc[i] != 0; ++i) { - - r = SQLGetFunctions(hDbc, optFunc[i], &sup); - - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this); - return false; - } - if (sup == SQL_FALSE) { - qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')'; - return true; - } - } -#endif //ODBC_CHECK_DRIVER - - return true; -} - -void QODBCDriverPrivate::checkSchemaUsage() -{ - SQLRETURN r; - SQLUINTEGER val; - - r = SQLGetInfo(hDbc, - SQL_SCHEMA_USAGE, - (SQLPOINTER) &val, - sizeof(val), - NULL); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - useSchema = (val != 0); -} - -void QODBCDriverPrivate::checkDBMS() -{ - SQLRETURN r; - QVarLengthArray serverString(200); - SQLSMALLINT t; - memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR)); - - r = SQLGetInfo(hDbc, - SQL_DBMS_NAME, - serverString.data(), - SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)), - &t); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR)); - if (serverType.contains(QLatin1String("PostgreSQL"), Qt::CaseInsensitive)) - dbmsType = QSqlDriver::PostgreSQL; - else if (serverType.contains(QLatin1String("Oracle"), Qt::CaseInsensitive)) - dbmsType = QSqlDriver::Oracle; - else if (serverType.contains(QLatin1String("MySql"), Qt::CaseInsensitive)) - dbmsType = QSqlDriver::MySqlServer; - else if (serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive)) - dbmsType = QSqlDriver::MSSqlServer; - else if (serverType.contains(QLatin1String("Sybase"), Qt::CaseInsensitive)) - dbmsType = QSqlDriver::Sybase; - } - r = SQLGetInfo(hDbc, - SQL_DRIVER_NAME, - serverString.data(), - SQLSMALLINT(serverString.size() * sizeof(SQLTCHAR)), - &t); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - const QString serverType = fromSQLTCHAR(serverString, t / sizeof(SQLTCHAR)); - isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive); - unicode = unicode && !isFreeTDSDriver; - } -} - -void QODBCDriverPrivate::checkHasSQLFetchScroll() -{ - SQLUSMALLINT sup; - SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup); - if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) { - hasSQLFetchScroll = false; - qWarning("QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries"); - } -} - -void QODBCDriverPrivate::checkHasMultiResults() -{ - QVarLengthArray driverResponse(2); - SQLSMALLINT length; - SQLRETURN r = SQLGetInfo(hDbc, - SQL_MULT_RESULT_SETS, - driverResponse.data(), - SQLSMALLINT(driverResponse.size() * sizeof(SQLTCHAR)), - &length); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) - hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y')); -} - -void QODBCDriverPrivate::checkDateTimePrecision() -{ - SQLINTEGER columnSize; - SQLHANDLE hStmt; - - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt); - if (r != SQL_SUCCESS) { - return; - } - - r = SQLGetTypeInfo(hStmt, SQL_TIMESTAMP); - if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { - r = SQLFetch(hStmt); - if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) - { - if (SQLGetData(hStmt, 3, SQL_INTEGER, &columnSize, sizeof(columnSize), 0) == SQL_SUCCESS) { - datetime_precision = (int)columnSize; - } - } - } - SQLFreeHandle(SQL_HANDLE_STMT, hStmt); -} - -QSqlResult *QODBCDriver::createResult() const -{ - return new QODBCResult(this); -} - -bool QODBCDriver::beginTransaction() -{ - Q_D(QODBCDriver); - if (!isOpen()) { - qWarning("QODBCDriver::beginTransaction: Database not open"); - return false; - } - SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF); - SQLRETURN r = SQLSetConnectAttr(d->hDbc, - SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER)size_t(ac), - sizeof(ac)); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to disable autocommit"), - QSqlError::TransactionError, d)); - return false; - } - return true; -} - -bool QODBCDriver::commitTransaction() -{ - Q_D(QODBCDriver); - if (!isOpen()) { - qWarning("QODBCDriver::commitTransaction: Database not open"); - return false; - } - SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, - d->hDbc, - SQL_COMMIT); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to commit transaction"), - QSqlError::TransactionError, d)); - return false; - } - return endTrans(); -} - -bool QODBCDriver::rollbackTransaction() -{ - Q_D(QODBCDriver); - if (!isOpen()) { - qWarning("QODBCDriver::rollbackTransaction: Database not open"); - return false; - } - SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC, - d->hDbc, - SQL_ROLLBACK); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to rollback transaction"), - QSqlError::TransactionError, d)); - return false; - } - return endTrans(); -} - -bool QODBCDriver::endTrans() -{ - Q_D(QODBCDriver); - SQLUINTEGER ac(SQL_AUTOCOMMIT_ON); - SQLRETURN r = SQLSetConnectAttr(d->hDbc, - SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER)size_t(ac), - sizeof(ac)); - if (r != SQL_SUCCESS) { - setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d)); - return false; - } - return true; -} - -QStringList QODBCDriver::tables(QSql::TableType type) const -{ - Q_D(const QODBCDriver); - QStringList tl; - if (!isOpen()) - return tl; - SQLHANDLE hStmt; - - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d); - return tl; - } - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - QStringList tableType; - if (type & QSql::Tables) - tableType += QLatin1String("TABLE"); - if (type & QSql::Views) - tableType += QLatin1String("VIEW"); - if (type & QSql::SystemTables) - tableType += QLatin1String("SYSTEM TABLE"); - if (tableType.isEmpty()) - return tl; - - QString joinedTableTypeString = tableType.join(QLatin1Char(',')); - - r = SQLTables(hStmt, - NULL, - 0, - NULL, - 0, - NULL, - 0, - toSQLTCHAR(joinedTableTypeString).data(), - joinedTableTypeString.length() /* characters, not bytes */); - - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - - if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) { - qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")"; - return QStringList(); - } - - while (r == SQL_SUCCESS) { - QString fieldVal = qGetStringData(hStmt, 2, -1, false); - tl.append(fieldVal); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - } - - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r!= SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d); - return tl; -} - -QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const -{ - Q_D(const QODBCDriver); - QSqlIndex index(tablename); - if (!isOpen()) - return index; - bool usingSpecialColumns = false; - QSqlRecord rec = record(tablename); - - SQLHANDLE hStmt; - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d); - return index; - } - QString catalog, schema, table; - const_cast(d)->splitTableQualifier(tablename, catalog, schema, table); - - if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) - catalog = stripDelimiters(catalog, QSqlDriver::TableName); - else - catalog = d->adjustCase(catalog); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = d->adjustCase(schema); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = d->adjustCase(table); - - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - r = SQLPrimaryKeys(hStmt, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length() /* in characters, not in bytes */); - - // if the SQLPrimaryKeys() call does not succeed (e.g the driver - // does not support it) - try an alternative method to get hold of - // the primary index (e.g MS Access and FoxPro) - if (r != SQL_SUCCESS) { - r = SQLSpecialColumns(hStmt, - SQL_BEST_ROWID, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length(), - SQL_SCOPE_CURROW, - SQL_NULLABLE); - - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d); - } else { - usingSpecialColumns = true; - } - } - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - - int fakeId = 0; - QString cName, idxName; - // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop - while (r == SQL_SUCCESS) { - if (usingSpecialColumns) { - cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name - idxName = QString::number(fakeId++); // invent a fake index name - } else { - cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name - idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name - } - index.append(rec.field(cName)); - index.setName(idxName); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - - } - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r!= SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d); - return index; -} - -QSqlRecord QODBCDriver::record(const QString& tablename) const -{ - Q_D(const QODBCDriver); - QSqlRecord fil; - if (!isOpen()) - return fil; - - SQLHANDLE hStmt; - QString catalog, schema, table; - const_cast(d)->splitTableQualifier(tablename, catalog, schema, table); - - if (isIdentifierEscaped(catalog, QSqlDriver::TableName)) - catalog = stripDelimiters(catalog, QSqlDriver::TableName); - else - catalog = d->adjustCase(catalog); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = d->adjustCase(schema); - - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - else - table = d->adjustCase(table); - - SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT, - d->hDbc, - &hStmt); - if (r != SQL_SUCCESS) { - qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d); - return fil; - } - r = SQLSetStmtAttr(hStmt, - SQL_ATTR_CURSOR_TYPE, - (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, - SQL_IS_UINTEGER); - r = SQLColumns(hStmt, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length(), - NULL, - 0); - if (r != SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - - // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop - while (r == SQL_SUCCESS) { - - fil.append(qMakeFieldInfo(hStmt, d)); - - if (d->hasSQLFetchScroll) - r = SQLFetchScroll(hStmt, - SQL_FETCH_NEXT, - 0); - else - r = SQLFetch(hStmt); - } - - r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - if (r!= SQL_SUCCESS) - qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d); - - return fil; -} - -QString QODBCDriver::formatValue(const QSqlField &field, - bool trimStrings) const -{ - QString r; - if (field.isNull()) { - r = QLatin1String("NULL"); - } else if (field.type() == QVariant::DateTime) { - // Use an escape sequence for the datetime fields - if (field.value().toDateTime().isValid()){ - QDate dt = field.value().toDateTime().date(); - QTime tm = field.value().toDateTime().time(); - // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10 - r = QLatin1String("{ ts '") + - QString::number(dt.year()) + QLatin1Char('-') + - QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) + - QLatin1Char('-') + - QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) + - QLatin1Char(' ') + - tm.toString() + - QLatin1String("' }"); - } else - r = QLatin1String("NULL"); - } else if (field.type() == QVariant::ByteArray) { - QByteArray ba = field.value().toByteArray(); - QString res; - static const char hexchars[] = "0123456789abcdef"; - for (int i = 0; i < ba.size(); ++i) { - uchar s = (uchar) ba[i]; - res += QLatin1Char(hexchars[s >> 4]); - res += QLatin1Char(hexchars[s & 0x0f]); - } - r = QLatin1String("0x") + res; - } else { - r = QSqlDriver::formatValue(field, trimStrings); - } - return r; -} - -QVariant QODBCDriver::handle() const -{ - Q_D(const QODBCDriver); - return QVariant(qRegisterMetaType("SQLHANDLE"), &d->hDbc); -} - -QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const -{ - Q_D(const QODBCDriver); - QChar quote = const_cast(d)->quoteChar(); - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) { - res.replace(quote, QString(quote)+QString(quote)); - res.prepend(quote).append(quote); - res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote)); - } - return res; -} - -bool QODBCDriver::isIdentifierEscaped(const QString &identifier, IdentifierType) const -{ - Q_D(const QODBCDriver); - QChar quote = const_cast(d)->quoteChar(); - return identifier.size() > 2 - && identifier.startsWith(quote) //left delimited - && identifier.endsWith(quote); //right delimited -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/odbc/qsql_odbc.pri b/src/sql/drivers/odbc/qsql_odbc.pri deleted file mode 100644 index b206df37c3..0000000000 --- a/src/sql/drivers/odbc/qsql_odbc.pri +++ /dev/null @@ -1,12 +0,0 @@ -HEADERS += $$PWD/qsql_odbc_p.h -SOURCES += $$PWD/qsql_odbc.cpp - -unix { - DEFINES += UNICODE - !contains(LIBS, .*odbc.*) { - macx:LIBS += -liodbc - else:LIBS += $$QT_LFLAGS_ODBC - } -} else { - LIBS *= -lodbc32 -} diff --git a/src/sql/drivers/odbc/qsql_odbc_p.h b/src/sql/drivers/odbc/qsql_odbc_p.h deleted file mode 100644 index f4ce8bc243..0000000000 --- a/src/sql/drivers/odbc/qsql_odbc_p.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** 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_ODBC_H -#define QSQL_ODBC_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 - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_ODBC -#else -#define Q_EXPORT_SQLDRIVER_ODBC Q_SQL_EXPORT -#endif - -#ifdef Q_OS_UNIX -#define HAVE_LONG_LONG 1 // force UnixODBC NOT to fall back to a struct for BIGINTs -#endif - -#if defined(Q_CC_BOR) -// workaround for Borland to make sure that SQLBIGINT is defined -# define _MSC_VER 900 -#endif -#include -#if defined(Q_CC_BOR) -# undef _MSC_VER -#endif - -#include - -QT_BEGIN_NAMESPACE - -class QODBCDriverPrivate; - -class Q_EXPORT_SQLDRIVER_ODBC QODBCDriver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QODBCDriver) - Q_OBJECT - friend class QODBCResultPrivate; - -public: - explicit QODBCDriver(QObject *parent=0); - QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent=0); - virtual ~QODBCDriver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, - bool trimStrings) const Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - - bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - -private: - bool endTrans(); - void cleanup(); -}; - -QT_END_NAMESPACE - -#endif // QSQL_ODBC_H diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp deleted file mode 100644 index fcf75af298..0000000000 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ /dev/null @@ -1,1508 +0,0 @@ -/**************************************************************************** -** -** 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_psql_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html -#ifndef isnan - # define isnan(x) \ - (sizeof (x) == sizeof (long double) ? isnan_ld (x) \ - : sizeof (x) == sizeof (double) ? isnan_d (x) \ - : isnan_f (x)) - static inline int isnan_f (float x) { return x != x; } - static inline int isnan_d (double x) { return x != x; } - static inline int isnan_ld (long double x) { return x != x; } -#endif - -#ifndef isinf - # define isinf(x) \ - (sizeof (x) == sizeof (long double) ? isinf_ld (x) \ - : sizeof (x) == sizeof (double) ? isinf_d (x) \ - : isinf_f (x)) - static inline int isinf_f (float x) { return isnan (x - x); } - static inline int isinf_d (double x) { return isnan (x - x); } - static inline int isinf_ld (long double x) { return isnan (x - x); } -#endif - - -// workaround for postgres defining their OIDs in a private header file -#define QBOOLOID 16 -#define QINT8OID 20 -#define QINT2OID 21 -#define QINT4OID 23 -#define QNUMERICOID 1700 -#define QFLOAT4OID 700 -#define QFLOAT8OID 701 -#define QABSTIMEOID 702 -#define QRELTIMEOID 703 -#define QDATEOID 1082 -#define QTIMEOID 1083 -#define QTIMETZOID 1266 -#define QTIMESTAMPOID 1114 -#define QTIMESTAMPTZOID 1184 -#define QOIDOID 2278 -#define QBYTEAOID 17 -#define QREGPROCOID 24 -#define QXIDOID 28 -#define QCIDOID 29 - -#define QBITOID 1560 -#define QVARBITOID 1562 - -#define VARHDRSZ 4 - -/* This is a compile time switch - if PQfreemem is declared, the compiler will use that one, - otherwise it'll run in this template */ -template -inline void PQfreemem(T *t, int = 0) { free(t); } - -Q_DECLARE_OPAQUE_POINTER(PGconn*) -Q_DECLARE_METATYPE(PGconn*) - -Q_DECLARE_OPAQUE_POINTER(PGresult*) -Q_DECLARE_METATYPE(PGresult*) - -QT_BEGIN_NAMESPACE - -inline void qPQfreemem(void *buffer) -{ - PQfreemem(buffer); -} - -class QPSQLResultPrivate; - -class QPSQLResult: public QSqlResult -{ - Q_DECLARE_PRIVATE(QPSQLResult) - -public: - QPSQLResult(const QPSQLDriver *db); - ~QPSQLResult(); - - QVariant handle() const Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; - -protected: - void cleanup(); - bool fetch(int i) Q_DECL_OVERRIDE; - bool fetchFirst() Q_DECL_OVERRIDE; - bool fetchLast() Q_DECL_OVERRIDE; - QVariant data(int i) 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; - QSqlRecord record() const Q_DECL_OVERRIDE; - QVariant lastInsertId() const Q_DECL_OVERRIDE; - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() Q_DECL_OVERRIDE; -}; - -class QPSQLDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QPSQLDriver) -public: - QPSQLDriverPrivate() : QSqlDriverPrivate(), - connection(0), - isUtf8(false), - pro(QPSQLDriver::Version6), - sn(0), - pendingNotifyCheck(false), - hasBackslashEscape(false) - { dbmsType = QSqlDriver::PostgreSQL; } - - PGconn *connection; - bool isUtf8; - QPSQLDriver::Protocol pro; - QSocketNotifier *sn; - QStringList seid; - mutable bool pendingNotifyCheck; - bool hasBackslashEscape; - - void appendTables(QStringList &tl, QSqlQuery &t, QChar type); - PGresult * exec(const char * stmt) const; - PGresult * exec(const QString & stmt) const; - QPSQLDriver::Protocol getPSQLVersion(); - bool setEncodingUtf8(); - void setDatestyle(); - void detectBackslashEscape(); -}; - -void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type) -{ - QString query; - if (pro >= QPSQLDriver::Version73) { - query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class " - "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) " - "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') " - "and (pg_class.relname !~ '^pg_') " - "and (pg_namespace.nspname != 'information_schema') ").arg(type); - } else { - query = QString::fromLatin1("select relname, null from pg_class where (relkind = '%1') " - "and (relname !~ '^Inv') " - "and (relname !~ '^pg_') ").arg(type); - } - t.exec(query); - while (t.next()) { - QString schema = t.value(1).toString(); - if (schema.isEmpty() || schema == QLatin1String("public")) - tl.append(t.value(0).toString()); - else - tl.append(t.value(0).toString().prepend(QLatin1Char('.')).prepend(schema)); - } -} - -PGresult * QPSQLDriverPrivate::exec(const char * stmt) const -{ - Q_Q(const QPSQLDriver); - PGresult *result = PQexec(connection, stmt); - if (seid.size() && !pendingNotifyCheck) { - pendingNotifyCheck = true; - QMetaObject::invokeMethod(const_cast(q), "_q_handleNotification", Qt::QueuedConnection, Q_ARG(int,0)); - } - return result; -} - -PGresult * QPSQLDriverPrivate::exec(const QString & stmt) const -{ - return exec(isUtf8 ? stmt.toUtf8().constData() : stmt.toLocal8Bit().constData()); -} - -class QPSQLResultPrivate : public QSqlResultPrivate -{ - Q_DECLARE_PUBLIC(QPSQLResult) -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QPSQLDriver); - QPSQLResultPrivate(QPSQLResult *q, const QPSQLDriver *drv) - : QSqlResultPrivate(q, drv), - result(0), - currentSize(-1), - preparedQueriesEnabled(false) - { } - - QString fieldSerial(int i) const Q_DECL_OVERRIDE { return QLatin1Char('$') + QString::number(i + 1); } - void deallocatePreparedStmt(); - - PGresult *result; - int currentSize; - bool preparedQueriesEnabled; - QString preparedStmtId; - - bool processResults(); -}; - -static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, - const QPSQLDriverPrivate *p, PGresult* result = 0) -{ - const char *s = PQerrorMessage(p->connection); - QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s); - QString errorCode; - if (result) { - errorCode = QString::fromLatin1(PQresultErrorField(result, PG_DIAG_SQLSTATE)); - msg += QString::fromLatin1("(%1)").arg(errorCode); - } - return QSqlError(QLatin1String("QPSQL: ") + err, msg, type, errorCode); -} - -bool QPSQLResultPrivate::processResults() -{ - Q_Q(QPSQLResult); - if (!result) - return false; - - int status = PQresultStatus(result); - if (status == PGRES_TUPLES_OK) { - q->setSelect(true); - q->setActive(true); - currentSize = PQntuples(result); - return true; - } else if (status == PGRES_COMMAND_OK) { - q->setSelect(false); - q->setActive(true); - currentSize = -1; - return true; - } - q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult", - "Unable to create query"), QSqlError::StatementError, drv_d_func(), result)); - return false; -} - -static QVariant::Type qDecodePSQLType(int t) -{ - QVariant::Type type = QVariant::Invalid; - switch (t) { - case QBOOLOID: - type = QVariant::Bool; - break; - case QINT8OID: - type = QVariant::LongLong; - break; - case QINT2OID: - case QINT4OID: - case QOIDOID: - case QREGPROCOID: - case QXIDOID: - case QCIDOID: - type = QVariant::Int; - break; - case QNUMERICOID: - case QFLOAT4OID: - case QFLOAT8OID: - type = QVariant::Double; - break; - case QABSTIMEOID: - case QRELTIMEOID: - case QDATEOID: - type = QVariant::Date; - break; - case QTIMEOID: - case QTIMETZOID: - type = QVariant::Time; - break; - case QTIMESTAMPOID: - case QTIMESTAMPTZOID: - type = QVariant::DateTime; - break; - case QBYTEAOID: - type = QVariant::ByteArray; - break; - default: - type = QVariant::String; - break; - } - return type; -} - -void QPSQLResultPrivate::deallocatePreparedStmt() -{ - const QString stmt = QLatin1String("DEALLOCATE ") + preparedStmtId; - PGresult *result = drv_d_func()->exec(stmt); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) - qWarning("Unable to free statement: %s", PQerrorMessage(drv_d_func()->connection)); - PQclear(result); - preparedStmtId.clear(); -} - -QPSQLResult::QPSQLResult(const QPSQLDriver* db) - : QSqlResult(*new QPSQLResultPrivate(this, db)) -{ - Q_D(QPSQLResult); - d->preparedQueriesEnabled = db->hasFeature(QSqlDriver::PreparedQueries); -} - -QPSQLResult::~QPSQLResult() -{ - Q_D(QPSQLResult); - cleanup(); - - if (d->preparedQueriesEnabled && !d->preparedStmtId.isNull()) - d->deallocatePreparedStmt(); -} - -QVariant QPSQLResult::handle() const -{ - Q_D(const QPSQLResult); - return QVariant::fromValue(d->result); -} - -void QPSQLResult::cleanup() -{ - Q_D(QPSQLResult); - if (d->result) - PQclear(d->result); - d->result = 0; - setAt(QSql::BeforeFirstRow); - d->currentSize = -1; - setActive(false); -} - -bool QPSQLResult::fetch(int i) -{ - Q_D(const QPSQLResult); - if (!isActive()) - return false; - if (i < 0) - return false; - if (i >= d->currentSize) - return false; - if (at() == i) - return true; - setAt(i); - return true; -} - -bool QPSQLResult::fetchFirst() -{ - return fetch(0); -} - -bool QPSQLResult::fetchLast() -{ - Q_D(const QPSQLResult); - return fetch(PQntuples(d->result) - 1); -} - -QVariant QPSQLResult::data(int i) -{ - Q_D(const QPSQLResult); - if (i >= PQnfields(d->result)) { - qWarning("QPSQLResult::data: column %d out of range", i); - return QVariant(); - } - int ptype = PQftype(d->result, i); - QVariant::Type type = qDecodePSQLType(ptype); - const char *val = PQgetvalue(d->result, at(), i); - if (PQgetisnull(d->result, at(), i)) - return QVariant(type); - switch (type) { - case QVariant::Bool: - return QVariant((bool)(val[0] == 't')); - case QVariant::String: - return d->drv_d_func()->isUtf8 ? QString::fromUtf8(val) : QString::fromLatin1(val); - case QVariant::LongLong: - if (val[0] == '-') - return QString::fromLatin1(val).toLongLong(); - else - return QString::fromLatin1(val).toULongLong(); - case QVariant::Int: - return atoi(val); - case QVariant::Double: - if (ptype == QNUMERICOID) { - if (numericalPrecisionPolicy() != QSql::HighPrecision) { - QVariant retval; - bool convert; - double dbl=QString::fromLatin1(val).toDouble(&convert); - if (numericalPrecisionPolicy() == QSql::LowPrecisionInt64) - retval = (qlonglong)dbl; - else if (numericalPrecisionPolicy() == QSql::LowPrecisionInt32) - retval = (int)dbl; - else if (numericalPrecisionPolicy() == QSql::LowPrecisionDouble) - retval = dbl; - if (!convert) - return QVariant(); - return retval; - } - return QString::fromLatin1(val); - } - return QString::fromLatin1(val).toDouble(); - case QVariant::Date: - if (val[0] == '\0') { - return QVariant(QDate()); - } else { -#ifndef QT_NO_DATESTRING - return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate)); -#else - return QVariant(QString::fromLatin1(val)); -#endif - } - case QVariant::Time: { - const QString str = QString::fromLatin1(val); -#ifndef QT_NO_DATESTRING - if (str.isEmpty()) - return QVariant(QTime()); - else - return QVariant(QTime::fromString(str, Qt::ISODate)); -#else - return QVariant(str); -#endif - } - case QVariant::DateTime: { - QString dtval = QString::fromLatin1(val); -#ifndef QT_NO_DATESTRING - if (dtval.length() < 10) { - return QVariant(QDateTime()); - } else { - QChar sign = dtval[dtval.size() - 3]; - if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00"); - return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime()); - } -#else - return QVariant(dtval); -#endif - } - case QVariant::ByteArray: { - size_t len; - unsigned char *data = PQunescapeBytea((const unsigned char*)val, &len); - QByteArray ba(reinterpret_cast(data), int(len)); - qPQfreemem(data); - return QVariant(ba); - } - default: - case QVariant::Invalid: - qWarning("QPSQLResult::data: unknown data type"); - } - return QVariant(); -} - -bool QPSQLResult::isNull(int field) -{ - Q_D(const QPSQLResult); - PQgetvalue(d->result, at(), field); - return PQgetisnull(d->result, at(), field); -} - -bool QPSQLResult::reset (const QString& query) -{ - Q_D(QPSQLResult); - cleanup(); - if (!driver()) - return false; - if (!driver()->isOpen() || driver()->isOpenError()) - return false; - d->result = d->drv_d_func()->exec(query); - return d->processResults(); -} - -int QPSQLResult::size() -{ - Q_D(const QPSQLResult); - return d->currentSize; -} - -int QPSQLResult::numRowsAffected() -{ - Q_D(const QPSQLResult); - return QString::fromLatin1(PQcmdTuples(d->result)).toInt(); -} - -QVariant QPSQLResult::lastInsertId() const -{ - Q_D(const QPSQLResult); - if (d->drv_d_func()->pro >= QPSQLDriver::Version81) { - QSqlQuery qry(driver()->createResult()); - // Most recent sequence value obtained from nextval - if (qry.exec(QLatin1String("SELECT lastval();")) && qry.next()) - return qry.value(0); - } else if (isActive()) { - Oid id = PQoidValue(d->result); - if (id != InvalidOid) - return QVariant(id); - } - return QVariant(); -} - -QSqlRecord QPSQLResult::record() const -{ - Q_D(const QPSQLResult); - QSqlRecord info; - if (!isActive() || !isSelect()) - return info; - - int count = PQnfields(d->result); - for (int i = 0; i < count; ++i) { - QSqlField f; - if (d->drv_d_func()->isUtf8) - f.setName(QString::fromUtf8(PQfname(d->result, i))); - else - f.setName(QString::fromLocal8Bit(PQfname(d->result, i))); - int ptype = PQftype(d->result, i); - f.setType(qDecodePSQLType(ptype)); - int len = PQfsize(d->result, i); - int precision = PQfmod(d->result, i); - - switch (ptype) { - case QTIMESTAMPOID: - case QTIMESTAMPTZOID: - precision = 3; - break; - - case QNUMERICOID: - if (precision != -1) { - len = (precision >> 16); - precision = ((precision - VARHDRSZ) & 0xffff); - } - break; - case QBITOID: - case QVARBITOID: - len = precision; - precision = -1; - break; - default: - if (len == -1 && precision >= VARHDRSZ) { - len = precision - VARHDRSZ; - precision = -1; - } - } - - f.setLength(len); - f.setPrecision(precision); - f.setSqlType(ptype); - info.append(f); - } - return info; -} - -void QPSQLResult::virtual_hook(int id, void *data) -{ - Q_ASSERT(data); - - QSqlResult::virtual_hook(id, data); -} - -static QString qCreateParamString(const QVector &boundValues, const QSqlDriver *driver) -{ - if (boundValues.isEmpty()) - return QString(); - - QString params; - QSqlField f; - for (int i = 0; i < boundValues.count(); ++i) { - const QVariant &val = boundValues.at(i); - - f.setType(val.type()); - if (val.isNull()) - f.clear(); - else - f.setValue(val); - if(!params.isNull()) - params.append(QLatin1String(", ")); - params.append(driver->formatValue(f)); - } - return params; -} - -Q_GLOBAL_STATIC(QMutex, qMutex) -QString qMakePreparedStmtId() -{ - qMutex()->lock(); - static unsigned int qPreparedStmtCount = 0; - QString id = QLatin1String("qpsqlpstmt_") + QString::number(++qPreparedStmtCount, 16); - qMutex()->unlock(); - return id; -} - -bool QPSQLResult::prepare(const QString &query) -{ - Q_D(QPSQLResult); - if (!d->preparedQueriesEnabled) - return QSqlResult::prepare(query); - - cleanup(); - - if (!d->preparedStmtId.isEmpty()) - d->deallocatePreparedStmt(); - - const QString stmtId = qMakePreparedStmtId(); - const QString stmt = QString::fromLatin1("PREPARE %1 AS ").arg(stmtId).append(d->positionalToNamedBinding(query)); - - PGresult *result = d->drv_d_func()->exec(stmt); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) { - setLastError(qMakeError(QCoreApplication::translate("QPSQLResult", - "Unable to prepare statement"), QSqlError::StatementError, d->drv_d_func(), result)); - PQclear(result); - d->preparedStmtId.clear(); - return false; - } - - PQclear(result); - d->preparedStmtId = stmtId; - return true; -} - -bool QPSQLResult::exec() -{ - Q_D(QPSQLResult); - if (!d->preparedQueriesEnabled) - return QSqlResult::exec(); - - cleanup(); - - QString stmt; - const QString params = qCreateParamString(boundValues(), driver()); - if (params.isEmpty()) - stmt = QString::fromLatin1("EXECUTE %1").arg(d->preparedStmtId); - else - stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId).arg(params); - - d->result = d->drv_d_func()->exec(stmt); - - return d->processResults(); -} - -/////////////////////////////////////////////////////////////////// - -bool QPSQLDriverPrivate::setEncodingUtf8() -{ - PGresult* result = exec("SET CLIENT_ENCODING TO 'UNICODE'"); - int status = PQresultStatus(result); - PQclear(result); - return status == PGRES_COMMAND_OK; -} - -void QPSQLDriverPrivate::setDatestyle() -{ - PGresult* result = exec("SET DATESTYLE TO 'ISO'"); - int status = PQresultStatus(result); - if (status != PGRES_COMMAND_OK) - qWarning("%s", PQerrorMessage(connection)); - PQclear(result); -} - -void QPSQLDriverPrivate::detectBackslashEscape() -{ - // standard_conforming_strings option introduced in 8.2 - // http://www.postgresql.org/docs/8.2/static/runtime-config-compatible.html - if (pro < QPSQLDriver::Version82) { - hasBackslashEscape = true; - } else { - hasBackslashEscape = false; - PGresult* result = exec(QLatin1Literal("SELECT '\\\\' x")); - int status = PQresultStatus(result); - if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) - if (QString::fromLatin1(PQgetvalue(result, 0, 0)) == QLatin1Literal("\\")) - hasBackslashEscape = true; - PQclear(result); - } -} - -static QPSQLDriver::Protocol qMakePSQLVersion(int vMaj, int vMin) -{ - switch (vMaj) { - case 6: - return QPSQLDriver::Version6; - case 7: - { - switch (vMin) { - case 1: - return QPSQLDriver::Version71; - case 3: - return QPSQLDriver::Version73; - case 4: - return QPSQLDriver::Version74; - default: - return QPSQLDriver::Version7; - } - break; - } - case 8: - { - switch (vMin) { - case 1: - return QPSQLDriver::Version81; - case 2: - return QPSQLDriver::Version82; - case 3: - return QPSQLDriver::Version83; - case 4: - return QPSQLDriver::Version84; - default: - return QPSQLDriver::Version8; - } - break; - } - case 9: - return QPSQLDriver::Version9; - break; - default: - break; - } - return QPSQLDriver::VersionUnknown; -} - -QPSQLDriver::Protocol QPSQLDriverPrivate::getPSQLVersion() -{ - QPSQLDriver::Protocol serverVersion = QPSQLDriver::Version6; - PGresult* result = exec("select version()"); - int status = PQresultStatus(result); - if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) { - QString val = QString::fromLatin1(PQgetvalue(result, 0, 0)); - - QRegExp rx(QLatin1String("(\\d+)\\.(\\d+)")); - rx.setMinimal(true); // enforce non-greedy RegExp - - if (rx.indexIn(val) != -1) { - int vMaj = rx.cap(1).toInt(); - int vMin = rx.cap(2).toInt(); - serverVersion = qMakePSQLVersion(vMaj, vMin); -#if defined(PG_MAJORVERSION) - if (rx.indexIn(QLatin1String(PG_MAJORVERSION)) != -1) -#elif defined(PG_VERSION) - if (rx.indexIn(QLatin1String(PG_VERSION)) != -1) -#else - if (0) -#endif - { - vMaj = rx.cap(1).toInt(); - vMin = rx.cap(2).toInt(); - QPSQLDriver::Protocol clientVersion = qMakePSQLVersion(vMaj, vMin); - - if (serverVersion >= QPSQLDriver::Version9 && clientVersion < QPSQLDriver::Version9) { - //Client version before QPSQLDriver::Version9 only supports escape mode for bytea type, - //but bytea format is set to hex by default in PSQL 9 and above. So need to force the - //server use the old escape mode when connects to the new server with old client library. - PQclear(result); - result = exec("SET bytea_output=escape; "); - status = PQresultStatus(result); - } else if (serverVersion == QPSQLDriver::VersionUnknown) { - serverVersion = clientVersion; - if (serverVersion != QPSQLDriver::VersionUnknown) - qWarning("The server version of this PostgreSQL is unknown, falling back to the client version."); - } - } - } - } - PQclear(result); - - //keep the old behavior unchanged - if (serverVersion == QPSQLDriver::VersionUnknown) - serverVersion = QPSQLDriver::Version6; - - if (serverVersion < QPSQLDriver::Version71) { - qWarning("This version of PostgreSQL is not supported and may not work."); - } - - return serverVersion; -} - -QPSQLDriver::QPSQLDriver(QObject *parent) - : QSqlDriver(*new QPSQLDriverPrivate, parent) -{ -} - -QPSQLDriver::QPSQLDriver(PGconn *conn, QObject *parent) - : QSqlDriver(*new QPSQLDriverPrivate, parent) -{ - Q_D(QPSQLDriver); - d->connection = conn; - if (conn) { - d->pro = d->getPSQLVersion(); - d->detectBackslashEscape(); - setOpen(true); - setOpenError(false); - } -} - -QPSQLDriver::~QPSQLDriver() -{ - Q_D(QPSQLDriver); - if (d->connection) - PQfinish(d->connection); -} - -QVariant QPSQLDriver::handle() const -{ - Q_D(const QPSQLDriver); - return QVariant::fromValue(d->connection); -} - -bool QPSQLDriver::hasFeature(DriverFeature f) const -{ - Q_D(const QPSQLDriver); - switch (f) { - case Transactions: - case QuerySize: - case LastInsertId: - case LowPrecisionNumbers: - case EventNotifications: - return true; - case PreparedQueries: - case PositionalPlaceholders: - return d->pro >= QPSQLDriver::Version82; - case BatchOperations: - case NamedPlaceholders: - case SimpleLocking: - case FinishQuery: - case MultipleResultSets: - case CancelQuery: - return false; - case BLOB: - return d->pro >= QPSQLDriver::Version71; - case Unicode: - return d->isUtf8; - } - return false; -} - -/* - Quote a string for inclusion into the connection string - \ -> \\ - ' -> \' - surround string by single quotes - */ -static QString qQuote(QString s) -{ - s.replace(QLatin1Char('\\'), QLatin1String("\\\\")); - s.replace(QLatin1Char('\''), QLatin1String("\\'")); - s.append(QLatin1Char('\'')).prepend(QLatin1Char('\'')); - return s; -} - -bool QPSQLDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString& connOpts) -{ - Q_D(QPSQLDriver); - if (isOpen()) - close(); - QString connectString; - if (!host.isEmpty()) - connectString.append(QLatin1String("host=")).append(qQuote(host)); - if (!db.isEmpty()) - connectString.append(QLatin1String(" dbname=")).append(qQuote(db)); - if (!user.isEmpty()) - connectString.append(QLatin1String(" user=")).append(qQuote(user)); - if (!password.isEmpty()) - connectString.append(QLatin1String(" password=")).append(qQuote(password)); - if (port != -1) - connectString.append(QLatin1String(" port=")).append(qQuote(QString::number(port))); - - // add any connect options - the server will handle error detection - if (!connOpts.isEmpty()) { - QString opt = connOpts; - opt.replace(QLatin1Char(';'), QLatin1Char(' '), Qt::CaseInsensitive); - connectString.append(QLatin1Char(' ')).append(opt); - } - - d->connection = PQconnectdb(connectString.toLocal8Bit().constData()); - if (PQstatus(d->connection) == CONNECTION_BAD) { - setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); - setOpenError(true); - PQfinish(d->connection); - d->connection = 0; - return false; - } - - d->pro = d->getPSQLVersion(); - d->detectBackslashEscape(); - d->isUtf8 = d->setEncodingUtf8(); - d->setDatestyle(); - - setOpen(true); - setOpenError(false); - return true; -} - -void QPSQLDriver::close() -{ - Q_D(QPSQLDriver); - if (isOpen()) { - - d->seid.clear(); - if (d->sn) { - disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int))); - delete d->sn; - d->sn = 0; - } - - if (d->connection) - PQfinish(d->connection); - d->connection = 0; - setOpen(false); - setOpenError(false); - } -} - -QSqlResult *QPSQLDriver::createResult() const -{ - return new QPSQLResult(this); -} - -bool QPSQLDriver::beginTransaction() -{ - Q_D(const QPSQLDriver); - if (!isOpen()) { - qWarning("QPSQLDriver::beginTransaction: Database not open"); - return false; - } - PGresult* res = d->exec("BEGIN"); - if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { - setLastError(qMakeError(tr("Could not begin transaction"), - QSqlError::TransactionError, d, res)); - PQclear(res); - return false; - } - PQclear(res); - return true; -} - -bool QPSQLDriver::commitTransaction() -{ - Q_D(QPSQLDriver); - if (!isOpen()) { - qWarning("QPSQLDriver::commitTransaction: Database not open"); - return false; - } - PGresult* res = d->exec("COMMIT"); - - bool transaction_failed = false; - - // XXX - // This hack is used to tell if the transaction has succeeded for the protocol versions of - // PostgreSQL below. For 7.x and other protocol versions we are left in the dark. - // This hack can dissapear once there is an API to query this sort of information. - if (d->pro == QPSQLDriver::Version8 || - d->pro == QPSQLDriver::Version81 || - d->pro == QPSQLDriver::Version82 || - d->pro == QPSQLDriver::Version83 || - d->pro == QPSQLDriver::Version84 || - d->pro == QPSQLDriver::Version9) { - transaction_failed = qstrcmp(PQcmdStatus(res), "ROLLBACK") == 0; - } - - if (!res || PQresultStatus(res) != PGRES_COMMAND_OK || transaction_failed) { - setLastError(qMakeError(tr("Could not commit transaction"), - QSqlError::TransactionError, d, res)); - PQclear(res); - return false; - } - PQclear(res); - return true; -} - -bool QPSQLDriver::rollbackTransaction() -{ - Q_D(QPSQLDriver); - if (!isOpen()) { - qWarning("QPSQLDriver::rollbackTransaction: Database not open"); - return false; - } - PGresult* res = d->exec("ROLLBACK"); - if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { - setLastError(qMakeError(tr("Could not rollback transaction"), - QSqlError::TransactionError, d, res)); - PQclear(res); - return false; - } - PQclear(res); - return true; -} - -QStringList QPSQLDriver::tables(QSql::TableType type) const -{ - Q_D(const QPSQLDriver); - QStringList tl; - if (!isOpen()) - return tl; - QSqlQuery t(createResult()); - t.setForwardOnly(true); - - if (type & QSql::Tables) - const_cast(d)->appendTables(tl, t, QLatin1Char('r')); - if (type & QSql::Views) - const_cast(d)->appendTables(tl, t, QLatin1Char('v')); - if (type & QSql::SystemTables) { - t.exec(QLatin1String("select relname from pg_class where (relkind = 'r') " - "and (relname like 'pg_%') ")); - while (t.next()) - tl.append(t.value(0).toString()); - } - - return tl; -} - -static void qSplitTableName(QString &tablename, QString &schema) -{ - int dot = tablename.indexOf(QLatin1Char('.')); - if (dot == -1) - return; - schema = tablename.left(dot); - tablename = tablename.mid(dot + 1); -} - -QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const -{ - Q_D(const QPSQLDriver); - QSqlIndex idx(tablename); - if (!isOpen()) - return idx; - QSqlQuery i(createResult()); - QString stmt; - - QString tbl = tablename; - QString schema; - qSplitTableName(tbl, schema); - - if (isIdentifierEscaped(tbl, QSqlDriver::TableName)) - tbl = stripDelimiters(tbl, QSqlDriver::TableName); - else - tbl = tbl.toLower(); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = schema.toLower(); - - switch(d->pro) { - case QPSQLDriver::Version6: - stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname " - "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where pg_cl.relname = '%1_pkey' " - "and pg_cl.oid = pg_ind.indexrelid " - "and pg_att2.attrelid = pg_ind.indexrelid " - "and pg_att1.attrelid = pg_ind.indrelid " - "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " - "order by pg_att2.attnum"); - break; - case QPSQLDriver::Version7: - case QPSQLDriver::Version71: - stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " - "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where pg_cl.relname = '%1_pkey' " - "and pg_cl.oid = pg_ind.indexrelid " - "and pg_att2.attrelid = pg_ind.indexrelid " - "and pg_att1.attrelid = pg_ind.indrelid " - "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " - "order by pg_att2.attnum"); - break; - case QPSQLDriver::Version73: - case QPSQLDriver::Version74: - case QPSQLDriver::Version8: - case QPSQLDriver::Version81: - case QPSQLDriver::Version82: - case QPSQLDriver::Version83: - case QPSQLDriver::Version84: - case QPSQLDriver::Version9: - stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, " - "pg_class.relname " - "FROM pg_attribute, pg_class " - "WHERE %1 pg_class.oid IN " - "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN " - " (SELECT oid FROM pg_class WHERE relname = '%2')) " - "AND pg_attribute.attrelid = pg_class.oid " - "AND pg_attribute.attisdropped = false " - "ORDER BY pg_attribute.attnum"); - if (schema.isEmpty()) - stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND")); - else - stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " - "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema)); - break; - case QPSQLDriver::VersionUnknown: - qFatal("PSQL version is unknown"); - break; - } - - i.exec(stmt.arg(tbl)); - while (i.isActive() && i.next()) { - QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt())); - idx.append(f); - idx.setName(i.value(2).toString()); - } - return idx; -} - -QSqlRecord QPSQLDriver::record(const QString& tablename) const -{ - Q_D(const QPSQLDriver); - QSqlRecord info; - if (!isOpen()) - return info; - - QString tbl = tablename; - QString schema; - qSplitTableName(tbl, schema); - - if (isIdentifierEscaped(tbl, QSqlDriver::TableName)) - tbl = stripDelimiters(tbl, QSqlDriver::TableName); - else - tbl = tbl.toLower(); - - if (isIdentifierEscaped(schema, QSqlDriver::TableName)) - schema = stripDelimiters(schema, QSqlDriver::TableName); - else - schema = schema.toLower(); - - QString stmt; - switch(d->pro) { - case QPSQLDriver::Version6: - stmt = QLatin1String("select pg_attribute.attname, int(pg_attribute.atttypid), " - "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " - "int(pg_attribute.attrelid), pg_attribute.attnum " - "from pg_class, pg_attribute " - "where pg_class.relname = '%1' " - "and pg_attribute.attnum > 0 " - "and pg_attribute.attrelid = pg_class.oid "); - break; - case QPSQLDriver::Version7: - stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, " - "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " - "pg_attribute.attrelid::int, pg_attribute.attnum " - "from pg_class, pg_attribute " - "where pg_class.relname = '%1' " - "and pg_attribute.attnum > 0 " - "and pg_attribute.attrelid = pg_class.oid "); - break; - case QPSQLDriver::Version71: - stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, " - "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " - "pg_attrdef.adsrc " - "from pg_class, pg_attribute " - "left join pg_attrdef on (pg_attrdef.adrelid = " - "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " - "where pg_class.relname = '%1' " - "and pg_attribute.attnum > 0 " - "and pg_attribute.attrelid = pg_class.oid " - "order by pg_attribute.attnum "); - break; - case QPSQLDriver::Version73: - case QPSQLDriver::Version74: - case QPSQLDriver::Version8: - case QPSQLDriver::Version81: - case QPSQLDriver::Version82: - case QPSQLDriver::Version83: - case QPSQLDriver::Version84: - case QPSQLDriver::Version9: - stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, " - "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " - "pg_attrdef.adsrc " - "from pg_class, pg_attribute " - "left join pg_attrdef on (pg_attrdef.adrelid = " - "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " - "where %1 " - "and pg_class.relname = '%2' " - "and pg_attribute.attnum > 0 " - "and pg_attribute.attrelid = pg_class.oid " - "and pg_attribute.attisdropped = false " - "order by pg_attribute.attnum "); - if (schema.isEmpty()) - stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)")); - else - stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " - "pg_namespace where pg_namespace.nspname = '%1')").arg(schema)); - break; - case QPSQLDriver::VersionUnknown: - qFatal("PSQL version is unknown"); - break; - } - - QSqlQuery query(createResult()); - query.exec(stmt.arg(tbl)); - if (d->pro >= QPSQLDriver::Version71) { - while (query.next()) { - int len = query.value(3).toInt(); - int precision = query.value(4).toInt(); - // swap length and precision if length == -1 - if (len == -1 && precision > -1) { - len = precision - 4; - precision = -1; - } - QString defVal = query.value(5).toString(); - if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\'')) - defVal = defVal.mid(1, defVal.length() - 2); - QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt())); - f.setRequired(query.value(2).toBool()); - f.setLength(len); - f.setPrecision(precision); - f.setDefaultValue(defVal); - f.setSqlType(query.value(1).toInt()); - info.append(f); - } - } else { - // Postgres < 7.1 cannot handle outer joins - while (query.next()) { - QString defVal; - QString stmt2 = QLatin1String("select pg_attrdef.adsrc from pg_attrdef where " - "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 "); - QSqlQuery query2(createResult()); - query2.exec(stmt2.arg(query.value(5).toInt()).arg(query.value(6).toInt())); - if (query2.isActive() && query2.next()) - defVal = query2.value(0).toString(); - if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\'')) - defVal = defVal.mid(1, defVal.length() - 2); - int len = query.value(3).toInt(); - int precision = query.value(4).toInt(); - // swap length and precision if length == -1 - if (len == -1 && precision > -1) { - len = precision - 4; - precision = -1; - } - QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt())); - f.setRequired(query.value(2).toBool()); - f.setLength(len); - f.setPrecision(precision); - f.setDefaultValue(defVal); - f.setSqlType(query.value(1).toInt()); - info.append(f); - } - } - - return info; -} - -template -inline void assignSpecialPsqlFloatValue(FloatType val, QString *target) -{ - if (isnan(val)) { - *target = QLatin1String("'NaN'"); - } else { - switch (isinf(val)) { - case 1: - *target = QLatin1String("'Infinity'"); - break; - case -1: - *target = QLatin1String("'-Infinity'"); - break; - } - } -} - -QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const -{ - Q_D(const QPSQLDriver); - QString r; - if (field.isNull()) { - r = QLatin1String("NULL"); - } else { - switch (int(field.type())) { - case QVariant::DateTime: -#ifndef QT_NO_DATESTRING - if (field.value().toDateTime().isValid()) { - // we force the value to be considered with a timezone information, and we force it to be UTC - // this is safe since postgresql stores only the UTC value and not the timezone offset (only used - // while parsing), so we have correct behavior in both case of with timezone and without tz - r = QLatin1String("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') + field.value().toDateTime().toUTC().toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz")) + QLatin1Char('Z') + QLatin1Char('\''); - } else { - r = QLatin1String("NULL"); - } -#else - r = QLatin1String("NULL"); -#endif // QT_NO_DATESTRING - break; - case QVariant::Time: -#ifndef QT_NO_DATESTRING - if (field.value().toTime().isValid()) { - r = QLatin1Char('\'') + field.value().toTime().toString(QLatin1String("hh:mm:ss.zzz")) + QLatin1Char('\''); - } else -#endif - { - r = QLatin1String("NULL"); - } - break; - case QVariant::String: - r = QSqlDriver::formatValue(field, trimStrings); - if (d->hasBackslashEscape) - r.replace(QLatin1String("\\"), QLatin1String("\\\\")); - break; - case QVariant::Bool: - if (field.value().toBool()) - r = QLatin1String("TRUE"); - else - r = QLatin1String("FALSE"); - break; - case QVariant::ByteArray: { - QByteArray ba(field.value().toByteArray()); - size_t len; -#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 80200 - unsigned char *data = PQescapeByteaConn(d->connection, (const unsigned char*)ba.constData(), ba.size(), &len); -#else - unsigned char *data = PQescapeBytea((const unsigned char*)ba.constData(), ba.size(), &len); -#endif - r += QLatin1Char('\''); - r += QLatin1String((const char*)data); - r += QLatin1Char('\''); - qPQfreemem(data); - break; - } - case QMetaType::Float: - assignSpecialPsqlFloatValue(field.value().toFloat(), &r); - if (r.isEmpty()) - r = QSqlDriver::formatValue(field, trimStrings); - break; - case QVariant::Double: - assignSpecialPsqlFloatValue(field.value().toDouble(), &r); - if (r.isEmpty()) - r = QSqlDriver::formatValue(field, trimStrings); - break; - case QVariant::Uuid: - r = QLatin1Char('\'') + field.value().toString() + QLatin1Char('\''); - break; - default: - r = QSqlDriver::formatValue(field, trimStrings); - break; - } - } - return r; -} - -QString QPSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -bool QPSQLDriver::isOpen() const -{ - Q_D(const QPSQLDriver); - return PQstatus(d->connection) == CONNECTION_OK; -} - -QPSQLDriver::Protocol QPSQLDriver::protocol() const -{ - Q_D(const QPSQLDriver); - return d->pro; -} - -bool QPSQLDriver::subscribeToNotification(const QString &name) -{ - Q_D(QPSQLDriver); - if (!isOpen()) { - qWarning("QPSQLDriver::subscribeToNotificationImplementation: database not open."); - return false; - } - - if (d->seid.contains(name)) { - qWarning("QPSQLDriver::subscribeToNotificationImplementation: already subscribing to '%s'.", - qPrintable(name)); - return false; - } - - int socket = PQsocket(d->connection); - if (socket) { - // Add the name to the list of subscriptions here so that QSQLDriverPrivate::exec knows - // to check for notifications immediately after executing the LISTEN - d->seid << name; - QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName); - PGresult *result = d->exec(query); - if (PQresultStatus(result) != PGRES_COMMAND_OK) { - setLastError(qMakeError(tr("Unable to subscribe"), QSqlError::StatementError, d, result)); - PQclear(result); - return false; - } - PQclear(result); - - if (!d->sn) { - d->sn = new QSocketNotifier(socket, QSocketNotifier::Read); - connect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int))); - } - } else { - qWarning("QPSQLDriver::subscribeToNotificationImplementation: PQsocket didn't return a valid socket to listen on"); - return false; - } - - return true; -} - -bool QPSQLDriver::unsubscribeFromNotification(const QString &name) -{ - Q_D(QPSQLDriver); - if (!isOpen()) { - qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: database not open."); - return false; - } - - if (!d->seid.contains(name)) { - qWarning("QPSQLDriver::unsubscribeFromNotificationImplementation: not subscribed to '%s'.", - qPrintable(name)); - return false; - } - - QString query = QLatin1String("UNLISTEN ") + escapeIdentifier(name, QSqlDriver::TableName); - PGresult *result = d->exec(query); - if (PQresultStatus(result) != PGRES_COMMAND_OK) { - setLastError(qMakeError(tr("Unable to unsubscribe"), QSqlError::StatementError, d, result)); - PQclear(result); - return false; - } - PQclear(result); - - d->seid.removeAll(name); - - if (d->seid.isEmpty()) { - disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int))); - delete d->sn; - d->sn = 0; - } - - return true; -} - -QStringList QPSQLDriver::subscribedToNotifications() const -{ - Q_D(const QPSQLDriver); - return d->seid; -} - -void QPSQLDriver::_q_handleNotification(int) -{ - Q_D(QPSQLDriver); - d->pendingNotifyCheck = false; - PQconsumeInput(d->connection); - - PGnotify *notify = 0; - while((notify = PQnotifies(d->connection)) != 0) { - QString name(QLatin1String(notify->relname)); - if (d->seid.contains(name)) { - QString payload; -#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 70400 - if (notify->extra) - payload = d->isUtf8 ? QString::fromUtf8(notify->extra) : QString::fromLatin1(notify->extra); -#endif - emit notification(name); - QSqlDriver::NotificationSource source = (notify->be_pid == PQbackendPID(d->connection)) ? QSqlDriver::SelfSource : QSqlDriver::OtherSource; - emit notification(name, source, payload); - } - else - qWarning("QPSQLDriver: received notification for '%s' which isn't subscribed to.", - qPrintable(name)); - - qPQfreemem(notify); - } -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/psql/qsql_psql.pri b/src/sql/drivers/psql/qsql_psql.pri deleted file mode 100644 index 867be3edb8..0000000000 --- a/src/sql/drivers/psql/qsql_psql.pri +++ /dev/null @@ -1,10 +0,0 @@ -HEADERS += $$PWD/qsql_psql_p.h -SOURCES += $$PWD/qsql_psql.cpp - -unix|mingw { - LIBS += $$QT_LFLAGS_PSQL - !contains(LIBS, .*pq.*):LIBS += -lpq - QMAKE_CXXFLAGS *= $$QT_CFLAGS_PSQL -} else { - !contains(LIBS, .*pq.*):LIBS += -llibpq -lws2_32 -ladvapi32 -} diff --git a/src/sql/drivers/psql/qsql_psql_p.h b/src/sql/drivers/psql/qsql_psql_p.h deleted file mode 100644 index 8468b9af93..0000000000 --- a/src/sql/drivers/psql/qsql_psql_p.h +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** 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_PSQL_H -#define QSQL_PSQL_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 - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_PSQL -#else -#define Q_EXPORT_SQLDRIVER_PSQL Q_SQL_EXPORT -#endif - -typedef struct pg_conn PGconn; -typedef struct pg_result PGresult; - -QT_BEGIN_NAMESPACE - -class QPSQLDriverPrivate; - -class Q_EXPORT_SQLDRIVER_PSQL QPSQLDriver : public QSqlDriver -{ - friend class QPSQLResultPrivate; - Q_DECLARE_PRIVATE(QPSQLDriver) - Q_OBJECT -public: - enum Protocol { - VersionUnknown = -1, - Version6 = 6, - Version7 = 7, - Version71 = 8, - Version73 = 9, - Version74 = 10, - Version8 = 11, - Version81 = 12, - Version82 = 13, - Version83 = 14, - Version84 = 15, - Version9 = 16 - }; - - explicit QPSQLDriver(QObject *parent=0); - explicit QPSQLDriver(PGconn *conn, QObject *parent=0); - ~QPSQLDriver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - bool open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString& connOpts) Q_DECL_OVERRIDE; - bool isOpen() const Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE; - QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE; - - Protocol protocol() const; - QVariant handle() const Q_DECL_OVERRIDE; - - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE; - - bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE; - bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE; - QStringList subscribedToNotifications() const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - -private Q_SLOTS: - void _q_handleNotification(int); -}; - -QT_END_NAMESPACE - -#endif // QSQL_PSQL_H diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp deleted file mode 100644 index 2a45b73d14..0000000000 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ /dev/null @@ -1,828 +0,0 @@ -/**************************************************************************** -** -** 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_sqlite_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined Q_OS_WIN -# include -#else -# include -#endif - -#include - -Q_DECLARE_OPAQUE_POINTER(sqlite3*) -Q_DECLARE_METATYPE(sqlite3*) - -Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) -Q_DECLARE_METATYPE(sqlite3_stmt*) - -QT_BEGIN_NAMESPACE - -static QString _q_escapeIdentifier(const QString &identifier) -{ - QString res = identifier; - if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -static QVariant::Type qGetColumnType(const QString &tpName) -{ - const QString typeName = tpName.toLower(); - - if (typeName == QLatin1String("integer") - || typeName == QLatin1String("int")) - return QVariant::Int; - if (typeName == QLatin1String("double") - || typeName == QLatin1String("float") - || typeName == QLatin1String("real") - || typeName.startsWith(QLatin1String("numeric"))) - return QVariant::Double; - if (typeName == QLatin1String("blob")) - return QVariant::ByteArray; - if (typeName == QLatin1String("boolean") - || typeName == QLatin1String("bool")) - return QVariant::Bool; - return QVariant::String; -} - -static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, - int errorCode = -1) -{ - return QSqlError(descr, - QString(reinterpret_cast(sqlite3_errmsg16(access))), - type, QString::number(errorCode)); -} - -class QSQLiteResultPrivate; - -class QSQLiteResult : public QSqlCachedResult -{ - Q_DECLARE_PRIVATE(QSQLiteResult) - friend class QSQLiteDriver; - -public: - explicit QSQLiteResult(const QSQLiteDriver* db); - ~QSQLiteResult(); - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - bool prepare(const QString &query) Q_DECL_OVERRIDE; - bool exec() 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 detachFromResultSet() Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; -}; - -class QSQLiteDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QSQLiteDriver) - -public: - inline QSQLiteDriverPrivate() : QSqlDriverPrivate(), access(0) { dbmsType = QSqlDriver::SQLite; } - sqlite3 *access; - QList results; -}; - - -class QSQLiteResultPrivate: public QSqlCachedResultPrivate -{ - Q_DECLARE_PUBLIC(QSQLiteResult) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QSQLiteDriver) - QSQLiteResultPrivate(QSQLiteResult *q, const QSQLiteDriver *drv); - void cleanup(); - bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); - // initializes the recordInfo and the cache - void initColumns(bool emptyResultset); - void finalize(); - - sqlite3_stmt *stmt; - - bool skippedStatus; // the status of the fetchNext() that's skipped - bool skipRow; // skip the next fetchNext()? - QSqlRecord rInf; - QVector firstRow; -}; - -QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult *q, const QSQLiteDriver *drv) - : QSqlCachedResultPrivate(q, drv), - stmt(0), - skippedStatus(false), - skipRow(false) -{ -} - -void QSQLiteResultPrivate::cleanup() -{ - Q_Q(QSQLiteResult); - finalize(); - rInf.clear(); - skippedStatus = false; - skipRow = false; - q->setAt(QSql::BeforeFirstRow); - q->setActive(false); - q->cleanup(); -} - -void QSQLiteResultPrivate::finalize() -{ - if (!stmt) - return; - - sqlite3_finalize(stmt); - stmt = 0; -} - -void QSQLiteResultPrivate::initColumns(bool emptyResultset) -{ - Q_Q(QSQLiteResult); - int nCols = sqlite3_column_count(stmt); - if (nCols <= 0) - return; - - q->init(nCols); - - for (int i = 0; i < nCols; ++i) { - QString colName = QString(reinterpret_cast( - sqlite3_column_name16(stmt, i)) - ).remove(QLatin1Char('"')); - - // must use typeName for resolving the type to match QSqliteDriver::record - QString typeName = QString(reinterpret_cast( - sqlite3_column_decltype16(stmt, i))); - // sqlite3_column_type is documented to have undefined behavior if the result set is empty - int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); - - QVariant::Type fieldType; - - if (!typeName.isEmpty()) { - fieldType = qGetColumnType(typeName); - } else { - // Get the proper type for the field based on stp value - switch (stp) { - case SQLITE_INTEGER: - fieldType = QVariant::Int; - break; - case SQLITE_FLOAT: - fieldType = QVariant::Double; - break; - case SQLITE_BLOB: - fieldType = QVariant::ByteArray; - break; - case SQLITE_TEXT: - fieldType = QVariant::String; - break; - case SQLITE_NULL: - default: - fieldType = QVariant::Invalid; - break; - } - } - - QSqlField fld(colName, fieldType); - fld.setSqlType(stp); - rInf.append(fld); - } -} - -bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) -{ - Q_Q(QSQLiteResult); - int res; - int i; - - if (skipRow) { - // already fetched - Q_ASSERT(!initialFetch); - skipRow = false; - for(int i=0;isetLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), - QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError)); - q->setAt(QSql::AfterLastRow); - return false; - } - res = sqlite3_step(stmt); - - switch(res) { - case SQLITE_ROW: - // check to see if should fill out columns - if (rInf.isEmpty()) - // must be first call. - initColumns(false); - if (idx < 0 && !initialFetch) - return true; - for (i = 0; i < rInf.count(); ++i) { - switch (sqlite3_column_type(stmt, i)) { - case SQLITE_BLOB: - values[i + idx] = QByteArray(static_cast( - sqlite3_column_blob(stmt, i)), - sqlite3_column_bytes(stmt, i)); - break; - case SQLITE_INTEGER: - values[i + idx] = sqlite3_column_int64(stmt, i); - break; - case SQLITE_FLOAT: - switch(q->numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - values[i + idx] = sqlite3_column_int(stmt, i); - break; - case QSql::LowPrecisionInt64: - values[i + idx] = sqlite3_column_int64(stmt, i); - break; - case QSql::LowPrecisionDouble: - case QSql::HighPrecision: - default: - values[i + idx] = sqlite3_column_double(stmt, i); - break; - }; - break; - case SQLITE_NULL: - values[i + idx] = QVariant(QVariant::String); - break; - default: - values[i + idx] = QString(reinterpret_cast( - sqlite3_column_text16(stmt, i)), - sqlite3_column_bytes16(stmt, i) / sizeof(QChar)); - break; - } - } - return true; - case SQLITE_DONE: - if (rInf.isEmpty()) - // must be first call. - initColumns(true); - q->setAt(QSql::AfterLastRow); - sqlite3_reset(stmt); - return false; - case SQLITE_CONSTRAINT: - case SQLITE_ERROR: - // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() - // to get the specific error message. - res = sqlite3_reset(stmt); - q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to fetch row"), QSqlError::ConnectionError, res)); - q->setAt(QSql::AfterLastRow); - return false; - case SQLITE_MISUSE: - case SQLITE_BUSY: - default: - // something wrong, don't get col info, but still return false - q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to fetch row"), QSqlError::ConnectionError, res)); - sqlite3_reset(stmt); - q->setAt(QSql::AfterLastRow); - return false; - } - return false; -} - -QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) - : QSqlCachedResult(*new QSQLiteResultPrivate(this, db)) -{ - Q_D(QSQLiteResult); - const_cast(d->drv_d_func())->results.append(this); -} - -QSQLiteResult::~QSQLiteResult() -{ - Q_D(QSQLiteResult); - if (d->drv_d_func()) - const_cast(d->drv_d_func())->results.removeOne(this); - d->cleanup(); -} - -void QSQLiteResult::virtual_hook(int id, void *data) -{ - QSqlCachedResult::virtual_hook(id, data); -} - -bool QSQLiteResult::reset(const QString &query) -{ - if (!prepare(query)) - return false; - return exec(); -} - -bool QSQLiteResult::prepare(const QString &query) -{ - Q_D(QSQLiteResult); - if (!driver() || !driver()->isOpen() || driver()->isOpenError()) - return false; - - d->cleanup(); - - setSelect(false); - - const void *pzTail = NULL; - -#if (SQLITE_VERSION_NUMBER >= 3003011) - int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, &pzTail); -#else - int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, &pzTail); -#endif - - if (res != SQLITE_OK) { - setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to execute statement"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } else if (pzTail && !QString(reinterpret_cast(pzTail)).trimmed().isEmpty()) { - setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); - d->finalize(); - return false; - } - return true; -} - -bool QSQLiteResult::exec() -{ - Q_D(QSQLiteResult); - const QVector values = boundValues(); - - d->skippedStatus = false; - d->skipRow = false; - d->rInf.clear(); - clearValues(); - setLastError(QSqlError()); - - int res = sqlite3_reset(d->stmt); - if (res != SQLITE_OK) { - setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to reset statement"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } - int paramCount = sqlite3_bind_parameter_count(d->stmt); - if (paramCount == values.count()) { - for (int i = 0; i < paramCount; ++i) { - res = SQLITE_OK; - const QVariant value = values.at(i); - - if (value.isNull()) { - res = sqlite3_bind_null(d->stmt, i + 1); - } else { - switch (value.type()) { - case QVariant::ByteArray: { - const QByteArray *ba = static_cast(value.constData()); - res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(), - ba->size(), SQLITE_STATIC); - break; } - case QVariant::Int: - case QVariant::Bool: - res = sqlite3_bind_int(d->stmt, i + 1, value.toInt()); - break; - case QVariant::Double: - res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble()); - break; - case QVariant::UInt: - case QVariant::LongLong: - res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong()); - break; - case QVariant::DateTime: { - const QDateTime dateTime = value.toDateTime(); - const QString str = dateTime.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz")); - res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), - str.size() * sizeof(ushort), SQLITE_TRANSIENT); - break; - } - case QVariant::Time: { - const QTime time = value.toTime(); - const QString str = time.toString(QStringLiteral("hh:mm:ss.zzz")); - res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), - str.size() * sizeof(ushort), SQLITE_TRANSIENT); - break; - } - case QVariant::String: { - // lifetime of string == lifetime of its qvariant - const QString *str = static_cast(value.constData()); - res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(), - (str->size()) * sizeof(QChar), SQLITE_STATIC); - break; } - default: { - QString str = value.toString(); - // SQLITE_TRANSIENT makes sure that sqlite buffers the data - res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), - (str.size()) * sizeof(QChar), SQLITE_TRANSIENT); - break; } - } - } - if (res != SQLITE_OK) { - setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", - "Unable to bind parameters"), QSqlError::StatementError, res)); - d->finalize(); - return false; - } - } - } else { - setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", - "Parameter count mismatch"), QString(), QSqlError::StatementError)); - return false; - } - d->skippedStatus = d->fetchNext(d->firstRow, 0, true); - if (lastError().isValid()) { - setSelect(false); - setActive(false); - return false; - } - setSelect(!d->rInf.isEmpty()); - setActive(true); - return true; -} - -bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) -{ - Q_D(QSQLiteResult); - return d->fetchNext(row, idx, false); -} - -int QSQLiteResult::size() -{ - return -1; -} - -int QSQLiteResult::numRowsAffected() -{ - Q_D(const QSQLiteResult); - return sqlite3_changes(d->drv_d_func()->access); -} - -QVariant QSQLiteResult::lastInsertId() const -{ - Q_D(const QSQLiteResult); - if (isActive()) { - qint64 id = sqlite3_last_insert_rowid(d->drv_d_func()->access); - if (id) - return id; - } - return QVariant(); -} - -QSqlRecord QSQLiteResult::record() const -{ - Q_D(const QSQLiteResult); - if (!isActive() || !isSelect()) - return QSqlRecord(); - return d->rInf; -} - -void QSQLiteResult::detachFromResultSet() -{ - Q_D(QSQLiteResult); - if (d->stmt) - sqlite3_reset(d->stmt); -} - -QVariant QSQLiteResult::handle() const -{ - Q_D(const QSQLiteResult); - return QVariant::fromValue(d->stmt); -} - -///////////////////////////////////////////////////////// - -QSQLiteDriver::QSQLiteDriver(QObject * parent) - : QSqlDriver(*new QSQLiteDriverPrivate, parent) -{ -} - -QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) - : QSqlDriver(*new QSQLiteDriverPrivate, parent) -{ - Q_D(QSQLiteDriver); - d->access = connection; - setOpen(true); - setOpenError(false); -} - - -QSQLiteDriver::~QSQLiteDriver() -{ -} - -bool QSQLiteDriver::hasFeature(DriverFeature f) const -{ - switch (f) { - case BLOB: - case Transactions: - case Unicode: - case LastInsertId: - case PreparedQueries: - case PositionalPlaceholders: - case SimpleLocking: - case FinishQuery: - case LowPrecisionNumbers: - return true; - case QuerySize: - case NamedPlaceholders: - case BatchOperations: - case EventNotifications: - case MultipleResultSets: - case CancelQuery: - return false; - } - return false; -} - -/* - SQLite dbs have no user name, passwords, hosts or ports. - just file names. -*/ -bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) -{ - Q_D(QSQLiteDriver); - if (isOpen()) - close(); - - - int timeOut = 5000; - bool sharedCache = false; - bool openReadOnlyOption = false; - bool openUriOption = false; - - const auto opts = conOpts.splitRef(QLatin1Char(';')); - for (auto option : opts) { - option = option.trimmed(); - if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT"))) { - option = option.mid(20).trimmed(); - if (option.startsWith(QLatin1Char('='))) { - bool ok; - const int nt = option.mid(1).trimmed().toInt(&ok); - if (ok) - timeOut = nt; - } - } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) { - openReadOnlyOption = true; - } else if (option == QLatin1String("QSQLITE_OPEN_URI")) { - openUriOption = true; - } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) { - sharedCache = true; - } - } - - int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); - if (openUriOption) - openMode |= SQLITE_OPEN_URI; - - sqlite3_enable_shared_cache(sharedCache); - - if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) { - sqlite3_busy_timeout(d->access, timeOut); - setOpen(true); - setOpenError(false); - return true; - } else { - if (d->access) { - sqlite3_close(d->access); - d->access = 0; - } - - setLastError(qMakeError(d->access, tr("Error opening database"), - QSqlError::ConnectionError)); - setOpenError(true); - return false; - } -} - -void QSQLiteDriver::close() -{ - Q_D(QSQLiteDriver); - if (isOpen()) { - for (QSQLiteResult *result : qAsConst(d->results)) - result->d_func()->finalize(); - - if (sqlite3_close(d->access) != SQLITE_OK) - setLastError(qMakeError(d->access, tr("Error closing database"), - QSqlError::ConnectionError)); - d->access = 0; - setOpen(false); - setOpenError(false); - } -} - -QSqlResult *QSQLiteDriver::createResult() const -{ - return new QSQLiteResult(this); -} - -bool QSQLiteDriver::beginTransaction() -{ - if (!isOpen() || isOpenError()) - return false; - - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("BEGIN"))) { - setLastError(QSqlError(tr("Unable to begin transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } - - return true; -} - -bool QSQLiteDriver::commitTransaction() -{ - if (!isOpen() || isOpenError()) - return false; - - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("COMMIT"))) { - setLastError(QSqlError(tr("Unable to commit transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } - - return true; -} - -bool QSQLiteDriver::rollbackTransaction() -{ - if (!isOpen() || isOpenError()) - return false; - - QSqlQuery q(createResult()); - if (!q.exec(QLatin1String("ROLLBACK"))) { - setLastError(QSqlError(tr("Unable to rollback transaction"), - q.lastError().databaseText(), QSqlError::TransactionError)); - return false; - } - - return true; -} - -QStringList QSQLiteDriver::tables(QSql::TableType type) const -{ - QStringList res; - if (!isOpen()) - return res; - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - - QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " - "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); - if ((type & QSql::Tables) && (type & QSql::Views)) - sql = sql.arg(QLatin1String("type='table' OR type='view'")); - else if (type & QSql::Tables) - sql = sql.arg(QLatin1String("type='table'")); - else if (type & QSql::Views) - sql = sql.arg(QLatin1String("type='view'")); - else - sql.clear(); - - if (!sql.isEmpty() && q.exec(sql)) { - while(q.next()) - res.append(q.value(0).toString()); - } - - if (type & QSql::SystemTables) { - // there are no internal tables beside this one: - res.append(QLatin1String("sqlite_master")); - } - - return res; -} - -static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) -{ - QString schema; - QString table(tableName); - int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); - if (indexOfSeparator > -1) { - schema = tableName.left(indexOfSeparator).append(QLatin1Char('.')); - table = tableName.mid(indexOfSeparator + 1); - } - q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1Char(')')); - - QSqlIndex ind; - while (q.next()) { - bool isPk = q.value(5).toInt(); - if (onlyPIndex && !isPk) - continue; - QString typeName = q.value(2).toString().toLower(); - QSqlField fld(q.value(1).toString(), qGetColumnType(typeName)); - if (isPk && (typeName == QLatin1String("integer"))) - // INTEGER PRIMARY KEY fields are auto-generated in sqlite - // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! - fld.setAutoValue(true); - fld.setRequired(q.value(3).toInt() != 0); - fld.setDefaultValue(q.value(4)); - ind.append(fld); - } - return ind; -} - -QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const -{ - if (!isOpen()) - return QSqlIndex(); - - QString table = tblname; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - return qGetTableInfo(q, table, true); -} - -QSqlRecord QSQLiteDriver::record(const QString &tbl) const -{ - if (!isOpen()) - return QSqlRecord(); - - QString table = tbl; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - return qGetTableInfo(q, table); -} - -QVariant QSQLiteDriver::handle() const -{ - Q_D(const QSQLiteDriver); - return QVariant::fromValue(d->access); -} - -QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const -{ - Q_UNUSED(type); - return _q_escapeIdentifier(identifier); -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/sqlite/qsql_sqlite.pri b/src/sql/drivers/sqlite/qsql_sqlite.pri deleted file mode 100644 index e323f2eba5..0000000000 --- a/src/sql/drivers/sqlite/qsql_sqlite.pri +++ /dev/null @@ -1,9 +0,0 @@ -HEADERS += $$PWD/qsql_sqlite_p.h -SOURCES += $$PWD/qsql_sqlite.cpp - -!system-sqlite:!contains(LIBS, .*sqlite3.*) { - include($$PWD/../../../3rdparty/sqlite.pri) -} else { - LIBS += $$QT_LFLAGS_SQLITE - QMAKE_CXXFLAGS *= $$QT_CFLAGS_SQLITE -} diff --git a/src/sql/drivers/sqlite/qsql_sqlite_p.h b/src/sql/drivers/sqlite/qsql_sqlite_p.h deleted file mode 100644 index c9b7708698..0000000000 --- a/src/sql/drivers/sqlite/qsql_sqlite_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** 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_SQLITE_H -#define QSQL_SQLITE_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 - -struct sqlite3; - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_SQLITE -#else -#define Q_EXPORT_SQLDRIVER_SQLITE Q_SQL_EXPORT -#endif - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QSQLiteDriverPrivate; - -class Q_EXPORT_SQLDRIVER_SQLITE QSQLiteDriver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QSQLiteDriver) - Q_OBJECT - friend class QSQLiteResultPrivate; -public: - explicit QSQLiteDriver(QObject *parent = 0); - explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0); - ~QSQLiteDriver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - bool open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int port, - const QString & connOpts) Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - - QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE; -}; - -QT_END_NAMESPACE - -#endif // QSQL_SQLITE_H diff --git a/src/sql/drivers/sqlite2/qsql_sqlite2.cpp b/src/sql/drivers/sqlite2/qsql_sqlite2.cpp deleted file mode 100644 index 67c24e4168..0000000000 --- a/src/sql/drivers/sqlite2/qsql_sqlite2.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/**************************************************************************** -** -** 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_sqlite2_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined Q_OS_WIN -# include -#endif -#include - -typedef struct sqlite_vm sqlite_vm; - -Q_DECLARE_OPAQUE_POINTER(sqlite_vm*) -Q_DECLARE_METATYPE(sqlite_vm*) - -Q_DECLARE_OPAQUE_POINTER(sqlite*) -Q_DECLARE_METATYPE(sqlite*) - -QT_BEGIN_NAMESPACE - -static QVariant::Type nameToType(const QString& typeName) -{ - QString tName = typeName.toUpper(); - if (tName.startsWith(QLatin1String("INT"))) - return QVariant::Int; - if (tName.startsWith(QLatin1String("FLOAT")) || tName.startsWith(QLatin1String("NUMERIC"))) - return QVariant::Double; - if (tName.startsWith(QLatin1String("BOOL"))) - return QVariant::Bool; - // SQLite is typeless - consider everything else as string - return QVariant::String; -} - -class QSQLite2DriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QSQLite2Driver) - -public: - QSQLite2DriverPrivate(); - sqlite *access; - bool utf8; -}; - -QSQLite2DriverPrivate::QSQLite2DriverPrivate() : QSqlDriverPrivate(), access(0) -{ - utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0); - dbmsType = QSqlDriver::SQLite; -} - -class QSQLite2ResultPrivate; - -class QSQLite2Result : public QSqlCachedResult -{ - Q_DECLARE_PRIVATE(QSQLite2Result) - friend class QSQLite2Driver; - -public: - explicit QSQLite2Result(const QSQLite2Driver* db); - ~QSQLite2Result(); - QVariant handle() const Q_DECL_OVERRIDE; - -protected: - bool gotoNext(QSqlCachedResult::ValueCache &row, int idx) Q_DECL_OVERRIDE; - bool reset(const QString &query) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; - void detachFromResultSet() Q_DECL_OVERRIDE; - void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; -}; - -class QSQLite2ResultPrivate: public QSqlCachedResultPrivate -{ - Q_DECLARE_PUBLIC(QSQLite2Result) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QSQLite2Driver); - QSQLite2ResultPrivate(QSQLite2Result *q, const QSQLite2Driver *drv); - void cleanup(); - bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); - bool isSelect(); - // initializes the recordInfo and the cache - void init(const char **cnames, int numCols); - void finalize(); - - // and we have too keep our own struct for the data (sqlite works via - // callback. - const char *currentTail; - sqlite_vm *currentMachine; - - bool skippedStatus; // the status of the fetchNext() that's skipped - bool skipRow; // skip the next fetchNext()? - QSqlRecord rInf; - QVector firstRow; -}; - -QSQLite2ResultPrivate::QSQLite2ResultPrivate(QSQLite2Result *q, const QSQLite2Driver *drv) - : QSqlCachedResultPrivate(q, drv), - currentTail(0), - currentMachine(0), - skippedStatus(false), - skipRow(false) -{ -} - -void QSQLite2ResultPrivate::cleanup() -{ - Q_Q(QSQLite2Result); - finalize(); - rInf.clear(); - currentTail = 0; - currentMachine = 0; - skippedStatus = false; - skipRow = false; - q->setAt(QSql::BeforeFirstRow); - q->setActive(false); - q->cleanup(); -} - -void QSQLite2ResultPrivate::finalize() -{ - Q_Q(QSQLite2Result); - if (!currentMachine) - return; - - char* err = 0; - int res = sqlite_finalize(currentMachine, &err); - if (err) { - q->setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result", - "Unable to fetch results"), QString::fromLatin1(err), - QSqlError::StatementError, res)); - sqlite_freemem(err); - } - currentMachine = 0; -} - -// called on first fetch -void QSQLite2ResultPrivate::init(const char **cnames, int numCols) -{ - Q_Q(QSQLite2Result); - if (!cnames) - return; - - rInf.clear(); - if (numCols <= 0) - return; - q->init(numCols); - - for (int i = 0; i < numCols; ++i) { - const char* lastDot = strrchr(cnames[i], '.'); - const char* fieldName = lastDot ? lastDot + 1 : cnames[i]; - - //remove quotations around the field name if any - QString fieldStr = QString::fromLatin1(fieldName); - QLatin1Char quote('\"'); - if ( fieldStr.length() > 2 && fieldStr.startsWith(quote) && fieldStr.endsWith(quote)) { - fieldStr = fieldStr.mid(1); - fieldStr.chop(1); - } - rInf.append(QSqlField(fieldStr, - nameToType(QString::fromLatin1(cnames[i+numCols])))); - } -} - -bool QSQLite2ResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) -{ - Q_Q(QSQLite2Result); - // may be caching. - const char **fvals; - const char **cnames; - int colNum; - int res; - int i; - - if (skipRow) { - // already fetched - Q_ASSERT(!initialFetch); - skipRow = false; - for(int i=0;iutf8 ? QString::fromUtf8(fvals[i]) : QString::fromLatin1(fvals[i]); - return true; - case SQLITE_DONE: - if (rInf.isEmpty()) - // must be first call. - init(cnames, colNum); - q->setAt(QSql::AfterLastRow); - return false; - case SQLITE_ERROR: - case SQLITE_MISUSE: - default: - // something wrong, don't get col info, but still return false - finalize(); // finalize to get the error message. - q->setAt(QSql::AfterLastRow); - return false; - } - return false; -} - -QSQLite2Result::QSQLite2Result(const QSQLite2Driver* db) - : QSqlCachedResult(*new QSQLite2ResultPrivate(this, db)) -{ -} - -QSQLite2Result::~QSQLite2Result() -{ - Q_D(QSQLite2Result); - d->cleanup(); -} - -void QSQLite2Result::virtual_hook(int id, void *data) -{ - QSqlCachedResult::virtual_hook(id, data); -} - -/* - Execute \a query. -*/ -bool QSQLite2Result::reset (const QString& query) -{ - Q_D(QSQLite2Result); - // this is where we build a query. - if (!driver()) - return false; - if (!driver()-> isOpen() || driver()->isOpenError()) - return false; - - d->cleanup(); - - // Um, ok. callback based so.... pass private static function for this. - setSelect(false); - char *err = 0; - int res = sqlite_compile(d->drv_d_func()->access, - d->drv_d_func()->utf8 ? query.toUtf8().constData() - : query.toLatin1().constData(), - &(d->currentTail), - &(d->currentMachine), - &err); - if (res != SQLITE_OK || err) { - setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result", - "Unable to execute statement"), QString::fromLatin1(err), - QSqlError::StatementError, res)); - sqlite_freemem(err); - } - //if (*d->currentTail != '\000' then there is more sql to eval - if (!d->currentMachine) { - setActive(false); - return false; - } - // we have to fetch one row to find out about - // the structure of the result set - d->skippedStatus = d->fetchNext(d->firstRow, 0, true); - if (lastError().isValid()) { - setSelect(false); - setActive(false); - return false; - } - setSelect(!d->rInf.isEmpty()); - setActive(true); - return true; -} - -bool QSQLite2Result::gotoNext(QSqlCachedResult::ValueCache& row, int idx) -{ - Q_D(QSQLite2Result); - return d->fetchNext(row, idx, false); -} - -int QSQLite2Result::size() -{ - return -1; -} - -int QSQLite2Result::numRowsAffected() -{ - Q_D(QSQLite2Result); - return sqlite_changes(d->drv_d_func()->access); -} - -QSqlRecord QSQLite2Result::record() const -{ - Q_D(const QSQLite2Result); - if (!isActive() || !isSelect()) - return QSqlRecord(); - return d->rInf; -} - -void QSQLite2Result::detachFromResultSet() -{ - Q_D(QSQLite2Result); - d->finalize(); -} - -QVariant QSQLite2Result::handle() const -{ - Q_D(const QSQLite2Result); - return QVariant::fromValue(d->currentMachine); -} - -///////////////////////////////////////////////////////// - -QSQLite2Driver::QSQLite2Driver(QObject *parent) - : QSqlDriver(*new QSQLite2DriverPrivate, parent) -{ -} - -QSQLite2Driver::QSQLite2Driver(sqlite *connection, QObject *parent) - : QSqlDriver(*new QSQLite2DriverPrivate, parent) -{ - Q_D(QSQLite2Driver); - d->access = connection; - setOpen(true); - setOpenError(false); -} - - -QSQLite2Driver::~QSQLite2Driver() -{ -} - -bool QSQLite2Driver::hasFeature(DriverFeature f) const -{ - Q_D(const QSQLite2Driver); - switch (f) { - case Transactions: - case SimpleLocking: - return true; - case Unicode: - return d->utf8; - default: - return false; - } -} - -/* - SQLite dbs have no user name, passwords, hosts or ports. - just file names. -*/ -bool QSQLite2Driver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &) -{ - Q_D(QSQLite2Driver); - if (isOpen()) - close(); - - if (db.isEmpty()) - return false; - - char* err = 0; - d->access = sqlite_open(QFile::encodeName(db), 0, &err); - if (err) { - setLastError(QSqlError(tr("Error opening database"), QString::fromLatin1(err), - QSqlError::ConnectionError)); - sqlite_freemem(err); - err = 0; - } - - if (d->access) { - setOpen(true); - setOpenError(false); - return true; - } - setOpenError(true); - return false; -} - -void QSQLite2Driver::close() -{ - Q_D(QSQLite2Driver); - if (isOpen()) { - sqlite_close(d->access); - d->access = 0; - setOpen(false); - setOpenError(false); - } -} - -QSqlResult *QSQLite2Driver::createResult() const -{ - return new QSQLite2Result(this); -} - -bool QSQLite2Driver::beginTransaction() -{ - Q_D(QSQLite2Driver); - if (!isOpen() || isOpenError()) - return false; - - char* err; - int res = sqlite_exec(d->access, "BEGIN", 0, this, &err); - - if (res == SQLITE_OK) - return true; - - setLastError(QSqlError(tr("Unable to begin transaction"), - QString::fromLatin1(err), QSqlError::TransactionError, res)); - sqlite_freemem(err); - return false; -} - -bool QSQLite2Driver::commitTransaction() -{ - Q_D(QSQLite2Driver); - if (!isOpen() || isOpenError()) - return false; - - char* err; - int res = sqlite_exec(d->access, "COMMIT", 0, this, &err); - - if (res == SQLITE_OK) - return true; - - setLastError(QSqlError(tr("Unable to commit transaction"), - QString::fromLatin1(err), QSqlError::TransactionError, res)); - sqlite_freemem(err); - return false; -} - -bool QSQLite2Driver::rollbackTransaction() -{ - Q_D(QSQLite2Driver); - if (!isOpen() || isOpenError()) - return false; - - char* err; - int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err); - - if (res == SQLITE_OK) - return true; - - setLastError(QSqlError(tr("Unable to rollback transaction"), - QString::fromLatin1(err), QSqlError::TransactionError, res)); - sqlite_freemem(err); - return false; -} - -QStringList QSQLite2Driver::tables(QSql::TableType type) const -{ - QStringList res; - if (!isOpen()) - return res; - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - if ((type & QSql::Tables) && (type & QSql::Views)) - q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='table' OR type='view'")); - else if (type & QSql::Tables) - q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='table'")); - else if (type & QSql::Views) - q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='view'")); - - if (q.isActive()) { - while(q.next()) - res.append(q.value(0).toString()); - } - - if (type & QSql::SystemTables) { - // there are no internal tables beside this one: - res.append(QLatin1String("sqlite_master")); - } - - return res; -} - -QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const -{ - QSqlRecord rec(record(tblname)); // expensive :( - - if (!isOpen()) - return QSqlIndex(); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - QString table = tblname; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - // finrst find a UNIQUE INDEX - q.exec(QLatin1String("PRAGMA index_list('") + table + QLatin1String("');")); - QString indexname; - while(q.next()) { - if (q.value(2).toInt()==1) { - indexname = q.value(1).toString(); - break; - } - } - if (indexname.isEmpty()) - return QSqlIndex(); - - q.exec(QLatin1String("PRAGMA index_info('") + indexname + QLatin1String("');")); - - QSqlIndex index(table, indexname); - while(q.next()) { - QString name = q.value(2).toString(); - QVariant::Type type = QVariant::Invalid; - if (rec.contains(name)) - type = rec.field(name).type(); - index.append(QSqlField(name, type)); - } - return index; -} - -QSqlRecord QSQLite2Driver::record(const QString &tbl) const -{ - if (!isOpen()) - return QSqlRecord(); - QString table = tbl; - if (isIdentifierEscaped(tbl, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - - QSqlQuery q(createResult()); - q.setForwardOnly(true); - q.exec(QLatin1String("SELECT * FROM ") + tbl + QLatin1String(" LIMIT 1")); - return q.record(); -} - -QVariant QSQLite2Driver::handle() const -{ - Q_D(const QSQLite2Driver); - return QVariant::fromValue(d->access); -} - -QString QSQLite2Driver::escapeIdentifier(const QString &identifier, IdentifierType /*type*/) const -{ - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/sqlite2/qsql_sqlite2.pri b/src/sql/drivers/sqlite2/qsql_sqlite2.pri deleted file mode 100644 index 5baba30db0..0000000000 --- a/src/sql/drivers/sqlite2/qsql_sqlite2.pri +++ /dev/null @@ -1,4 +0,0 @@ -HEADERS += $$PWD/qsql_sqlite2_p.h -SOURCES += $$PWD/qsql_sqlite2.cpp - -!contains(LIBS, .*sqlite.*):LIBS += -lsqlite diff --git a/src/sql/drivers/sqlite2/qsql_sqlite2_p.h b/src/sql/drivers/sqlite2/qsql_sqlite2_p.h deleted file mode 100644 index 83b248ec6a..0000000000 --- a/src/sql/drivers/sqlite2/qsql_sqlite2_p.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** 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_SQLITE2_H -#define QSQL_SQLITE2_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 - -struct sqlite; - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_SQLITE2 -#else -#define Q_EXPORT_SQLDRIVER_SQLITE2 Q_SQL_EXPORT -#endif - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QSQLite2DriverPrivate; - -class Q_EXPORT_SQLDRIVER_SQLITE2 QSQLite2Driver : public QSqlDriver -{ - friend class QSQLite2ResultPrivate; - Q_DECLARE_PRIVATE(QSQLite2Driver) - Q_OBJECT -public: - explicit QSQLite2Driver(QObject *parent = 0); - explicit QSQLite2Driver(sqlite *connection, QObject *parent = 0); - ~QSQLite2Driver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port) { return open(db, user, password, host, port, QString()); } - void close() Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - - QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE; -}; - -QT_END_NAMESPACE - -#endif // QSQL_SQLITE2_H diff --git a/src/sql/drivers/tds/qsql_tds.cpp b/src/sql/drivers/tds/qsql_tds.cpp deleted file mode 100644 index 10d9fe7298..0000000000 --- a/src/sql/drivers/tds/qsql_tds.cpp +++ /dev/null @@ -1,881 +0,0 @@ -/**************************************************************************** -** -** 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_tds_p.h" - -#include -#ifdef Q_OS_WIN32 // We assume that MS SQL Server is used. Set Q_USE_SYBASE to force Sybase. -// Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h -#define _WINSCARD_H_ -#include -#else -#define Q_USE_SYBASE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -Q_DECLARE_OPAQUE_POINTER(LOGINREC*) -Q_DECLARE_OPAQUE_POINTER(DBPROCESS*) - -QT_BEGIN_NAMESPACE - -#ifdef DBNTWIN32 -#define QMSGHANDLE DBMSGHANDLE_PROC -#define QERRHANDLE DBERRHANDLE_PROC -#define QTDSCHAR SQLCHAR -#define QTDSDATETIME4 SQLDATETIM4 -#define QTDSDATETIME SQLDATETIME -#define QTDSDATETIME_N SQLDATETIMN -#define QTDSDECIMAL SQLDECIMAL -#define QTDSFLT4 SQLFLT4 -#define QTDSFLT8 SQLFLT8 -#define QTDSFLT8_N SQLFLTN -#define QTDSINT1 SQLINT1 -#define QTDSINT2 SQLINT2 -#define QTDSINT4 SQLINT4 -#define QTDSINT4_N SQLINTN -#define QTDSMONEY4 SQLMONEY4 -#define QTDSMONEY SQLMONEY -#define QTDSMONEY_N SQLMONEYN -#define QTDSNUMERIC SQLNUMERIC -#define QTDSTEXT SQLTEXT -#define QTDSVARCHAR SQLVARCHAR -#define QTDSBIT SQLBIT -#define QTDSBINARY SQLBINARY -#define QTDSVARBINARY SQLVARBINARY -#define QTDSIMAGE SQLIMAGE -#else -#define QMSGHANDLE MHANDLEFUNC -#define QERRHANDLE EHANDLEFUNC -#define QTDSCHAR SYBCHAR -#define QTDSDATETIME4 SYBDATETIME4 -#define QTDSDATETIME SYBDATETIME -#define QTDSDATETIME_N SYBDATETIMN -#define QTDSDECIMAL SYBDECIMAL -#define QTDSFLT8 SYBFLT8 -#define QTDSFLT8_N SYBFLTN -#define QTDSFLT4 SYBREAL -#define QTDSINT1 SYBINT1 -#define QTDSINT2 SYBINT2 -#define QTDSINT4 SYBINT4 -#define QTDSINT4_N SYBINTN -#define QTDSMONEY4 SYBMONEY4 -#define QTDSMONEY SYBMONEY -#define QTDSMONEY_N SYBMONEYN -#define QTDSNUMERIC SYBNUMERIC -#define QTDSTEXT SYBTEXT -#define QTDSVARCHAR SYBVARCHAR -#define QTDSBIT SYBBIT -#define QTDSBINARY SYBBINARY -#define QTDSVARBINARY SYBVARBINARY -#define QTDSIMAGE SYBIMAGE -// magic numbers not defined anywhere in Sybase headers -#define QTDSDECIMAL_2 55 -#define QTDSNUMERIC_2 63 -#endif //DBNTWIN32 - -#define TDS_CURSOR_SIZE 50 - -// workaround for FreeTDS -#ifndef CS_PUBLIC -#define CS_PUBLIC -#endif - -QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1) -{ - return QSqlError(QLatin1String("QTDS: ") + err, QString(), type, errNo); -} - -class QTDSDriverPrivate : public QSqlDriverPrivate -{ - Q_DECLARE_PUBLIC(QTDSDriver) - -public: - QTDSDriverPrivate() : QSqlDriverPrivate(), login(0), initialized(false) { dbmsType = QSqlDriver::Sybase; } - LOGINREC* login; // login information - QString hostName; - QString db; - bool initialized; -}; - -struct QTDSColumnData -{ - void *data; - DBINT nullbind; -}; -Q_DECLARE_TYPEINFO(QTDSColumnData, Q_MOVABLE_TYPE); - -class QTDSResultPrivate; - -class QTDSResult : public QSqlCachedResult -{ - Q_DECLARE_PRIVATE(QTDSResult) - -public: - explicit QTDSResult(const QTDSDriver* db); - ~QTDSResult(); - QVariant handle() const; - -protected: - void cleanup(); - bool reset(const QString &query) Q_DECL_OVERRIDE; - int size() Q_DECL_OVERRIDE; - int numRowsAffected() Q_DECL_OVERRIDE; - bool gotoNext(QSqlCachedResult::ValueCache &values, int index) Q_DECL_OVERRIDE; - QSqlRecord record() const Q_DECL_OVERRIDE; -}; - -class QTDSResultPrivate: public QSqlCachedResultPrivate -{ - Q_DECLARE_PUBLIC(QTDSResult) - -public: - Q_DECLARE_SQLDRIVER_PRIVATE(QTDSDriver) - QTDSResultPrivate(QTDSResult *q, const QTDSDriver *drv) - : QSqlCachedResultPrivate(q, drv), - login(0), - dbproc(0) {} - LOGINREC* login; // login information - DBPROCESS* dbproc; // connection from app to server - QSqlError lastError; - void addErrorMsg(QString& errMsg) { errorMsgs.append(errMsg); } - QString getErrorMsgs() { return errorMsgs.join(QLatin1String("\n")); } - void clearErrorMsgs() { errorMsgs.clear(); } - QVector buffer; - QSqlRecord rec; - -private: - QStringList errorMsgs; -}; - -typedef QHash QTDSErrorHash; -Q_GLOBAL_STATIC(QTDSErrorHash, errs) - -extern "C" { -static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc, - DBINT msgno, - int msgstate, - int severity, - char* msgtext, - char* srvname, - char* /*procname*/, - int line) -{ - QTDSResultPrivate* p = errs()->value(dbproc); - - if (!p) { -// ### umm... temporary disabled since this throws a lot of warnings... -// qWarning("QTDSDriver warning (%d): [%s] from server [%s]", msgstate, msgtext, srvname); - return INT_CANCEL; - } - - if (severity > 0) { - QString errMsg = QString::fromLatin1("%1 (Msg %2, Level %3, State %4, Server %5, Line %6)") - .arg(QString::fromLatin1(msgtext)) - .arg(msgno) - .arg(severity) - .arg(msgstate) - .arg(QString::fromLatin1(srvname)) - .arg(line); - p->addErrorMsg(errMsg); - if (severity > 10) { - // Severe messages are really errors in the sense of lastError - errMsg = p->getErrorMsgs(); - p->lastError = qMakeError(errMsg, QSqlError::UnknownError, msgno); - p->clearErrorMsgs(); - } - } - - return INT_CANCEL; -} - -static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc, - int /*severity*/, - int dberr, - int /*oserr*/, - char* dberrstr, - char* oserrstr) -{ - QTDSResultPrivate* p = errs()->value(dbproc); - if (!p) { - qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr); - return INT_CANCEL; - } - /* - * If the process is dead or NULL and - * we are not in the middle of logging in... - */ - if((dbproc == NULL || DBDEAD(dbproc))) { - qWarning("QTDSDriver error (%d): [%s] [%s]", dberr, dberrstr, oserrstr); - return INT_CANCEL; - } - - - QString errMsg = QString::fromLatin1("%1 %2\n").arg(QLatin1String(dberrstr)).arg( - QLatin1String(oserrstr)); - errMsg += p->getErrorMsgs(); - p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr); - p->clearErrorMsgs(); - - return INT_CANCEL ; -} - -} //extern "C" - - -QVariant::Type qDecodeTDSType(int type) -{ - QVariant::Type t = QVariant::Invalid; - switch (type) { - case QTDSCHAR: - case QTDSTEXT: - case QTDSVARCHAR: - t = QVariant::String; - break; - case QTDSINT1: - case QTDSINT2: - case QTDSINT4: - case QTDSINT4_N: - case QTDSBIT: - t = QVariant::Int; - break; - case QTDSFLT4: - case QTDSFLT8: - case QTDSFLT8_N: - case QTDSMONEY4: - case QTDSMONEY: - case QTDSDECIMAL: - case QTDSNUMERIC: -#ifdef QTDSNUMERIC_2 - case QTDSNUMERIC_2: -#endif -#ifdef QTDSDECIMAL_2 - case QTDSDECIMAL_2: -#endif - case QTDSMONEY_N: - t = QVariant::Double; - break; - case QTDSDATETIME4: - case QTDSDATETIME: - case QTDSDATETIME_N: - t = QVariant::DateTime; - break; - case QTDSBINARY: - case QTDSVARBINARY: - case QTDSIMAGE: - t = QVariant::ByteArray; - break; - default: - t = QVariant::Invalid; - break; - } - return t; -} - -QVariant::Type qFieldType(QTDSResultPrivate* d, int i) -{ - QVariant::Type type = qDecodeTDSType(dbcoltype(d->dbproc, i+1)); - return type; -} - - -QTDSResult::QTDSResult(const QTDSDriver* db) - : QSqlCachedResult(*new QTDSResultPrivate(this, db)) -{ - Q_D(QTDSResult); - d->login = d->drv_d_func()->login; - - d->dbproc = dbopen(d->login, const_cast(d->drv_d_func()->hostName.toLatin1().constData())); - if (!d->dbproc) - return; - if (dbuse(d->dbproc, const_cast(d->drv_d_func()->db.toLatin1().constData())) == FAIL) - return; - - // insert d in error handler dict - errs()->insert(d->dbproc, d); - dbcmd(d->dbproc, "set quoted_identifier on"); - dbsqlexec(d->dbproc); -} - -QTDSResult::~QTDSResult() -{ - Q_D(QTDSResult); - cleanup(); - if (d->dbproc) - dbclose(d->dbproc); - errs()->remove(d->dbproc); -} - -void QTDSResult::cleanup() -{ - Q_D(QTDSResult); - d->clearErrorMsgs(); - d->rec.clear(); - for (int i = 0; i < d->buffer.size(); ++i) - free(d->buffer.at(i).data); - d->buffer.clear(); - // "can" stands for "cancel"... very clever. - dbcanquery(d->dbproc); - dbfreebuf(d->dbproc); - - QSqlCachedResult::cleanup(); -} - -QVariant QTDSResult::handle() const -{ - Q_D(const QTDSResult); - return QVariant(qRegisterMetaType("DBPROCESS*"), &d->dbproc); -} - -static inline bool qIsNull(const QTDSColumnData &p) -{ - return p.nullbind == -1; -} - -bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index) -{ - Q_D(QTDSResult); - STATUS stat = dbnextrow(d->dbproc); - if (stat == NO_MORE_ROWS) { - setAt(QSql::AfterLastRow); - return false; - } - if ((stat == FAIL) || (stat == BUF_FULL)) { - setLastError(d->lastError); - return false; - } - - if (index < 0) - return true; - - for (int i = 0; i < d->rec.count(); ++i) { - int idx = index + i; - switch (d->rec.field(i).type()) { - case QVariant::DateTime: - if (qIsNull(d->buffer.at(i))) { - values[idx] = QVariant(QVariant::DateTime); - } else { - DBDATETIME *bdt = (DBDATETIME*) d->buffer.at(i).data; - QDate date = QDate::fromString(QLatin1String("1900-01-01"), Qt::ISODate); - QTime time = QTime::fromString(QLatin1String("00:00:00"), Qt::ISODate); - values[idx] = QDateTime(date.addDays(bdt->dtdays), time.addMSecs(int(bdt->dttime / 0.3))); - } - break; - case QVariant::Int: - if (qIsNull(d->buffer.at(i))) - values[idx] = QVariant(QVariant::Int); - else - values[idx] = *((int*)d->buffer.at(i).data); - break; - case QVariant::Double: - case QVariant::String: - if (qIsNull(d->buffer.at(i))) - values[idx] = QVariant(QVariant::String); - else - values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i).data).trimmed(); - break; - case QVariant::ByteArray: { - if (qIsNull(d->buffer.at(i))) - values[idx] = QVariant(QVariant::ByteArray); - else - values[idx] = QByteArray((const char*)d->buffer.at(i).data); - break; - } - default: - // should never happen, and we already fired - // a warning while binding. - values[idx] = QVariant(); - break; - } - } - - return true; -} - -bool QTDSResult::reset (const QString& query) -{ - Q_D(QTDSResult); - cleanup(); - if (!driver() || !driver()-> isOpen() || driver()->isOpenError()) - return false; - setActive(false); - setAt(QSql::BeforeFirstRow); - if (dbcmd(d->dbproc, const_cast(query.toLocal8Bit().constData())) == FAIL) { - setLastError(d->lastError); - return false; - } - - if (dbsqlexec(d->dbproc) == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - if (dbresults(d->dbproc) != SUCCEED) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - - setSelect((DBCMDROW(d->dbproc) == SUCCEED)); // decide whether or not we are dealing with a SELECT query - int numCols = dbnumcols(d->dbproc); - if (numCols > 0) { - d->buffer.resize(numCols); - init(numCols); - } - for (int i = 0; i < numCols; ++i) { - int dbType = dbcoltype(d->dbproc, i+1); - QVariant::Type vType = qDecodeTDSType(dbType); - QSqlField f(QString::fromLatin1(dbcolname(d->dbproc, i+1)), vType); - f.setSqlType(dbType); - f.setLength(dbcollen(d->dbproc, i+1)); - d->rec.append(f); - - RETCODE ret = -1; - void* p = 0; - switch (vType) { - case QVariant::Int: - p = malloc(4); - ret = dbbind(d->dbproc, i+1, INTBIND, (DBINT) 4, (unsigned char *)p); - break; - case QVariant::Double: - // use string binding to prevent loss of precision - p = malloc(50); - ret = dbbind(d->dbproc, i+1, STRINGBIND, 50, (unsigned char *)p); - break; - case QVariant::String: - p = malloc(dbcollen(d->dbproc, i+1) + 1); - ret = dbbind(d->dbproc, i+1, STRINGBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p); - break; - case QVariant::DateTime: - p = malloc(8); - ret = dbbind(d->dbproc, i+1, DATETIMEBIND, (DBINT) 8, (unsigned char *)p); - break; - case QVariant::ByteArray: - p = malloc(dbcollen(d->dbproc, i+1) + 1); - ret = dbbind(d->dbproc, i+1, BINARYBIND, DBINT(dbcollen(d->dbproc, i+1) + 1), (unsigned char *)p); - break; - default: //don't bind the field since we do not support it - qWarning("QTDSResult::reset: Unsupported type for field \"%s\"", dbcolname(d->dbproc, i+1)); - break; - } - if (ret == SUCCEED) { - d->buffer[i].data = p; - ret = dbnullbind(d->dbproc, i+1, &d->buffer[i].nullbind); - } else { - d->buffer[i].data = 0; - d->buffer[i].nullbind = 0; - free(p); - } - if ((ret != SUCCEED) && (ret != -1)) { - setLastError(d->lastError); - return false; - } - } - - setActive(true); - return true; -} - -int QTDSResult::size() -{ - return -1; -} - -int QTDSResult::numRowsAffected() -{ - Q_D(const QTDSResult); -#ifdef DBNTWIN32 - if (dbiscount(d->dbproc)) { - return DBCOUNT(d->dbproc); - } - return -1; -#else - return DBCOUNT(d->dbproc); -#endif -} - -QSqlRecord QTDSResult::record() const -{ - Q_D(const QTDSResult); - return d->rec; -} - -/////////////////////////////////////////////////////////////////// - -QTDSDriver::QTDSDriver(QObject* parent) - : QSqlDriver(*new QTDSDriverPrivate, parent) -{ - init(); -} - -QTDSDriver::QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent) - : QSqlDriver(*new QTDSDriverPrivate, parent) -{ - Q_D(QTDSDriver); - init(); - d->login = rec; - d->hostName = host; - d->db = db; - if (rec) { - setOpen(true); - setOpenError(false); - } -} - -QVariant QTDSDriver::handle() const -{ - Q_D(const QTDSDriver); - return QVariant(qRegisterMetaType("LOGINREC*"), &d->login); -} - -void QTDSDriver::init() -{ - Q_D(QTDSDriver); - d->initialized = (dbinit() == SUCCEED); - // the following two code-lines will fail compilation on some FreeTDS versions - // just comment them out if you have FreeTDS (you won't get any errors and warnings then) - dberrhandle((QERRHANDLE)qTdsErrHandler); - dbmsghandle((QMSGHANDLE)qTdsMsgHandler); -} - -QTDSDriver::~QTDSDriver() -{ - dberrhandle(0); - dbmsghandle(0); - // dbexit also calls dbclose if necessary - dbexit(); -} - -bool QTDSDriver::hasFeature(DriverFeature f) const -{ - switch (f) { - case Transactions: - case QuerySize: - case Unicode: - case SimpleLocking: - case EventNotifications: - case MultipleResultSets: - return false; - case BLOB: - return true; - default: - return false; - } -} - -bool QTDSDriver::open(const QString & db, - const QString & user, - const QString & password, - const QString & host, - int /*port*/, - const QString& /*connOpts*/) -{ - Q_D(QTDSDriver); - if (isOpen()) - close(); - if (!d->initialized) { - setOpenError(true); - return false; - } - d->login = dblogin(); - if (!d->login) { - setOpenError(true); - return false; - } - DBSETLPWD(d->login, const_cast(password.toLocal8Bit().constData())); - DBSETLUSER(d->login, const_cast(user.toLocal8Bit().constData())); - - // Now, try to open and use the database. If this fails, return false. - DBPROCESS* dbproc; - - dbproc = dbopen(d->login, const_cast(host.toLatin1().constData())); - if (!dbproc) { - setLastError(qMakeError(tr("Unable to open connection"), QSqlError::ConnectionError, -1)); - setOpenError(true); - return false; - } - if (dbuse(dbproc, const_cast(db.toLatin1().constData())) == FAIL) { - setLastError(qMakeError(tr("Unable to use database"), QSqlError::ConnectionError, -1)); - setOpenError(true); - return false; - } - dbclose( dbproc ); - - setOpen(true); - setOpenError(false); - d->hostName = host; - d->db = db; - return true; -} - -void QTDSDriver::close() -{ - Q_D(QTDSDriver); - if (isOpen()) { -#ifdef Q_USE_SYBASE - dbloginfree(d->login); -#else - dbfreelogin(d->login); -#endif - d->login = 0; - setOpen(false); - setOpenError(false); - } -} - -QSqlResult *QTDSDriver::createResult() const -{ - return new QTDSResult(this); -} - -bool QTDSDriver::beginTransaction() -{ - return false; -/* - if (!isOpen()) { - qWarning("QTDSDriver::beginTransaction: Database not open"); - return false; - } - if (dbcmd(d->dbproc, "BEGIN TRANSACTION") == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - if (dbsqlexec(d->dbproc) == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - while(dbresults(d->dbproc) == NO_MORE_RESULTS) {} - dbfreebuf(d->dbproc); - inTransaction = true; - return true; -*/ -} - -bool QTDSDriver::commitTransaction() -{ - return false; -/* - if (!isOpen()) { - qWarning("QTDSDriver::commitTransaction: Database not open"); - return false; - } - if (dbcmd(d->dbproc, "COMMIT TRANSACTION") == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - if (dbsqlexec(d->dbproc) == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - while(dbresults(d->dbproc) == NO_MORE_RESULTS) {} - dbfreebuf(d->dbproc); - inTransaction = false; - return true; -*/ -} - -bool QTDSDriver::rollbackTransaction() -{ - return false; -/* - if (!isOpen()) { - qWarning("QTDSDriver::rollbackTransaction: Database not open"); - return false; - } - if (dbcmd(d->dbproc, "ROLLBACK TRANSACTION") == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - if (dbsqlexec(d->dbproc) == FAIL) { - setLastError(d->lastError); - dbfreebuf(d->dbproc); - return false; - } - while(dbresults(d->dbproc) == NO_MORE_RESULTS) {} - dbfreebuf(d->dbproc); - inTransaction = false; - return true; -*/ -} - -QSqlRecord QTDSDriver::record(const QString& tablename) const -{ - QSqlRecord info; - if (!isOpen()) - return info; - QSqlQuery t(createResult()); - t.setForwardOnly(true); - - QString table = tablename; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - - QString stmt (QLatin1String("select name, type, length, prec from syscolumns " - "where id = (select id from sysobjects where name = '%1')")); - t.exec(stmt.arg(table)); - while (t.next()) { - QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt())); - f.setLength(t.value(2).toInt()); - f.setPrecision(t.value(3).toInt()); - f.setSqlType(t.value(1).toInt()); - info.append(f); - } - return info; -} - -QStringList QTDSDriver::tables(QSql::TableType type) const -{ - QStringList list; - - if (!isOpen()) - return list; - - QStringList typeFilter; - - if (type & QSql::Tables) - typeFilter += QLatin1String("type='U'"); - if (type & QSql::SystemTables) - typeFilter += QLatin1String("type='S'"); - if (type & QSql::Views) - typeFilter += QLatin1String("type='V'"); - - if (typeFilter.isEmpty()) - return list; - - QSqlQuery t(createResult()); - t.setForwardOnly(true); - t.exec(QLatin1String("select name from sysobjects where ") + typeFilter.join(QLatin1String(" or "))); - while (t.next()) - list.append(t.value(0).toString().simplified()); - - return list; -} - -QString QTDSDriver::formatValue(const QSqlField &field, - bool trim) const -{ - QString r; - if (field.isNull()) - r = QLatin1String("NULL"); - else if (field.type() == QVariant::DateTime) { - if (field.value().toDateTime().isValid()){ - r = field.value().toDateTime().toString(QLatin1String("yyyyMMdd hh:mm:ss")); - r.prepend(QLatin1String("'")); - r.append(QLatin1String("'")); - } else - r = QLatin1String("NULL"); - } else if (field.type() == QVariant::ByteArray) { - QByteArray ba = field.value().toByteArray(); - QString res; - static const char hexchars[] = "0123456789abcdef"; - for (int i = 0; i < ba.size(); ++i) { - uchar s = (uchar) ba[i]; - res += QLatin1Char(hexchars[s >> 4]); - res += QLatin1Char(hexchars[s & 0x0f]); - } - r = QLatin1String("0x") + res; - } else { - r = QSqlDriver::formatValue(field, trim); - } - return r; -} - -QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const -{ - QSqlRecord rec = record(tablename); - - QString table = tablename; - if (isIdentifierEscaped(table, QSqlDriver::TableName)) - table = stripDelimiters(table, QSqlDriver::TableName); - - QSqlIndex idx(table); - if ((!isOpen()) || (table.isEmpty())) - return QSqlIndex(); - - QSqlQuery t(createResult()); - t.setForwardOnly(true); - t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table)); - if (t.next()) { - QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(',')); - QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*")); - for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) { - regx.indexIn(*it); - QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type()); - if (regx.cap(2).toLower() == QLatin1String("desc")) { - idx.append(f, true); - } else { - idx.append(f, false); - } - } - idx.setName(t.value(0).toString().simplified()); - } - return idx; -} - -QString QTDSDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const -{ - Q_UNUSED(type) - QString res = identifier; - if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) { - res.replace(QLatin1Char('"'), QLatin1String("\"\"")); - res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); - res.replace(QLatin1Char('.'), QLatin1String("\".\"")); - } - return res; -} - -QT_END_NAMESPACE diff --git a/src/sql/drivers/tds/qsql_tds.pri b/src/sql/drivers/tds/qsql_tds.pri deleted file mode 100644 index 8c528c154d..0000000000 --- a/src/sql/drivers/tds/qsql_tds.pri +++ /dev/null @@ -1,10 +0,0 @@ -HEADERS += $$PWD/qsql_tds_p.h -SOURCES += $$PWD/qsql_tds.cpp - -unix|mingw: { - LIBS += $$QT_LFLAGS_TDS - !contains(LIBS, .*sybdb.*):LIBS += -lsybdb - QMAKE_CXXFLAGS *= $$QT_CFLAGS_TDS -} else { - LIBS *= -lNTWDBLIB -} diff --git a/src/sql/drivers/tds/qsql_tds_p.h b/src/sql/drivers/tds/qsql_tds_p.h deleted file mode 100644 index d0914455a2..0000000000 --- a/src/sql/drivers/tds/qsql_tds_p.h +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************** -** -** 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_TDS_H -#define QSQL_TDS_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 - -#ifdef Q_OS_WIN32 -#define WIN32_LEAN_AND_MEAN -#ifndef Q_USE_SYBASE -#define DBNTWIN32 // indicates 32bit windows dblib -#endif -#include -#include -#include -#include -#define CS_PUBLIC -#else -#include -#include -#endif //Q_OS_WIN32 - -#ifdef QT_PLUGIN -#define Q_EXPORT_SQLDRIVER_TDS -#else -#define Q_EXPORT_SQLDRIVER_TDS Q_SQL_EXPORT -#endif - -QT_BEGIN_NAMESPACE - -class QSqlResult; -class QTDSDriverPrivate; - -class Q_EXPORT_SQLDRIVER_TDS QTDSDriver : public QSqlDriver -{ - Q_DECLARE_PRIVATE(QTDSDriver) - Q_OBJECT - friend class QTDSResultPrivate; -public: - explicit QTDSDriver(QObject* parent = 0); - QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent = 0); - ~QTDSDriver(); - bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; - bool open(const QString &db, - const QString &user, - const QString &password, - const QString &host, - int port, - const QString &connOpts) Q_DECL_OVERRIDE; - void close() Q_DECL_OVERRIDE; - QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; - QSqlResult *createResult() const Q_DECL_OVERRIDE; - QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE; - QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE; - - QString formatValue(const QSqlField &field, - bool trimStrings) const Q_DECL_OVERRIDE; - QVariant handle() const Q_DECL_OVERRIDE; - - QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE; - -protected: - bool beginTransaction() Q_DECL_OVERRIDE; - bool commitTransaction() Q_DECL_OVERRIDE; - bool rollbackTransaction() Q_DECL_OVERRIDE; -private: - void init(); -}; - -QT_END_NAMESPACE - -#endif // QSQL_TDS_H -- cgit v1.2.3