diff options
Diffstat (limited to 'src/sql/drivers/odbc')
-rw-r--r-- | src/sql/drivers/odbc/qsql_odbc.cpp | 2639 | ||||
-rw-r--r-- | src/sql/drivers/odbc/qsql_odbc.pri | 12 | ||||
-rw-r--r-- | src/sql/drivers/odbc/qsql_odbc_p.h | 127 |
3 files changed, 0 insertions, 2778 deletions
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 <qsqlrecord.h> - -#if defined (Q_OS_WIN32) -#include <qt_windows.h> -#endif -#include <qcoreapplication.h> -#include <qvariant.h> -#include <qdatetime.h> -#include <qsqlerror.h> -#include <qsqlfield.h> -#include <qsqlindex.h> -#include <qstringlist.h> -#include <qvarlengtharray.h> -#include <qvector.h> -#include <qmath.h> -#include <QDebug> -#include <QSqlQuery> -#include <QtSql/private/qsqldriver_p.h> -#include <QtSql/private/qsqlresult_p.h> - -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<SQLTCHAR>& 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<SQLTCHAR> toSQLTCHAR(const QString &input) -{ - QVarLengthArray<SQLTCHAR> 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<QVariant> 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<SQLTCHAR> 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<SQLTCHAR> 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<SQLCHAR> 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<SQLTCHAR> 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<char *>(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<SQLTCHAR> 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<QVariant>& values = boundValues(); - QVector<QByteArray> tmpStorage(values.count(), QByteArray()); // holds temporary buffers - QVarLengthArray<SQLLEN, 32> 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<char *>(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<char *>(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<char *>(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<void *>(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<void *>(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<void *>(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<void *>(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<void *>(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<char *>(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<void *>(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<SQLTCHAR> 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<char *>(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<char *>(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<char *>(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<char *>(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<char *>(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<char *>(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<SQLTCHAR> 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>("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<SQLTCHAR> 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<SQLWCHAR> 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<SQLTCHAR> 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<SQLTCHAR> 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<QODBCDriverPrivate*>(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<QODBCDriverPrivate*>(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>("SQLHANDLE"), &d->hDbc); -} - -QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const -{ - Q_D(const QODBCDriver); - QChar quote = const_cast<QODBCDriverPrivate*>(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<QODBCDriverPrivate*>(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 <QtSql/qsqldriver.h> - -#if defined (Q_OS_WIN32) -#include <QtCore/qt_windows.h> -#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 <sql.h> -#if defined(Q_CC_BOR) -# undef _MSC_VER -#endif - -#include <sqlext.h> - -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 |