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_db2_p.h"
41 #include <qcoreapplication.h>
42 #include <qdatetime.h>
43 #include <qsqlfield.h>
44 #include <qsqlerror.h>
45 #include <qsqlindex.h>
46 #include <qsqlrecord.h>
47 #include <qstringlist.h>
48 #include <qvarlengtharray.h>
49 #include <qvector.h>
50 #include <QDebug>
51 #include <QtSql/private/qsqldriver_p.h>
52 #include <QtSql/private/qsqlresult_p.h>
53 
54 #if defined(Q_CC_BOR)
55 // DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE
56 // and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to
57 // the right type before including the header
58 #define SQL_BIGINT_TYPE qint64
59 #define SQL_BIGUINT_TYPE quint64
60 #endif
61 
62 #include <sqlcli1.h>
63 
64 #include <string.h>
65 
66 QT_BEGIN_NAMESPACE
67 
68 static const int COLNAMESIZE = 255;
69 // Based on what is mentioned in the documentation here:
70 // https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/sqlref/src/tpc/db2z_limits.html
71 // The limit is 128 bytes for table names
72 static const SQLSMALLINT TABLENAMESIZE = 128;
73 static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
74 
75 class QDB2DriverPrivate : public QSqlDriverPrivate
76 {
77     Q_DECLARE_PUBLIC(QDB2Driver)
78 
79 public:
QDB2DriverPrivate()80     QDB2DriverPrivate() : QSqlDriverPrivate(), hEnv(0), hDbc(0) { dbmsType = QSqlDriver::DB2; }
81     SQLHANDLE hEnv;
82     SQLHANDLE hDbc;
83     QString user;
84 };
85 
86 class QDB2ResultPrivate;
87 
88 class QDB2Result: public QSqlResult
89 {
90     Q_DECLARE_PRIVATE(QDB2Result)
91 
92 public:
93     QDB2Result(const QDB2Driver *drv);
94     ~QDB2Result();
95     bool prepare(const QString &query) override;
96     bool exec() override;
97     QVariant handle() const override;
98 
99 protected:
100     QVariant data(int field) override;
101     bool reset(const QString &query) override;
102     bool fetch(int i) override;
103     bool fetchNext() override;
104     bool fetchFirst() override;
105     bool fetchLast() override;
106     bool isNull(int i) override;
107     int size() override;
108     int numRowsAffected() override;
109     QSqlRecord record() const override;
110     void virtual_hook(int id, void *data) override;
111     void detachFromResultSet() override;
112     bool nextResult() override;
113 };
114 
115 class QDB2ResultPrivate: public QSqlResultPrivate
116 {
117     Q_DECLARE_PUBLIC(QDB2Result)
118 
119 public:
120     Q_DECLARE_SQLDRIVER_PRIVATE(QDB2Driver)
QDB2ResultPrivate(QDB2Result * q,const QDB2Driver * drv)121     QDB2ResultPrivate(QDB2Result *q, const QDB2Driver *drv)
122         : QSqlResultPrivate(q, drv),
123           hStmt(0)
124     {}
~QDB2ResultPrivate()125     ~QDB2ResultPrivate()
126     {
127         emptyValueCache();
128     }
clearValueCache()129     void clearValueCache()
130     {
131         for (int i = 0; i < valueCache.count(); ++i) {
132             delete valueCache[i];
133             valueCache[i] = NULL;
134         }
135     }
emptyValueCache()136     void emptyValueCache()
137     {
138         clearValueCache();
139         valueCache.clear();
140     }
141 
142     SQLHANDLE hStmt;
143     QSqlRecord recInf;
144     QVector<QVariant*> valueCache;
145 };
146 
qFromTChar(SQLTCHAR * str)147 static QString qFromTChar(SQLTCHAR* str)
148 {
149     return QString((const QChar *)str);
150 }
151 
152 // dangerous!! (but fast). Don't use in functions that
153 // require out parameters!
qToTChar(const QString & str)154 static SQLTCHAR* qToTChar(const QString& str)
155 {
156     return (SQLTCHAR*)str.utf16();
157 }
158 
qWarnDB2Handle(int handleType,SQLHANDLE handle,int * errorCode)159 static QString qWarnDB2Handle(int handleType, SQLHANDLE handle, int *errorCode)
160 {
161     SQLINTEGER nativeCode;
162     SQLSMALLINT msgLen;
163     SQLRETURN r = SQL_ERROR;
164     SQLTCHAR state[SQL_SQLSTATE_SIZE + 1];
165     SQLTCHAR description[SQL_MAX_MESSAGE_LENGTH];
166     r = SQLGetDiagRec(handleType,
167                        handle,
168                        1,
169                        (SQLTCHAR*) state,
170                        &nativeCode,
171                        (SQLTCHAR*) description,
172                        SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */
173                        &msgLen);
174     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
175         if (errorCode)
176             *errorCode = nativeCode;
177         return QString(qFromTChar(description));
178     }
179     return QString();
180 }
181 
qDB2Warn(const QDB2DriverPrivate * d,QStringList * errorCodes=nullptr)182 static QString qDB2Warn(const QDB2DriverPrivate* d, QStringList *errorCodes = nullptr)
183 {
184     int errorCode = 0;
185     QString error = qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv, &errorCode);
186     if (errorCodes && errorCode != 0) {
187         *errorCodes << QString::number(errorCode);
188         errorCode = 0;
189     }
190     if (!error.isEmpty())
191         error += QLatin1Char(' ');
192     error += qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc, &errorCode);
193     if (errorCodes && errorCode != 0)
194         *errorCodes << QString::number(errorCode);
195     return error;
196 }
197 
qDB2Warn(const QDB2ResultPrivate * d,QStringList * errorCodes=nullptr)198 static QString qDB2Warn(const QDB2ResultPrivate* d, QStringList *errorCodes = nullptr)
199 {
200     int errorCode = 0;
201     QString error = qWarnDB2Handle(SQL_HANDLE_ENV, d->drv_d_func()->hEnv, &errorCode);
202     if (errorCodes && errorCode != 0) {
203         *errorCodes << QString::number(errorCode);
204         errorCode = 0;
205     }
206     if (!error.isEmpty())
207         error += QLatin1Char(' ');
208     error += qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->hDbc, &errorCode);
209     if (errorCodes && errorCode != 0) {
210         *errorCodes << QString::number(errorCode);
211         errorCode = 0;
212     }
213     if (!error.isEmpty())
214         error += QLatin1Char(' ');
215     error += qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt, &errorCode);
216     if (errorCodes && errorCode != 0)
217         *errorCodes << QString::number(errorCode);
218     return error;
219 }
220 
qSqlWarning(const QString & message,const QDB2DriverPrivate * d)221 static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d)
222 {
223     qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
224                               qDB2Warn(d).toLocal8Bit().constData());
225 }
226 
qSqlWarning(const QString & message,const QDB2ResultPrivate * d)227 static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d)
228 {
229     qWarning("%s\tError: %s", message.toLocal8Bit().constData(),
230                               qDB2Warn(d).toLocal8Bit().constData());
231 }
232 
qMakeError(const QString & err,QSqlError::ErrorType type,const QDB2DriverPrivate * p)233 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
234                             const QDB2DriverPrivate* p)
235 {
236     QStringList errorCodes;
237     const QString error = qDB2Warn(p, &errorCodes);
238     return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
239                      errorCodes.join(QLatin1Char(';')));
240 }
241 
qMakeError(const QString & err,QSqlError::ErrorType type,const QDB2ResultPrivate * p)242 static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
243                             const QDB2ResultPrivate* p)
244 {
245     QStringList errorCodes;
246     const QString error = qDB2Warn(p, &errorCodes);
247     return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
248                      errorCodes.join(QLatin1Char(';')));
249 }
250 
qDecodeDB2Type(SQLSMALLINT sqltype)251 static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype)
252 {
253     QVariant::Type type = QVariant::Invalid;
254     switch (sqltype) {
255     case SQL_REAL:
256     case SQL_FLOAT:
257     case SQL_DOUBLE:
258     case SQL_DECIMAL:
259     case SQL_NUMERIC:
260         type = QVariant::Double;
261         break;
262     case SQL_SMALLINT:
263     case SQL_INTEGER:
264     case SQL_BIT:
265     case SQL_TINYINT:
266         type = QVariant::Int;
267         break;
268     case SQL_BIGINT:
269         type = QVariant::LongLong;
270         break;
271     case SQL_BLOB:
272     case SQL_BINARY:
273     case SQL_VARBINARY:
274     case SQL_LONGVARBINARY:
275     case SQL_CLOB:
276     case SQL_DBCLOB:
277         type = QVariant::ByteArray;
278         break;
279     case SQL_DATE:
280     case SQL_TYPE_DATE:
281         type = QVariant::Date;
282         break;
283     case SQL_TIME:
284     case SQL_TYPE_TIME:
285         type = QVariant::Time;
286         break;
287     case SQL_TIMESTAMP:
288     case SQL_TYPE_TIMESTAMP:
289         type = QVariant::DateTime;
290         break;
291     case SQL_WCHAR:
292     case SQL_WVARCHAR:
293     case SQL_WLONGVARCHAR:
294     case SQL_CHAR:
295     case SQL_VARCHAR:
296     case SQL_LONGVARCHAR:
297         type = QVariant::String;
298         break;
299     default:
300         type = QVariant::ByteArray;
301         break;
302     }
303     return type;
304 }
305 
qMakeFieldInfo(const QDB2ResultPrivate * d,int i)306 static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
307 {
308     SQLSMALLINT colNameLen;
309     SQLSMALLINT colType;
310     SQLULEN colSize;
311     SQLSMALLINT colScale;
312     SQLSMALLINT nullable;
313     SQLRETURN r = SQL_ERROR;
314     SQLTCHAR colName[COLNAMESIZE];
315     r = SQLDescribeCol(d->hStmt,
316                         i+1,
317                         colName,
318                         (SQLSMALLINT) COLNAMESIZE,
319                         &colNameLen,
320                         &colType,
321                         &colSize,
322                         &colScale,
323                         &nullable);
324 
325     if (r != SQL_SUCCESS) {
326         qSqlWarning(QString::fromLatin1("qMakeFieldInfo: Unable to describe column %1").arg(i), d);
327         return QSqlField();
328     }
329     QSqlField f(qFromTChar(colName), qDecodeDB2Type(colType));
330     // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
331     if (nullable == SQL_NO_NULLS)
332         f.setRequired(true);
333     else if (nullable == SQL_NULLABLE)
334         f.setRequired(false);
335     // else required is unknown
336     f.setLength(colSize == 0 ? -1 : int(colSize));
337     f.setPrecision(colScale == 0 ? -1 : int(colScale));
338     f.setSqlType(int(colType));
339     SQLTCHAR tableName[TABLENAMESIZE];
340     SQLSMALLINT tableNameLen;
341     r = SQLColAttribute(d->hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName,
342                         TABLENAMESIZE, &tableNameLen, 0);
343     if (r == SQL_SUCCESS)
344         f.setTableName(qFromTChar(tableName));
345     return f;
346 }
347 
qGetIntData(SQLHANDLE hStmt,int column,bool & isNull)348 static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull)
349 {
350     SQLINTEGER intbuf;
351     isNull = false;
352     SQLLEN lengthIndicator = 0;
353     SQLRETURN r = SQLGetData(hStmt,
354                               column + 1,
355                               SQL_C_SLONG,
356                               (SQLPOINTER) &intbuf,
357                               0,
358                               &lengthIndicator);
359     if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
360         isNull = true;
361         return 0;
362     }
363     return int(intbuf);
364 }
365 
qGetDoubleData(SQLHANDLE hStmt,int column,bool & isNull)366 static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull)
367 {
368     SQLDOUBLE dblbuf;
369     isNull = false;
370     SQLLEN lengthIndicator = 0;
371     SQLRETURN r = SQLGetData(hStmt,
372                               column+1,
373                               SQL_C_DOUBLE,
374                               (SQLPOINTER) &dblbuf,
375                               0,
376                               &lengthIndicator);
377     if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
378         isNull = true;
379         return 0.0;
380     }
381 
382     return (double) dblbuf;
383 }
384 
qGetBigIntData(SQLHANDLE hStmt,int column,bool & isNull)385 static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull)
386 {
387     SQLBIGINT lngbuf = Q_INT64_C(0);
388     isNull = false;
389     SQLLEN lengthIndicator = 0;
390     SQLRETURN r = SQLGetData(hStmt,
391                               column+1,
392                               SQL_C_SBIGINT,
393                               (SQLPOINTER) &lngbuf,
394                               0,
395                               &lengthIndicator);
396     if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA)
397         isNull = true;
398 
399     return lngbuf;
400 }
401 
qGetStringData(SQLHANDLE hStmt,int column,int colSize,bool & isNull)402 static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull)
403 {
404     QString     fieldVal;
405     SQLRETURN   r = SQL_ERROR;
406     SQLLEN      lengthIndicator = 0;
407 
408     if (colSize <= 0)
409         colSize = 255;
410     else if (colSize > 65536) // limit buffer size to 64 KB
411         colSize = 65536;
412     else
413         colSize++; // make sure there is room for more than the 0 termination
414     SQLTCHAR* buf = new SQLTCHAR[colSize];
415 
416     while (true) {
417         r = SQLGetData(hStmt,
418                         column + 1,
419                         SQL_C_WCHAR,
420                         (SQLPOINTER)buf,
421                         colSize * sizeof(SQLTCHAR),
422                         &lengthIndicator);
423         if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
424             if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
425                 fieldVal.clear();
426                 isNull = true;
427                 break;
428             }
429             fieldVal += qFromTChar(buf);
430         } else if (r == SQL_NO_DATA) {
431             break;
432         } else {
433             qWarning("qGetStringData: Error while fetching data (%d)", r);
434             fieldVal.clear();
435             break;
436         }
437     }
438     delete[] buf;
439     return fieldVal;
440 }
441 
qGetBinaryData(SQLHANDLE hStmt,int column,SQLLEN & lengthIndicator,bool & isNull)442 static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLLEN& lengthIndicator, bool& isNull)
443 {
444     QByteArray fieldVal;
445     SQLSMALLINT colNameLen;
446     SQLSMALLINT colType;
447     SQLULEN colSize;
448     SQLSMALLINT colScale;
449     SQLSMALLINT nullable;
450     SQLRETURN r = SQL_ERROR;
451 
452     SQLTCHAR colName[COLNAMESIZE];
453     r = SQLDescribeCol(hStmt,
454                         column+1,
455                         colName,
456                         COLNAMESIZE,
457                         &colNameLen,
458                         &colType,
459                         &colSize,
460                         &colScale,
461                         &nullable);
462     if (r != SQL_SUCCESS)
463         qWarning("qGetBinaryData: Unable to describe column %d", column);
464     // SQLDescribeCol may return 0 if size cannot be determined
465     if (!colSize)
466         colSize = 255;
467     else if (colSize > 65536) // read the field in 64 KB chunks
468         colSize = 65536;
469     char * buf = new char[colSize];
470     while (true) {
471         r = SQLGetData(hStmt,
472                         column+1,
473                         colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY,
474                         (SQLPOINTER) buf,
475                         colSize,
476                         &lengthIndicator);
477         if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
478             if (lengthIndicator == SQL_NULL_DATA) {
479                 isNull = true;
480                 break;
481             } else {
482                 int rSize;
483                 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
484                 if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined
485                     rSize = colSize;
486                 fieldVal.append(QByteArray(buf, rSize));
487                 if (r == SQL_SUCCESS) // the whole field was read in one chunk
488                     break;
489             }
490         } else {
491             break;
492         }
493     }
494     delete [] buf;
495     return fieldVal;
496 }
497 
qSplitTableQualifier(const QString & qualifier,QString * catalog,QString * schema,QString * table)498 static void qSplitTableQualifier(const QString & qualifier, QString * catalog,
499                                   QString * schema, QString * table)
500 {
501     if (!catalog || !schema || !table)
502         return;
503     QStringList l = qualifier.split(QLatin1Char('.'));
504     if (l.count() > 3)
505         return; // can't possibly be a valid table qualifier
506     int i = 0, n = l.count();
507     if (n == 1) {
508         *table = qualifier;
509     } else {
510         for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
511             if (n == 3) {
512                 if (i == 0)
513                     *catalog = *it;
514                 else if (i == 1)
515                     *schema = *it;
516                 else if (i == 2)
517                     *table = *it;
518             } else if (n == 2) {
519                 if (i == 0)
520                     *schema = *it;
521                 else if (i == 1)
522                     *table = *it;
523             }
524             i++;
525         }
526     }
527 }
528 
529 // creates a QSqlField from a valid hStmt generated
530 // by SQLColumns. The hStmt has to point to a valid position.
qMakeFieldInfo(const SQLHANDLE hStmt)531 static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
532 {
533     bool isNull;
534     int type = qGetIntData(hStmt, 4, isNull);
535     QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type));
536     int required = qGetIntData(hStmt, 10, isNull); // nullable-flag
537     // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
538     if (required == SQL_NO_NULLS)
539         f.setRequired(true);
540     else if (required == SQL_NULLABLE)
541         f.setRequired(false);
542     // else we don't know.
543     f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
544     f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
545     f.setSqlType(type);
546     return f;
547 }
548 
qMakeStatement(QDB2ResultPrivate * d,bool forwardOnly,bool setForwardOnly=true)549 static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true)
550 {
551     SQLRETURN r;
552     if (!d->hStmt) {
553         r = SQLAllocHandle(SQL_HANDLE_STMT,
554                             d->drv_d_func()->hDbc,
555                             &d->hStmt);
556         if (r != SQL_SUCCESS) {
557             qSqlWarning(QLatin1String("QDB2Result::reset: Unable to allocate statement handle"), d);
558             return false;
559         }
560     } else {
561         r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
562         if (r != SQL_SUCCESS) {
563             qSqlWarning(QLatin1String("QDB2Result::reset: Unable to close statement handle"), d);
564             return false;
565         }
566     }
567 
568     if (!setForwardOnly)
569         return true;
570 
571     if (forwardOnly) {
572         r = SQLSetStmtAttr(d->hStmt,
573                             SQL_ATTR_CURSOR_TYPE,
574                             (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
575                             SQL_IS_UINTEGER);
576     } else {
577         r = SQLSetStmtAttr(d->hStmt,
578                             SQL_ATTR_CURSOR_TYPE,
579                             (SQLPOINTER) SQL_CURSOR_STATIC,
580                             SQL_IS_UINTEGER);
581     }
582     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
583         qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
584                      forwardOnly ? QLatin1String("SQL_CURSOR_FORWARD_ONLY")
585                                  : QLatin1String("SQL_CURSOR_STATIC")), d);
586         return false;
587     }
588     return true;
589 }
590 
handle() const591 QVariant QDB2Result::handle() const
592 {
593     Q_D(const QDB2Result);
594     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hStmt);
595 }
596 
597 /************************************/
598 
QDB2Result(const QDB2Driver * drv)599 QDB2Result::QDB2Result(const QDB2Driver *drv)
600     : QSqlResult(*new QDB2ResultPrivate(this, drv))
601 {
602 }
603 
~QDB2Result()604 QDB2Result::~QDB2Result()
605 {
606     Q_D(const QDB2Result);
607     if (d->hStmt) {
608         SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
609         if (r != SQL_SUCCESS)
610             qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
611                         + QString::number(r), d);
612     }
613 }
614 
reset(const QString & query)615 bool QDB2Result::reset (const QString& query)
616 {
617     Q_D(QDB2Result);
618     setActive(false);
619     setAt(QSql::BeforeFirstRow);
620     SQLRETURN r;
621 
622     d->recInf.clear();
623     d->emptyValueCache();
624 
625     if (!qMakeStatement(d, isForwardOnly()))
626         return false;
627 
628     r = SQLExecDirect(d->hStmt,
629                        qToTChar(query),
630                        (SQLINTEGER) query.length());
631     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
632         setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
633                                 "Unable to execute statement"), QSqlError::StatementError, d));
634         return false;
635     }
636     SQLSMALLINT count = 0;
637     r = SQLNumResultCols(d->hStmt, &count);
638     if (count) {
639         setSelect(true);
640         for (int i = 0; i < count; ++i) {
641             d->recInf.append(qMakeFieldInfo(d, i));
642         }
643     } else {
644         setSelect(false);
645     }
646     d->valueCache.resize(count);
647     d->valueCache.fill(NULL);
648     setActive(true);
649     return true;
650 }
651 
prepare(const QString & query)652 bool QDB2Result::prepare(const QString& query)
653 {
654     Q_D(QDB2Result);
655     setActive(false);
656     setAt(QSql::BeforeFirstRow);
657     SQLRETURN r;
658 
659     d->recInf.clear();
660     d->emptyValueCache();
661 
662     if (!qMakeStatement(d, isForwardOnly()))
663         return false;
664 
665     r = SQLPrepare(d->hStmt,
666                     qToTChar(query),
667                     (SQLINTEGER) query.length());
668 
669     if (r != SQL_SUCCESS) {
670         setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
671                      "Unable to prepare statement"), QSqlError::StatementError, d));
672         return false;
673     }
674     return true;
675 }
676 
exec()677 bool QDB2Result::exec()
678 {
679     Q_D(QDB2Result);
680     QList<QByteArray> tmpStorage; // holds temporary ptrs
681     QVarLengthArray<SQLLEN, 32> indicators(boundValues().count());
682 
683     memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
684     setActive(false);
685     setAt(QSql::BeforeFirstRow);
686     SQLRETURN r;
687 
688     d->recInf.clear();
689     d->emptyValueCache();
690 
691     if (!qMakeStatement(d, isForwardOnly(), false))
692         return false;
693 
694 
695     QVector<QVariant> &values = boundValues();
696     int i;
697     for (i = 0; i < values.count(); ++i) {
698         // bind parameters - only positional binding allowed
699         SQLLEN *ind = &indicators[i];
700         if (values.at(i).isNull())
701             *ind = SQL_NULL_DATA;
702         if (bindValueType(i) & QSql::Out)
703             values[i].detach();
704 
705         switch (values.at(i).type()) {
706             case QVariant::Date: {
707                 QByteArray ba;
708                 ba.resize(sizeof(DATE_STRUCT));
709                 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
710                 QDate qdt = values.at(i).toDate();
711                 dt->year = qdt.year();
712                 dt->month = qdt.month();
713                 dt->day = qdt.day();
714                 r = SQLBindParameter(d->hStmt,
715                                      i + 1,
716                                      qParamType[bindValueType(i) & 3],
717                                      SQL_C_DATE,
718                                      SQL_DATE,
719                                      0,
720                                      0,
721                                      (void *) dt,
722                                      0,
723                                      *ind == SQL_NULL_DATA ? ind : NULL);
724                 tmpStorage.append(ba);
725                 break; }
726             case QVariant::Time: {
727                 QByteArray ba;
728                 ba.resize(sizeof(TIME_STRUCT));
729                 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
730                 QTime qdt = values.at(i).toTime();
731                 dt->hour = qdt.hour();
732                 dt->minute = qdt.minute();
733                 dt->second = qdt.second();
734                 r = SQLBindParameter(d->hStmt,
735                                       i + 1,
736                                       qParamType[bindValueType(i) & 3],
737                                       SQL_C_TIME,
738                                       SQL_TIME,
739                                       0,
740                                       0,
741                                       (void *) dt,
742                                       0,
743                                       *ind == SQL_NULL_DATA ? ind : NULL);
744                 tmpStorage.append(ba);
745                 break; }
746             case QVariant::DateTime: {
747                 QByteArray ba;
748                 ba.resize(sizeof(TIMESTAMP_STRUCT));
749                 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
750                 QDateTime qdt = values.at(i).toDateTime();
751                 dt->year = qdt.date().year();
752                 dt->month = qdt.date().month();
753                 dt->day = qdt.date().day();
754                 dt->hour = qdt.time().hour();
755                 dt->minute = qdt.time().minute();
756                 dt->second = qdt.time().second();
757                 dt->fraction = qdt.time().msec() * 1000000;
758                 r = SQLBindParameter(d->hStmt,
759                                       i + 1,
760                                       qParamType[bindValueType(i) & 3],
761                                       SQL_C_TIMESTAMP,
762                                       SQL_TIMESTAMP,
763                                       0,
764                                       0,
765                                       (void *) dt,
766                                       0,
767                                       *ind == SQL_NULL_DATA ? ind : NULL);
768                 tmpStorage.append(ba);
769                 break; }
770             case QVariant::Int:
771                 r = SQLBindParameter(d->hStmt,
772                                       i + 1,
773                                       qParamType[bindValueType(i) & 3],
774                                       SQL_C_SLONG,
775                                       SQL_INTEGER,
776                                       0,
777                                       0,
778                                       (void *)values.at(i).constData(),
779                                       0,
780                                       *ind == SQL_NULL_DATA ? ind : NULL);
781                 break;
782             case QVariant::Double:
783                 r = SQLBindParameter(d->hStmt,
784                                       i + 1,
785                                       qParamType[bindValueType(i) & 3],
786                                       SQL_C_DOUBLE,
787                                       SQL_DOUBLE,
788                                       0,
789                                       0,
790                                       (void *)values.at(i).constData(),
791                                       0,
792                                       *ind == SQL_NULL_DATA ? ind : NULL);
793                 break;
794             case QVariant::ByteArray: {
795                 int len = values.at(i).toByteArray().size();
796                 if (*ind != SQL_NULL_DATA)
797                     *ind = len;
798                 r = SQLBindParameter(d->hStmt,
799                                       i + 1,
800                                       qParamType[bindValueType(i) & 3],
801                                       SQL_C_BINARY,
802                                       SQL_LONGVARBINARY,
803                                       len,
804                                       0,
805                                       (void *)values.at(i).toByteArray().constData(),
806                                       len,
807                                       ind);
808                 break; }
809             case QVariant::String:
810             {
811                 QString str(values.at(i).toString());
812                 if (*ind != SQL_NULL_DATA)
813                     *ind = str.length() * sizeof(QChar);
814                 if (bindValueType(i) & QSql::Out) {
815                     QByteArray ba((char*)str.utf16(), str.capacity() * sizeof(QChar));
816                     r = SQLBindParameter(d->hStmt,
817                                         i + 1,
818                                         qParamType[bindValueType(i) & 3],
819                                         SQL_C_WCHAR,
820                                         SQL_WVARCHAR,
821                                         str.length(),
822                                         0,
823                                         (void *)ba.constData(),
824                                         ba.size(),
825                                         ind);
826                     tmpStorage.append(ba);
827                 } else {
828                     void *data = (void*)str.utf16();
829                     int len = str.length();
830                     r = SQLBindParameter(d->hStmt,
831                                         i + 1,
832                                         qParamType[bindValueType(i) & 3],
833                                         SQL_C_WCHAR,
834                                         SQL_WVARCHAR,
835                                         len,
836                                         0,
837                                         data,
838                                         len * sizeof(QChar),
839                                         ind);
840                 }
841                 break;
842             }
843             default: {
844                 QByteArray ba = values.at(i).toString().toLatin1();
845                 int len = ba.length() + 1;
846                 if (*ind != SQL_NULL_DATA)
847                     *ind = ba.length();
848                 r = SQLBindParameter(d->hStmt,
849                                       i + 1,
850                                       qParamType[bindValueType(i) & 3],
851                                       SQL_C_CHAR,
852                                       SQL_VARCHAR,
853                                       len,
854                                       0,
855                                       (void *) ba.constData(),
856                                       len,
857                                       ind);
858                 tmpStorage.append(ba);
859                 break; }
860         }
861         if (r != SQL_SUCCESS) {
862             qWarning("QDB2Result::exec: unable to bind variable: %s",
863                      qDB2Warn(d).toLocal8Bit().constData());
864             setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
865                                     "Unable to bind variable"), QSqlError::StatementError, d));
866             return false;
867         }
868     }
869 
870     r = SQLExecute(d->hStmt);
871     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
872         qWarning("QDB2Result::exec: Unable to execute statement: %s",
873                  qDB2Warn(d).toLocal8Bit().constData());
874         setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
875                                 "Unable to execute statement"), QSqlError::StatementError, d));
876         return false;
877     }
878     SQLSMALLINT count = 0;
879     r = SQLNumResultCols(d->hStmt, &count);
880     if (count) {
881         setSelect(true);
882         for (int i = 0; i < count; ++i) {
883             d->recInf.append(qMakeFieldInfo(d, i));
884         }
885     } else {
886         setSelect(false);
887     }
888     setActive(true);
889     d->valueCache.resize(count);
890     d->valueCache.fill(NULL);
891 
892     //get out parameters
893     if (!hasOutValues())
894         return true;
895 
896     for (i = 0; i < values.count(); ++i) {
897         switch (values[i].type()) {
898             case QVariant::Date: {
899                 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
900                 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
901                 break; }
902             case QVariant::Time: {
903                 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
904                 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
905                 break; }
906             case QVariant::DateTime: {
907                 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData());
908                 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
909                               QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
910                 break; }
911             case QVariant::Int:
912             case QVariant::Double:
913             case QVariant::ByteArray:
914                 break;
915             case QVariant::String:
916                 if (bindValueType(i) & QSql::Out)
917                     values[i] = QString((const QChar *)tmpStorage.takeFirst().constData());
918                 break;
919             default: {
920                 values[i] = QString::fromLatin1(tmpStorage.takeFirst().constData());
921                 break; }
922         }
923         if (indicators[i] == SQL_NULL_DATA)
924             values[i] = QVariant(values[i].type());
925     }
926     return true;
927 }
928 
fetch(int i)929 bool QDB2Result::fetch(int i)
930 {
931     Q_D(QDB2Result);
932     if (isForwardOnly() && i < at())
933         return false;
934     if (i == at())
935         return true;
936     d->clearValueCache();
937     int actualIdx = i + 1;
938     if (actualIdx <= 0) {
939         setAt(QSql::BeforeFirstRow);
940         return false;
941     }
942     SQLRETURN r;
943     if (isForwardOnly()) {
944         bool ok = true;
945         while (ok && i > at())
946             ok = fetchNext();
947         return ok;
948     } else {
949         r = SQLFetchScroll(d->hStmt,
950                             SQL_FETCH_ABSOLUTE,
951                             actualIdx);
952     }
953     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
954         setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
955                                 "Unable to fetch record %1").arg(i), QSqlError::StatementError, d));
956         return false;
957     }
958     else if (r == SQL_NO_DATA)
959         return false;
960     setAt(i);
961     return true;
962 }
963 
fetchNext()964 bool QDB2Result::fetchNext()
965 {
966     Q_D(QDB2Result);
967     SQLRETURN r;
968     d->clearValueCache();
969     r = SQLFetchScroll(d->hStmt,
970                        SQL_FETCH_NEXT,
971                        0);
972     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
973         if (r != SQL_NO_DATA)
974             setLastError(qMakeError(QCoreApplication::translate("QDB2Result",
975                                     "Unable to fetch next"), QSqlError::StatementError, d));
976         return false;
977     }
978     setAt(at() + 1);
979     return true;
980 }
981 
fetchFirst()982 bool QDB2Result::fetchFirst()
983 {
984     Q_D(QDB2Result);
985     if (isForwardOnly() && at() != QSql::BeforeFirstRow)
986         return false;
987     if (isForwardOnly())
988         return fetchNext();
989     d->clearValueCache();
990     SQLRETURN r;
991     r = SQLFetchScroll(d->hStmt,
992                        SQL_FETCH_FIRST,
993                        0);
994     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
995         if(r!= SQL_NO_DATA)
996             setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
997                                     QSqlError::StatementError, d));
998         return false;
999     }
1000     setAt(0);
1001     return true;
1002 }
1003 
fetchLast()1004 bool QDB2Result::fetchLast()
1005 {
1006     Q_D(QDB2Result);
1007     d->clearValueCache();
1008 
1009     int i = at();
1010     if (i == QSql::AfterLastRow) {
1011         if (isForwardOnly()) {
1012             return false;
1013         } else {
1014             if (!fetch(0))
1015                 return false;
1016             i = at();
1017         }
1018     }
1019 
1020     while (fetchNext())
1021         ++i;
1022 
1023     if (i == QSql::BeforeFirstRow) {
1024         setAt(QSql::AfterLastRow);
1025         return false;
1026     }
1027 
1028     if (!isForwardOnly())
1029         return fetch(i);
1030 
1031     setAt(i);
1032     return true;
1033 }
1034 
1035 
data(int field)1036 QVariant QDB2Result::data(int field)
1037 {
1038     Q_D(QDB2Result);
1039     if (field >= d->recInf.count()) {
1040         qWarning("QDB2Result::data: column %d out of range", field);
1041         return QVariant();
1042     }
1043     SQLRETURN r = 0;
1044     SQLLEN lengthIndicator = 0;
1045     bool isNull = false;
1046     const QSqlField info = d->recInf.field(field);
1047 
1048     if (!info.isValid() || field >= d->valueCache.size())
1049         return QVariant();
1050 
1051     if (d->valueCache[field])
1052         return *d->valueCache[field];
1053 
1054 
1055     QVariant* v = 0;
1056     switch (info.type()) {
1057         case QVariant::LongLong:
1058             v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
1059             break;
1060         case QVariant::Int:
1061             v = new QVariant(qGetIntData(d->hStmt, field, isNull));
1062             break;
1063         case QVariant::Date: {
1064             DATE_STRUCT dbuf;
1065             r = SQLGetData(d->hStmt,
1066                             field + 1,
1067                             SQL_C_DATE,
1068                             (SQLPOINTER) &dbuf,
1069                             0,
1070                             &lengthIndicator);
1071             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1072                 v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
1073             } else {
1074                 v = new QVariant(QDate());
1075                 isNull = true;
1076             }
1077             break; }
1078         case QVariant::Time: {
1079             TIME_STRUCT tbuf;
1080             r = SQLGetData(d->hStmt,
1081                             field + 1,
1082                             SQL_C_TIME,
1083                             (SQLPOINTER) &tbuf,
1084                             0,
1085                             &lengthIndicator);
1086             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1087                 v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1088             } else {
1089                 v = new QVariant(QTime());
1090                 isNull = true;
1091             }
1092             break; }
1093         case QVariant::DateTime: {
1094             TIMESTAMP_STRUCT dtbuf;
1095             r = SQLGetData(d->hStmt,
1096                             field + 1,
1097                             SQL_C_TIMESTAMP,
1098                             (SQLPOINTER) &dtbuf,
1099                             0,
1100                             &lengthIndicator);
1101             if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1102                 v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1103                                              QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1104             } else {
1105                 v = new QVariant(QDateTime());
1106                 isNull = true;
1107             }
1108             break; }
1109         case QVariant::ByteArray:
1110             v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull));
1111             break;
1112         case QVariant::Double:
1113             {
1114             switch(numericalPrecisionPolicy()) {
1115                 case QSql::LowPrecisionInt32:
1116                     v = new QVariant(qGetIntData(d->hStmt, field, isNull));
1117                     break;
1118                 case QSql::LowPrecisionInt64:
1119                     v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
1120                     break;
1121                 case QSql::LowPrecisionDouble:
1122                     v = new QVariant(qGetDoubleData(d->hStmt, field, isNull));
1123                     break;
1124                 case QSql::HighPrecision:
1125                 default:
1126                     // length + 1 for the comma
1127                     v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
1128                     break;
1129             }
1130             break;
1131             }
1132         case QVariant::String:
1133         default:
1134             v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull));
1135             break;
1136     }
1137     if (isNull)
1138         *v = QVariant(info.type());
1139     d->valueCache[field] = v;
1140     return *v;
1141 }
1142 
isNull(int i)1143 bool QDB2Result::isNull(int i)
1144 {
1145     Q_D(const QDB2Result);
1146     if (i >= d->valueCache.size())
1147         return true;
1148 
1149     if (d->valueCache[i])
1150         return d->valueCache[i]->isNull();
1151     return data(i).isNull();
1152 }
1153 
numRowsAffected()1154 int QDB2Result::numRowsAffected()
1155 {
1156     Q_D(const QDB2Result);
1157     SQLLEN affectedRowCount = 0;
1158     SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1159     if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1160         return affectedRowCount;
1161     else
1162         qSqlWarning(QLatin1String("QDB2Result::numRowsAffected: Unable to count affected rows"), d);
1163     return -1;
1164 }
1165 
size()1166 int QDB2Result::size()
1167 {
1168     return -1;
1169 }
1170 
record() const1171 QSqlRecord QDB2Result::record() const
1172 {
1173     Q_D(const QDB2Result);
1174     if (isActive())
1175         return d->recInf;
1176     return QSqlRecord();
1177 }
1178 
nextResult()1179 bool QDB2Result::nextResult()
1180 {
1181     Q_D(QDB2Result);
1182     setActive(false);
1183     setAt(QSql::BeforeFirstRow);
1184     d->recInf.clear();
1185     d->emptyValueCache();
1186     setSelect(false);
1187 
1188     SQLRETURN r = SQLMoreResults(d->hStmt);
1189     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1190         if (r != SQL_NO_DATA) {
1191             setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
1192                 "Unable to fetch last"), QSqlError::ConnectionError, d));
1193         }
1194         return false;
1195     }
1196 
1197     SQLSMALLINT fieldCount = 0;
1198     r = SQLNumResultCols(d->hStmt, &fieldCount);
1199     setSelect(fieldCount > 0);
1200     for (int i = 0; i < fieldCount; ++i)
1201         d->recInf.append(qMakeFieldInfo(d, i));
1202 
1203     d->valueCache.resize(fieldCount);
1204     d->valueCache.fill(NULL);
1205     setActive(true);
1206 
1207     return true;
1208 }
1209 
virtual_hook(int id,void * data)1210 void QDB2Result::virtual_hook(int id, void *data)
1211 {
1212     QSqlResult::virtual_hook(id, data);
1213 }
1214 
detachFromResultSet()1215 void QDB2Result::detachFromResultSet()
1216 {
1217     Q_D(QDB2Result);
1218     if (d->hStmt)
1219         SQLCloseCursor(d->hStmt);
1220 }
1221 
1222 /************************************/
1223 
QDB2Driver(QObject * parent)1224 QDB2Driver::QDB2Driver(QObject* parent)
1225     : QSqlDriver(*new QDB2DriverPrivate, parent)
1226 {
1227 }
1228 
QDB2Driver(Qt::HANDLE env,Qt::HANDLE con,QObject * parent)1229 QDB2Driver::QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent)
1230     : QSqlDriver(*new QDB2DriverPrivate, parent)
1231 {
1232     Q_D(QDB2Driver);
1233     d->hEnv = reinterpret_cast<SQLHANDLE>(env);
1234     d->hDbc = reinterpret_cast<SQLHANDLE>(con);
1235     if (env && con) {
1236         setOpen(true);
1237         setOpenError(false);
1238     }
1239 }
1240 
~QDB2Driver()1241 QDB2Driver::~QDB2Driver()
1242 {
1243     close();
1244 }
1245 
open(const QString & db,const QString & user,const QString & password,const QString & host,int port,const QString & connOpts)1246 bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
1247                        const QString& connOpts)
1248 {
1249     Q_D(QDB2Driver);
1250     if (isOpen())
1251       close();
1252     SQLRETURN r;
1253     r = SQLAllocHandle(SQL_HANDLE_ENV,
1254                         SQL_NULL_HANDLE,
1255                         &d->hEnv);
1256     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1257         qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate environment"), d);
1258         setOpenError(true);
1259         return false;
1260     }
1261 
1262     r = SQLAllocHandle(SQL_HANDLE_DBC,
1263                         d->hEnv,
1264                         &d->hDbc);
1265     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1266         qSqlWarning(QLatin1String("QDB2Driver::open: Unable to allocate connection"), d);
1267         setOpenError(true);
1268         return false;
1269     }
1270 
1271     QString protocol;
1272     // Set connection attributes
1273     const QStringList opts(connOpts.split(QLatin1Char(';'), Qt::SkipEmptyParts));
1274     for (int i = 0; i < opts.count(); ++i) {
1275         const QString tmp(opts.at(i));
1276         int idx;
1277         if ((idx = tmp.indexOf(QLatin1Char('='))) == -1) {
1278             qWarning("QDB2Driver::open: Illegal connect option value '%s'",
1279                      tmp.toLocal8Bit().constData());
1280             continue;
1281         }
1282 
1283         const QString opt(tmp.left(idx));
1284         const QString val(tmp.mid(idx + 1).simplified());
1285 
1286         SQLULEN v = 0;
1287         r = SQL_SUCCESS;
1288         if (opt == QLatin1String("SQL_ATTR_ACCESS_MODE")) {
1289             if (val == QLatin1String("SQL_MODE_READ_ONLY")) {
1290                 v = SQL_MODE_READ_ONLY;
1291             } else if (val == QLatin1String("SQL_MODE_READ_WRITE")) {
1292                 v = SQL_MODE_READ_WRITE;
1293             } else {
1294                 qWarning("QDB2Driver::open: Unknown option value '%s'",
1295                          tmp.toLocal8Bit().constData());
1296                 continue;
1297             }
1298             r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, reinterpret_cast<SQLPOINTER>(v), 0);
1299         } else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
1300             v = val.toUInt();
1301             r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(v), 0);
1302         } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) {
1303                         protocol = tmp;
1304         }
1305         else {
1306             qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
1307                       tmp.toLocal8Bit().constData());
1308         }
1309         if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1310             qSqlWarning(QString::fromLatin1("QDB2Driver::open: "
1311                            "Unable to set connection attribute '%1'").arg(opt), d);
1312     }
1313 
1314     if (protocol.isEmpty())
1315         protocol = QLatin1String("PROTOCOL=TCPIP");
1316 
1317     if (port < 0 )
1318         port = 50000;
1319 
1320     QString connQStr;
1321     connQStr =  protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host
1322         + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user
1323         + QLatin1String(";PWD=") + password;
1324 
1325 
1326     SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
1327     SQLSMALLINT cb;
1328 
1329     r = SQLDriverConnect(d->hDbc,
1330                           NULL,
1331                           qToTChar(connQStr),
1332                           (SQLSMALLINT) connQStr.length(),
1333                           connOut,
1334                           SQL_MAX_OPTION_STRING_LENGTH,
1335                           &cb,
1336                           SQL_DRIVER_NOPROMPT);
1337     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1338         setLastError(qMakeError(tr("Unable to connect"),
1339                                 QSqlError::ConnectionError, d));
1340         setOpenError(true);
1341         return false;
1342     }
1343 
1344     d->user = user;
1345     setOpen(true);
1346     setOpenError(false);
1347     return true;
1348 }
1349 
close()1350 void QDB2Driver::close()
1351 {
1352     Q_D(QDB2Driver);
1353     SQLRETURN r;
1354     if (d->hDbc) {
1355         // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1356         if (isOpen()) {
1357             r = SQLDisconnect(d->hDbc);
1358             if (r != SQL_SUCCESS)
1359                 qSqlWarning(QLatin1String("QDB2Driver::close: Unable to disconnect datasource"), d);
1360         }
1361         r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1362         if (r != SQL_SUCCESS)
1363             qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free connection handle"), d);
1364         d->hDbc = 0;
1365     }
1366 
1367     if (d->hEnv) {
1368         r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1369         if (r != SQL_SUCCESS)
1370             qSqlWarning(QLatin1String("QDB2Driver::close: Unable to free environment handle"), d);
1371         d->hEnv = 0;
1372     }
1373     setOpen(false);
1374     setOpenError(false);
1375 }
1376 
createResult() const1377 QSqlResult *QDB2Driver::createResult() const
1378 {
1379     return new QDB2Result(this);
1380 }
1381 
record(const QString & tableName) const1382 QSqlRecord QDB2Driver::record(const QString& tableName) const
1383 {
1384     Q_D(const QDB2Driver);
1385     QSqlRecord fil;
1386     if (!isOpen())
1387         return fil;
1388 
1389     SQLHANDLE hStmt;
1390     QString catalog, schema, table;
1391     qSplitTableQualifier(tableName, &catalog, &schema, &table);
1392     if (schema.isEmpty())
1393         schema = d->user;
1394 
1395     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
1396         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1397     else
1398         catalog = catalog.toUpper();
1399 
1400     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
1401         schema = stripDelimiters(schema, QSqlDriver::TableName);
1402     else
1403         schema = schema.toUpper();
1404 
1405     if (isIdentifierEscaped(table, QSqlDriver::TableName))
1406         table = stripDelimiters(table, QSqlDriver::TableName);
1407     else
1408         table = table.toUpper();
1409 
1410     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1411                                   d->hDbc,
1412                                   &hStmt);
1413     if (r != SQL_SUCCESS) {
1414         qSqlWarning(QLatin1String("QDB2Driver::record: Unable to allocate handle"), d);
1415         return fil;
1416     }
1417 
1418     r = SQLSetStmtAttr(hStmt,
1419                         SQL_ATTR_CURSOR_TYPE,
1420                         (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
1421                         SQL_IS_UINTEGER);
1422 
1423 
1424     //Aside: szSchemaName and szTableName parameters of SQLColumns
1425     //are case sensitive search patterns, so no escaping is used.
1426     r =  SQLColumns(hStmt,
1427                      NULL,
1428                      0,
1429                      qToTChar(schema),
1430                      schema.length(),
1431                      qToTChar(table),
1432                      table.length(),
1433                      NULL,
1434                      0);
1435 
1436     if (r != SQL_SUCCESS)
1437         qSqlWarning(QLatin1String("QDB2Driver::record: Unable to execute column list"), d);
1438     r = SQLFetchScroll(hStmt,
1439                         SQL_FETCH_NEXT,
1440                         0);
1441     while (r == SQL_SUCCESS) {
1442         QSqlField fld = qMakeFieldInfo(hStmt);
1443         fld.setTableName(tableName);
1444         fil.append(fld);
1445         r = SQLFetchScroll(hStmt,
1446                             SQL_FETCH_NEXT,
1447                             0);
1448     }
1449 
1450     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1451     if (r != SQL_SUCCESS)
1452         qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1453                     + QString::number(r), d);
1454 
1455     return fil;
1456 }
1457 
tables(QSql::TableType type) const1458 QStringList QDB2Driver::tables(QSql::TableType type) const
1459 {
1460     Q_D(const QDB2Driver);
1461     QStringList tl;
1462     if (!isOpen())
1463         return tl;
1464 
1465     SQLHANDLE hStmt;
1466 
1467     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1468                                   d->hDbc,
1469                                   &hStmt);
1470     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1471         qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to allocate handle"), d);
1472         return tl;
1473     }
1474     r = SQLSetStmtAttr(hStmt,
1475                         SQL_ATTR_CURSOR_TYPE,
1476                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1477                         SQL_IS_UINTEGER);
1478 
1479     QString tableType;
1480     if (type & QSql::Tables)
1481         tableType += QLatin1String("TABLE,");
1482     if (type & QSql::Views)
1483         tableType += QLatin1String("VIEW,");
1484     if (type & QSql::SystemTables)
1485         tableType += QLatin1String("SYSTEM TABLE,");
1486     if (tableType.isEmpty())
1487         return tl;
1488     tableType.chop(1);
1489 
1490     r = SQLTables(hStmt,
1491                    NULL,
1492                    0,
1493                    NULL,
1494                    0,
1495                    NULL,
1496                    0,
1497                    qToTChar(tableType),
1498                    tableType.length());
1499 
1500     if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1501         qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to execute table list"), d);
1502     r = SQLFetchScroll(hStmt,
1503                         SQL_FETCH_NEXT,
1504                         0);
1505     while (r == SQL_SUCCESS) {
1506         bool isNull;
1507         QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
1508         QString userVal = qGetStringData(hStmt, 1, -1, isNull);
1509         QString user = d->user;
1510         if ( isIdentifierEscaped(user, QSqlDriver::TableName))
1511             user = stripDelimiters(user, QSqlDriver::TableName);
1512         else
1513             user = user.toUpper();
1514 
1515         if (userVal != user)
1516             fieldVal = userVal + QLatin1Char('.') + fieldVal;
1517         tl.append(fieldVal);
1518         r = SQLFetchScroll(hStmt,
1519                             SQL_FETCH_NEXT,
1520                             0);
1521     }
1522 
1523     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1524     if (r != SQL_SUCCESS)
1525         qSqlWarning(QLatin1String("QDB2Driver::tables: Unable to free statement handle ")
1526                     + QString::number(r), d);
1527     return tl;
1528 }
1529 
primaryIndex(const QString & tablename) const1530 QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
1531 {
1532     Q_D(const QDB2Driver);
1533     QSqlIndex index(tablename);
1534     if (!isOpen())
1535         return index;
1536     QSqlRecord rec = record(tablename);
1537 
1538     SQLHANDLE hStmt;
1539     SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1540                                   d->hDbc,
1541                                   &hStmt);
1542     if (r != SQL_SUCCESS) {
1543         qSqlWarning(QLatin1String("QDB2Driver::primaryIndex: Unable to list primary key"), d);
1544         return index;
1545     }
1546     QString catalog, schema, table;
1547     qSplitTableQualifier(tablename, &catalog, &schema, &table);
1548 
1549     if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
1550         catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1551     else
1552         catalog = catalog.toUpper();
1553 
1554     if (isIdentifierEscaped(schema, QSqlDriver::TableName))
1555         schema = stripDelimiters(schema, QSqlDriver::TableName);
1556     else
1557         schema = schema.toUpper();
1558 
1559     if (isIdentifierEscaped(table, QSqlDriver::TableName))
1560         table = stripDelimiters(table, QSqlDriver::TableName);
1561     else
1562         table = table.toUpper();
1563 
1564     r = SQLSetStmtAttr(hStmt,
1565                         SQL_ATTR_CURSOR_TYPE,
1566                         (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1567                         SQL_IS_UINTEGER);
1568 
1569     r = SQLPrimaryKeys(hStmt,
1570                         NULL,
1571                         0,
1572                         qToTChar(schema),
1573                         schema.length(),
1574                         qToTChar(table),
1575                         table.length());
1576     r = SQLFetchScroll(hStmt,
1577                         SQL_FETCH_NEXT,
1578                         0);
1579 
1580     bool isNull;
1581     QString cName, idxName;
1582     // Store all fields in a StringList because the driver can't detail fields in this FETCH loop
1583     while (r == SQL_SUCCESS) {
1584         cName = qGetStringData(hStmt, 3, -1, isNull); // column name
1585         idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name
1586         index.append(rec.field(cName));
1587         index.setName(idxName);
1588         r = SQLFetchScroll(hStmt,
1589                             SQL_FETCH_NEXT,
1590                             0);
1591     }
1592     r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1593     if (r!= SQL_SUCCESS)
1594         qSqlWarning(QLatin1String("QDB2Driver: Unable to free statement handle ")
1595                     + QString::number(r), d);
1596     return index;
1597 }
1598 
hasFeature(DriverFeature f) const1599 bool QDB2Driver::hasFeature(DriverFeature f) const
1600 {
1601     switch (f) {
1602         case QuerySize:
1603         case NamedPlaceholders:
1604         case BatchOperations:
1605         case LastInsertId:
1606         case SimpleLocking:
1607         case EventNotifications:
1608         case CancelQuery:
1609             return false;
1610         case BLOB:
1611         case Transactions:
1612         case MultipleResultSets:
1613         case PreparedQueries:
1614         case PositionalPlaceholders:
1615         case LowPrecisionNumbers:
1616         case FinishQuery:
1617             return true;
1618         case Unicode:
1619             return true;
1620     }
1621     return false;
1622 }
1623 
beginTransaction()1624 bool QDB2Driver::beginTransaction()
1625 {
1626     if (!isOpen()) {
1627         qWarning("QDB2Driver::beginTransaction: Database not open");
1628         return false;
1629     }
1630     return setAutoCommit(false);
1631 }
1632 
commitTransaction()1633 bool QDB2Driver::commitTransaction()
1634 {
1635     Q_D(QDB2Driver);
1636     if (!isOpen()) {
1637         qWarning("QDB2Driver::commitTransaction: Database not open");
1638         return false;
1639     }
1640     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1641                               d->hDbc,
1642                               SQL_COMMIT);
1643     if (r != SQL_SUCCESS) {
1644         setLastError(qMakeError(tr("Unable to commit transaction"),
1645                      QSqlError::TransactionError, d));
1646         return false;
1647     }
1648     return setAutoCommit(true);
1649 }
1650 
rollbackTransaction()1651 bool QDB2Driver::rollbackTransaction()
1652 {
1653     Q_D(QDB2Driver);
1654     if (!isOpen()) {
1655         qWarning("QDB2Driver::rollbackTransaction: Database not open");
1656         return false;
1657     }
1658     SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1659                               d->hDbc,
1660                               SQL_ROLLBACK);
1661     if (r != SQL_SUCCESS) {
1662         setLastError(qMakeError(tr("Unable to rollback transaction"),
1663                                 QSqlError::TransactionError, d));
1664         return false;
1665     }
1666     return setAutoCommit(true);
1667 }
1668 
setAutoCommit(bool autoCommit)1669 bool QDB2Driver::setAutoCommit(bool autoCommit)
1670 {
1671     Q_D(QDB2Driver);
1672     SQLULEN ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
1673     SQLRETURN r  = SQLSetConnectAttr(d->hDbc,
1674                                       SQL_ATTR_AUTOCOMMIT,
1675                                       reinterpret_cast<SQLPOINTER>(ac),
1676                                       sizeof(ac));
1677     if (r != SQL_SUCCESS) {
1678         setLastError(qMakeError(tr("Unable to set autocommit"),
1679                                 QSqlError::TransactionError, d));
1680         return false;
1681     }
1682     return true;
1683 }
1684 
formatValue(const QSqlField & field,bool trimStrings) const1685 QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
1686 {
1687     if (field.isNull())
1688         return QLatin1String("NULL");
1689 
1690     switch (field.type()) {
1691         case QVariant::DateTime: {
1692             // Use an escape sequence for the datetime fields
1693             if (field.value().toDateTime().isValid()) {
1694                 QDate dt = field.value().toDateTime().date();
1695                 QTime tm = field.value().toDateTime().time();
1696                 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
1697                 return QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
1698                        + QString::number(dt.month()) + QLatin1Char('-')
1699                        + QString::number(dt.day()) + QLatin1Char('-')
1700                        + QString::number(tm.hour()) + QLatin1Char('.')
1701                        + QString::number(tm.minute()).rightJustified(2, QLatin1Char('0'), true)
1702                        + QLatin1Char('.')
1703                        + QString::number(tm.second()).rightJustified(2, QLatin1Char('0'), true)
1704                        + QLatin1Char('.')
1705                        + QString::number(tm.msec() * 1000).rightJustified(6, QLatin1Char('0'), true)
1706                        + QLatin1Char('\'');
1707                 } else {
1708                     return QLatin1String("NULL");
1709                 }
1710         }
1711         case QVariant::ByteArray: {
1712             QByteArray ba = field.value().toByteArray();
1713             QString res;
1714             res += QLatin1String("BLOB(X'");
1715             static const char hexchars[] = "0123456789abcdef";
1716             for (int i = 0; i < ba.size(); ++i) {
1717                 uchar s = (uchar) ba[i];
1718                 res += QLatin1Char(hexchars[s >> 4]);
1719                 res += QLatin1Char(hexchars[s & 0x0f]);
1720             }
1721             res += QLatin1String("')");
1722             return res;
1723         }
1724         default:
1725             return QSqlDriver::formatValue(field, trimStrings);
1726     }
1727 }
1728 
handle() const1729 QVariant QDB2Driver::handle() const
1730 {
1731     Q_D(const QDB2Driver);
1732     return QVariant(qRegisterMetaType<SQLHANDLE>("SQLHANDLE"), &d->hDbc);
1733 }
1734 
escapeIdentifier(const QString & identifier,IdentifierType) const1735 QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
1736 {
1737     QString res = identifier;
1738     if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
1739         res.replace(QLatin1Char('"'), QLatin1String("\"\""));
1740         res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
1741         res.replace(QLatin1Char('.'), QLatin1String("\".\""));
1742     }
1743     return res;
1744 }
1745 
1746 QT_END_NAMESPACE
1747