Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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:
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;
114 QList<QVariant*> valueCache;
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 SQLTCHAR tableName[TABLENAMESIZE];
309 SQLSMALLINT tableNameLen;
310 r = SQLColAttribute(d->hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName,
311 TABLENAMESIZE, &tableNameLen, 0);
312 if (r == SQL_SUCCESS)
313 f.setTableName(qFromTChar(tableName));
314 return f;
315}
316
317static int qGetIntData(SQLHANDLE hStmt, int column, bool& isNull)
318{
319 SQLINTEGER intbuf;
320 isNull = false;
321 SQLLEN lengthIndicator = 0;
322 SQLRETURN r = SQLGetData(hStmt,
323 column + 1,
324 SQL_C_SLONG,
325 (SQLPOINTER) &intbuf,
326 0,
327 &lengthIndicator);
328 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
329 isNull = true;
330 return 0;
331 }
332 return int(intbuf);
333}
334
335static double qGetDoubleData(SQLHANDLE hStmt, int column, bool& isNull)
336{
337 SQLDOUBLE dblbuf;
338 isNull = false;
339 SQLLEN lengthIndicator = 0;
340 SQLRETURN r = SQLGetData(hStmt,
341 column+1,
342 SQL_C_DOUBLE,
343 (SQLPOINTER) &dblbuf,
344 0,
345 &lengthIndicator);
346 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA) {
347 isNull = true;
348 return 0.0;
349 }
350
351 return (double) dblbuf;
352}
353
354static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool& isNull)
355{
356 SQLBIGINT lngbuf = Q_INT64_C(0);
357 isNull = false;
358 SQLLEN lengthIndicator = 0;
359 SQLRETURN r = SQLGetData(hStmt,
360 column+1,
361 SQL_C_SBIGINT,
362 (SQLPOINTER) &lngbuf,
363 0,
364 &lengthIndicator);
365 if ((r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) || lengthIndicator == SQL_NULL_DATA)
366 isNull = true;
367
368 return lngbuf;
369}
370
371static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& isNull)
372{
373 QString fieldVal;
374 SQLRETURN r = SQL_ERROR;
375 SQLLEN lengthIndicator = 0;
376
377 if (colSize <= 0)
378 colSize = 255;
379 else if (colSize > 65536) // limit buffer size to 64 KB
380 colSize = 65536;
381 else
382 colSize++; // make sure there is room for more than the 0 termination
383 SQLTCHAR* buf = new SQLTCHAR[colSize];
384
385 while (true) {
386 r = SQLGetData(hStmt,
387 column + 1,
388 SQL_C_WCHAR,
389 (SQLPOINTER)buf,
390 colSize * sizeof(SQLTCHAR),
391 &lengthIndicator);
392 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
393 if (lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL) {
394 fieldVal.clear();
395 isNull = true;
396 break;
397 }
398 fieldVal += qFromTChar(buf);
399 } else if (r == SQL_NO_DATA) {
400 break;
401 } else {
402 qWarning("qGetStringData: Error while fetching data (%d)", r);
403 fieldVal.clear();
404 break;
405 }
406 }
407 delete[] buf;
408 return fieldVal;
409}
410
411static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLLEN& lengthIndicator, bool& isNull)
412{
413 QByteArray fieldVal;
414 SQLSMALLINT colNameLen;
415 SQLSMALLINT colType;
416 SQLULEN colSize;
417 SQLSMALLINT colScale;
418 SQLSMALLINT nullable;
419 SQLRETURN r = SQL_ERROR;
420
421 SQLTCHAR colName[COLNAMESIZE];
422 r = SQLDescribeCol(hStmt,
423 column+1,
424 colName,
426 &colNameLen,
427 &colType,
428 &colSize,
429 &colScale,
430 &nullable);
431 if (r != SQL_SUCCESS)
432 qWarning("qGetBinaryData: Unable to describe column %d", column);
433 // SQLDescribeCol may return 0 if size cannot be determined
434 if (!colSize)
435 colSize = 255;
436 else if (colSize > 65536) // read the field in 64 KB chunks
437 colSize = 65536;
438 char * buf = new char[colSize];
439 while (true) {
440 r = SQLGetData(hStmt,
441 column+1,
442 colType == SQL_DBCLOB ? SQL_C_CHAR : SQL_C_BINARY,
443 (SQLPOINTER) buf,
444 colSize,
445 &lengthIndicator);
446 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
447 if (lengthIndicator == SQL_NULL_DATA) {
448 isNull = true;
449 break;
450 } else {
451 int rSize;
452 r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
453 if (lengthIndicator == SQL_NO_TOTAL) // size cannot be determined
454 rSize = colSize;
455 fieldVal.append(QByteArray(buf, rSize));
456 if (r == SQL_SUCCESS) // the whole field was read in one chunk
457 break;
458 }
459 } else {
460 break;
461 }
462 }
463 delete [] buf;
464 return fieldVal;
465}
466
468 QString &schema, QString &table) const
469{
470 const QList<QStringView> l = QStringView(qualifier).split(u'.');
471 switch (l.count()) {
472 case 1:
473 table = qualifier;
474 break;
475 case 2:
476 schema = l.at(0).toString();
477 table = l.at(1).toString();
478 break;
479 case 3:
480 catalog = l.at(0).toString();
481 schema = l.at(1).toString();
482 table = l.at(2).toString();
483 break;
484 default:
485 qSqlWarning(QString::fromLatin1("QODBCDriver::splitTableQualifier: Unable to split table qualifier '%1'")
486 .arg(qualifier), this);
487 break;
488 }
489}
490
491// creates a QSqlField from a valid hStmt generated
492// by SQLColumns. The hStmt has to point to a valid position.
493static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt)
494{
495 bool isNull;
496 int type = qGetIntData(hStmt, 4, isNull);
497 QSqlField f(qGetStringData(hStmt, 3, -1, isNull), qDecodeDB2Type(type));
498 int required = qGetIntData(hStmt, 10, isNull); // nullable-flag
499 // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
500 if (required == SQL_NO_NULLS)
501 f.setRequired(true);
502 else if (required == SQL_NULLABLE)
503 f.setRequired(false);
504 // else we don't know.
505 f.setLength(qGetIntData(hStmt, 6, isNull)); // column size
506 f.setPrecision(qGetIntData(hStmt, 8, isNull)); // precision
507 return f;
508}
509
510static bool qMakeStatement(QDB2ResultPrivate* d, bool forwardOnly, bool setForwardOnly = true)
511{
512 SQLRETURN r;
513 if (!d->hStmt) {
514 r = SQLAllocHandle(SQL_HANDLE_STMT,
515 d->drv_d_func()->hDbc,
516 &d->hStmt);
517 if (r != SQL_SUCCESS) {
518 qSqlWarning("QDB2Result::reset: Unable to allocate statement handle"_L1, d);
519 return false;
520 }
521 } else {
522 r = SQLFreeStmt(d->hStmt, SQL_CLOSE);
523 if (r != SQL_SUCCESS) {
524 qSqlWarning("QDB2Result::reset: Unable to close statement handle"_L1, d);
525 return false;
526 }
527 }
528
529 if (!setForwardOnly)
530 return true;
531
532 if (forwardOnly) {
533 r = SQLSetStmtAttr(d->hStmt,
534 SQL_ATTR_CURSOR_TYPE,
535 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
536 SQL_IS_UINTEGER);
537 } else {
538 r = SQLSetStmtAttr(d->hStmt,
539 SQL_ATTR_CURSOR_TYPE,
540 (SQLPOINTER) SQL_CURSOR_STATIC,
541 SQL_IS_UINTEGER);
542 }
543 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
544 qSqlWarning(QString::fromLatin1("QDB2Result::reset: Unable to set %1 attribute.").arg(
545 forwardOnly ? "SQL_CURSOR_FORWARD_ONLY"_L1
546 : "SQL_CURSOR_STATIC"_L1), d);
547 return false;
548 }
549 return true;
550}
551
553{
554 Q_D(const QDB2Result);
555 return QVariant(QMetaType::fromType<SQLHANDLE>(), &d->hStmt);
556}
557
558/************************************/
559
564
566{
567 Q_D(const QDB2Result);
568 if (d->hStmt) {
569 SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
570 if (r != SQL_SUCCESS)
571 qSqlWarning("QDB2Driver: Unable to free statement handle "_L1 + QString::number(r), d);
572 }
573}
574
576{
577 Q_D(QDB2Result);
578 setActive(false);
580 SQLRETURN r;
581
582 d->recInf.clear();
583 d->emptyValueCache();
584
586 return false;
587
588 r = SQLExecDirect(d->hStmt,
590 (SQLINTEGER) query.length());
591 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
593 "Unable to execute statement"), QSqlError::StatementError, d));
594 return false;
595 }
596 SQLSMALLINT count = 0;
597 r = SQLNumResultCols(d->hStmt, &count);
598 if (count) {
599 setSelect(true);
600 for (int i = 0; i < count; ++i) {
601 d->recInf.append(qMakeFieldInfo(d, i));
602 }
603 } else {
604 setSelect(false);
605 }
606 d->valueCache.resize(count);
607 d->valueCache.fill(NULL);
608 setActive(true);
609 return true;
610}
611
613{
614 Q_D(QDB2Result);
615 setActive(false);
617 SQLRETURN r;
618
619 d->recInf.clear();
620 d->emptyValueCache();
621
623 return false;
624
625 r = SQLPrepare(d->hStmt,
627 (SQLINTEGER) query.length());
628
629 if (r != SQL_SUCCESS) {
631 "Unable to prepare statement"), QSqlError::StatementError, d));
632 return false;
633 }
634 return true;
635}
636
638{
639 Q_D(QDB2Result);
640 QList<QByteArray> tmpStorage; // holds temporary ptrs
641 QVarLengthArray<SQLLEN, 32> indicators(boundValues().count());
642
643 memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
644 setActive(false);
646 SQLRETURN r;
647
648 d->recInf.clear();
649 d->emptyValueCache();
650
651 if (!qMakeStatement(d, isForwardOnly(), false))
652 return false;
653
654
655 QList<QVariant> &values = boundValues();
656 int i;
657 for (i = 0; i < values.count(); ++i) {
658 // bind parameters - only positional binding allowed
659 SQLLEN *ind = &indicators[i];
661 *ind = SQL_NULL_DATA;
663 values[i].detach();
664
665 switch (values.at(i).metaType().id()) {
666 case QMetaType::QDate: {
668 ba.resize(sizeof(DATE_STRUCT));
669 DATE_STRUCT *dt = (DATE_STRUCT *)ba.constData();
670 QDate qdt = values.at(i).toDate();
671 dt->year = qdt.year();
672 dt->month = qdt.month();
673 dt->day = qdt.day();
674 r = SQLBindParameter(d->hStmt,
675 i + 1,
677 SQL_C_DATE,
678 SQL_DATE,
679 0,
680 0,
681 (void *) dt,
682 0,
683 *ind == SQL_NULL_DATA ? ind : NULL);
684 tmpStorage.append(ba);
685 break; }
686 case QMetaType::QTime: {
688 ba.resize(sizeof(TIME_STRUCT));
689 TIME_STRUCT *dt = (TIME_STRUCT *)ba.constData();
690 QTime qdt = values.at(i).toTime();
691 dt->hour = qdt.hour();
692 dt->minute = qdt.minute();
693 dt->second = qdt.second();
694 r = SQLBindParameter(d->hStmt,
695 i + 1,
697 SQL_C_TIME,
698 SQL_TIME,
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::QDateTime: {
708 ba.resize(sizeof(TIMESTAMP_STRUCT));
709 TIMESTAMP_STRUCT * dt = (TIMESTAMP_STRUCT *)ba.constData();
710 QDateTime qdt = values.at(i).toDateTime();
711 dt->year = qdt.date().year();
712 dt->month = qdt.date().month();
713 dt->day = qdt.date().day();
714 dt->hour = qdt.time().hour();
715 dt->minute = qdt.time().minute();
716 dt->second = qdt.time().second();
717 dt->fraction = qdt.time().msec() * 1000000;
718 r = SQLBindParameter(d->hStmt,
719 i + 1,
721 SQL_C_TIMESTAMP,
722 SQL_TIMESTAMP,
723 0,
724 0,
725 (void *) dt,
726 0,
727 *ind == SQL_NULL_DATA ? ind : NULL);
728 tmpStorage.append(ba);
729 break; }
730 case QMetaType::Int:
731 r = SQLBindParameter(d->hStmt,
732 i + 1,
734 SQL_C_SLONG,
735 SQL_INTEGER,
736 0,
737 0,
738 (void *)values.at(i).constData(),
739 0,
740 *ind == SQL_NULL_DATA ? ind : NULL);
741 break;
742 case QMetaType::Double:
743 r = SQLBindParameter(d->hStmt,
744 i + 1,
746 SQL_C_DOUBLE,
747 SQL_DOUBLE,
748 0,
749 0,
750 (void *)values.at(i).constData(),
751 0,
752 *ind == SQL_NULL_DATA ? ind : NULL);
753 break;
754 case QMetaType::QByteArray: {
755 int len = values.at(i).toByteArray().size();
756 if (*ind != SQL_NULL_DATA)
757 *ind = len;
758 r = SQLBindParameter(d->hStmt,
759 i + 1,
761 SQL_C_BINARY,
762 SQL_LONGVARBINARY,
763 len,
764 0,
765 (void *)values.at(i).toByteArray().constData(),
766 len,
767 ind);
768 break; }
769 case QMetaType::QString:
770 {
771 const QString str(values.at(i).toString());
772 if (*ind != SQL_NULL_DATA)
773 *ind = str.length() * sizeof(QChar);
774 if (bindValueType(i) & QSql::Out) {
775 QByteArray ba((char *)str.data(), str.capacity() * sizeof(QChar));
776 r = SQLBindParameter(d->hStmt,
777 i + 1,
779 SQL_C_WCHAR,
780 SQL_WVARCHAR,
781 str.length(),
782 0,
783 (void *)ba.constData(),
784 ba.size(),
785 ind);
786 tmpStorage.append(ba);
787 } else {
788 void *data = (void*)str.utf16();
789 int len = str.length();
790 r = SQLBindParameter(d->hStmt,
791 i + 1,
793 SQL_C_WCHAR,
794 SQL_WVARCHAR,
795 len,
796 0,
797 data,
798 len * sizeof(QChar),
799 ind);
800 }
801 break;
802 }
803 default: {
804 QByteArray ba = values.at(i).toString().toLatin1();
805 int len = ba.length() + 1;
806 if (*ind != SQL_NULL_DATA)
807 *ind = ba.length();
808 r = SQLBindParameter(d->hStmt,
809 i + 1,
811 SQL_C_CHAR,
812 SQL_VARCHAR,
813 len,
814 0,
815 (void *) ba.constData(),
816 len,
817 ind);
818 tmpStorage.append(ba);
819 break; }
820 }
821 if (r != SQL_SUCCESS) {
822 qWarning("QDB2Result::exec: unable to bind variable: %s",
823 qDB2Warn(d).toLocal8Bit().constData());
825 "Unable to bind variable"), QSqlError::StatementError, d));
826 return false;
827 }
828 }
829
830 r = SQLExecute(d->hStmt);
831 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
832 qWarning("QDB2Result::exec: Unable to execute statement: %s",
833 qDB2Warn(d).toLocal8Bit().constData());
835 "Unable to execute statement"), QSqlError::StatementError, d));
836 return false;
837 }
838 SQLSMALLINT count = 0;
839 r = SQLNumResultCols(d->hStmt, &count);
840 if (count) {
841 setSelect(true);
842 for (int i = 0; i < count; ++i) {
843 d->recInf.append(qMakeFieldInfo(d, i));
844 }
845 } else {
846 setSelect(false);
847 }
848 setActive(true);
849 d->valueCache.resize(count);
850 d->valueCache.fill(NULL);
851
852 //get out parameters
853 if (!hasOutValues())
854 return true;
855
856 for (i = 0; i < values.count(); ++i) {
857 switch (values[i].metaType().id()) {
858 case QMetaType::QDate: {
859 DATE_STRUCT ds = *((DATE_STRUCT *)tmpStorage.takeFirst().constData());
860 values[i] = QVariant(QDate(ds.year, ds.month, ds.day));
861 break; }
862 case QMetaType::QTime: {
863 TIME_STRUCT dt = *((TIME_STRUCT *)tmpStorage.takeFirst().constData());
864 values[i] = QVariant(QTime(dt.hour, dt.minute, dt.second));
865 break; }
866 case QMetaType::QDateTime: {
867 TIMESTAMP_STRUCT dt = *((TIMESTAMP_STRUCT *)tmpStorage.takeFirst().constData());
868 values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
869 QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
870 break; }
871 case QMetaType::Int:
872 case QMetaType::Double:
873 case QMetaType::QByteArray:
874 break;
875 case QMetaType::QString:
877 values[i] = QString((const QChar *)tmpStorage.takeFirst().constData());
878 break;
879 default: {
880 values[i] = QString::fromLatin1(tmpStorage.takeFirst().constData());
881 break; }
882 }
883 if (indicators[i] == SQL_NULL_DATA)
884 values[i] = QVariant(values[i].metaType());
885 }
886 return true;
887}
888
890{
891 Q_D(QDB2Result);
892 if (isForwardOnly() && i < at())
893 return false;
894 if (i == at())
895 return true;
896 d->clearValueCache();
897 int actualIdx = i + 1;
898 if (actualIdx <= 0) {
900 return false;
901 }
902 SQLRETURN r;
903 if (isForwardOnly()) {
904 bool ok = true;
905 while (ok && i > at())
906 ok = fetchNext();
907 return ok;
908 } else {
909 r = SQLFetchScroll(d->hStmt,
910 SQL_FETCH_ABSOLUTE,
911 actualIdx);
912 }
913 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r != SQL_NO_DATA) {
915 "Unable to fetch record %1").arg(i), QSqlError::StatementError, d));
916 return false;
917 }
918 else if (r == SQL_NO_DATA)
919 return false;
920 setAt(i);
921 return true;
922}
923
925{
926 Q_D(QDB2Result);
927 SQLRETURN r;
928 d->clearValueCache();
929 r = SQLFetchScroll(d->hStmt,
930 SQL_FETCH_NEXT,
931 0);
932 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
933 if (r != SQL_NO_DATA)
935 "Unable to fetch next"), QSqlError::StatementError, d));
936 return false;
937 }
938 setAt(at() + 1);
939 return true;
940}
941
943{
944 Q_D(QDB2Result);
946 return false;
947 if (isForwardOnly())
948 return fetchNext();
949 d->clearValueCache();
950 SQLRETURN r;
951 r = SQLFetchScroll(d->hStmt,
952 SQL_FETCH_FIRST,
953 0);
954 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
955 if (r!= SQL_NO_DATA)
956 setLastError(qMakeError(QCoreApplication::translate("QDB2Result", "Unable to fetch first"),
958 return false;
959 }
960 setAt(0);
961 return true;
962}
963
965{
966 Q_D(QDB2Result);
967 d->clearValueCache();
968
969 int i = at();
970 if (i == QSql::AfterLastRow) {
971 if (isForwardOnly()) {
972 return false;
973 } else {
974 if (!fetch(0))
975 return false;
976 i = at();
977 }
978 }
979
980 while (fetchNext())
981 ++i;
982
983 if (i == QSql::BeforeFirstRow) {
985 return false;
986 }
987
988 if (!isForwardOnly())
989 return fetch(i);
990
991 setAt(i);
992 return true;
993}
994
995
997{
998 Q_D(QDB2Result);
999 if (field >= d->recInf.count()) {
1000 qWarning("QDB2Result::data: column %d out of range", field);
1001 return QVariant();
1002 }
1003 SQLRETURN r = 0;
1004 SQLLEN lengthIndicator = 0;
1005 bool isNull = false;
1006 const QSqlField info = d->recInf.field(field);
1007
1008 if (!info.isValid() || field >= d->valueCache.size())
1009 return QVariant();
1010
1011 if (d->valueCache[field])
1012 return *d->valueCache[field];
1013
1014
1015 QVariant *v = nullptr;
1016 switch (info.metaType().id()) {
1017 case QMetaType::LongLong:
1018 v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
1019 break;
1020 case QMetaType::Int:
1021 v = new QVariant(qGetIntData(d->hStmt, field, isNull));
1022 break;
1023 case QMetaType::QDate: {
1024 DATE_STRUCT dbuf;
1025 r = SQLGetData(d->hStmt,
1026 field + 1,
1027 SQL_C_DATE,
1028 (SQLPOINTER) &dbuf,
1029 0,
1030 &lengthIndicator);
1031 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1032 v = new QVariant(QDate(dbuf.year, dbuf.month, dbuf.day));
1033 } else {
1034 v = new QVariant(QDate());
1035 isNull = true;
1036 }
1037 break; }
1038 case QMetaType::QTime: {
1039 TIME_STRUCT tbuf;
1040 r = SQLGetData(d->hStmt,
1041 field + 1,
1042 SQL_C_TIME,
1043 (SQLPOINTER) &tbuf,
1044 0,
1045 &lengthIndicator);
1046 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1047 v = new QVariant(QTime(tbuf.hour, tbuf.minute, tbuf.second));
1048 } else {
1049 v = new QVariant(QTime());
1050 isNull = true;
1051 }
1052 break; }
1053 case QMetaType::QDateTime: {
1054 TIMESTAMP_STRUCT dtbuf;
1055 r = SQLGetData(d->hStmt,
1056 field + 1,
1057 SQL_C_TIMESTAMP,
1058 (SQLPOINTER) &dtbuf,
1059 0,
1060 &lengthIndicator);
1061 if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && (lengthIndicator != SQL_NULL_DATA)) {
1062 v = new QVariant(QDateTime(QDate(dtbuf.year, dtbuf.month, dtbuf.day),
1063 QTime(dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000)));
1064 } else {
1065 v = new QVariant(QDateTime());
1066 isNull = true;
1067 }
1068 break; }
1069 case QMetaType::QByteArray:
1070 v = new QVariant(qGetBinaryData(d->hStmt, field, lengthIndicator, isNull));
1071 break;
1072 case QMetaType::Double:
1073 {
1074 switch(numericalPrecisionPolicy()) {
1076 v = new QVariant(qGetIntData(d->hStmt, field, isNull));
1077 break;
1079 v = new QVariant((qint64) qGetBigIntData(d->hStmt, field, isNull));
1080 break;
1082 v = new QVariant(qGetDoubleData(d->hStmt, field, isNull));
1083 break;
1085 default:
1086 // length + 1 for the comma
1087 v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
1088 break;
1089 }
1090 break;
1091 }
1092 case QMetaType::QString:
1093 default:
1094 v = new QVariant(qGetStringData(d->hStmt, field, info.length(), isNull));
1095 break;
1096 }
1097 if (isNull)
1098 *v = QVariant(info.metaType());
1099 d->valueCache[field] = v;
1100 return *v;
1101}
1102
1104{
1105 Q_D(const QDB2Result);
1106 if (i >= d->valueCache.size())
1107 return true;
1108
1109 if (d->valueCache[i])
1110 return d->valueCache[i]->isNull();
1111 return data(i).isNull();
1112}
1113
1115{
1116 Q_D(const QDB2Result);
1117 SQLLEN affectedRowCount = 0;
1118 SQLRETURN r = SQLRowCount(d->hStmt, &affectedRowCount);
1119 if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
1120 return affectedRowCount;
1121 else
1122 qSqlWarning("QDB2Result::numRowsAffected: Unable to count affected rows"_L1, d);
1123 return -1;
1124}
1125
1127{
1128 return -1;
1129}
1130
1132{
1133 Q_D(const QDB2Result);
1134 if (isActive())
1135 return d->recInf;
1136 return QSqlRecord();
1137}
1138
1140{
1141 Q_D(QDB2Result);
1142 setActive(false);
1144 d->recInf.clear();
1145 d->emptyValueCache();
1146 setSelect(false);
1147
1148 SQLRETURN r = SQLMoreResults(d->hStmt);
1149 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1150 if (r != SQL_NO_DATA) {
1152 "Unable to fetch last"), QSqlError::ConnectionError, d));
1153 }
1154 return false;
1155 }
1156
1157 SQLSMALLINT fieldCount = 0;
1158 r = SQLNumResultCols(d->hStmt, &fieldCount);
1159 setSelect(fieldCount > 0);
1160 for (int i = 0; i < fieldCount; ++i)
1161 d->recInf.append(qMakeFieldInfo(d, i));
1162
1163 d->valueCache.resize(fieldCount);
1164 d->valueCache.fill(NULL);
1165 setActive(true);
1166
1167 return true;
1168}
1169
1171{
1173}
1174
1176{
1177 Q_D(QDB2Result);
1178 if (d->hStmt)
1179 SQLCloseCursor(d->hStmt);
1180}
1181
1182/************************************/
1183
1185 : QSqlDriver(*new QDB2DriverPrivate, parent)
1186{
1187}
1188
1190 : QSqlDriver(*new QDB2DriverPrivate, parent)
1191{
1192 Q_D(QDB2Driver);
1193 d->hEnv = reinterpret_cast<SQLHANDLE>(env);
1194 d->hDbc = reinterpret_cast<SQLHANDLE>(con);
1195 if (env && con) {
1196 setOpen(true);
1197 setOpenError(false);
1198 }
1199}
1200
1202{
1203 close();
1204}
1205
1206bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
1207 const QString& connOpts)
1208{
1209 Q_D(QDB2Driver);
1210 if (isOpen())
1211 close();
1212 SQLRETURN r;
1213 r = SQLAllocHandle(SQL_HANDLE_ENV,
1214 SQL_NULL_HANDLE,
1215 &d->hEnv);
1216 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1217 qSqlWarning("QDB2Driver::open: Unable to allocate environment"_L1, d);
1218 setOpenError(true);
1219 return false;
1220 }
1221
1222 r = SQLAllocHandle(SQL_HANDLE_DBC,
1223 d->hEnv,
1224 &d->hDbc);
1225 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1226 qSqlWarning("QDB2Driver::open: Unable to allocate connection"_L1, d);
1227 setOpenError(true);
1228 return false;
1229 }
1230
1231 QString protocol;
1232 // Set connection attributes
1233 const QStringList opts(connOpts.split(u';', Qt::SkipEmptyParts));
1234 for (int i = 0; i < opts.count(); ++i) {
1235 const QString tmp(opts.at(i));
1236 int idx;
1237 if ((idx = tmp.indexOf(u'=')) == -1) {
1238 qWarning("QDB2Driver::open: Illegal connect option value '%s'",
1239 tmp.toLocal8Bit().constData());
1240 continue;
1241 }
1242
1243 const QString opt(tmp.left(idx));
1244 const QString val(tmp.mid(idx + 1).simplified());
1245
1246 SQLULEN v = 0;
1247 r = SQL_SUCCESS;
1248 if (opt == "SQL_ATTR_ACCESS_MODE"_L1) {
1249 if (val == "SQL_MODE_READ_ONLY"_L1) {
1250 v = SQL_MODE_READ_ONLY;
1251 } else if (val == "SQL_MODE_READ_WRITE"_L1) {
1252 v = SQL_MODE_READ_WRITE;
1253 } else {
1254 qWarning("QDB2Driver::open: Unknown option value '%s'",
1255 tmp.toLocal8Bit().constData());
1256 continue;
1257 }
1258 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_ACCESS_MODE, reinterpret_cast<SQLPOINTER>(v), 0);
1259 } else if (opt == "SQL_ATTR_LOGIN_TIMEOUT"_L1) {
1260 v = val.toUInt();
1261 r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(v), 0);
1262 } else if (opt.compare("PROTOCOL"_L1, Qt::CaseInsensitive) == 0) {
1263 protocol = tmp;
1264 }
1265 else {
1266 qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
1267 tmp.toLocal8Bit().constData());
1268 }
1269 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1270 qSqlWarning(QString::fromLatin1("QDB2Driver::open: "
1271 "Unable to set connection attribute '%1'").arg(opt), d);
1272 }
1273
1274 if (protocol.isEmpty())
1275 protocol = "PROTOCOL=TCPIP"_L1;
1276
1277 if (port < 0 )
1278 port = 50000;
1279
1280 QString connQStr;
1281 connQStr = protocol + ";DATABASE="_L1 + db + ";HOSTNAME="_L1 + host
1282 + ";PORT="_L1 + QString::number(port) + ";UID="_L1 + user
1283 + ";PWD="_L1 + password;
1284
1285
1286 SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
1287 SQLSMALLINT cb;
1288
1289 r = SQLDriverConnect(d->hDbc,
1290 NULL,
1291 qToTChar(connQStr),
1292 (SQLSMALLINT) connQStr.length(),
1293 connOut,
1294 SQL_MAX_OPTION_STRING_LENGTH,
1295 &cb,
1296 SQL_DRIVER_NOPROMPT);
1297 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1298 setLastError(qMakeError(tr("Unable to connect"),
1300 setOpenError(true);
1301 return false;
1302 }
1303
1304 d->user = user;
1305 setOpen(true);
1306 setOpenError(false);
1307 return true;
1308}
1309
1311{
1312 Q_D(QDB2Driver);
1313 SQLRETURN r;
1314 if (d->hDbc) {
1315 // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
1316 if (isOpen()) {
1317 r = SQLDisconnect(d->hDbc);
1318 if (r != SQL_SUCCESS)
1319 qSqlWarning("QDB2Driver::close: Unable to disconnect datasource"_L1, d);
1320 }
1321 r = SQLFreeHandle(SQL_HANDLE_DBC, d->hDbc);
1322 if (r != SQL_SUCCESS)
1323 qSqlWarning("QDB2Driver::close: Unable to free connection handle"_L1, d);
1324 d->hDbc = 0;
1325 }
1326
1327 if (d->hEnv) {
1328 r = SQLFreeHandle(SQL_HANDLE_ENV, d->hEnv);
1329 if (r != SQL_SUCCESS)
1330 qSqlWarning("QDB2Driver::close: Unable to free environment handle"_L1, d);
1331 d->hEnv = 0;
1332 }
1333 setOpen(false);
1334 setOpenError(false);
1335}
1336
1338{
1339 return new QDB2Result(this);
1340}
1341
1343{
1344 Q_D(const QDB2Driver);
1345 QSqlRecord fil;
1346 if (!isOpen())
1347 return fil;
1348
1349 SQLHANDLE hStmt;
1350 QString catalog, schema, table;
1351 d->qSplitTableQualifier(tableName, catalog, schema, table);
1352 if (schema.isEmpty())
1353 schema = d->user;
1354
1356 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1357 else
1358 catalog = catalog.toUpper();
1359
1361 schema = stripDelimiters(schema, QSqlDriver::TableName);
1362 else
1363 schema = schema.toUpper();
1364
1367 else
1368 table = table.toUpper();
1369
1370 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1371 d->hDbc,
1372 &hStmt);
1373 if (r != SQL_SUCCESS) {
1374 qSqlWarning("QDB2Driver::record: Unable to allocate handle"_L1, d);
1375 return fil;
1376 }
1377
1378 r = SQLSetStmtAttr(hStmt,
1379 SQL_ATTR_CURSOR_TYPE,
1380 (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
1381 SQL_IS_UINTEGER);
1382
1383
1384 //Aside: szSchemaName and szTableName parameters of SQLColumns
1385 //are case sensitive search patterns, so no escaping is used.
1386 r = SQLColumns(hStmt,
1387 NULL,
1388 0,
1389 qToTChar(schema),
1390 schema.length(),
1391 qToTChar(table),
1392 table.length(),
1393 NULL,
1394 0);
1395
1396 if (r != SQL_SUCCESS)
1397 qSqlWarning("QDB2Driver::record: Unable to execute column list"_L1, d);
1398 r = SQLFetchScroll(hStmt,
1399 SQL_FETCH_NEXT,
1400 0);
1401 while (r == SQL_SUCCESS) {
1402 QSqlField fld = qMakeFieldInfo(hStmt);
1403 fld.setTableName(tableName);
1404 fil.append(fld);
1405 r = SQLFetchScroll(hStmt,
1406 SQL_FETCH_NEXT,
1407 0);
1408 }
1409
1410 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1411 if (r != SQL_SUCCESS)
1412 qSqlWarning("QDB2Driver: Unable to free statement handle "_L1
1413 + QString::number(r), d);
1414
1415 return fil;
1416}
1417
1419{
1420 Q_D(const QDB2Driver);
1421 QStringList tl;
1422 if (!isOpen())
1423 return tl;
1424
1425 SQLHANDLE hStmt;
1426
1427 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1428 d->hDbc,
1429 &hStmt);
1430 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
1431 qSqlWarning("QDB2Driver::tables: Unable to allocate handle"_L1, d);
1432 return tl;
1433 }
1434 r = SQLSetStmtAttr(hStmt,
1435 SQL_ATTR_CURSOR_TYPE,
1436 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1437 SQL_IS_UINTEGER);
1438
1439 QString tableType;
1440 if (type & QSql::Tables)
1441 tableType += "TABLE,"_L1;
1442 if (type & QSql::Views)
1443 tableType += "VIEW,"_L1;
1445 tableType += "SYSTEM TABLE,"_L1;
1446 if (tableType.isEmpty())
1447 return tl;
1448 tableType.chop(1);
1449
1450 r = SQLTables(hStmt,
1451 NULL,
1452 0,
1453 NULL,
1454 0,
1455 NULL,
1456 0,
1457 qToTChar(tableType),
1458 tableType.length());
1459
1460 if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
1461 qSqlWarning("QDB2Driver::tables: Unable to execute table list"_L1, d);
1462 r = SQLFetchScroll(hStmt,
1463 SQL_FETCH_NEXT,
1464 0);
1465 while (r == SQL_SUCCESS) {
1466 bool isNull;
1467 QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
1468 QString userVal = qGetStringData(hStmt, 1, -1, isNull);
1469 QString user = d->user;
1472 else
1473 user = user.toUpper();
1474
1475 if (userVal != user)
1476 fieldVal = userVal + u'.' + fieldVal;
1477 tl.append(fieldVal);
1478 r = SQLFetchScroll(hStmt,
1479 SQL_FETCH_NEXT,
1480 0);
1481 }
1482
1483 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1484 if (r != SQL_SUCCESS)
1485 qSqlWarning("QDB2Driver::tables: Unable to free statement handle "_L1
1486 + QString::number(r), d);
1487 return tl;
1488}
1489
1491{
1492 Q_D(const QDB2Driver);
1493 QSqlIndex index(tablename);
1494 if (!isOpen())
1495 return index;
1496 QSqlRecord rec = record(tablename);
1497
1498 SQLHANDLE hStmt;
1499 SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
1500 d->hDbc,
1501 &hStmt);
1502 if (r != SQL_SUCCESS) {
1503 qSqlWarning("QDB2Driver::primaryIndex: Unable to list primary key"_L1, d);
1504 return index;
1505 }
1506 QString catalog, schema, table;
1507 d->qSplitTableQualifier(tablename, catalog, schema, table);
1508
1510 catalog = stripDelimiters(catalog, QSqlDriver::TableName);
1511 else
1512 catalog = catalog.toUpper();
1513
1515 schema = stripDelimiters(schema, QSqlDriver::TableName);
1516 else
1517 schema = schema.toUpper();
1518
1521 else
1522 table = table.toUpper();
1523
1524 r = SQLSetStmtAttr(hStmt,
1525 SQL_ATTR_CURSOR_TYPE,
1526 (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
1527 SQL_IS_UINTEGER);
1528
1529 r = SQLPrimaryKeys(hStmt,
1530 NULL,
1531 0,
1532 qToTChar(schema),
1533 schema.length(),
1534 qToTChar(table),
1535 table.length());
1536 r = SQLFetchScroll(hStmt,
1537 SQL_FETCH_NEXT,
1538 0);
1539
1540 bool isNull;
1541 QString cName, idxName;
1542 // Store all fields in a StringList because the driver can't detail fields in this FETCH loop
1543 while (r == SQL_SUCCESS) {
1544 cName = qGetStringData(hStmt, 3, -1, isNull); // column name
1545 idxName = qGetStringData(hStmt, 5, -1, isNull); // pk index name
1546 index.append(rec.field(cName));
1547 index.setName(idxName);
1548 r = SQLFetchScroll(hStmt,
1549 SQL_FETCH_NEXT,
1550 0);
1551 }
1552 r = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
1553 if (r!= SQL_SUCCESS)
1554 qSqlWarning("QDB2Driver: Unable to free statement handle "_L1
1555 + QString::number(r), d);
1556 return index;
1557}
1558
1560{
1561 switch (f) {
1562 case QuerySize:
1563 case NamedPlaceholders:
1564 case BatchOperations:
1565 case LastInsertId:
1566 case SimpleLocking:
1567 case EventNotifications:
1568 case CancelQuery:
1569 return false;
1570 case BLOB:
1571 case Transactions:
1572 case MultipleResultSets:
1573 case PreparedQueries:
1576 case FinishQuery:
1577 return true;
1578 case Unicode:
1579 return true;
1580 }
1581 return false;
1582}
1583
1585{
1586 if (!isOpen()) {
1587 qWarning("QDB2Driver::beginTransaction: Database not open");
1588 return false;
1589 }
1590 return setAutoCommit(false);
1591}
1592
1594{
1595 Q_D(QDB2Driver);
1596 if (!isOpen()) {
1597 qWarning("QDB2Driver::commitTransaction: Database not open");
1598 return false;
1599 }
1600 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1601 d->hDbc,
1602 SQL_COMMIT);
1603 if (r != SQL_SUCCESS) {
1604 setLastError(qMakeError(tr("Unable to commit transaction"),
1606 return false;
1607 }
1608 return setAutoCommit(true);
1609}
1610
1612{
1613 Q_D(QDB2Driver);
1614 if (!isOpen()) {
1615 qWarning("QDB2Driver::rollbackTransaction: Database not open");
1616 return false;
1617 }
1618 SQLRETURN r = SQLEndTran(SQL_HANDLE_DBC,
1619 d->hDbc,
1620 SQL_ROLLBACK);
1621 if (r != SQL_SUCCESS) {
1622 setLastError(qMakeError(tr("Unable to rollback transaction"),
1624 return false;
1625 }
1626 return setAutoCommit(true);
1627}
1628
1629bool QDB2Driver::setAutoCommit(bool autoCommit)
1630{
1631 Q_D(QDB2Driver);
1632 SQLULEN ac = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
1633 SQLRETURN r = SQLSetConnectAttr(d->hDbc,
1634 SQL_ATTR_AUTOCOMMIT,
1635 reinterpret_cast<SQLPOINTER>(ac),
1636 sizeof(ac));
1637 if (r != SQL_SUCCESS) {
1638 setLastError(qMakeError(tr("Unable to set autocommit"),
1640 return false;
1641 }
1642 return true;
1643}
1644
1645QString QDB2Driver::formatValue(const QSqlField &field, bool trimStrings) const
1646{
1647 if (field.isNull())
1648 return "NULL"_L1;
1649
1650 switch (field.metaType().id()) {
1651 case QMetaType::QDateTime: {
1652 // Use an escape sequence for the datetime fields
1653 if (field.value().toDateTime().isValid()) {
1654 QDate dt = field.value().toDateTime().date();
1655 QTime tm = field.value().toDateTime().time();
1656 // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
1657 return u'\'' + QString::number(dt.year()) + u'-'
1658 + QString::number(dt.month()) + u'-'
1659 + QString::number(dt.day()) + u'-'
1660 + QString::number(tm.hour()) + u'.'
1661 + QString::number(tm.minute()).rightJustified(2, u'0', true)
1662 + u'.'
1663 + QString::number(tm.second()).rightJustified(2, u'0', true)
1664 + u'.'
1665 + QString::number(tm.msec() * 1000).rightJustified(6, u'0', true)
1666 + u'\'';
1667 } else {
1668 return "NULL"_L1;
1669 }
1670 }
1671 case QMetaType::QByteArray: {
1672 const QByteArray ba = field.value().toByteArray();
1673 QString r;
1674 r.reserve(ba.size() * 2 + 9);
1675 r += "BLOB(X'"_L1;
1676 for (const char c : ba) {
1677 const uchar s = uchar(c);
1680 }
1681 r += "')"_L1;
1682 return r;
1683 }
1684 default:
1685 return QSqlDriver::formatValue(field, trimStrings);
1686 }
1687}
1688
1690{
1691 Q_D(const QDB2Driver);
1692 return QVariant(QMetaType::fromType<SQLHANDLE>(), &d->hDbc);
1693}
1694
1696{
1697 QString res = identifier;
1698 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
1699 res.replace(u'"', "\"\""_L1);
1700 res.replace(u'.', "\".\""_L1);
1701 res = u'"' + res + u'"';
1702 }
1703 return res;
1704}
1705
1707
1708#include "moc_qsql_db2_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:600
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
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:467
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:964
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:552
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:560
bool fetch(int i) override
Positions the result to an arbitrary (zero-based) row index.
Definition qsql_db2.cpp:889
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:637
bool fetchFirst() override
Positions the result to the first record (row 0) in the result.
Definition qsql_db2.cpp:942
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:612
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:924
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
Definition qsql_db2.cpp:575
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:996
\inmodule QtCore\reentrant
Definition qdatetime.h:283
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:29
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
qsizetype count() const noexcept
Definition qlist.h:398
void clear()
Definition qlist.h:434
\inmodule QtCore
Definition qmetatype.h:341
int id(int=0) const
Definition qmetatype.h:475
\inmodule QtCore
Definition qobject.h:103
The QSqlDriver class is an abstract base class for accessing specific SQL databases.
Definition qsqldriver.h:26
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:41
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:33
@ PositionalPlaceholders
Definition qsqldriver.h:34
@ LowPrecisionNumbers
Definition qsqldriver.h:35
@ EventNotifications
Definition qsqldriver.h:36
@ PreparedQueries
Definition qsqldriver.h:33
@ NamedPlaceholders
Definition qsqldriver.h:34
@ BatchOperations
Definition qsqldriver.h:35
@ MultipleResultSets
Definition qsqldriver.h:36
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
Definition qsqlfield.h:28
bool isNull() const
Returns true if the field's value is NULL; otherwise returns false.
QVariant value
Definition qsqlfield.h:24
The QSqlIndex class provides functions to manipulate and describe database indexes.
Definition qsqlindex.h:18
The QSqlRecord class encapsulates a database record.
Definition qsqlrecord.h:20
QSqlField field(int i) const
Returns the field at position index.
static bool isVariantNull(const QVariant &variant)
The QSqlResult class provides an abstract interface for accessing data from specific SQL databases.
Definition qsqlresult.h:22
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:78
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:8249
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
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:5455
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype capacity() const
Returns the maximum number of characters that can be stored in the string without forcing a reallocat...
Definition qstring.h:1256
QString simplified() const &
Definition qstring.h:451
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:5506
QByteArray toLocal8Bit() const &
Definition qstring.h:638
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
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:8084
QString toUpper() const &
Definition qstring.h:439
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
\inmodule QtCore \reentrant
Definition qdatetime.h:215
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.
\inmodule QtCore
Definition qvariant.h:65
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:22
@ BeforeFirstRow
Definition qtsqlglobal.h:21
@ SystemTables
Definition qtsqlglobal.h:37
@ Views
Definition qtsqlglobal.h:38
@ Tables
Definition qtsqlglobal.h:36
@ LowPrecisionInt32
Definition qtsqlglobal.h:44
@ LowPrecisionDouble
Definition qtsqlglobal.h:46
@ LowPrecisionInt64
Definition qtsqlglobal.h:45
@ HighPrecision
Definition qtsqlglobal.h:48
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:128
DBusConnection const char DBusError * error
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputPortEXT port
#define qWarning
Definition qlogging.h:166
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint64 GLenum handleType
GLuint GLsizei const GLchar * message
GLenum GLenum GLsizei void GLsizei void * column
GLdouble s
[6]
Definition qopenglext.h:235
GLboolean reset
GLenum query
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
GLenum GLsizei len
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:510
static QByteArray qGetBinaryData(SQLHANDLE hStmt, int column, SQLLEN &lengthIndicator, bool &isNull)
Definition qsql_db2.cpp:411
static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool &isNull)
Definition qsql_db2.cpp:371
static SQLBIGINT qGetBigIntData(SQLHANDLE hStmt, int column, bool &isNull)
Definition qsql_db2.cpp:354
static int qGetIntData(SQLHANDLE hStmt, int column, bool &isNull)
Definition qsql_db2.cpp:317
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:335
#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:32
long long qint64
Definition qtypes.h:60
#define Q_INT64_C(c)
Definition qtypes.h:57
QByteArray ba
[0]
QMimeDatabase db
[0]
QHostInfo info
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18