Merge remote-tracking branch 'origin/master' into api_changes
[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> ba(toSQLTCHAR(str));
1506                         ba.reserve(str.capacity());
1507                         r = SQLBindParameter(d->hStmt,
1508                                             i + 1,
1509                                             qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1510                                             SQL_C_TCHAR,
1511                                             strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1512                                             0, // god knows... don't change this!
1513                                             0,
1514                                             (void *)ba.constData(),
1515                                             ba.size(),
1516                                             ind);
1517                         tmpStorage.append(QByteArray((const char *)ba.constData(), ba.size()*sizeof(SQLTCHAR)));
1518                         break;
1519                     }
1520                     QByteArray strba((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
1521                     r = SQLBindParameter(d->hStmt,
1522                                           i + 1,
1523                                           qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1524                                           SQL_C_TCHAR,
1525                                           strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
1526                                           strSize,
1527                                           0,
1528                                           (SQLPOINTER)strba.constData(),
1529                                           strba.size(),
1530                                           ind);
1531                     tmpStorage.append(strba);
1532                     break;
1533                 }
1534                 else
1535                 {
1536                     QByteArray str = val.toString().toUtf8();
1537                     if (*ind != SQL_NULL_DATA)
1538                         *ind = str.length();
1539                     int strSize = str.length();
1540
1541                     r = SQLBindParameter(d->hStmt,
1542                                           i + 1,
1543                                           qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1544                                           SQL_C_CHAR,
1545                                           strSize > 254 ? SQL_LONGVARCHAR : SQL_VARCHAR,
1546                                           strSize,
1547                                           0,
1548                                           (void *)str.constData(),
1549                                           strSize,
1550                                           ind);
1551                     tmpStorage.append(str);
1552                     break;
1553                 }
1554             // fall through
1555             default: {
1556                 QByteArray ba = val.toByteArray();
1557                 if (*ind != SQL_NULL_DATA)
1558                     *ind = ba.size();
1559                 r = SQLBindParameter(d->hStmt,
1560                                       i + 1,
1561                                       qParamType[(QFlag)(bindValueType(i)) & QSql::InOut],
1562                                       SQL_C_BINARY,
1563                                       SQL_VARBINARY,
1564                                       ba.length() + 1,
1565                                       0,
1566                                       (void *) ba.constData(),
1567                                       ba.length() + 1,
1568                                       ind);
1569                 tmpStorage.append(ba);
1570                 break; }
1571         }
1572         if (r != SQL_SUCCESS) {
1573             qWarning() << "QODBCResult::exec: unable to bind variable:" << qODBCWarn(d);
1574             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1575                          "Unable to bind variable"), QSqlError::StatementError, d));
1576             return false;
1577         }
1578     }
1579     r = SQLExecute(d->hStmt);
1580     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1581         qWarning() << "QODBCResult::exec: Unable to execute statement:" << qODBCWarn(d);
1582         setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1583                      "Unable to execute statement"), QSqlError::StatementError, d));
1584         return false;
1585     }
1586
1587     SQLINTEGER isScrollable, bufferLength;
1588     r = SQLGetStmtAttr(d->hStmt, SQL_ATTR_CURSOR_SCROLLABLE, &isScrollable, SQL_IS_INTEGER, &bufferLength);
1589     if(r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1590         QSqlResult::setForwardOnly(isScrollable==SQL_NONSCROLLABLE);
1591
1592     SQLSMALLINT count;
1593     SQLNumResultCols(d->hStmt, &count);
1594     if (count) {
1595         setSelect(true);
1596         for (int i = 0; i < count; ++i) {
1597             d->rInf.append(qMakeFieldInfo(d, i));
1598         }
1599         d->fieldCache.resize(count);
1600     } else {
1601         setSelect(false);
1602     }
1603     setActive(true);
1604
1605
1606     //get out parameters
1607     if (!hasOutValues())
1608         return true;
1609
1610     for (i = 0; i < values.count(); ++i) {
1611         switch (values.at(i).type()) {
1612             case QVariant::Date: {
1613                 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
1614                 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
1615                 break; }
1616             case QVariant::Time: {
1617                 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
1618                 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
1619                 break; }
1620             case QVariant::DateTime: {
1621                 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT*)
1622                                         tmpStorage.takeFirst().constData());
1623                 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
1624                                QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
1625                 break; }
1626             case QVariant::Bool:
1627             case QVariant::Int:
1628             case QVariant::UInt:
1629             case QVariant::Double:
1630             case QVariant::ByteArray:
1631             case QVariant::LongLong:
1632             case QVariant::ULongLong:
1633                 //nothing to do
1634                 break;
1635             case QVariant::String:
1636                 if (d->unicode) {
1637                     if (bindValueType(i) & QSql::Out) {
1638                         QByteArray first = tmpStorage.takeFirst();
1639                         QVarLengthArray<SQLTCHAR> array;
1640                         array.append((SQLTCHAR *)first.constData(), first.size());
1641                         values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR*));
1642                     }
1643                     break;
1644                 }
1645                 // fall through
1646             default: {
1647                 if (bindValueType(i) & QSql::Out)
1648                     values[i] = tmpStorage.takeFirst();
1649                 break; }
1650         }
1651         if (indicators[i] == SQL_NULL_DATA)
1652             values[i] = QVariant(values[i].type());
1653     }
1654     return true;
1655 }
1656
1657 QSqlRecord QODBCResult::record() const
1658 {
1659     if (!isActive() || !isSelect())
1660         return QSqlRecord();
1661     return d->rInf;
1662 }
1663
1664 QVariant QODBCResult::handle() const
1665 {
1666     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
1667 }
1668
1669 bool QODBCResult::nextResult()
1670 {
1671     setActive(false);
1672     setAt(QSql::BeforeFirstRow);
1673     d->rInf.clear();
1674     d->fieldCache.clear();
1675     d->fieldCacheIdx = 0;
1676     setSelect(false);
1677
1678     SQLRETURN r = SQLMoreResults(d->hStmt);
1679     if (r != SQL_SUCCESS) {
1680         if (r == SQL_SUCCESS_WITH_INFO) {
1681             int nativeCode = -1;
1682             QString message = qODBCWarn(d, &nativeCode);
1683             qWarning() << "QODBCResult::nextResult():" << message;
1684         } else {
1685             if (r != SQL_NO_DATA)
1686                 setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1687                     "Unable to fetch last"), QSqlError::ConnectionError, d));
1688             return false;
1689         }
1690     }
1691
1692     SQLSMALLINT count;
1693     SQLNumResultCols(d->hStmt, &count);
1694     if (count) {
1695         setSelect(true);
1696         for (int i = 0; i < count; ++i) {
1697             d->rInf.append(qMakeFieldInfo(d, i));
1698         }
1699         d->fieldCache.resize(count);
1700     } else {
1701         setSelect(false);
1702     }
1703     setActive(true);
1704
1705     return true;
1706 }
1707
1708 void QODBCResult::virtual_hook(int id, void *data)
1709 {
1710     switch (id) {
1711     case QSqlResult::DetachFromResultSet:
1712         if (d->hStmt)
1713             SQLCloseCursor(d->hStmt);
1714         break;
1715     case QSqlResult::NextResult:
1716         Q_ASSERT(data);
1717         *static_cast<bool*>(data) = nextResult();
1718         break;
1719     default:
1720         QSqlResult::virtual_hook(id, data);
1721     }
1722 }
1723
1724 void QODBCResult::setForwardOnly(bool forward)
1725 {
1726     d->userForwardOnly = forward;
1727     QSqlResult::setForwardOnly(forward);
1728 }
1729
1730 ////////////////////////////////////////
1731
1732
1733 QODBCDriver::QODBCDriver(QObject *parent)
1734     : QSqlDriver(parent)
1735 {
1736     init();
1737 }
1738
1739 QODBCDriver::QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent)
1740     : QSqlDriver(parent)
1741 {
1742     init();
1743     d->hEnv = env;
1744     d->hDbc = con;
1745     if (env && con) {
1746         setOpen(true);
1747         setOpenError(false);
1748     }
1749 }
1750
1751 void QODBCDriver::init()
1752 {
1753     d = new QODBCDriverPrivate();
1754 }
1755
1756 QODBCDriver::~QODBCDriver()
1757 {
1758     cleanup();
1759     delete d;
1760 }
1761
1762 bool QODBCDriver::hasFeature(DriverFeature f) const
1763 {
1764     switch (f) {
1765     case Transactions: {
1766         if (!d->hDbc)
1767             return false;
1768         SQLUSMALLINT txn;
1769         SQLSMALLINT t;
1770         int r = SQLGetInfo(d->hDbc,
1771                         (SQLUSMALLINT)SQL_TXN_CAPABLE,
1772                         &txn,
1773                         sizeof(txn),
1774                         &t);
1775         if (r != SQL_SUCCESS || txn == SQL_TC_NONE)
1776             return false;
1777         else
1778             return true;
1779     }
1780     case Unicode:
1781         return d->unicode;
1782     case PreparedQueries:
1783     case PositionalPlaceholders:
1784     case FinishQuery:
1785     case LowPrecisionNumbers:
1786         return true;
1787     case QuerySize:
1788     case NamedPlaceholders:
1789     case LastInsertId:
1790     case BatchOperations:
1791     case SimpleLocking:
1792     case EventNotifications:
1793         return false;
1794     case MultipleResultSets:
1795         return d->hasMultiResultSets;
1796     case BLOB: {
1797         if(d->isMySqlServer)
1798             return true;
1799         else
1800             return false;
1801     }
1802     }
1803     return false;
1804 }
1805
1806 bool QODBCDriver::open(const QString & db,
1807                         const QString & user,
1808                         const QString & password,
1809                         const QString &,
1810                         int,
1811                         const QString& connOpts)
1812 {
1813     if (isOpen())
1814       close();
1815     SQLRETURN r;
1816     r = SQLAllocHandle(SQL_HANDLE_ENV,
1817                         SQL_NULL_HANDLE,
1818                         &d->hEnv);
1819     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1820         qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate environment"), d);
1821         setOpenError(true);
1822         return false;
1823     }
1824     r = SQLSetEnvAttr(d->hEnv,
1825                        SQL_ATTR_ODBC_VERSION,
1826                        (SQLPOINTER)qGetODBCVersion(connOpts),
1827                        SQL_IS_UINTEGER);
1828     r = SQLAllocHandle(SQL_HANDLE_DBC,
1829                         d->hEnv,
1830                         &d->hDbc);
1831     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1832         qSqlWarning(QLatin1String("QODBCDriver::open: Unable to allocate connection"), d);
1833         setOpenError(true);
1834         return false;
1835     }
1836
1837     if (!d->setConnectionOptions(connOpts))
1838         return false;
1839
1840     // Create the connection string
1841     QString connQStr;
1842     // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
1843     if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive))
1844         connQStr = QLatin1String("FILEDSN=") + db;
1845     else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive)
1846             || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive))
1847         connQStr = db;
1848     else
1849         connQStr = QLatin1String("DSN=") + db;
1850
1851     if (!user.isEmpty())
1852         connQStr += QLatin1String(";UID=") + user;
1853     if (!password.isEmpty())
1854         connQStr += QLatin1String(";PWD=") + password;
1855
1856     SQLSMALLINT cb;
1857     QVarLengthArray<SQLTCHAR> connOut(1024);
1858     memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
1859     r = SQLDriverConnect(d->hDbc,
1860                           NULL,
1861 #ifdef UNICODE
1862                           toSQLTCHAR(connQStr).data(),
1863 #else
1864                           (SQLCHAR*)connQStr.toUtf8().data(),
1865 #endif
1866                           (SQLSMALLINT)connQStr.length(),
1867                           connOut.data(),
1868                           1024,
1869                           &cb,
1870                           /*SQL_DRIVER_NOPROMPT*/0);
1871
1872     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1873         setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
1874         setOpenError(true);
1875         return false;
1876     }
1877
1878     if (!d->checkDriver()) {
1879         setLastError(qMakeError(tr("Unable to connect - Driver doesn't support all "
1880                      "functionality required"), QSqlError::ConnectionError, d));
1881         setOpenError(true);
1882         return false;
1883     }
1884
1885     d->checkUnicode();
1886     d->checkSchemaUsage();
1887     d->checkSqlServer();
1888     d->checkHasSQLFetchScroll();
1889     d->checkHasMultiResults();
1890     setOpen(true);
1891     setOpenError(false);
1892     if(d->isMSSqlServer) {
1893         QSqlQuery i(createResult());
1894         i.exec(QLatin1String("SET QUOTED_IDENTIFIER ON"));
1895     }
1896     return true;
1897 }
1898
1899 void QODBCDriver::close()
1900 {
1901     cleanup();
1902     setOpen(false);
1903     setOpenError(false);
1904 }
1905
1906 void QODBCDriver::cleanup()
1907 {
1908     SQLRETURN r;
1909     if (!d)
1910         return;
1911
1912     if(d->hDbc) {
1913         // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1914         if (isOpen()) {
1915             r = SQLDisconnect(d->hDbc);
1916             if (r != SQL_SUCCESS)
1917                 qSqlWarning(QLatin1String("QODBCDriver::disconnect: Unable to disconnect datasource"), d);
1918             else
1919                 d->disconnectCount++;
1920         }
1921
1922         r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1923         if (r != SQL_SUCCESS)
1924             qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free connection handle"), d);
1925         d->hDbc = 0;
1926     }
1927
1928     if (d->hEnv) {
1929         r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1930         if (r != SQL_SUCCESS)
1931             qSqlWarning(QLatin1String("QODBCDriver::cleanup: Unable to free environment handle"), d);
1932         d->hEnv = 0;
1933     }
1934 }
1935
1936 // checks whether the server can return char, varchar and longvarchar
1937 // as two byte unicode characters
1938 void QODBCDriverPrivate::checkUnicode()
1939 {
1940     SQLRETURN   r;
1941     SQLUINTEGER fFunc;
1942
1943     unicode = false;
1944     r = SQLGetInfo(hDbc,
1945                     SQL_CONVERT_CHAR,
1946                     (SQLPOINTER)&fFunc,
1947                     sizeof(fFunc),
1948                     NULL);
1949     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WCHAR)) {
1950         unicode = true;
1951         return;
1952     }
1953
1954     r = SQLGetInfo(hDbc,
1955                     SQL_CONVERT_VARCHAR,
1956                     (SQLPOINTER)&fFunc,
1957                     sizeof(fFunc),
1958                     NULL);
1959     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WVARCHAR)) {
1960         unicode = true;
1961         return;
1962     }
1963
1964     r = SQLGetInfo(hDbc,
1965                     SQL_CONVERT_LONGVARCHAR,
1966                     (SQLPOINTER)&fFunc,
1967                     sizeof(fFunc),
1968                     NULL);
1969     if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (fFunc & SQL_CVT_WLONGVARCHAR)) {
1970         unicode = true;
1971         return;
1972     }
1973     SQLHANDLE hStmt;
1974     r = SQLAllocHandle(SQL_HANDLE_STMT,
1975                                   hDbc,
1976                                   &hStmt);
1977
1978     r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
1979     if(r == SQL_SUCCESS) {
1980         r = SQLFetch(hStmt);
1981         if(r == SQL_SUCCESS) {
1982             QVarLengthArray<SQLWCHAR> buffer(10);
1983             r = SQLGetData(hStmt, 1, SQL_C_WCHAR, buffer.data(), buffer.size() * sizeof(SQLWCHAR), NULL);
1984             if(r == SQL_SUCCESS && fromSQLTCHAR(buffer) == QLatin1String("test")) {
1985                 unicode = true;
1986             }
1987         }
1988     }
1989     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1990 }
1991
1992 bool QODBCDriverPrivate::checkDriver() const
1993 {
1994 #ifdef ODBC_CHECK_DRIVER
1995     static const SQLUSMALLINT reqFunc[] = {
1996                 SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
1997                 SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
1998                 SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
1999     };
2000
2001     // these functions are optional
2002     static const SQLUSMALLINT optFunc[] = {
2003         SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
2004     };
2005
2006     SQLRETURN r;
2007     SQLUSMALLINT sup;
2008
2009     int i;
2010     // check the required functions
2011     for (i = 0; reqFunc[i] != 0; ++i) {
2012
2013         r = SQLGetFunctions(hDbc, reqFunc[i], &sup);
2014
2015         if (r != SQL_SUCCESS) {
2016             qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2017             return false;
2018         }
2019         if (sup == SQL_FALSE) {
2020             qWarning () << "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (" << reqFunc[i] <<
2021                     ").\nPlease look at the Qt SQL Module Driver documentation for more information.";
2022             return false;
2023         }
2024     }
2025
2026     // these functions are optional and just generate a warning
2027     for (i = 0; optFunc[i] != 0; ++i) {
2028
2029         r = SQLGetFunctions(hDbc, optFunc[i], &sup);
2030
2031         if (r != SQL_SUCCESS) {
2032             qSqlWarning(QLatin1String("QODBCDriver::checkDriver: Cannot get list of supported functions"), this);
2033             return false;
2034         }
2035         if (sup == SQL_FALSE) {
2036             qWarning() << "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (" << optFunc[i] << ')';
2037             return true;
2038         }
2039     }
2040 #endif //ODBC_CHECK_DRIVER
2041
2042     return true;
2043 }
2044
2045 void QODBCDriverPrivate::checkSchemaUsage()
2046 {
2047     SQLRETURN   r;
2048     SQLUINTEGER val;
2049
2050     r = SQLGetInfo(hDbc,
2051                    SQL_SCHEMA_USAGE,
2052                    (SQLPOINTER) &val,
2053                    sizeof(val),
2054                    NULL);
2055     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2056         useSchema = (val != 0);
2057 }
2058
2059 void QODBCDriverPrivate::checkSqlServer()
2060 {
2061     SQLRETURN   r;
2062     QVarLengthArray<SQLTCHAR> serverString(200);
2063     SQLSMALLINT t;
2064     memset(serverString.data(), 0, serverString.size() * sizeof(SQLTCHAR));
2065
2066     r = SQLGetInfo(hDbc,
2067                    SQL_DBMS_NAME,
2068                    serverString.data(),
2069                    serverString.size() * sizeof(SQLTCHAR),
2070                    &t);
2071     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2072         QString serverType;
2073 #ifdef UNICODE
2074         serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2075 #else
2076         serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2077 #endif
2078         isMySqlServer = serverType.contains(QLatin1String("mysql"), Qt::CaseInsensitive);
2079         isMSSqlServer = serverType.contains(QLatin1String("Microsoft SQL Server"), Qt::CaseInsensitive);
2080     }
2081     r = SQLGetInfo(hDbc,
2082                    SQL_DRIVER_NAME,
2083                    serverString.data(),
2084                    serverString.size() * sizeof(SQLTCHAR),
2085                    &t);
2086     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
2087         QString serverType;
2088 #ifdef UNICODE
2089         serverType = fromSQLTCHAR(serverString, t/sizeof(SQLTCHAR));
2090 #else
2091         serverType = QString::fromUtf8((const char *)serverString.constData(), t);
2092 #endif
2093         isFreeTDSDriver = serverType.contains(QLatin1String("tdsodbc"), Qt::CaseInsensitive);
2094         unicode = unicode && !isFreeTDSDriver;
2095     }
2096 }
2097
2098 void QODBCDriverPrivate::checkHasSQLFetchScroll()
2099 {
2100     SQLUSMALLINT sup;
2101     SQLRETURN r = SQLGetFunctions(hDbc, SQL_API_SQLFETCHSCROLL, &sup);
2102     if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || sup != SQL_TRUE) {
2103         hasSQLFetchScroll = false;
2104         qWarning() << "QODBCDriver::checkHasSQLFetchScroll: Warning - Driver doesn't support scrollable result sets, use forward only mode for queries";
2105     }
2106 }
2107
2108 void QODBCDriverPrivate::checkHasMultiResults()
2109 {
2110     QVarLengthArray<SQLTCHAR> driverResponse(2);
2111     SQLSMALLINT length;
2112     SQLRETURN r = SQLGetInfo(hDbc,
2113                              SQL_MULT_RESULT_SETS,
2114                              driverResponse.data(),
2115                              driverResponse.size() * sizeof(SQLTCHAR),
2116                              &length);
2117     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
2118 #ifdef UNICODE
2119         hasMultiResultSets = fromSQLTCHAR(driverResponse, length/sizeof(SQLTCHAR)).startsWith(QLatin1Char('Y'));
2120 #else
2121         hasMultiResultSets = QString::fromUtf8((const char *)driverResponse.constData(), length).startsWith(QLatin1Char('Y'));
2122 #endif
2123 }
2124
2125 QSqlResult *QODBCDriver::createResult() const
2126 {
2127     return new QODBCResult(this, d);
2128 }
2129
2130 bool QODBCDriver::beginTransaction()
2131 {
2132     if (!isOpen()) {
2133         qWarning() << "QODBCDriver::beginTransaction: Database not open";
2134         return false;
2135     }
2136     SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
2137     SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
2138                                       SQL_ATTR_AUTOCOMMIT,
2139                                       (SQLPOINTER)ac,
2140                                       sizeof(ac));
2141     if (r != SQL_SUCCESS) {
2142         setLastError(qMakeError(tr("Unable to disable autocommit"),
2143                      QSqlError::TransactionError, d));
2144         return false;
2145     }
2146     return true;
2147 }
2148
2149 bool QODBCDriver::commitTransaction()
2150 {
2151     if (!isOpen()) {
2152         qWarning() << "QODBCDriver::commitTransaction: Database not open";
2153         return false;
2154     }
2155     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2156                               d->hDbc,
2157                               SQL_COMMIT);
2158     if (r != SQL_SUCCESS) {
2159         setLastError(qMakeError(tr("Unable to commit transaction"),
2160                      QSqlError::TransactionError, d));
2161         return false;
2162     }
2163     return endTrans();
2164 }
2165
2166 bool QODBCDriver::rollbackTransaction()
2167 {
2168     if (!isOpen()) {
2169         qWarning() << "QODBCDriver::rollbackTransaction: Database not open";
2170         return false;
2171     }
2172     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
2173                               d->hDbc,
2174                               SQL_ROLLBACK);
2175     if (r != SQL_SUCCESS) {
2176         setLastError(qMakeError(tr("Unable to rollback transaction"),
2177                      QSqlError::TransactionError, d));
2178         return false;
2179     }
2180     return endTrans();
2181 }
2182
2183 bool QODBCDriver::endTrans()
2184 {
2185     SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
2186     SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
2187                                       SQL_ATTR_AUTOCOMMIT,
2188                                       (SQLPOINTER)ac,
2189                                       sizeof(ac));
2190     if (r != SQL_SUCCESS) {
2191         setLastError(qMakeError(tr("Unable to enable autocommit"), QSqlError::TransactionError, d));
2192         return false;
2193     }
2194     return true;
2195 }
2196
2197 QStringList QODBCDriver::tables(QSql::TableType type) const
2198 {
2199     QStringList tl;
2200     if (!isOpen())
2201         return tl;
2202     SQLHANDLE hStmt;
2203
2204     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2205                                   d->hDbc,
2206                                   &hStmt);
2207     if (r != SQL_SUCCESS) {
2208         qSqlWarning(QLatin1String("QODBCDriver::tables: Unable to allocate handle"), d);
2209         return tl;
2210     }
2211     r = SQLSetStmtAttr(hStmt,
2212                         SQL_ATTR_CURSOR_TYPE,
2213                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2214                         SQL_IS_UINTEGER);
2215     QStringList tableType;
2216     if (type & QSql::Tables)
2217         tableType += QLatin1String("TABLE");
2218     if (type & QSql::Views)
2219         tableType += QLatin1String("VIEW");
2220     if (type & QSql::SystemTables)
2221         tableType += QLatin1String("SYSTEM TABLE");
2222     if (tableType.isEmpty())
2223         return tl;
2224
2225     QString joinedTableTypeString = tableType.join(QLatin1String(","));
2226
2227     r = SQLTables(hStmt,
2228                    NULL,
2229                    0,
2230                    NULL,
2231                    0,
2232                    NULL,
2233                    0,
2234 #ifdef UNICODE
2235                    toSQLTCHAR(joinedTableTypeString).data(),
2236 #else
2237                    (SQLCHAR*)joinedTableTypeString.toUtf8().data(),
2238 #endif
2239                    joinedTableTypeString.length() /* characters, not bytes */);
2240
2241     if (r != SQL_SUCCESS)
2242         qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
2243
2244     if (d->hasSQLFetchScroll)
2245         r = SQLFetchScroll(hStmt,
2246                            SQL_FETCH_NEXT,
2247                            0);
2248     else
2249         r = SQLFetch(hStmt);
2250
2251     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
2252         qWarning() << "QODBCDriver::tables failed to retrieve table/view list: (" << r << "," << qWarnODBCHandle(SQL_HANDLE_STMT, hStmt) << ")";
2253         return QStringList();
2254     }
2255
2256     while (r == SQL_SUCCESS) {
2257         QString fieldVal = qGetStringData(hStmt, 2, -1, false);
2258         tl.append(fieldVal);
2259
2260         if (d->hasSQLFetchScroll)
2261             r = SQLFetchScroll(hStmt,
2262                                SQL_FETCH_NEXT,
2263                                0);
2264         else
2265             r = SQLFetch(hStmt);
2266     }
2267
2268     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2269     if (r!= SQL_SUCCESS)
2270         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2271     return tl;
2272 }
2273
2274 QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
2275 {
2276     QSqlIndex index(tablename);
2277     if (!isOpen())
2278         return index;
2279     bool usingSpecialColumns = false;
2280     QSqlRecord rec = record(tablename);
2281
2282     SQLHANDLE hStmt;
2283     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2284                                   d->hDbc,
2285                                   &hStmt);
2286     if (r != SQL_SUCCESS) {
2287         qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to list primary key"), d);
2288         return index;
2289     }
2290     QString catalog, schema, table;
2291     d->splitTableQualifier(tablename, catalog, schema, table);
2292
2293     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2294         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2295     else
2296         catalog = d->adjustCase(catalog);
2297
2298     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2299         schema = stripDelimiters(schema, QSqlDriver::TableName);
2300     else
2301         schema = d->adjustCase(schema);
2302
2303     if (isIdentifierEscaped(table, QSqlDriver::TableName))
2304         table = stripDelimiters(table, QSqlDriver::TableName);
2305     else
2306         table = d->adjustCase(table);
2307
2308     r = SQLSetStmtAttr(hStmt,
2309                         SQL_ATTR_CURSOR_TYPE,
2310                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2311                         SQL_IS_UINTEGER);
2312     r = SQLPrimaryKeys(hStmt,
2313 #ifdef UNICODE
2314                         catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2315 #else
2316                         catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2317 #endif
2318                         catalog.length(),
2319 #ifdef UNICODE
2320                         schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2321 #else
2322                         schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2323 #endif
2324                         schema.length(),
2325 #ifdef UNICODE
2326                         toSQLTCHAR(table).data(),
2327 #else
2328                         (SQLCHAR*)table.toUtf8().data(),
2329 #endif
2330                         table.length() /* in characters, not in bytes */);
2331
2332     // if the SQLPrimaryKeys() call does not succeed (e.g the driver
2333     // does not support it) - try an alternative method to get hold of
2334     // the primary index (e.g MS Access and FoxPro)
2335     if (r != SQL_SUCCESS) {
2336             r = SQLSpecialColumns(hStmt,
2337                         SQL_BEST_ROWID,
2338 #ifdef UNICODE
2339                         catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2340 #else
2341                         catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2342 #endif
2343                         catalog.length(),
2344 #ifdef UNICODE
2345                         schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2346 #else
2347                         schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2348 #endif
2349                         schema.length(),
2350 #ifdef UNICODE
2351                         toSQLTCHAR(table).data(),
2352 #else
2353                         (SQLCHAR*)table.toUtf8().data(),
2354 #endif
2355                         table.length(),
2356                         SQL_SCOPE_CURROW,
2357                         SQL_NULLABLE);
2358
2359             if (r != SQL_SUCCESS) {
2360                 qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
2361             } else {
2362                 usingSpecialColumns = true;
2363             }
2364     }
2365
2366     if (d->hasSQLFetchScroll)
2367         r = SQLFetchScroll(hStmt,
2368                            SQL_FETCH_NEXT,
2369                            0);
2370     else
2371         r = SQLFetch(hStmt);
2372
2373     int fakeId = 0;
2374     QString cName, idxName;
2375     // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2376     while (r == SQL_SUCCESS) {
2377         if (usingSpecialColumns) {
2378             cName = qGetStringData(hStmt, 1, -1, d->unicode); // column name
2379             idxName = QString::number(fakeId++); // invent a fake index name
2380         } else {
2381             cName = qGetStringData(hStmt, 3, -1, d->unicode); // column name
2382             idxName = qGetStringData(hStmt, 5, -1, d->unicode); // pk index name
2383         }
2384         index.append(rec.field(cName));
2385         index.setName(idxName);
2386
2387         if (d->hasSQLFetchScroll)
2388             r = SQLFetchScroll(hStmt,
2389                                SQL_FETCH_NEXT,
2390                                0);
2391         else
2392             r = SQLFetch(hStmt);
2393
2394     }
2395     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2396     if (r!= SQL_SUCCESS)
2397         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle") + QString::number(r), d);
2398     return index;
2399 }
2400
2401 QSqlRecord QODBCDriver::record(const QString& tablename) const
2402 {
2403     QSqlRecord fil;
2404     if (!isOpen())
2405         return fil;
2406
2407     SQLHANDLE hStmt;
2408     QString catalog, schema, table;
2409     d->splitTableQualifier(tablename, catalog, schema, table);
2410
2411     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
2412         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
2413     else
2414         catalog = d->adjustCase(catalog);
2415
2416     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
2417         schema = stripDelimiters(schema, QSqlDriver::TableName);
2418     else
2419         schema = d->adjustCase(schema);
2420
2421     if (isIdentifierEscaped(table, QSqlDriver::TableName))
2422         table = stripDelimiters(table, QSqlDriver::TableName);
2423     else
2424         table = d->adjustCase(table);
2425
2426     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
2427                                   d->hDbc,
2428                                   &hStmt);
2429     if (r != SQL_SUCCESS) {
2430         qSqlWarning(QLatin1String("QODBCDriver::record: Unable to allocate handle"), d);
2431         return fil;
2432     }
2433     r = SQLSetStmtAttr(hStmt,
2434                         SQL_ATTR_CURSOR_TYPE,
2435                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
2436                         SQL_IS_UINTEGER);
2437     r =  SQLColumns(hStmt,
2438 #ifdef UNICODE
2439                      catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
2440 #else
2441                      catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.toUtf8().data(),
2442 #endif
2443                      catalog.length(),
2444 #ifdef UNICODE
2445                      schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
2446 #else
2447                      schema.length() == 0 ? NULL : (SQLCHAR*)schema.toUtf8().data(),
2448 #endif
2449                      schema.length(),
2450 #ifdef UNICODE
2451                      toSQLTCHAR(table).data(),
2452 #else
2453                      (SQLCHAR*)table.toUtf8().data(),
2454 #endif
2455                      table.length(),
2456                      NULL,
2457                      0);
2458     if (r != SQL_SUCCESS)
2459         qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
2460
2461     if (d->hasSQLFetchScroll)
2462         r = SQLFetchScroll(hStmt,
2463                            SQL_FETCH_NEXT,
2464                            0);
2465     else
2466         r = SQLFetch(hStmt);
2467
2468     // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
2469     while (r == SQL_SUCCESS) {
2470
2471         fil.append(qMakeFieldInfo(hStmt, d));
2472
2473         if (d->hasSQLFetchScroll)
2474             r = SQLFetchScroll(hStmt,
2475                                SQL_FETCH_NEXT,
2476                                0);
2477         else
2478             r = SQLFetch(hStmt);
2479     }
2480
2481     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
2482     if (r!= SQL_SUCCESS)
2483         qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") + QString::number(r), d);
2484
2485     return fil;
2486 }
2487
2488 QString QODBCDriver::formatValue(const QSqlField &field,
2489                                  bool trimStrings) const
2490 {
2491     QString r;
2492     if (field.isNull()) {
2493         r = QLatin1String("NULL");
2494     } else if (field.type() == QVariant::DateTime) {
2495         // Use an escape sequence for the datetime fields
2496         if (field.value().toDateTime().isValid()){
2497             QDate dt = field.value().toDateTime().date();
2498             QTime tm = field.value().toDateTime().time();
2499             // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
2500             r = QLatin1String("{ ts '") +
2501                 QString::number(dt.year()) + QLatin1Char('-') +
2502                 QString::number(dt.month()).rightJustified(2, QLatin1Char('0'), true) +
2503                 QLatin1Char('-') +
2504                 QString::number(dt.day()).rightJustified(2, QLatin1Char('0'), true) +
2505                 QLatin1Char(' ') +
2506                 tm.toString() +
2507                 QLatin1String("' }");
2508         } else
2509             r = QLatin1String("NULL");
2510     } else if (field.type() == QVariant::ByteArray) {
2511         QByteArray ba = field.value().toByteArray();
2512         QString res;
2513         static const char hexchars[] = "0123456789abcdef";
2514         for (int i = 0; i < ba.size(); ++i) {
2515             uchar s = (uchar) ba[i];
2516             res += QLatin1Char(hexchars[s >> 4]);
2517             res += QLatin1Char(hexchars[s & 0x0f]);
2518         }
2519         r = QLatin1String("0x") + res;
2520     } else {
2521         r = QSqlDriver::formatValue(field, trimStrings);
2522     }
2523     return r;
2524 }
2525
2526 QVariant QODBCDriver::handle() const
2527 {
2528     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
2529 }
2530
2531 QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
2532 {
2533     QChar quote = d->quoteChar();
2534     QString res = identifier;
2535     if(!identifier.isEmpty() && !identifier.startsWith(quote) && !identifier.endsWith(quote) ) {
2536         res.replace(quote, QString(quote)+QString(quote));
2537         res.prepend(quote).append(quote);
2538         res.replace(QLatin1Char('.'), QString(quote)+QLatin1Char('.')+QString(quote));
2539     }
2540     return res;
2541 }
2542
2543 bool QODBCDriver::isIdentifierEscaped(const QString &identifier, IdentifierType) const
2544 {
2545     QChar quote = d->quoteChar();
2546     return identifier.size() > 2
2547         && identifier.startsWith(quote) //left delimited
2548         && identifier.endsWith(quote); //right delimited
2549 }
2550
2551 QT_END_NAMESPACE