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