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