util/src/sql/drivers/odbc/qsql_odbc.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSql module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qsql_odbc.h"
       
    43 #include <qsqlrecord.h>
       
    44 
       
    45 #if defined (Q_OS_WIN32)
       
    46 #include <qt_windows.h>
       
    47 #endif
       
    48 #include <qcoreapplication.h>
       
    49 #include <qvariant.h>
       
    50 #include <qdatetime.h>
       
    51 #include <qsqlerror.h>
       
    52 #include <qsqlfield.h>
       
    53 #include <qsqlindex.h>
       
    54 #include <qstringlist.h>
       
    55 #include <qvarlengtharray.h>
       
    56 #include <qvector.h>
       
    57 #include <QDebug>
       
    58 #include <QSqlQuery>
       
    59 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 // undefine this to prevent initial check of the ODBC driver
       
    63 #define ODBC_CHECK_DRIVER
       
    64 
       
    65 #if defined(Q_ODBC_VERSION_2)
       
    66 //crude hack to get non-unicode capable driver managers to work
       
    67 # undef UNICODE
       
    68 # define SQLTCHAR SQLCHAR
       
    69 # define SQL_C_TCHAR SQL_C_CHAR
       
    70 #endif
       
    71 
       
    72 // newer platform SDKs use SQLLEN instead of SQLINTEGER
       
    73 #if defined(WIN32) && (_MSC_VER < 1300)
       
    74 # define QSQLLEN SQLINTEGER
       
    75 # define QSQLULEN SQLUINTEGER
       
    76 #else
       
    77 # define QSQLLEN SQLLEN
       
    78 # define QSQLULEN SQLULEN
       
    79 #endif
       
    80 
       
    81 static const int COLNAMESIZE = 256;
       
    82 //Map Qt parameter types to ODBC types
       
    83 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
       
    84 
       
    85 inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, int size=-1)
       
    86 {
       
    87     QString result;
       
    88 
       
    89     int realsize = qMin(size, input.size());
       
    90     if(realsize > 0 && input[realsize-1] == 0)
       
    91         realsize--;
       
    92     switch(sizeof(SQLTCHAR)) {
       
    93         case 1:
       
    94             result=QString::fromUtf8((const char *)input.constData(), realsize);
       
    95             break;
       
    96         case 2:
       
    97             result=QString::fromUtf16((const ushort *)input.constData(), realsize);
       
    98             break;
       
    99         case 4:
       
   100             result=QString::fromUcs4((const uint *)input.constData(), realsize);
       
   101             break;
       
   102         default:
       
   103             qCritical() << "sizeof(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
       
   104     }
       
   105     return result;
       
   106 }
       
   107 
       
   108 inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
       
   109 {
       
   110     QVarLengthArray<SQLTCHAR> result;
       
   111     result.resize(input.size());
       
   112     switch(sizeof(SQLTCHAR)) {
       
   113         case 1:
       
   114             memcpy(result.data(), input.toUtf8().data(), input.size());
       
   115             break;
       
   116         case 2:
       
   117             memcpy(result.data(), input.unicode(), input.size() * 2);
       
   118             break;
       
   119         case 4:
       
   120             memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
       
   121             break;
       
   122         default:
       
   123             qCritical() << "sizeof(SQLTCHAR) is " << sizeof(SQLTCHAR) << "Don't know how to handle this";
       
   124     }
       
   125     result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
       
   126     return result;
       
   127 }
       
   128 
       
   129 class QODBCDriverPrivate
       
   130 {
       
   131 public:
       
   132     enum DefaultCase{Lower, Mixed, Upper, Sensitive};
       
   133     QODBCDriverPrivate()
       
   134     : hEnv(0), hDbc(0), unicode(false), useSchema(false), disconnectCount(0), isMySqlServer(false),
       
   135            isMSSqlServer(false), isFreeTDSDriver(false), hasSQLFetchScroll(true),
       
   136            hasMultiResultSets(false), isQuoteInitialized(false), quote(QLatin1Char('"'))
       
   137     {
       
   138     }
       
   139 
       
   140     SQLHANDLE hEnv;
       
   141     SQLHANDLE hDbc;
       
   142 
       
   143     bool unicode;
       
   144     bool useSchema;
       
   145     int disconnectCount;
       
   146     bool isMySqlServer;
       
   147     bool isMSSqlServer;
       
   148     bool isFreeTDSDriver;
       
   149     bool hasSQLFetchScroll;
       
   150     bool hasMultiResultSets;
       
   151 
       
   152     bool checkDriver() const;
       
   153     void checkUnicode();
       
   154     void checkSqlServer();
       
   155     void checkHasSQLFetchScroll();
       
   156     void checkHasMultiResults();
       
   157     void checkSchemaUsage();
       
   158     bool setConnectionOptions(const QString& connOpts);
       
   159     void splitTableQualifier(const QString &qualifier, QString &catalog,
       
   160                              QString &schema, QString &table);
       
   161     DefaultCase defaultCase() const;
       
   162     QString adjustCase(const QString&) const;
       
   163     QChar quoteChar();
       
   164 private:
       
   165     bool isQuoteInitialized;
       
   166     QChar quote;
       
   167 };
       
   168 
       
   169 class QODBCPrivate
       
   170 {
       
   171 public:
       
   172     QODBCPrivate(QODBCDriverPrivate *dpp)
       
   173     : hStmt(0), useSchema(false), hasSQLFetchScroll(true), driverPrivate(dpp), userForwardOnly(false)
       
   174     {
       
   175         unicode = dpp->unicode;
       
   176         useSchema = dpp->useSchema;
       
   177         disconnectCount = dpp->disconnectCount;
       
   178         hasSQLFetchScroll = dpp->hasSQLFetchScroll;
       
   179     }
       
   180 
       
   181     inline void clearValues()
       
   182     { fieldCache.fill(QVariant()); fieldCacheIdx = 0; }
       
   183 
       
   184     SQLHANDLE dpEnv() const { return driverPrivate ? driverPrivate->hEnv : 0;}
       
   185     SQLHANDLE dpDbc() const { return driverPrivate ? driverPrivate->hDbc : 0;}
       
   186     SQLHANDLE hStmt;
       
   187 
       
   188     bool unicode;
       
   189     bool useSchema;
       
   190 
       
   191     QSqlRecord rInf;
       
   192     QVector<QVariant> fieldCache;
       
   193     int fieldCacheIdx;
       
   194     int disconnectCount;
       
   195     bool hasSQLFetchScroll;
       
   196     QODBCDriverPrivate *driverPrivate;
       
   197     bool userForwardOnly;
       
   198 
       
   199     bool isStmtHandleValid(const QSqlDriver *driver);
       
   200     void updateStmtHandleState(const QSqlDriver *driver);
       
   201 };
       
   202 
       
   203 bool QODBCPrivate::isStmtHandleValid(const QSqlDriver *driver)
       
   204 {
       
   205     const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
       
   206     return disconnectCount == odbcdriver->d->disconnectCount;
       
   207 }
       
   208 
       
   209 void QODBCPrivate::updateStmtHandleState(const QSqlDriver *driver)
       
   210 {
       
   211     const QODBCDriver *odbcdriver = static_cast<const QODBCDriver*> (driver);
       
   212     disconnectCount = odbcdriver->d->disconnectCount;
       
   213 }
       
   214 
       
   215 static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
       
   216 {
       
   217     SQLINTEGER nativeCode_ = 0;
       
   218     SQLSMALLINT msgLen = 0;
       
   219     SQLRETURN r = SQL_NO_DATA;
       
   220     SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
       
   221     QVarLengthArray<SQLTCHAR> description_(SQL_MAX_MESSAGE_LENGTH);
       
   222     QString result;
       
   223     int i = 1;
       
   224 
       
   225     description_[0] = 0;
       
   226     do {
       
   227         r = SQLGetDiagRec(handleType,
       
   228                           handle,
       
   229                           i,
       
   230                           state_,
       
   231                           &nativeCode_,
       
   232                           0,
       
   233                           NULL,
       
   234                           &msgLen);
       
   235         if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0)
       
   236             description_.resize(msgLen+1);
       
   237         r = SQLGetDiagRec(handleType,
       
   238                             handle,
       
   239                             i,
       
   240                             state_,
       
   241                             &nativeCode_,
       
   242                             description_.data(),
       
   243                             description_.size(),
       
   244                             &msgLen);
       
   245         if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
       
   246             if (nativeCode)
       
   247                 *nativeCode = nativeCode_;
       
   248             QString tmpstore;
       
   249 #ifdef UNICODE
       
   250             tmpstore = fromSQLTCHAR(description_, msgLen);
       
   251 #else
       
   252             tmpstore = QString::fromUtf8((const char*)description_.constData(), msgLen);
       
   253 #endif
       
   254             if(result != tmpstore) {
       
   255                 if(!result.isEmpty())
       
   256                     result += QLatin1Char(' ');
       
   257                 result += tmpstore;
       
   258             }
       
   259         } else if (r == SQL_ERROR || r == SQL_INVALID_HANDLE) {
       
   260             return result;
       
   261         }
       
   262         ++i;
       
   263     } while (r != SQL_NO_DATA);
       
   264     return result;
       
   265 }
       
   266 
       
   267 static QString qODBCWarn(const QODBCPrivate* odbc, int *nativeCode = 0)
       
   268 {
       
   269     return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->dpEnv()) + QLatin1Char(' ')
       
   270              + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->dpDbc()) + QLatin1Char(' ')
       
   271              + qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode)).simplified();
       
   272 }
       
   273 
       
   274 static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0)
       
   275 {
       
   276     return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
       
   277              + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode)).simplified();
       
   278 }
       
   279 
       
   280 static void qSqlWarning(const QString& message, const QODBCPrivate* odbc)
       
   281 {
       
   282     qWarning() << message << "\tError:" << qODBCWarn(odbc);
       
   283 }
       
   284 
       
   285 static void qSqlWarning(const QString &message, const QODBCDriverPrivate *odbc)
       
   286 {
       
   287     qWarning() << message << "\tError:" << qODBCWarn(odbc);
       
   288 }
       
   289 
       
   290 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const QODBCPrivate* p)
       
   291 {
       
   292     int nativeCode = -1;
       
   293     QString message = qODBCWarn(p, &nativeCode);
       
   294     return QSqlError(QLatin1String("QODBC3: ") + err, message, type, nativeCode);
       
   295 }
       
   296 
       
   297 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
       
   298                             const QODBCDriverPrivate* p)
       
   299 {
       
   300     int nativeCode = -1;
       
   301     QString message = qODBCWarn(p, &nativeCode);
       
   302     return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type, nativeCode);
       
   303 }
       
   304 
       
   305 template<class T>
       
   306 static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSigned = true)
       
   307 {
       
   308     Q_UNUSED(p);
       
   309     QVariant::Type type = QVariant::Invalid;
       
   310     switch (sqltype) {
       
   311     case SQL_DECIMAL:
       
   312     case SQL_NUMERIC:
       
   313     case SQL_REAL:
       
   314     case SQL_FLOAT:
       
   315     case SQL_DOUBLE:
       
   316         type = QVariant::Double;
       
   317         break;
       
   318     case SQL_SMALLINT:
       
   319     case SQL_INTEGER:
       
   320     case SQL_BIT:
       
   321         type = isSigned ? QVariant::Int : QVariant::UInt;
       
   322         break;
       
   323     case SQL_TINYINT:
       
   324         type = QVariant::UInt;
       
   325         break;
       
   326     case SQL_BIGINT:
       
   327         type = isSigned ? QVariant::LongLong : QVariant::ULongLong;
       
   328         break;
       
   329     case SQL_BINARY:
       
   330     case SQL_VARBINARY:
       
   331     case SQL_LONGVARBINARY:
       
   332         type = QVariant::ByteArray;
       
   333         break;
       
   334     case SQL_DATE:
       
   335     case SQL_TYPE_DATE:
       
   336         type = QVariant::Date;
       
   337         break;
       
   338     case SQL_TIME:
       
   339     case SQL_TYPE_TIME:
       
   340         type = QVariant::Time;
       
   341         break;
       
   342     case SQL_TIMESTAMP:
       
   343     case SQL_TYPE_TIMESTAMP:
       
   344         type = QVariant::DateTime;
       
   345         break;
       
   346 #ifndef Q_ODBC_VERSION_2
       
   347     case SQL_WCHAR:
       
   348     case SQL_WVARCHAR:
       
   349     case SQL_WLONGVARCHAR:
       
   350         type = QVariant::String;
       
   351         break;
       
   352 #endif
       
   353     case SQL_CHAR:
       
   354     case SQL_VARCHAR:
       
   355 #if (ODBCVER >= 0x0350)
       
   356     case SQL_GUID:
       
   357 #endif
       
   358     case SQL_LONGVARCHAR:
       
   359         type = QVariant::String;
       
   360         break;
       
   361     default:
       
   362         type = QVariant::ByteArray;
       
   363         break;
       
   364     }
       
   365     return type;
       
   366 }
       
   367 
       
   368 static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool unicode = false)
       
   369 {
       
   370     QString fieldVal;
       
   371     SQLRETURN r = SQL_ERROR;
       
   372     QSQLLEN lengthIndicator = 0;
       
   373 
       
   374     // NB! colSize must be a multiple of 2 for unicode enabled DBs
       
   375     if (colSize <= 0) {
       
   376         colSize = 256;
       
   377     } else if (colSize > 65536) { // limit buffer size to 64 KB
       
   378         colSize = 65536;
       
   379     } else {
       
   380         colSize++; // make sure there is room for more than the 0 termination
       
   381     }
       
   382     if(unicode) {
       
   383         r = SQLGetData(hStmt,
       
   384                         column+1,
       
   385                         SQL_C_TCHAR,
       
   386                         NULL,
       
   387                         0,
       
   388                         &lengthIndicator);
       
   389         if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
       
   390             colSize = lengthIndicator/sizeof(SQLTCHAR) + 1;
       
   391         QVarLengthArray<SQLTCHAR> buf(colSize);
       
   392         memset(buf.data(), 0, colSize*sizeof(SQLTCHAR));
       
   393         while (true) {
       
   394             r = SQLGetData(hStmt,
       
   395                             column+1,
       
   396                             SQL_C_TCHAR,
       
   397                             (SQLPOINTER)buf.data(),
       
   398                             colSize*sizeof(SQLTCHAR),
       
   399                             &lengthIndicator);
       
   400             if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
       
   401                 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
       
   402                     fieldVal.clear();
       
   403                     break;
       
   404                 }
       
   405                 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
       
   406                 // more data can be fetched, the length indicator does NOT
       
   407                 // contain the number of bytes returned - it contains the
       
   408                 // total number of bytes that CAN be fetched
       
   409                 // colSize-1: remove 0 termination when there is more data to fetch
       
   410                 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator/sizeof(SQLTCHAR);
       
   411                     fieldVal += fromSQLTCHAR(buf, rSize);
       
   412                 if (lengthIndicator < (unsigned int)colSize*sizeof(SQLTCHAR)) {
       
   413                     // workaround for Drivermanagers that don't return SQL_NO_DATA
       
   414                     break;
       
   415                 }
       
   416             } else if (r == SQL_NO_DATA) {
       
   417                 break;
       
   418             } else {
       
   419                 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
       
   420                 fieldVal.clear();
       
   421                 break;
       
   422             }
       
   423         }
       
   424     } else {
       
   425         r = SQLGetData(hStmt,
       
   426                         column+1,
       
   427                         SQL_C_CHAR,
       
   428                         NULL,
       
   429                         0,
       
   430                         &lengthIndicator);
       
   431         if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && lengthIndicator > 0)
       
   432             colSize = lengthIndicator + 1;
       
   433         QVarLengthArray<SQLCHAR> buf(colSize);
       
   434         while (true) {
       
   435             r = SQLGetData(hStmt,
       
   436                             column+1,
       
   437                             SQL_C_CHAR,
       
   438                             (SQLPOINTER)buf.data(),
       
   439                             colSize,
       
   440                             &lengthIndicator);
       
   441             if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
       
   442                 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
       
   443                     fieldVal.clear();
       
   444                     break;
       
   445                 }
       
   446                 // if SQL_SUCCESS_WITH_INFO is returned, indicating that
       
   447                 // more data can be fetched, the length indicator does NOT
       
   448                 // contain the number of bytes returned - it contains the
       
   449                 // total number of bytes that CAN be fetched
       
   450                 // colSize-1: remove 0 termination when there is more data to fetch
       
   451                 int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator;
       
   452                     fieldVal += QString::fromUtf8((const char *)buf.constData(), rSize);
       
   453                 if (lengthIndicator < (unsigned int)colSize) {
       
   454                     // workaround for Drivermanagers that don't return SQL_NO_DATA
       
   455                     break;
       
   456                 }
       
   457             } else if (r == SQL_NO_DATA) {
       
   458                 break;
       
   459             } else {
       
   460                 qWarning() << "qGetStringData: Error while fetching data (" << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ')';
       
   461                 fieldVal.clear();
       
   462                 break;
       
   463             }
       
   464         }
       
   465     }
       
   466     return fieldVal;
       
   467 }
       
   468 
       
   469 static QVariant qGetBinaryData(SQLHANDLE hStmt, int column)
       
   470 {
       
   471     QByteArray fieldVal;
       
   472     SQLSMALLINT colNameLen;
       
   473     SQLSMALLINT colType;
       
   474     QSQLULEN colSize;
       
   475     SQLSMALLINT colScale;
       
   476     SQLSMALLINT nullable;
       
   477     QSQLLEN lengthIndicator = 0;
       
   478     SQLRETURN r = SQL_ERROR;
       
   479 
       
   480     QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
       
   481 
       
   482     r = SQLDescribeCol(hStmt,
       
   483                        column + 1,
       
   484                        colName.data(),
       
   485                        COLNAMESIZE,
       
   486                        &colNameLen,
       
   487                        &colType,
       
   488                        &colSize,
       
   489                        &colScale,
       
   490                        &nullable);
       
   491     if (r != SQL_SUCCESS)
       
   492         qWarning() << "qGetBinaryData: Unable to describe column" << column;
       
   493     // SQLDescribeCol may return 0 if size cannot be determined
       
   494     if (!colSize)
       
   495         colSize = 255;
       
   496     else if (colSize > 65536) // read the field in 64 KB chunks
       
   497         colSize = 65536;
       
   498     fieldVal.resize(colSize);
       
   499     ulong read = 0;
       
   500     while (true) {
       
   501         r = SQLGetData(hStmt,
       
   502                         column+1,
       
   503                         SQL_C_BINARY,
       
   504                         (SQLPOINTER)(fieldVal.constData() + read),
       
   505                         colSize,
       
   506                         &lengthIndicator);
       
   507         if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
       
   508             break;
       
   509         if (lengthIndicator == SQL_NULL_DATA)
       
   510             return QVariant(QVariant::ByteArray);
       
   511         if (lengthIndicator > QSQLLEN(colSize) || lengthIndicator == SQL_NO_TOTAL) {
       
   512             read += colSize;
       
   513             colSize = 65536;
       
   514         } else {
       
   515             read += lengthIndicator;
       
   516         }
       
   517         if (r == SQL_SUCCESS) { // the whole field was read in one chunk
       
   518             fieldVal.resize(read);
       
   519             break;
       
   520         }
       
   521         fieldVal.resize(fieldVal.size() + colSize);
       
   522     }
       
   523     return fieldVal;
       
   524 }
       
   525 
       
   526 static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
       
   527 {
       
   528     SQLINTEGER intbuf = 0;
       
   529     QSQLLEN lengthIndicator = 0;
       
   530     SQLRETURN r = SQLGetData(hStmt,
       
   531                               column+1,
       
   532                               isSigned ? SQL_C_SLONG : SQL_C_ULONG,
       
   533                               (SQLPOINTER)&intbuf,
       
   534                               sizeof(intbuf),
       
   535                               &lengthIndicator);
       
   536     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
       
   537         return QVariant(QVariant::Invalid);
       
   538     if (lengthIndicator == SQL_NULL_DATA)
       
   539         return QVariant(QVariant::Int);
       
   540     if (isSigned)
       
   541         return int(intbuf);
       
   542     else
       
   543         return uint(intbuf);
       
   544 }
       
   545 
       
   546 static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
       
   547 {
       
   548     SQLDOUBLE dblbuf;
       
   549     QSQLLEN lengthIndicator = 0;
       
   550     SQLRETURN r = SQLGetData(hStmt,
       
   551                               column+1,
       
   552                               SQL_C_DOUBLE,
       
   553                               (SQLPOINTER) &dblbuf,
       
   554                               0,
       
   555                               &lengthIndicator);
       
   556     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
   557         return QVariant(QVariant::Invalid);
       
   558     }
       
   559     if(lengthIndicator == SQL_NULL_DATA)
       
   560         return QVariant(QVariant::Double);
       
   561 
       
   562     return (double) dblbuf;
       
   563 }
       
   564 
       
   565 
       
   566 static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
       
   567 {
       
   568     SQLBIGINT lngbuf = 0;
       
   569     QSQLLEN lengthIndicator = 0;
       
   570     SQLRETURN r = SQLGetData(hStmt,
       
   571                               column+1,
       
   572                               isSigned ? SQL_C_SBIGINT : SQL_C_UBIGINT,
       
   573                               (SQLPOINTER) &lngbuf,
       
   574                               sizeof(lngbuf),
       
   575                               &lengthIndicator);
       
   576     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
       
   577         return QVariant(QVariant::Invalid);
       
   578     if (lengthIndicator == SQL_NULL_DATA)
       
   579         return QVariant(QVariant::LongLong);
       
   580 
       
   581     if (isSigned)
       
   582         return qint64(lngbuf);
       
   583     else
       
   584         return quint64(lngbuf);
       
   585 }
       
   586 
       
   587 // creates a QSqlField from a valid hStmt generated
       
   588 // by SQLColumns. The hStmt has to point to a valid position.
       
   589 static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, const QODBCDriverPrivate* p)
       
   590 {
       
   591     QString fname = qGetStringData(hStmt, 3, -1, p->unicode);
       
   592     int type = qGetIntData(hStmt, 4).toInt(); // column type
       
   593     QSqlField f(fname, qDecodeODBCType(type, p));
       
   594     int required = qGetIntData(hStmt, 10).toInt(); // nullable-flag
       
   595     // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
       
   596     if (required == SQL_NO_NULLS)
       
   597         f.setRequired(true);
       
   598     else if (required == SQL_NULLABLE)
       
   599         f.setRequired(false);
       
   600     // else we don't know
       
   601     QVariant var = qGetIntData(hStmt, 6);
       
   602     f.setLength(var.isNull() ? -1 : var.toInt()); // column size
       
   603     var = qGetIntData(hStmt, 8).toInt();
       
   604     f.setPrecision(var.isNull() ? -1 : var.toInt()); // precision
       
   605     f.setSqlType(type);
       
   606     return f;
       
   607 }
       
   608 
       
   609 static QSqlField qMakeFieldInfo(const QODBCPrivate* p, int i )
       
   610 {
       
   611     SQLSMALLINT colNameLen;
       
   612     SQLSMALLINT colType;
       
   613     QSQLULEN colSize;
       
   614     SQLSMALLINT colScale;
       
   615     SQLSMALLINT nullable;
       
   616     SQLRETURN r = SQL_ERROR;
       
   617     QVarLengthArray<SQLTCHAR> colName(COLNAMESIZE);
       
   618     r = SQLDescribeCol(p->hStmt,
       
   619                         i+1,
       
   620                         colName.data(),
       
   621                         (SQLSMALLINT)COLNAMESIZE,
       
   622                         &colNameLen,
       
   623                         &colType,
       
   624                         &colSize,
       
   625                         &colScale,
       
   626                         &nullable);
       
   627 
       
   628     if (r != SQL_SUCCESS) {
       
   629         qSqlWarning(QString::fromLatin1("qMakeField: Unable to describe column %1").arg(i), p);
       
   630         return QSqlField();
       
   631     }
       
   632 
       
   633     QSQLLEN unsignedFlag = SQL_FALSE;
       
   634     r = SQLColAttribute (p->hStmt,
       
   635                          i + 1,
       
   636                          SQL_DESC_UNSIGNED,
       
   637                          0,
       
   638                          0,
       
   639                          0,
       
   640                          &unsignedFlag);
       
   641     if (r != SQL_SUCCESS) {
       
   642         qSqlWarning(QString::fromLatin1("qMakeField: Unable to get column attributes for column %1").arg(i), p);
       
   643     }
       
   644 
       
   645 #ifdef UNICODE
       
   646     QString qColName(fromSQLTCHAR(colName, colNameLen));
       
   647 #else
       
   648     QString qColName = QString::fromUtf8((const char *)colName.constData());
       
   649 #endif
       
   650     // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
       
   651     int required = -1;
       
   652     if (nullable == SQL_NO_NULLS) {
       
   653         required = 1;
       
   654     } else if (nullable == SQL_NULLABLE) {
       
   655         required = 0;
       
   656     }
       
   657     QVariant::Type type = qDecodeODBCType(colType, p, unsignedFlag == SQL_FALSE);
       
   658     QSqlField f(qColName, type);
       
   659     f.setSqlType(colType);
       
   660     f.setLength(colSize == 0 ? -1 : int(colSize));
       
   661     f.setPrecision(colScale == 0 ? -1 : int(colScale));
       
   662     if (nullable == SQL_NO_NULLS)
       
   663         f.setRequired(true);
       
   664     else if (nullable == SQL_NULLABLE)
       
   665         f.setRequired(false);
       
   666     // else we don't know
       
   667     return f;
       
   668 }
       
   669 
       
   670 static int qGetODBCVersion(const QString &connOpts)
       
   671 {
       
   672 #ifndef Q_ODBC_VERSION_2
       
   673     if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive))
       
   674         return SQL_OV_ODBC3;
       
   675 #endif
       
   676     if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC2"), Qt::CaseInsensitive))
       
   677         return SQL_OV_ODBC2;
       
   678 #ifdef _IODBCUNIX_H
       
   679     return SQL_OV_ODBC3;
       
   680 #else
       
   681     return SQL_OV_ODBC2;
       
   682 #endif
       
   683 }
       
   684 
       
   685 QChar QODBCDriverPrivate::quoteChar()
       
   686 {
       
   687     if (!isQuoteInitialized) {
       
   688         SQLTCHAR driverResponse[4];
       
   689         SQLSMALLINT length;
       
   690         int r = SQLGetInfo(hDbc,
       
   691                 SQL_IDENTIFIER_QUOTE_CHAR,
       
   692                 &driverResponse,
       
   693                 sizeof(driverResponse),
       
   694                 &length);
       
   695         if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
       
   696 #ifdef UNICODE
       
   697             quote = QChar(driverResponse[0]);
       
   698 #else
       
   699             quote = QLatin1Char(driverResponse[0]);
       
   700 #endif
       
   701         else
       
   702             quote = QLatin1Char('"');
       
   703         isQuoteInitialized = true;
       
   704     }
       
   705     return quote;
       
   706 }
       
   707 
       
   708 
       
   709 bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
       
   710 {
       
   711     // Set any connection attributes
       
   712     const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
       
   713     SQLRETURN r = SQL_SUCCESS;
       
   714     for (int i = 0; i < opts.count(); ++i) {
       
   715         const QString tmp(opts.at(i));
       
   716         int idx;
       
   717         if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
       
   718             qWarning() << "QODBCDriver::open: Illegal connect option value '" << tmp << '\'';
       
   719             continue;
       
   720         }
       
   721         const QString opt(tmp.left(idx));
       
   722         const QString val(tmp.mid(idx + 1).simplified());
       
   723         SQLUINTEGER v = 0;
       
   724 
       
   725         r = SQL_SUCCESS;
       
   726         if (opt.toUpper() == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
       
   727             if (val.toUpper() == QLatin1String("SQL_MODE_READ_ONLY")) {
       
   728                 v = SQL_MODE_READ_ONLY;
       
   729             } else if (val.toUpper() == QLatin1String("SQL_MODE_READ_WRITE")) {
       
   730                 v = SQL_MODE_READ_WRITE;
       
   731             } else {
       
   732                 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
       
   733                 continue;
       
   734             }
       
   735             r = SQLSetConnectAttr(hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0);
       
   736         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_TIMEOUT")) {
       
   737             v = val.toUInt();
       
   738             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0);
       
   739         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
       
   740             v = val.toUInt();
       
   741             r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
       
   742         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
       
   743             val.utf16(); // 0 terminate
       
   744             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
       
   745 #ifdef UNICODE
       
   746                                     toSQLTCHAR(val).data(),
       
   747 #else
       
   748                                     (SQLCHAR*) val.toUtf8().data(),
       
   749 #endif
       
   750                                     val.length()*sizeof(SQLTCHAR));
       
   751         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
       
   752             if (val.toUpper() == QLatin1String("SQL_TRUE")) {
       
   753                 v = SQL_TRUE;
       
   754             } else if (val.toUpper() == QLatin1String("SQL_FALSE")) {
       
   755                 v = SQL_FALSE;
       
   756             } else {
       
   757                 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
       
   758                 continue;
       
   759             }
       
   760             r = SQLSetConnectAttr(hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0);
       
   761         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_PACKET_SIZE")) {
       
   762             v = val.toUInt();
       
   763             r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0);
       
   764         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
       
   765             val.utf16(); // 0 terminate
       
   766             r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
       
   767 #ifdef UNICODE
       
   768                                     toSQLTCHAR(val).data(),
       
   769 #else
       
   770                                     (SQLCHAR*) val.toUtf8().data(),
       
   771 #endif
       
   772                                     val.length()*sizeof(SQLTCHAR));
       
   773         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
       
   774             if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
       
   775                 v = SQL_OPT_TRACE_OFF;
       
   776             } else if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_ON")) {
       
   777                 v = SQL_OPT_TRACE_ON;
       
   778             } else {
       
   779                 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
       
   780                 continue;
       
   781             }
       
   782             r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0);
       
   783 #ifndef Q_ODBC_VERSION_2
       
   784         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) {
       
   785             if (val == QLatin1String("SQL_CP_OFF"))
       
   786                 v = SQL_CP_OFF;
       
   787             else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_DRIVER"))
       
   788                 v = SQL_CP_ONE_PER_DRIVER;
       
   789             else if (val.toUpper() == QLatin1String("SQL_CP_ONE_PER_HENV"))
       
   790                 v = SQL_CP_ONE_PER_HENV;
       
   791             else if (val.toUpper() == QLatin1String("SQL_CP_DEFAULT"))
       
   792                 v = SQL_CP_DEFAULT;
       
   793             else {
       
   794                 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
       
   795                 continue;
       
   796             }
       
   797             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)v, 0);
       
   798         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CP_MATCH")) {
       
   799             if (val.toUpper() == QLatin1String("SQL_CP_STRICT_MATCH"))
       
   800                 v = SQL_CP_STRICT_MATCH;
       
   801             else if (val.toUpper() == QLatin1String("SQL_CP_RELAXED_MATCH"))
       
   802                 v = SQL_CP_RELAXED_MATCH;
       
   803             else if (val.toUpper() == QLatin1String("SQL_CP_MATCH_DEFAULT"))
       
   804                 v = SQL_CP_MATCH_DEFAULT;
       
   805             else {
       
   806                 qWarning() << "QODBCDriver::open: Unknown option value '" << val << '\'';
       
   807                 continue;
       
   808             }
       
   809             r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER)v, 0);
       
   810 #endif
       
   811         } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) {
       
   812             // Already handled in QODBCDriver::open()
       
   813             continue;
       
   814         } else {
       
   815                 qWarning() << "QODBCDriver::open: Unknown connection attribute '" << opt << '\'';
       
   816         }
       
   817         if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
       
   818             qSqlWarning(QString::fromLatin1("QODBCDriver::open: Unable to set connection attribute'%1'").arg(
       
   819                         opt), this);
       
   820     }
       
   821     return true;
       
   822 }
       
   823 
       
   824 void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
       
   825                                        QString &schema, QString &table)
       
   826 {
       
   827     if (!useSchema) {
       
   828         table = qualifier;
       
   829         return;
       
   830     }
       
   831     QStringList l = qualifier.split(QLatin1Char('.'));
       
   832     if (l.count() > 3)
       
   833         return; // can't possibly be a valid table qualifier
       
   834     int i = 0, n = l.count();
       
   835     if (n == 1) {
       
   836         table = qualifier;
       
   837     } else {
       
   838         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
       
   839             if (n == 3) {
       
   840                 if (i == 0) {
       
   841                     catalog = *it;
       
   842                 } else if (i == 1) {
       
   843                     schema = *it;
       
   844                 } else if (i == 2) {
       
   845                     table = *it;
       
   846                 }
       
   847             } else if (n == 2) {
       
   848                 if (i == 0) {
       
   849                     schema = *it;
       
   850                 } else if (i == 1) {
       
   851                     table = *it;
       
   852                 }
       
   853             }
       
   854             i++;
       
   855         }
       
   856     }
       
   857 }
       
   858 
       
   859 QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
       
   860 {
       
   861     DefaultCase ret;
       
   862     SQLUSMALLINT casing;
       
   863     int r = SQLGetInfo(hDbc,
       
   864             SQL_IDENTIFIER_CASE,
       
   865             &casing,
       
   866             sizeof(casing),
       
   867             NULL);
       
   868     if ( r != SQL_SUCCESS)
       
   869         ret = Mixed;//arbitrary case if driver cannot be queried
       
   870     else {
       
   871         switch (casing) {
       
   872             case (SQL_IC_UPPER):
       
   873                 ret = Upper;
       
   874                 break;
       
   875             case (SQL_IC_LOWER):
       
   876                 ret = Lower;
       
   877                 break;
       
   878             case (SQL_IC_SENSITIVE):
       
   879                 ret = Sensitive;
       
   880                 break;
       
   881             case (SQL_IC_MIXED):
       
   882             default:
       
   883                 ret = Mixed;
       
   884                 break;
       
   885         }
       
   886     }
       
   887     return ret;
       
   888 }
       
   889 
       
   890 /*
       
   891    Adjust the casing of an identifier to match what the
       
   892    database engine would have done to it.
       
   893 */
       
   894 QString QODBCDriverPrivate::adjustCase(const QString &identifier) const
       
   895 {
       
   896     QString ret = identifier;
       
   897     switch(defaultCase()) {
       
   898         case (Lower):
       
   899             ret = identifier.toLower();
       
   900             break;
       
   901         case (Upper):
       
   902             ret = identifier.toUpper();
       
   903             break;
       
   904         case(Mixed):
       
   905         case(Sensitive):
       
   906         default:
       
   907             ret = identifier;
       
   908     }
       
   909     return ret;
       
   910 }
       
   911 
       
   912 ////////////////////////////////////////////////////////////////////////////
       
   913 
       
   914 QODBCResult::QODBCResult(const QODBCDriver * db, QODBCDriverPrivate* p)
       
   915 : QSqlResult(db)
       
   916 {
       
   917     d = new QODBCPrivate(p);
       
   918 }
       
   919 
       
   920 QODBCResult::~QODBCResult()
       
   921 {
       
   922     if (d->hStmt && d->isStmtHandleValid(driver()) && driver()->isOpen()) {
       
   923         SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
       
   924         if (r != SQL_SUCCESS)
       
   925             qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")
       
   926                          + QString::number(r), d);
       
   927     }
       
   928 
       
   929     delete d;
       
   930 }
       
   931 
       
   932 bool QODBCResult::reset (const QString& query)
       
   933 {
       
   934     setActive(false);
       
   935     setAt(QSql::BeforeFirstRow);
       
   936     d->rInf.clear();
       
   937     d->fieldCache.clear();
       
   938     d->fieldCacheIdx = 0;
       
   939 
       
   940     // Always reallocate the statement handle - the statement attributes
       
   941     // are not reset if SQLFreeStmt() is called which causes some problems.
       
   942     SQLRETURN r;
       
   943     if (d->hStmt && d->isStmtHandleValid(driver())) {
       
   944         r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
       
   945         if (r != SQL_SUCCESS) {
       
   946             qSqlWarning(QLatin1String("QODBCResult::reset: Unable to free statement handle"), d);
       
   947             return false;
       
   948         }
       
   949     }
       
   950     r  = SQLAllocHandle(SQL_HANDLE_STMT,
       
   951                          d->dpDbc(),
       
   952                          &d->hStmt);
       
   953     if (r != SQL_SUCCESS) {
       
   954         qSqlWarning(QLatin1String("QODBCResult::reset: Unable to allocate statement handle"), d);
       
   955         return false;
       
   956     }
       
   957 
       
   958     d->updateStmtHandleState(driver());
       
   959 
       
   960     if (d->userForwardOnly) {
       
   961         r = SQLSetStmtAttr(d->hStmt,
       
   962                             SQL_ATTR_CURSOR_TYPE,
       
   963                             (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
       
   964                             SQL_IS_UINTEGER);
       
   965     } else {
       
   966         r = SQLSetStmtAttr(d->hStmt,
       
   967                             SQL_ATTR_CURSOR_TYPE,
       
   968                             (SQLPOINTER)SQL_CURSOR_STATIC,
       
   969                             SQL_IS_UINTEGER);
       
   970     }
       
   971     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
   972         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
   973             "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
       
   974             "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
       
   975         return false;
       
   976     }
       
   977 
       
   978 #ifdef UNICODE
       
   979     r = SQLExecDirect(d->hStmt,
       
   980                        toSQLTCHAR(query).data(),
       
   981                        (SQLINTEGER) query.length());
       
   982 #else
       
   983     QByteArray query8 = query.toUtf8();
       
   984     r = SQLExecDirect(d->hStmt,
       
   985                        (SQLCHAR*) query8.data(),
       
   986                        (SQLINTEGER) query8.length());
       
   987 #endif
       
   988     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
       
   989         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
   990                      "Unable to execute statement"), QSqlError::StatementError, d));
       
   991         return false;
       
   992     }
       
   993 
       
   994     if(r == SQL_NO_DATA) {
       
   995         setSelect(false);
       
   996         return true;
       
   997     }
       
   998 
       
   999     SQLINTEGER isScrollable, bufferLength;
       
  1000     r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
       
  1001     if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
       
  1002         QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
       
  1003 
       
  1004     SQLSMALLINT count;
       
  1005     SQLNumResultCols(d->hStmt, &count);
       
  1006     if (count) {
       
  1007         setSelect(true);
       
  1008         for (int i = 0; i < count; ++i) {
       
  1009             d->rInf.append(qMakeFieldInfo(d, i));
       
  1010         }
       
  1011         d->fieldCache.resize(count);
       
  1012     } else {
       
  1013         setSelect(false);
       
  1014     }
       
  1015     setActive(true);
       
  1016 
       
  1017     return true;
       
  1018 }
       
  1019 
       
  1020 bool QODBCResult::fetch(int i)
       
  1021 {
       
  1022     if (!driver()->isOpen())
       
  1023         return false;
       
  1024 
       
  1025     if (isForwardOnly() && i < at())
       
  1026         return false;
       
  1027     if (i == at())
       
  1028         return true;
       
  1029     d->clearValues();
       
  1030     int actualIdx = i + 1;
       
  1031     if (actualIdx <= 0) {
       
  1032         setAt(QSql::BeforeFirstRow);
       
  1033         return false;
       
  1034     }
       
  1035     SQLRETURN r;
       
  1036     if (isForwardOnly()) {
       
  1037         bool ok = true;
       
  1038         while (ok && i > at())
       
  1039             ok = fetchNext();
       
  1040         return ok;
       
  1041     } else {
       
  1042         r = SQLFetchScroll(d->hStmt,
       
  1043                             SQL_FETCH_ABSOLUTE,
       
  1044                             actualIdx);
       
  1045     }
       
  1046     if (r != SQL_SUCCESS) {
       
  1047         if (r != SQL_NO_DATA)
       
  1048             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1049                 "Unable to fetch"), QSqlError::ConnectionError, d));
       
  1050         return false;
       
  1051     }
       
  1052     setAt(i);
       
  1053     return true;
       
  1054 }
       
  1055 
       
  1056 bool QODBCResult::fetchNext()
       
  1057 {
       
  1058     SQLRETURN r;
       
  1059     d->clearValues();
       
  1060 
       
  1061     if (d->hasSQLFetchScroll)
       
  1062         r = SQLFetchScroll(d->hStmt,
       
  1063                            SQL_FETCH_NEXT,
       
  1064                            0);
       
  1065     else
       
  1066         r = SQLFetch(d->hStmt);
       
  1067 
       
  1068     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1069         if (r != SQL_NO_DATA)
       
  1070             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1071                 "Unable to fetch next"), QSqlError::ConnectionError, d));
       
  1072         return false;
       
  1073     }
       
  1074     setAt(at() + 1);
       
  1075     return true;
       
  1076 }
       
  1077 
       
  1078 bool QODBCResult::fetchFirst()
       
  1079 {
       
  1080     if (isForwardOnly() && at() != QSql::BeforeFirstRow)
       
  1081         return false;
       
  1082     SQLRETURN r;
       
  1083     d->clearValues();
       
  1084     if (isForwardOnly()) {
       
  1085         return fetchNext();
       
  1086     }
       
  1087     r = SQLFetchScroll(d->hStmt,
       
  1088                        SQL_FETCH_FIRST,
       
  1089                        0);
       
  1090     if (r != SQL_SUCCESS) {
       
  1091         if (r != SQL_NO_DATA)
       
  1092             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1093                 "Unable to fetch first"), QSqlError::ConnectionError, d));
       
  1094         return false;
       
  1095     }
       
  1096     setAt(0);
       
  1097     return true;
       
  1098 }
       
  1099 
       
  1100 bool QODBCResult::fetchPrevious()
       
  1101 {
       
  1102     if (isForwardOnly())
       
  1103         return false;
       
  1104     SQLRETURN r;
       
  1105     d->clearValues();
       
  1106     r = SQLFetchScroll(d->hStmt,
       
  1107                        SQL_FETCH_PRIOR,
       
  1108                        0);
       
  1109     if (r != SQL_SUCCESS) {
       
  1110         if (r != SQL_NO_DATA)
       
  1111             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1112                 "Unable to fetch previous"), QSqlError::ConnectionError, d));
       
  1113         return false;
       
  1114     }
       
  1115     setAt(at() - 1);
       
  1116     return true;
       
  1117 }
       
  1118 
       
  1119 bool QODBCResult::fetchLast()
       
  1120 {
       
  1121     SQLRETURN r;
       
  1122     d->clearValues();
       
  1123 
       
  1124     if (isForwardOnly()) {
       
  1125         // cannot seek to last row in forwardOnly mode, so we have to use brute force
       
  1126         int i = at();
       
  1127         if (i == QSql::AfterLastRow)
       
  1128             return false;
       
  1129         if (i == QSql::BeforeFirstRow)
       
  1130             i = 0;
       
  1131         while (fetchNext())
       
  1132             ++i;
       
  1133         setAt(i);
       
  1134         return true;
       
  1135     }
       
  1136 
       
  1137     r = SQLFetchScroll(d->hStmt,
       
  1138                        SQL_FETCH_LAST,
       
  1139                        0);
       
  1140     if (r != SQL_SUCCESS) {
       
  1141         if (r != SQL_NO_DATA)
       
  1142             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1143                 "Unable to fetch last"), QSqlError::ConnectionError, d));
       
  1144         return false;
       
  1145     }
       
  1146     SQLINTEGER currRow;
       
  1147     r = SQLGetStmtAttr(d->hStmt,
       
  1148                         SQL_ROW_NUMBER,
       
  1149                         &currRow,
       
  1150                         SQL_IS_INTEGER,
       
  1151                         0);
       
  1152     if (r != SQL_SUCCESS)
       
  1153         return false;
       
  1154     setAt(currRow-1);
       
  1155     return true;
       
  1156 }
       
  1157 
       
  1158 QVariant QODBCResult::data(int field)
       
  1159 {
       
  1160     if (field >= d->rInf.count() || field < 0) {
       
  1161         qWarning() << "QODBCResult::data: column" << field << "out of range";
       
  1162         return QVariant();
       
  1163     }
       
  1164     if (field < d->fieldCacheIdx)
       
  1165         return d->fieldCache.at(field);
       
  1166 
       
  1167     SQLRETURN r(0);
       
  1168     QSQLLEN lengthIndicator = 0;
       
  1169 
       
  1170     for (int i = d->fieldCacheIdx; i <= field; ++i) {
       
  1171         // some servers do not support fetching column n after we already
       
  1172         // fetched column n+1, so cache all previous columns here
       
  1173         const QSqlField info = d->rInf.field(i);
       
  1174         switch (info.type()) {
       
  1175         case QVariant::LongLong:
       
  1176             d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
       
  1177         break;
       
  1178         case QVariant::ULongLong:
       
  1179             d->fieldCache[i] = qGetBigIntData(d->hStmt, i, false);
       
  1180             break;
       
  1181         case QVariant::Int:
       
  1182             d->fieldCache[i] = qGetIntData(d->hStmt, i);
       
  1183         break;
       
  1184         case QVariant::UInt:
       
  1185             d->fieldCache[i] = qGetIntData(d->hStmt, i, false);
       
  1186             break;
       
  1187         case QVariant::Date:
       
  1188             DATE_STRUCT dbuf;
       
  1189             r = SQLGetData(d->hStmt,
       
  1190                             i + 1,
       
  1191                             SQL_C_DATE,
       
  1192                             (SQLPOINTER)&dbuf,
       
  1193                             0,
       
  1194                             &lengthIndicator);
       
  1195             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
       
  1196                 d->fieldCache[i] = QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
       
  1197             else
       
  1198                 d->fieldCache[i] = QVariant(QVariant::Date);
       
  1199         break;
       
  1200         case QVariant::Time:
       
  1201             TIME_STRUCT tbuf;
       
  1202             r = SQLGetData(d->hStmt,
       
  1203                             i + 1,
       
  1204                             SQL_C_TIME,
       
  1205                             (SQLPOINTER)&tbuf,
       
  1206                             0,
       
  1207                             &lengthIndicator);
       
  1208             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
       
  1209                 d->fieldCache[i] = QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
       
  1210             else
       
  1211                 d->fieldCache[i] = QVariant(QVariant::Time);
       
  1212         break;
       
  1213         case QVariant::DateTime:
       
  1214             TIMESTAMP_STRUCT dtbuf;
       
  1215             r = SQLGetData(d->hStmt,
       
  1216                             i + 1,
       
  1217                             SQL_C_TIMESTAMP,
       
  1218                             (SQLPOINTER)&dtbuf,
       
  1219                             0,
       
  1220                             &lengthIndicator);
       
  1221             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA))
       
  1222                 d->fieldCache[i] = QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
       
  1223                        QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
       
  1224             else
       
  1225                 d->fieldCache[i] = QVariant(QVariant::DateTime);
       
  1226             break;
       
  1227         case QVariant::ByteArray:
       
  1228             d->fieldCache[i] = qGetBinaryData(d->hStmt, i);
       
  1229             break;
       
  1230         case QVariant::String:
       
  1231             d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode);
       
  1232             break;
       
  1233         case QVariant::Double:
       
  1234             switch(numericalPrecisionPolicy()) {
       
  1235                 case QSql::LowPrecisionInt32:
       
  1236                     d->fieldCache[i] = qGetIntData(d->hStmt, i);
       
  1237                     break;
       
  1238                 case QSql::LowPrecisionInt64:
       
  1239                     d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
       
  1240                     break;
       
  1241                 case QSql::LowPrecisionDouble:
       
  1242                     d->fieldCache[i] = qGetDoubleData(d->hStmt, i);
       
  1243                     break;
       
  1244                 case QSql::HighPrecision:
       
  1245                     d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
       
  1246                     break;
       
  1247             }
       
  1248             break;
       
  1249         default:
       
  1250             d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false));
       
  1251             break;
       
  1252         }
       
  1253         d->fieldCacheIdx = field + 1;
       
  1254     }
       
  1255     return d->fieldCache[field];
       
  1256 }
       
  1257 
       
  1258 bool QODBCResult::isNull(int field)
       
  1259 {
       
  1260     if (field < 0 || field > d->fieldCache.size())
       
  1261         return true;
       
  1262     if (field <= d->fieldCacheIdx) {
       
  1263         // since there is no good way to find out whether the value is NULL
       
  1264         // without fetching the field we'll fetch it here.
       
  1265         // (data() also sets the NULL flag)
       
  1266         data(field);
       
  1267     }
       
  1268     return d->fieldCache.at(field).isNull();
       
  1269 }
       
  1270 
       
  1271 int QODBCResult::size()
       
  1272 {
       
  1273     return -1;
       
  1274 }
       
  1275 
       
  1276 int QODBCResult::numRowsAffected()
       
  1277 {
       
  1278     QSQLLEN affectedRowCount = 0;
       
  1279     SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
       
  1280     if (r == SQL_SUCCESS)
       
  1281         return affectedRowCount;
       
  1282     else
       
  1283         qSqlWarning(QLatin1String("QODBCResult::numRowsAffected: Unable to count affected rows"), d);
       
  1284     return -1;
       
  1285 }
       
  1286 
       
  1287 bool QODBCResult::prepare(const QString& query)
       
  1288 {
       
  1289     setActive(false);
       
  1290     setAt(QSql::BeforeFirstRow);
       
  1291     SQLRETURN r;
       
  1292 
       
  1293     d->rInf.clear();
       
  1294     if (d->hStmt && d->isStmtHandleValid(driver())) {
       
  1295         r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
       
  1296         if (r != SQL_SUCCESS) {
       
  1297             qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to close statement"), d);
       
  1298             return false;
       
  1299         }
       
  1300     }
       
  1301     r  = SQLAllocHandle(SQL_HANDLE_STMT,
       
  1302                          d->dpDbc(),
       
  1303                          &d->hStmt);
       
  1304     if (r != SQL_SUCCESS) {
       
  1305         qSqlWarning(QLatin1String("QODBCResult::prepare: Unable to allocate statement handle"), d);
       
  1306         return false;
       
  1307     }
       
  1308 
       
  1309     d->updateStmtHandleState(driver());
       
  1310 
       
  1311     if (d->userForwardOnly) {
       
  1312         r = SQLSetStmtAttr(d->hStmt,
       
  1313                             SQL_ATTR_CURSOR_TYPE,
       
  1314                             (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
       
  1315                             SQL_IS_UINTEGER);
       
  1316     } else {
       
  1317         r = SQLSetStmtAttr(d->hStmt,
       
  1318                             SQL_ATTR_CURSOR_TYPE,
       
  1319                             (SQLPOINTER)SQL_CURSOR_STATIC,
       
  1320                             SQL_IS_UINTEGER);
       
  1321     }
       
  1322     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1323         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1324             "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. "
       
  1325             "Please check your ODBC driver configuration"), QSqlError::StatementError, d));
       
  1326         return false;
       
  1327     }
       
  1328 
       
  1329 #ifdef UNICODE
       
  1330     r = SQLPrepare(d->hStmt,
       
  1331                     toSQLTCHAR(query).data(),
       
  1332                     (SQLINTEGER) query.length());
       
  1333 #else
       
  1334     QByteArray query8 = query.toUtf8();
       
  1335     r = SQLPrepare(d->hStmt,
       
  1336                     (SQLCHAR*) query8.data(),
       
  1337                     (SQLINTEGER) query8.length());
       
  1338 #endif
       
  1339 
       
  1340     if (r != SQL_SUCCESS) {
       
  1341         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1342                      "Unable to prepare statement"), QSqlError::StatementError, d));
       
  1343         return false;
       
  1344     }
       
  1345     return true;
       
  1346 }
       
  1347 
       
  1348 bool QODBCResult::exec()
       
  1349 {
       
  1350     setActive(false);
       
  1351     setAt(QSql::BeforeFirstRow);
       
  1352     d->rInf.clear();
       
  1353     d->fieldCache.clear();
       
  1354     d->fieldCacheIdx = 0;
       
  1355 
       
  1356     if (!d->hStmt) {
       
  1357         qSqlWarning(QLatin1String("QODBCResult::exec: No statement handle available"), d);
       
  1358         return false;
       
  1359     }
       
  1360 
       
  1361     if (isSelect())
       
  1362         SQLCloseCursor(d->hStmt);
       
  1363 
       
  1364     QList<QByteArray> tmpStorage; // holds temporary buffers
       
  1365     QVarLengthArray<QSQLLEN, 32> indicators(boundValues().count());
       
  1366     memset(indicators.data(), 0, indicators.size() * sizeof(QSQLLEN));
       
  1367 
       
  1368     // bind parameters - only positional binding allowed
       
  1369     QVector<QVariant>& values = boundValues();
       
  1370     int i;
       
  1371     SQLRETURN r;
       
  1372     for (i = 0; i < values.count(); ++i) {
       
  1373         if (bindValueType(i) & QSql::Out)
       
  1374             values[i].detach();
       
  1375         const QVariant &val = values.at(i);
       
  1376         QSQLLEN *ind = &indicators[i];
       
  1377         if (val.isNull())
       
  1378             *ind = SQL_NULL_DATA;
       
  1379         switch (val.type()) {
       
  1380             case QVariant::Date: {
       
  1381                 QByteArray ba;
       
  1382                 ba.resize(sizeof(DATE_STRUCT));
       
  1383                 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
       
  1384                 QDate qdt = val.toDate();
       
  1385                 dt->year = qdt.year();
       
  1386                 dt->month = qdt.month();
       
  1387                 dt->day = qdt.day();
       
  1388                 r = SQLBindParameter(d->hStmt,
       
  1389                                       i + 1,
       
  1390                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1391                                       SQL_C_DATE,
       
  1392                                       SQL_DATE,
       
  1393                                       0,
       
  1394                                       0,
       
  1395                                       (void *) dt,
       
  1396                                       0,
       
  1397                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1398                 tmpStorage.append(ba);
       
  1399                 break; }
       
  1400             case QVariant::Time: {
       
  1401                 QByteArray ba;
       
  1402                 ba.resize(sizeof(TIME_STRUCT));
       
  1403                 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
       
  1404                 QTime qdt = val.toTime();
       
  1405                 dt->hour = qdt.hour();
       
  1406                 dt->minute = qdt.minute();
       
  1407                 dt->second = qdt.second();
       
  1408                 r = SQLBindParameter(d->hStmt,
       
  1409                                       i + 1,
       
  1410                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1411                                       SQL_C_TIME,
       
  1412                                       SQL_TIME,
       
  1413                                       0,
       
  1414                                       0,
       
  1415                                       (void *) dt,
       
  1416                                       0,
       
  1417                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1418                 tmpStorage.append(ba);
       
  1419                 break; }
       
  1420             case QVariant::DateTime: {
       
  1421                 QByteArray ba;
       
  1422                 ba.resize(sizeof(TIMESTAMP_STRUCT));
       
  1423                 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
       
  1424                 QDateTime qdt = val.toDateTime();
       
  1425                 dt->year = qdt.date().year();
       
  1426                 dt->month = qdt.date().month();
       
  1427                 dt->day = qdt.date().day();
       
  1428                 dt->hour = qdt.time().hour();
       
  1429                 dt->minute = qdt.time().minute();
       
  1430                 dt->second = qdt.time().second();
       
  1431                 dt->fraction = qdt.time().msec() * 1000000;
       
  1432                 r = SQLBindParameter(d->hStmt,
       
  1433                                       i + 1,
       
  1434                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1435                                       SQL_C_TIMESTAMP,
       
  1436                                       SQL_TIMESTAMP,
       
  1437                                       19,
       
  1438                                       0,
       
  1439                                       (void *) dt,
       
  1440                                       0,
       
  1441                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1442                 tmpStorage.append(ba);
       
  1443                 break; }
       
  1444             case QVariant::Int:
       
  1445                 r = SQLBindParameter(d->hStmt,
       
  1446                                       i + 1,
       
  1447                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1448                                       SQL_C_SLONG,
       
  1449                                       SQL_INTEGER,
       
  1450                                       0,
       
  1451                                       0,
       
  1452                                       (void *) val.constData(),
       
  1453                                       0,
       
  1454                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1455                 break;
       
  1456             case QVariant::UInt:
       
  1457                 r = SQLBindParameter(d->hStmt,
       
  1458                                       i + 1,
       
  1459                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1460                                       SQL_C_ULONG,
       
  1461                                       SQL_NUMERIC,
       
  1462                                       15,
       
  1463                                       0,
       
  1464                                       (void *) val.constData(),
       
  1465                                       0,
       
  1466                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1467                 break;
       
  1468             case QVariant::Double:
       
  1469                 r = SQLBindParameter(d->hStmt,
       
  1470                                       i + 1,
       
  1471                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1472                                       SQL_C_DOUBLE,
       
  1473                                       SQL_DOUBLE,
       
  1474                                       0,
       
  1475                                       0,
       
  1476                                       (void *) val.constData(),
       
  1477                                       0,
       
  1478                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1479                 break;
       
  1480             case QVariant::LongLong:
       
  1481                 r = SQLBindParameter(d->hStmt,
       
  1482                                       i + 1,
       
  1483                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1484                                       SQL_C_SBIGINT,
       
  1485                                       SQL_BIGINT,
       
  1486                                       0,
       
  1487                                       0,
       
  1488                                       (void *) val.constData(),
       
  1489                                       0,
       
  1490                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1491                 break;
       
  1492             case QVariant::ULongLong:
       
  1493                 r = SQLBindParameter(d->hStmt,
       
  1494                                       i + 1,
       
  1495                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1496                                       SQL_C_UBIGINT,
       
  1497                                       SQL_BIGINT,
       
  1498                                       0,
       
  1499                                       0,
       
  1500                                       (void *) val.constData(),
       
  1501                                       0,
       
  1502                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1503                 break;
       
  1504             case QVariant::ByteArray:
       
  1505                 if (*ind != SQL_NULL_DATA) {
       
  1506                     *ind = val.toByteArray().size();
       
  1507                 }
       
  1508                 r = SQLBindParameter(d->hStmt,
       
  1509                                       i + 1,
       
  1510                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1511                                       SQL_C_BINARY,
       
  1512                                       SQL_LONGVARBINARY,
       
  1513                                       val.toByteArray().size(),
       
  1514                                       0,
       
  1515                                       (void *) val.toByteArray().constData(),
       
  1516                                       val.toByteArray().size(),
       
  1517                                       ind);
       
  1518                 break;
       
  1519             case QVariant::Bool:
       
  1520                 r = SQLBindParameter(d->hStmt,
       
  1521                                       i + 1,
       
  1522                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1523                                       SQL_C_BIT,
       
  1524                                       SQL_BIT,
       
  1525                                       0,
       
  1526                                       0,
       
  1527                                       (void *) val.constData(),
       
  1528                                       0,
       
  1529                                       *ind == SQL_NULL_DATA ? ind : NULL);
       
  1530                 break;
       
  1531             case QVariant::String:
       
  1532 #ifndef Q_ODBC_VERSION_2
       
  1533                 if (d->unicode) {
       
  1534                     QString str = val.toString();
       
  1535                     if (*ind != SQL_NULL_DATA)
       
  1536                         *ind = str.length() * sizeof(SQLTCHAR);
       
  1537                     int strSize = str.length() * sizeof(SQLTCHAR);
       
  1538 
       
  1539                     if (bindValueType(i) & QSql::Out) {
       
  1540                         QVarLengthArray<SQLTCHAR> ba(toSQLTCHAR(str));
       
  1541                         ba.reserve(str.capacity());
       
  1542                         r = SQLBindParameter(d->hStmt,
       
  1543                                             i + 1,
       
  1544                                             qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1545                                             SQL_C_TCHAR,
       
  1546                                             strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
       
  1547                                             0, // god knows... don't change this!
       
  1548                                             0,
       
  1549                                             (void *)ba.constData(),
       
  1550                                             ba.size(),
       
  1551                                             ind);
       
  1552                         tmpStorage.append(QByteArray((const char *)ba.constData(), ba.size()*sizeof(SQLTCHAR)));
       
  1553                         break;
       
  1554                     }
       
  1555                     QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
       
  1556                     r = SQLBindParameter(d->hStmt,
       
  1557                                           i + 1,
       
  1558                                           qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1559                                           SQL_C_TCHAR,
       
  1560                                           strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
       
  1561                                           strSize,
       
  1562                                           0,
       
  1563                                           (SQLPOINTER)strba.constData(),
       
  1564                                           strba.size(),
       
  1565                                           ind);
       
  1566                     tmpStorage.append(strba);
       
  1567                     break;
       
  1568                 }
       
  1569                 else
       
  1570 #endif
       
  1571                 {
       
  1572                     QByteArray str = val.toString().toUtf8();
       
  1573                     if (*ind != SQL_NULL_DATA)
       
  1574                         *ind = str.length();
       
  1575                     int strSize = str.length();
       
  1576 
       
  1577                     r = SQLBindParameter(d->hStmt,
       
  1578                                           i + 1,
       
  1579                                           qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1580                                           SQL_C_CHAR,
       
  1581                                           strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
       
  1582                                           strSize,
       
  1583                                           0,
       
  1584                                           (void *)str.constData(),
       
  1585                                           strSize,
       
  1586                                           ind);
       
  1587                     tmpStorage.append(str);
       
  1588                     break;
       
  1589                 }
       
  1590             // fall through
       
  1591             default: {
       
  1592                 QByteArray ba = val.toByteArray();
       
  1593                 if (*ind != SQL_NULL_DATA)
       
  1594                     *ind = ba.size();
       
  1595                 r = SQLBindParameter(d->hStmt,
       
  1596                                       i + 1,
       
  1597                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
       
  1598                                       SQL_C_BINARY,
       
  1599                                       SQL_VARBINARY,
       
  1600                                       ba.length() + 1,
       
  1601                                       0,
       
  1602                                       (void *) ba.constData(),
       
  1603                                       ba.length() + 1,
       
  1604                                       ind);
       
  1605                 tmpStorage.append(ba);
       
  1606                 break; }
       
  1607         }
       
  1608         if (r != SQL_SUCCESS) {
       
  1609             qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
       
  1610             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1611                          "Unable to bind variable"), QSqlError::StatementError, d));
       
  1612             return false;
       
  1613         }
       
  1614     }
       
  1615     r = SQLExecute(d->hStmt);
       
  1616     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1617         qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
       
  1618         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1619                      "Unable to execute statement"), QSqlError::StatementError, d));
       
  1620         return false;
       
  1621     }
       
  1622 
       
  1623     SQLINTEGER isScrollable, bufferLength;
       
  1624     r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
       
  1625     if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
       
  1626         QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
       
  1627 
       
  1628     SQLSMALLINT count;
       
  1629     SQLNumResultCols(d->hStmt, &count);
       
  1630     if (count) {
       
  1631         setSelect(true);
       
  1632         for (int i = 0; i < count; ++i) {
       
  1633             d->rInf.append(qMakeFieldInfo(d, i));
       
  1634         }
       
  1635         d->fieldCache.resize(count);
       
  1636     } else {
       
  1637         setSelect(false);
       
  1638     }
       
  1639     setActive(true);
       
  1640 
       
  1641 
       
  1642     //get out parameters
       
  1643     if (!hasOutValues())
       
  1644         return true;
       
  1645 
       
  1646     for (i = 0; i < values.count(); ++i) {
       
  1647         switch (values.at(i).type()) {
       
  1648             case QVariant::Date: {
       
  1649                 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
       
  1650                 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
       
  1651                 break; }
       
  1652             case QVariant::Time: {
       
  1653                 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
       
  1654                 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
       
  1655                 break; }
       
  1656             case QVariant::DateTime: {
       
  1657                 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
       
  1658                                         tmpStorage.takeFirst().constData());
       
  1659                 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
       
  1660                                QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
       
  1661                 break; }
       
  1662             case QVariant::Bool:
       
  1663             case QVariant::Int:
       
  1664             case QVariant::UInt:
       
  1665             case QVariant::Double:
       
  1666             case QVariant::ByteArray:
       
  1667             case QVariant::LongLong:
       
  1668             case QVariant::ULongLong:
       
  1669                 //nothing to do
       
  1670                 break;
       
  1671             case QVariant::String:
       
  1672                 if (d->unicode) {
       
  1673                     if (bindValueType(i) & QSql::Out) {
       
  1674                         QByteArray first = tmpStorage.takeFirst();
       
  1675                         QVarLengthArray<SQLTCHAR> array;
       
  1676                         array.append((SQLTCHAR *)first.constData(), first.size());
       
  1677                         values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR*));
       
  1678                     }
       
  1679                     break;
       
  1680                 }
       
  1681                 // fall through
       
  1682             default: {
       
  1683                 if (bindValueType(i) & QSql::Out)
       
  1684                     values[i] = tmpStorage.takeFirst();
       
  1685                 break; }
       
  1686         }
       
  1687         if (indicators[i] == SQL_NULL_DATA)
       
  1688             values[i] = QVariant(values[i].type());
       
  1689     }
       
  1690     return true;
       
  1691 }
       
  1692 
       
  1693 QSqlRecord QODBCResult::record() const
       
  1694 {
       
  1695     if (!isActive() || !isSelect())
       
  1696         return QSqlRecord();
       
  1697     return d->rInf;
       
  1698 }
       
  1699 
       
  1700 QVariant QODBCResult::handle() const
       
  1701 {
       
  1702     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
       
  1703 }
       
  1704 
       
  1705 bool QODBCResult::nextResult()
       
  1706 {
       
  1707     setActive(false);
       
  1708     setAt(QSql::BeforeFirstRow);
       
  1709     d->rInf.clear();
       
  1710     d->fieldCache.clear();
       
  1711     d->fieldCacheIdx = 0;
       
  1712     setSelect(false);
       
  1713 
       
  1714     SQLRETURN r = SQLMoreResults(d->hStmt);
       
  1715     if (r != SQL_SUCCESS) {
       
  1716         if (r == SQL_SUCCESS_WITH_INFO) {
       
  1717             int nativeCode = -1;
       
  1718             QString message = qODBCWarn(d, &nativeCode);
       
  1719             qWarning() << "QODBCResult::nextResult():" << message;
       
  1720         } else {
       
  1721             if (r != SQL_NO_DATA)
       
  1722                 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
       
  1723                     "Unable to fetch last"), QSqlError::ConnectionError, d));
       
  1724             return false;
       
  1725         }
       
  1726     }
       
  1727 
       
  1728     SQLSMALLINT count;
       
  1729     SQLNumResultCols(d->hStmt, &count);
       
  1730     if (count) {
       
  1731         setSelect(true);
       
  1732         for (int i = 0; i < count; ++i) {
       
  1733             d->rInf.append(qMakeFieldInfo(d, i));
       
  1734         }
       
  1735         d->fieldCache.resize(count);
       
  1736     } else {
       
  1737         setSelect(false);
       
  1738     }
       
  1739     setActive(true);
       
  1740 
       
  1741     return true;
       
  1742 }
       
  1743 
       
  1744 void QODBCResult::virtual_hook(int id, void *data)
       
  1745 {
       
  1746     switch (id) {
       
  1747     case QSqlResult::DetachFromResultSet:
       
  1748         if (d->hStmt)
       
  1749             SQLCloseCursor(d->hStmt);
       
  1750         break;
       
  1751     case QSqlResult::NextResult:
       
  1752         Q_ASSERT(data);
       
  1753         *static_cast<bool*>(data) = nextResult();
       
  1754         break;
       
  1755     default:
       
  1756         QSqlResult::virtual_hook(id, data);
       
  1757     }
       
  1758 }
       
  1759 
       
  1760 void QODBCResult::setForwardOnly(bool forward)
       
  1761 {
       
  1762     d->userForwardOnly = forward;
       
  1763     QSqlResult::setForwardOnly(forward);
       
  1764 }
       
  1765 
       
  1766 ////////////////////////////////////////
       
  1767 
       
  1768 
       
  1769 QODBCDriver::QODBCDriver(QObject *parent)
       
  1770     : QSqlDriver(parent)
       
  1771 {
       
  1772     init();
       
  1773 }
       
  1774 
       
  1775 QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent)
       
  1776     : QSqlDriver(parent)
       
  1777 {
       
  1778     init();
       
  1779     d->hEnv = env;
       
  1780     d->hDbc = con;
       
  1781     if (env && con) {
       
  1782         setOpen(true);
       
  1783         setOpenError(false);
       
  1784     }
       
  1785 }
       
  1786 
       
  1787 void QODBCDriver::init()
       
  1788 {
       
  1789     d = new QODBCDriverPrivate();
       
  1790 }
       
  1791 
       
  1792 QODBCDriver::~QODBCDriver()
       
  1793 {
       
  1794     cleanup();
       
  1795     delete d;
       
  1796 }
       
  1797 
       
  1798 bool QODBCDriver::hasFeature(DriverFeature f) const
       
  1799 {
       
  1800     switch (f) {
       
  1801     case Transactions: {
       
  1802         if (!d->hDbc)
       
  1803             return false;
       
  1804         SQLUSMALLINT txn;
       
  1805         SQLSMALLINT t;
       
  1806         int r = SQLGetInfo(d->hDbc,
       
  1807                         (SQLUSMALLINT)SQL_TXN_CAPABLE,
       
  1808                         &txn,
       
  1809                         sizeof(txn),
       
  1810                         &t);
       
  1811         if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
       
  1812             return false;
       
  1813         else
       
  1814             return true;
       
  1815     }
       
  1816     case Unicode:
       
  1817         return d->unicode;
       
  1818     case PreparedQueries:
       
  1819     case PositionalPlaceholders:
       
  1820     case FinishQuery:
       
  1821     case LowPrecisionNumbers:
       
  1822         return true;
       
  1823     case QuerySize:
       
  1824     case NamedPlaceholders:
       
  1825     case LastInsertId:
       
  1826     case BatchOperations:
       
  1827     case SimpleLocking:
       
  1828     case EventNotifications:
       
  1829         return false;
       
  1830     case MultipleResultSets:
       
  1831         return d->hasMultiResultSets;
       
  1832     case BLOB: {
       
  1833         if(d->isMySqlServer)
       
  1834             return true;
       
  1835         else
       
  1836             return false;
       
  1837     }
       
  1838     }
       
  1839     return false;
       
  1840 }
       
  1841 
       
  1842 bool QODBCDriver::open(const QString & db,
       
  1843                         const QString & user,
       
  1844                         const QString & password,
       
  1845                         const QString &,
       
  1846                         int,
       
  1847                         const QString& connOpts)
       
  1848 {
       
  1849     if (isOpen())
       
  1850       close();
       
  1851     SQLRETURN r;
       
  1852     r = SQLAllocHandle(SQL_HANDLE_ENV,
       
  1853                         SQL_NULL_HANDLE,
       
  1854                         &d->hEnv);
       
  1855     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1856         qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
       
  1857         setOpenError(true);
       
  1858         return false;
       
  1859     }
       
  1860     r = SQLSetEnvAttr(d->hEnv,
       
  1861                        SQL_ATTR_ODBC_VERSION,
       
  1862                        (SQLPOINTER)qGetODBCVersion(connOpts),
       
  1863                        SQL_IS_UINTEGER);
       
  1864     r = SQLAllocHandle(SQL_HANDLE_DBC,
       
  1865                         d->hEnv,
       
  1866                         &d->hDbc);
       
  1867     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1868         qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
       
  1869         setOpenError(true);
       
  1870         return false;
       
  1871     }
       
  1872 
       
  1873     if (!d->setConnectionOptions(connOpts))
       
  1874         return false;
       
  1875 
       
  1876     // Create the connection string
       
  1877     QString connQStr;
       
  1878     // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
       
  1879     if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
       
  1880         connQStr = QLatin1String("FILEDSN=") + db;
       
  1881     else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
       
  1882             || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
       
  1883         connQStr = db;
       
  1884     else
       
  1885         connQStr = QLatin1String("DSN=") + db;
       
  1886 
       
  1887     if (!user.isEmpty())
       
  1888         connQStr += QLatin1String(";UID=") + user;
       
  1889     if (!password.isEmpty())
       
  1890         connQStr += QLatin1String(";PWD=") + password;
       
  1891 
       
  1892     SQLSMALLINT cb;
       
  1893     QVarLengthArray<SQLTCHAR> connOut(1024);
       
  1894     memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
       
  1895     r = SQLDriverConnect(d->hDbc,
       
  1896                           NULL,
       
  1897 #ifdef UNICODE
       
  1898                           toSQLTCHAR(connQStr).data(),
       
  1899 #else
       
  1900                           (SQLCHAR*)connQStr.toUtf8().data(),
       
  1901 #endif
       
  1902                           (SQLSMALLINT)connQStr.length(),
       
  1903                           connOut.data(),
       
  1904                           1024,
       
  1905                           &cb,
       
  1906                           /*SQL_DRIVER_NOPROMPT*/0);
       
  1907 
       
  1908     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
       
  1909         setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
       
  1910         setOpenError(true);
       
  1911         return false;
       
  1912     }
       
  1913 
       
  1914     if (!d->checkDriver()) {
       
  1915         setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
       
  1916                      "functionality required"), QSqlError::ConnectionError, d));
       
  1917         setOpenError(true);
       
  1918         return false;
       
  1919     }
       
  1920 
       
  1921     d->checkUnicode();
       
  1922     d->checkSchemaUsage();
       
  1923     d->checkSqlServer();
       
  1924     d->checkHasSQLFetchScroll();
       
  1925     d->checkHasMultiResults();
       
  1926     setOpen(true);
       
  1927     setOpenError(false);
       
  1928     if(d->isMSSqlServer) {
       
  1929         QSqlQuery i(createResult());
       
  1930         i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
       
  1931     }
       
  1932     return true;
       
  1933 }
       
  1934 
       
  1935 void QODBCDriver::close()
       
  1936 {
       
  1937     cleanup();
       
  1938     setOpen(false);
       
  1939     setOpenError(false);
       
  1940 }
       
  1941 
       
  1942 void QODBCDriver::cleanup()
       
  1943 {
       
  1944     SQLRETURN r;
       
  1945     if (!d)
       
  1946         return;
       
  1947 
       
  1948     if(d->hDbc) {
       
  1949         // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
       
  1950         if (isOpen()) {
       
  1951             r = SQLDisconnect(d->hDbc);
       
  1952             if (r != SQL_SUCCESS)
       
  1953                 qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
       
  1954             else
       
  1955                 d->disconnectCount++;
       
  1956         }
       
  1957 
       
  1958         r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
       
  1959         if (r != SQL_SUCCESS)
       
  1960             qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
       
  1961         d->hDbc = 0;
       
  1962     }
       
  1963 
       
  1964     if (d->hEnv) {
       
  1965         r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
       
  1966         if (r != SQL_SUCCESS)
       
  1967             qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
       
  1968         d->hEnv = 0;
       
  1969     }
       
  1970 }
       
  1971 
       
  1972 // checks whether the server can return char, varchar and longvarchar
       
  1973 // as two byte unicode characters
       
  1974 void QODBCDriverPrivate::checkUnicode()
       
  1975 {
       
  1976 #if defined(Q_ODBC_VERSION_2)
       
  1977     unicode = false;
       
  1978     return;
       
  1979 #endif
       
  1980 
       
  1981     SQLRETURN   r;
       
  1982     SQLUINTEGER fFunc;
       
  1983 
       
  1984     unicode = false;
       
  1985     r = SQLGetInfo(hDbc,
       
  1986                     SQL_CONVERT_CHAR,
       
  1987                     (SQLPOINTER)&fFunc,
       
  1988                     sizeof(fFunc),
       
  1989                     NULL);
       
  1990     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
       
  1991         unicode = true;
       
  1992         return;
       
  1993     }
       
  1994 
       
  1995     r = SQLGetInfo(hDbc,
       
  1996                     SQL_CONVERT_VARCHAR,
       
  1997                     (SQLPOINTER)&fFunc,
       
  1998                     sizeof(fFunc),
       
  1999                     NULL);
       
  2000     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
       
  2001         unicode = true;
       
  2002         return;
       
  2003     }
       
  2004 
       
  2005     r = SQLGetInfo(hDbc,
       
  2006                     SQL_CONVERT_LONGVARCHAR,
       
  2007                     (SQLPOINTER)&fFunc,
       
  2008                     sizeof(fFunc),
       
  2009                     NULL);
       
  2010     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
       
  2011         unicode = true;
       
  2012         return;
       
  2013     }
       
  2014     SQLHANDLE hStmt;
       
  2015     r = SQLAllocHandle(SQL_HANDLE_STMT,
       
  2016                                   hDbc,
       
  2017                                   &hStmt);
       
  2018 
       
  2019     r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
       
  2020     if(r == SQL_SUCCESS) {
       
  2021         r = SQLFetch(hStmt);
       
  2022         if(r == SQL_SUCCESS) {
       
  2023             QVarLengthArray<SQLWCHAR> buffer(10);
       
  2024             r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
       
  2025             if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
       
  2026                 unicode = true;
       
  2027             }
       
  2028         }
       
  2029     }
       
  2030     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
       
  2031 }
       
  2032 
       
  2033 bool QODBCDriverPrivate::checkDriver() const
       
  2034 {
       
  2035 #ifdef ODBC_CHECK_DRIVER
       
  2036     static const SQLUSMALLINT reqFunc[] = {
       
  2037                 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
       
  2038                 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
       
  2039                 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
       
  2040     };
       
  2041 
       
  2042     // these functions are optional
       
  2043     static const SQLUSMALLINT optFunc[] = {
       
  2044         SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
       
  2045     };
       
  2046 
       
  2047     SQLRETURN r;
       
  2048     SQLUSMALLINT sup;
       
  2049 
       
  2050     int i;
       
  2051     // check the required functions
       
  2052     for (i = 0; reqFunc[i] != 0; ++i) {
       
  2053 
       
  2054         r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
       
  2055 
       
  2056         if (r != SQL_SUCCESS) {
       
  2057             qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
       
  2058             return false;
       
  2059         }
       
  2060         if (sup == SQL_FALSE) {
       
  2061             qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
       
  2062                     ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
       
  2063             return false;
       
  2064         }
       
  2065     }
       
  2066 
       
  2067     // these functions are optional and just generate a warning
       
  2068     for (i = 0; optFunc[i] != 0; ++i) {
       
  2069 
       
  2070         r = SQLGetFunctions(hDbc, optFunc[i], &sup);
       
  2071 
       
  2072         if (r != SQL_SUCCESS) {
       
  2073             qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
       
  2074             return false;
       
  2075         }
       
  2076         if (sup == SQL_FALSE) {
       
  2077             qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
       
  2078             return true;
       
  2079         }
       
  2080     }
       
  2081 #endif //ODBC_CHECK_DRIVER
       
  2082 
       
  2083     return true;
       
  2084 }
       
  2085 
       
  2086 void QODBCDriverPrivate::checkSchemaUsage()
       
  2087 {
       
  2088     SQLRETURN   r;
       
  2089     SQLUINTEGER val;
       
  2090 
       
  2091     r = SQLGetInfo(hDbc,
       
  2092                    SQL_SCHEMA_USAGE,
       
  2093                    (SQLPOINTER) &val,
       
  2094                    sizeof(val),
       
  2095                    NULL);
       
  2096     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
       
  2097         useSchema = (val != 0);
       
  2098 }
       
  2099 
       
  2100 void QODBCDriverPrivate::checkSqlServer()
       
  2101 {
       
  2102     SQLRETURN   r;
       
  2103     QVarLengthArray<SQLTCHAR> serverString(200);
       
  2104     SQLSMALLINT t;
       
  2105     memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
       
  2106 
       
  2107     r = SQLGetInfo(hDbc,
       
  2108                    SQL_DBMS_NAME,
       
  2109                    serverString.data(),
       
  2110                    serverString.size() * sizeof(SQLTCHAR),
       
  2111                    &t);
       
  2112     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
       
  2113         QString serverType;
       
  2114 #ifdef UNICODE
       
  2115         serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
       
  2116 #else
       
  2117         serverType = QString::fromUtf8((const char *)serverString.constData(), t);
       
  2118 #endif
       
  2119         isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
       
  2120         isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
       
  2121     }
       
  2122     r = SQLGetInfo(hDbc,
       
  2123                    SQL_DRIVER_NAME,
       
  2124                    serverString.data(),
       
  2125                    serverString.size() * sizeof(SQLTCHAR),
       
  2126                    &t);
       
  2127     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
       
  2128         QString serverType;
       
  2129 #ifdef UNICODE
       
  2130         serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
       
  2131 #else
       
  2132         serverType = QString::fromUtf8((const char *)serverString.constData(), t);
       
  2133 #endif
       
  2134         isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
       
  2135         unicode = isFreeTDSDriver == false;
       
  2136     }
       
  2137 }
       
  2138 
       
  2139 void QODBCDriverPrivate::checkHasSQLFetchScroll()
       
  2140 {
       
  2141     SQLUSMALLINT sup;
       
  2142     SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
       
  2143     if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
       
  2144         hasSQLFetchScroll = false;
       
  2145         qWarning() << "QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries";
       
  2146     }
       
  2147 }
       
  2148 
       
  2149 void QODBCDriverPrivate::checkHasMultiResults()
       
  2150 {
       
  2151     QVarLengthArray<SQLTCHAR> driverResponse(2);
       
  2152     SQLSMALLINT length;
       
  2153     SQLRETURN r = SQLGetInfo(hDbc,
       
  2154                              SQL_MULT_RESULT_SETS,
       
  2155                              driverResponse.data(),
       
  2156                              driverResponse.size() * sizeof(SQLTCHAR),
       
  2157                              &length);
       
  2158     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
       
  2159 #ifdef UNICODE
       
  2160         hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
       
  2161 #else
       
  2162         hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
       
  2163 #endif
       
  2164 }
       
  2165 
       
  2166 QSqlResult *QODBCDriver::createResult() const
       
  2167 {
       
  2168     return new QODBCResult(this, d);
       
  2169 }
       
  2170 
       
  2171 bool QODBCDriver::beginTransaction()
       
  2172 {
       
  2173     if (!isOpen()) {
       
  2174         qWarning() << "QODBCDriver::beginTransaction: Database not open";
       
  2175         return false;
       
  2176     }
       
  2177     SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
       
  2178     SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
       
  2179                                       SQL_ATTR_AUTOCOMMIT,
       
  2180                                       (SQLPOINTER)ac,
       
  2181                                       sizeof(ac));
       
  2182     if (r != SQL_SUCCESS) {
       
  2183         setLastError(qMakeError(tr("Unable to disable autocommit"),
       
  2184                      QSqlError::TransactionError, d));
       
  2185         return false;
       
  2186     }
       
  2187     return true;
       
  2188 }
       
  2189 
       
  2190 bool QODBCDriver::commitTransaction()
       
  2191 {
       
  2192     if (!isOpen()) {
       
  2193         qWarning() << "QODBCDriver::commitTransaction: Database not open";
       
  2194         return false;
       
  2195     }
       
  2196     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
       
  2197                               d->hDbc,
       
  2198                               SQL_COMMIT);
       
  2199     if (r != SQL_SUCCESS) {
       
  2200         setLastError(qMakeError(tr("Unable to commit transaction"),
       
  2201                      QSqlError::TransactionError, d));
       
  2202         return false;
       
  2203     }
       
  2204     return endTrans();
       
  2205 }
       
  2206 
       
  2207 bool QODBCDriver::rollbackTransaction()
       
  2208 {
       
  2209     if (!isOpen()) {
       
  2210         qWarning() << "QODBCDriver::rollbackTransaction: Database not open";
       
  2211         return false;
       
  2212     }
       
  2213     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
       
  2214                               d->hDbc,
       
  2215                               SQL_ROLLBACK);
       
  2216     if (r != SQL_SUCCESS) {
       
  2217         setLastError(qMakeError(tr("Unable to rollback transaction"),
       
  2218                      QSqlError::TransactionError, d));
       
  2219         return false;
       
  2220     }
       
  2221     return endTrans();
       
  2222 }
       
  2223 
       
  2224 bool QODBCDriver::endTrans()
       
  2225 {
       
  2226     SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
       
  2227     SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
       
  2228                                       SQL_ATTR_AUTOCOMMIT,
       
  2229                                       (SQLPOINTER)ac,
       
  2230                                       sizeof(ac));
       
  2231     if (r != SQL_SUCCESS) {
       
  2232         setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
       
  2233         return false;
       
  2234     }
       
  2235     return true;
       
  2236 }
       
  2237 
       
  2238 QStringList QODBCDriver::tables(QSql::TableType type) const
       
  2239 {
       
  2240     QStringList tl;
       
  2241     if (!isOpen())
       
  2242         return tl;
       
  2243     SQLHANDLE hStmt;
       
  2244 
       
  2245     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
       
  2246                                   d->hDbc,
       
  2247                                   &hStmt);
       
  2248     if (r != SQL_SUCCESS) {
       
  2249         qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
       
  2250         return tl;
       
  2251     }
       
  2252     r = SQLSetStmtAttr(hStmt,
       
  2253                         SQL_ATTR_CURSOR_TYPE,
       
  2254                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
       
  2255                         SQL_IS_UINTEGER);
       
  2256     QStringList tableType;
       
  2257     if (type & QSql::Tables)
       
  2258         tableType += QLatin1String("TABLE");
       
  2259     if (type & QSql::Views)
       
  2260         tableType += QLatin1String("VIEW");
       
  2261     if (type & QSql::SystemTables)
       
  2262         tableType += QLatin1String("SYSTEM TABLE");
       
  2263     if (tableType.isEmpty())
       
  2264         return tl;
       
  2265 
       
  2266     QString joinedTableTypeString = tableType.join(QLatin1String(","));
       
  2267 
       
  2268     r = SQLTables(hStmt,
       
  2269                    NULL,
       
  2270                    0,
       
  2271                    NULL,
       
  2272                    0,
       
  2273                    NULL,
       
  2274                    0,
       
  2275 #ifdef UNICODE
       
  2276                    toSQLTCHAR(joinedTableTypeString).data(),
       
  2277 #else
       
  2278                    (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
       
  2279 #endif
       
  2280                    joinedTableTypeString.length() /* characters, not bytes */);
       
  2281 
       
  2282     if (r != SQL_SUCCESS)
       
  2283         qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
       
  2284 
       
  2285     if (d->hasSQLFetchScroll)
       
  2286         r = SQLFetchScroll(hStmt,
       
  2287                            SQL_FETCH_NEXT,
       
  2288                            0);
       
  2289     else
       
  2290         r = SQLFetch(hStmt);
       
  2291 
       
  2292     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
       
  2293         qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
       
  2294         return QStringList();
       
  2295     }
       
  2296 
       
  2297     while (r == SQL_SUCCESS) {
       
  2298         QString fieldVal = qGetStringData(hStmt, 2, -1, false);
       
  2299         tl.append(fieldVal);
       
  2300 
       
  2301         if (d->hasSQLFetchScroll)
       
  2302             r = SQLFetchScroll(hStmt,
       
  2303                                SQL_FETCH_NEXT,
       
  2304                                0);
       
  2305         else
       
  2306             r = SQLFetch(hStmt);
       
  2307     }
       
  2308 
       
  2309     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
       
  2310     if (r!= SQL_SUCCESS)
       
  2311         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
       
  2312     return tl;
       
  2313 }
       
  2314 
       
  2315 QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
       
  2316 {
       
  2317     QSqlIndex index(tablename);
       
  2318     if (!isOpen())
       
  2319         return index;
       
  2320     bool usingSpecialColumns = false;
       
  2321     QSqlRecord rec = record(tablename);
       
  2322 
       
  2323     SQLHANDLE hStmt;
       
  2324     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
       
  2325                                   d->hDbc,
       
  2326                                   &hStmt);
       
  2327     if (r != SQL_SUCCESS) {
       
  2328         qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
       
  2329         return index;
       
  2330     }
       
  2331     QString catalog, schema, table;
       
  2332     d->splitTableQualifier(tablename, catalog, schema, table);
       
  2333 
       
  2334     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
       
  2335         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
       
  2336     else
       
  2337         catalog = d->adjustCase(catalog);
       
  2338 
       
  2339     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
       
  2340         schema = stripDelimiters(schema, QSqlDriver::TableName);
       
  2341     else
       
  2342         schema = d->adjustCase(schema);
       
  2343 
       
  2344     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2345         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2346     else
       
  2347         table = d->adjustCase(table);
       
  2348 
       
  2349     r = SQLSetStmtAttr(hStmt,
       
  2350                         SQL_ATTR_CURSOR_TYPE,
       
  2351                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
       
  2352                         SQL_IS_UINTEGER);
       
  2353     r = SQLPrimaryKeys(hStmt,
       
  2354 #ifdef UNICODE
       
  2355                         catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
       
  2356 #else
       
  2357                         catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
       
  2358 #endif
       
  2359                         catalog.length(),
       
  2360 #ifdef UNICODE
       
  2361                         schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
       
  2362 #else
       
  2363                         schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
       
  2364 #endif
       
  2365                         schema.length(),
       
  2366 #ifdef UNICODE
       
  2367                         toSQLTCHAR(table).data(),
       
  2368 #else
       
  2369                         (SQLCHAR*)table.toUtf8().data(),
       
  2370 #endif
       
  2371                         table.length() /* in characters, not in bytes */);
       
  2372 
       
  2373     // if the SQLPrimaryKeys() call does not succeed (e.g the driver
       
  2374     // does not support it) - try an alternative method to get hold of
       
  2375     // the primary index (e.g MS Access and FoxPro)
       
  2376     if (r != SQL_SUCCESS) {
       
  2377             r = SQLSpecialColumns(hStmt,
       
  2378                         SQL_BEST_ROWID,
       
  2379 #ifdef UNICODE
       
  2380                         catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
       
  2381 #else
       
  2382                         catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
       
  2383 #endif
       
  2384                         catalog.length(),
       
  2385 #ifdef UNICODE
       
  2386                         schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
       
  2387 #else
       
  2388                         schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
       
  2389 #endif
       
  2390                         schema.length(),
       
  2391 #ifdef UNICODE
       
  2392                         toSQLTCHAR(table).data(),
       
  2393 #else
       
  2394                         (SQLCHAR*)table.toUtf8().data(),
       
  2395 #endif
       
  2396                         table.length(),
       
  2397                         SQL_SCOPE_CURROW,
       
  2398                         SQL_NULLABLE);
       
  2399 
       
  2400             if (r != SQL_SUCCESS) {
       
  2401                 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
       
  2402             } else {
       
  2403                 usingSpecialColumns = true;
       
  2404             }
       
  2405     }
       
  2406 
       
  2407     if (d->hasSQLFetchScroll)
       
  2408         r = SQLFetchScroll(hStmt,
       
  2409                            SQL_FETCH_NEXT,
       
  2410                            0);
       
  2411     else
       
  2412         r = SQLFetch(hStmt);
       
  2413 
       
  2414     int fakeId = 0;
       
  2415     QString cName, idxName;
       
  2416     // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
       
  2417     while (r == SQL_SUCCESS) {
       
  2418         if (usingSpecialColumns) {
       
  2419             cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
       
  2420             idxName = QString::number(fakeId++); // invent a fake index name
       
  2421         } else {
       
  2422             cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
       
  2423             idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
       
  2424         }
       
  2425         index.append(rec.field(cName));
       
  2426         index.setName(idxName);
       
  2427 
       
  2428         if (d->hasSQLFetchScroll)
       
  2429             r = SQLFetchScroll(hStmt,
       
  2430                                SQL_FETCH_NEXT,
       
  2431                                0);
       
  2432         else
       
  2433             r = SQLFetch(hStmt);
       
  2434 
       
  2435     }
       
  2436     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
       
  2437     if (r!= SQL_SUCCESS)
       
  2438         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
       
  2439     return index;
       
  2440 }
       
  2441 
       
  2442 QSqlRecord QODBCDriver::record(const QString& tablename) const
       
  2443 {
       
  2444     QSqlRecord fil;
       
  2445     if (!isOpen())
       
  2446         return fil;
       
  2447 
       
  2448     SQLHANDLE hStmt;
       
  2449     QString catalog, schema, table;
       
  2450     d->splitTableQualifier(tablename, catalog, schema, table);
       
  2451 
       
  2452     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
       
  2453         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
       
  2454     else
       
  2455         catalog = d->adjustCase(catalog);
       
  2456 
       
  2457     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
       
  2458         schema = stripDelimiters(schema, QSqlDriver::TableName);
       
  2459     else
       
  2460         schema = d->adjustCase(schema);
       
  2461 
       
  2462     if (isIdentifierEscaped(table, QSqlDriver::TableName))
       
  2463         table = stripDelimiters(table, QSqlDriver::TableName);
       
  2464     else
       
  2465         table = d->adjustCase(table);
       
  2466 
       
  2467     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
       
  2468                                   d->hDbc,
       
  2469                                   &hStmt);
       
  2470     if (r != SQL_SUCCESS) {
       
  2471         qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
       
  2472         return fil;
       
  2473     }
       
  2474     r = SQLSetStmtAttr(hStmt,
       
  2475                         SQL_ATTR_CURSOR_TYPE,
       
  2476                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
       
  2477                         SQL_IS_UINTEGER);
       
  2478     r =  SQLColumns(hStmt,
       
  2479 #ifdef UNICODE
       
  2480                      catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
       
  2481 #else
       
  2482                      catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
       
  2483 #endif
       
  2484                      catalog.length(),
       
  2485 #ifdef UNICODE
       
  2486                      schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
       
  2487 #else
       
  2488                      schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
       
  2489 #endif
       
  2490                      schema.length(),
       
  2491 #ifdef UNICODE
       
  2492                      toSQLTCHAR(table).data(),
       
  2493 #else
       
  2494                      (SQLCHAR*)table.toUtf8().data(),
       
  2495 #endif
       
  2496                      table.length(),
       
  2497                      NULL,
       
  2498                      0);
       
  2499     if (r != SQL_SUCCESS)
       
  2500         qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
       
  2501 
       
  2502     if (d->hasSQLFetchScroll)
       
  2503         r = SQLFetchScroll(hStmt,
       
  2504                            SQL_FETCH_NEXT,
       
  2505                            0);
       
  2506     else
       
  2507         r = SQLFetch(hStmt);
       
  2508 
       
  2509     // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
       
  2510     while (r == SQL_SUCCESS) {
       
  2511 
       
  2512         fil.append(qMakeFieldInfo(hStmt, d));
       
  2513 
       
  2514         if (d->hasSQLFetchScroll)
       
  2515             r = SQLFetchScroll(hStmt,
       
  2516                                SQL_FETCH_NEXT,
       
  2517                                0);
       
  2518         else
       
  2519             r = SQLFetch(hStmt);
       
  2520     }
       
  2521 
       
  2522     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
       
  2523     if (r!= SQL_SUCCESS)
       
  2524         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
       
  2525 
       
  2526     return fil;
       
  2527 }
       
  2528 
       
  2529 QString QODBCDriver::formatValue(const QSqlField &field,
       
  2530                                  bool trimStrings) const
       
  2531 {
       
  2532     QString r;
       
  2533     if (field.isNull()) {
       
  2534         r = QLatin1String("NULL");
       
  2535     } else if (field.type() == QVariant::DateTime) {
       
  2536         // Use an escape sequence for the datetime fields
       
  2537         if (field.value().toDateTime().isValid()){
       
  2538             QDate dt = field.value().toDateTime().date();
       
  2539             QTime tm = field.value().toDateTime().time();
       
  2540             // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
       
  2541             r = QLatin1String("{ ts '") +
       
  2542                 QString::number(dt.year()) + QLatin1Char('-') +
       
  2543                 QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
       
  2544                 QLatin1Char('-') +
       
  2545                 QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
       
  2546                 QLatin1Char(' ') +
       
  2547                 tm.toString() +
       
  2548                 QLatin1String("' }");
       
  2549         } else
       
  2550             r = QLatin1String("NULL");
       
  2551     } else if (field.type() == QVariant::ByteArray) {
       
  2552         QByteArray ba = field.value().toByteArray();
       
  2553         QString res;
       
  2554         static const char hexchars[] = "0123456789abcdef";
       
  2555         for (int i = 0; i < ba.size(); ++i) {
       
  2556             uchar s = (uchar) ba[i];
       
  2557             res += QLatin1Char(hexchars[s >> 4]);
       
  2558             res += QLatin1Char(hexchars[s & 0x0f]);
       
  2559         }
       
  2560         r = QLatin1String("0x") + res;
       
  2561     } else {
       
  2562         r = QSqlDriver::formatValue(field, trimStrings);
       
  2563     }
       
  2564     return r;
       
  2565 }
       
  2566 
       
  2567 QVariant QODBCDriver::handle() const
       
  2568 {
       
  2569     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
       
  2570 }
       
  2571 
       
  2572 QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
       
  2573 {
       
  2574     QChar quote = d->quoteChar();
       
  2575     QString res = identifier;
       
  2576     if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
       
  2577         res.replace(quote, QString(quote)+QString(quote));
       
  2578         res.prepend(quote).append(quote);
       
  2579         res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
       
  2580     }
       
  2581     return res;
       
  2582 }
       
  2583 
       
  2584 bool QODBCDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType) const
       
  2585 {
       
  2586     QChar quote = d->quoteChar();
       
  2587     return identifier.size() > 2
       
  2588         && identifier.startsWith(quote) //left delimited
       
  2589         && identifier.endsWith(quote); //right delimited
       
  2590 }
       
  2591 
       
  2592 QT_END_NAMESPACE