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