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