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