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