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_ibase.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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_ibase_p.h"
5#include <QtCore/qcoreapplication.h>
6#include <QtCore/qdatetime.h>
7#include <QtCore/qtimezone.h>
8#include <QtCore/qdeadlinetimer.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/qlist.h>
11#include <QtCore/qloggingcategory.h>
12#include <QtCore/qmap.h>
13#include <QtCore/qmutex.h>
14#include <QtCore/qvariant.h>
15#include <QtCore/qvarlengtharray.h>
16#include <QtSql/qsqlerror.h>
17#include <QtSql/qsqlfield.h>
18#include <QtSql/qsqlindex.h>
19#include <QtSql/qsqlquery.h>
20#include <QtSql/private/qsqlcachedresult_p.h>
21#include <QtSql/private/qsqldriver_p.h>
22#include <stdlib.h>
23#include <limits.h>
24#include <math.h>
25#include <mutex>
26
28
29static Q_LOGGING_CATEGORY(lcIbase, "qt.sql.ibase")
30
31using namespace Qt::StringLiterals;
32
33#define FBVERSION SQL_DIALECT_V6
34
35#ifndef SQLDA_CURRENT_VERSION
36#define SQLDA_CURRENT_VERSION SQLDA_VERSION1
37#endif
38
39// Firebird uses blr_bool and not blr_boolean_dtype which is what Interbase uses
40#ifndef blr_boolean_dtype
41#define blr_boolean_dtype blr_bool
42#endif
43
44constexpr qsizetype QIBaseChunkSize = SHRT_MAX / 2;
45
46#if (FB_API_VER >= 40)
47typedef QMap<quint16, QByteArray> QFbTzIdToIanaIdMap;
48typedef QMap<QByteArray, quint16> QIanaIdToFbTzIdMap;
49Q_GLOBAL_STATIC(QFbTzIdToIanaIdMap, qFbTzIdToIanaIdMap)
50Q_GLOBAL_STATIC(QIanaIdToFbTzIdMap, qIanaIdToFbTzIdMap)
51std::once_flag initTZMappingFlag;
52#endif
53
54static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode)
55{
56 if (status[0] != 1 || status[1] <= 0)
57 return false;
58
59 msg.clear();
60 sqlcode = isc_sqlcode(status);
61 char buf[512];
62 while(fb_interpret(buf, 512, &status)) {
63 if (!msg.isEmpty())
64 msg += " - "_L1;
65 msg += QString::fromUtf8(buf);
66 }
67 return true;
68}
69
70static void createDA(XSQLDA *&sqlda)
71{
72 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
73 if (sqlda == (XSQLDA*)0) return;
74 sqlda->sqln = 1;
75 sqlda->sqld = 0;
76 sqlda->version = SQLDA_CURRENT_VERSION;
77 sqlda->sqlvar[0].sqlind = 0;
78 sqlda->sqlvar[0].sqldata = 0;
79}
80
81static void enlargeDA(XSQLDA *&sqlda, int n)
82{
83 if (sqlda != (XSQLDA*)0)
84 free(sqlda);
85 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
86 if (sqlda == (XSQLDA*)0) return;
87 sqlda->sqln = n;
88 sqlda->version = SQLDA_CURRENT_VERSION;
89}
90
91static void initDA(XSQLDA *sqlda)
92{
93 for (int i = 0; i < sqlda->sqld; ++i) {
94 switch (sqlda->sqlvar[i].sqltype & ~1) {
95 case SQL_INT64:
96 case SQL_LONG:
97 case SQL_SHORT:
98 case SQL_FLOAT:
99 case SQL_DOUBLE:
100 case SQL_TIMESTAMP:
101#if (FB_API_VER >= 40)
102 case SQL_TIMESTAMP_TZ:
103#endif
104 case SQL_TYPE_TIME:
105 case SQL_TYPE_DATE:
106 case SQL_TEXT:
107 case SQL_BLOB:
108 case SQL_BOOLEAN:
109 sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen];
110 break;
111 case SQL_ARRAY:
112 sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)];
113 memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD));
114 break;
115 case SQL_VARYING:
116 sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)];
117 break;
118 default:
119 // not supported - do not bind.
120 sqlda->sqlvar[i].sqldata = 0;
121 qCWarning(lcIbase, "initDA: unknown sqltype: %d", sqlda->sqlvar[i].sqltype & ~1);
122 break;
123 }
124 if (sqlda->sqlvar[i].sqltype & 1) {
125 sqlda->sqlvar[i].sqlind = new short[1];
126 *(sqlda->sqlvar[i].sqlind) = 0;
127 } else {
128 sqlda->sqlvar[i].sqlind = 0;
129 }
130 }
131}
132
133static void delDA(XSQLDA *&sqlda)
134{
135 if (!sqlda)
136 return;
137 for (int i = 0; i < sqlda->sqld; ++i) {
138 delete [] sqlda->sqlvar[i].sqlind;
139 delete [] sqlda->sqlvar[i].sqldata;
140 }
141 free(sqlda);
142 sqlda = nullptr;
143}
144
145static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
146{
147 switch (iType) {
148 case blr_varying:
149 case blr_varying2:
150 case blr_text:
151 case blr_cstring:
152 case blr_cstring2:
153 return QMetaType::QString;
154 case blr_sql_time:
155 return QMetaType::QTime;
156 case blr_sql_date:
157 return QMetaType::QDate;
158 case blr_timestamp:
159#if (FB_API_VER >= 40)
160 case blr_timestamp_tz:
161#endif
162 return QMetaType::QDateTime;
163 case blr_blob:
164 return QMetaType::QByteArray;
165 case blr_quad:
166 case blr_short:
167 case blr_long:
168 return (hasScale ? QMetaType::Double : QMetaType::Int);
169 case blr_int64:
170 return (hasScale ? QMetaType::Double : QMetaType::LongLong);
171 case blr_float:
172 case blr_d_float:
173 case blr_double:
174 return QMetaType::Double;
176 return QMetaType::Bool;
177 }
178 qCWarning(lcIbase, "qIBaseTypeName: unknown datatype: %d", iType);
180}
181
182static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale)
183{
184 switch(iType & ~1) {
185 case SQL_VARYING:
186 case SQL_TEXT:
187 return QMetaType::QString;
188 case SQL_LONG:
189 case SQL_SHORT:
190 return (hasScale ? QMetaType::Double : QMetaType::Int);
191 case SQL_INT64:
192 return (hasScale ? QMetaType::Double : QMetaType::LongLong);
193 case SQL_FLOAT:
194 case SQL_DOUBLE:
195 return QMetaType::Double;
196 case SQL_TIMESTAMP:
197#if (FB_API_VER >= 40)
198 case SQL_TIMESTAMP_TZ:
199#endif
200 return QMetaType::QDateTime;
201 case SQL_TYPE_TIME:
202 return QMetaType::QTime;
203 case SQL_TYPE_DATE:
204 return QMetaType::QDate;
205 case SQL_ARRAY:
206 return QMetaType::QVariantList;
207 case SQL_BLOB:
208 return QMetaType::QByteArray;
209 case SQL_BOOLEAN:
210 return QMetaType::Bool;
211 default:
212 break;
213 }
214 qCWarning(lcIbase, "qIBaseTypeName: unknown datatype: %d", iType);
216}
217
218static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
219{
220 static const QTime midnight(0, 0, 0, 0);
221 static const QDate basedate(1858, 11, 17);
222 ISC_TIMESTAMP ts;
223 ts.timestamp_time = midnight.msecsTo(dt.time()) * 10;
224 ts.timestamp_date = basedate.daysTo(dt.date());
225 return ts;
226}
227
229{
230 static const QDate bd(1858, 11, 17);
231 QTime t(0, 0);
232 QDate d;
233
234 // have to demangle the structure ourselves because isc_decode_time
235 // strips the msecs
236 auto timebuf = reinterpret_cast<ISC_TIMESTAMP*>(buffer);
237 t = t.addMSecs(static_cast<int>(timebuf->timestamp_time / 10));
238 d = bd.addDays(timebuf->timestamp_date);
239 return QDateTime(d, t);
240}
241
242#if (FB_API_VER >= 40)
243QDateTime fromTimeStampTz(char *buffer)
244{
245 static const QDate bd(1858, 11, 17);
246 QTime t(0, 0);
247 QDate d;
248
249 // have to demangle the structure ourselves because isc_decode_time
250 // strips the msecs
251 auto timebuf = reinterpret_cast<ISC_TIMESTAMP_TZ*>(buffer);
252 t = t.addMSecs(static_cast<int>(timebuf->utc_timestamp.timestamp_time / 10));
253 d = bd.addDays(timebuf->utc_timestamp.timestamp_date);
254 quint16 fpTzID = timebuf->time_zone;
255
256 QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID);
257 if (!timeZoneName.isEmpty())
258 return QDateTime(d, t, QTimeZone(timeZoneName));
259 else
260 return {};
261}
262
263ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
264{
265 static const QTime midnight(0, 0, 0, 0);
266 static const QDate basedate(1858, 11, 17);
267 ISC_TIMESTAMP_TZ ts;
268 ts.utc_timestamp.timestamp_time = midnight.msecsTo(dt.time()) * 10;
269 ts.utc_timestamp.timestamp_date = basedate.daysTo(dt.date());
270 ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
271 return ts;
272}
273#endif
274
275static ISC_TIME toTime(QTime t)
276{
277 static const QTime midnight(0, 0, 0, 0);
278 return (ISC_TIME)midnight.msecsTo(t) * 10;
279}
280
281static QTime fromTime(char *buffer)
282{
283 QTime t(0, 0);
284 // have to demangle the structure ourselves because isc_decode_time
285 // strips the msecs
286 t = t.addMSecs(int((*(ISC_TIME*)buffer) / 10));
287
288 return t;
289}
290
291static ISC_DATE toDate(QDate t)
292{
293 static const QDate basedate(1858, 11, 17);
294 ISC_DATE date;
295
296 date = basedate.daysTo(t);
297 return date;
298}
299
300static QDate fromDate(char *buffer)
301{
302 static const QDate bd(1858, 11, 17);
303 QDate d;
304
305 // have to demangle the structure ourselves because isc_decode_time
306 // strips the msecs
307 d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
308
309 return d;
310}
311
321
323{
324 Q_DECLARE_PUBLIC(QIBaseDriver)
325public:
328
330 {
331 Q_Q(QIBaseDriver);
332 QString imsg;
333 ISC_LONG sqlcode;
334 if (!getIBaseError(imsg, status, sqlcode))
335 return false;
336
337 q->setLastError(QSqlError(QCoreApplication::translate("QIBaseDriver", msg),
338 imsg, typ,
339 sqlcode != -1 ? QString::number(sqlcode) : QString()));
340 return true;
341 }
342
343#if (FB_API_VER >= 40)
344 void initTZMappingCache()
345 {
346 Q_Q(QIBaseDriver);
347 QSqlQuery qry(q->createResult());
348 qry.setForwardOnly(true);
349 qry.exec(QString("select * from RDB$TIME_ZONES"_L1));
350 if (qry.lastError().type()) {
351 q->setLastError(QSqlError(
352 QCoreApplication::translate("QIBaseDriver",
353 "failed to query time zone mapping from system table"),
354 qry.lastError().databaseText(),
356 qry.lastError().nativeErrorCode()));
357
358 return;
359 }
360
361 while (qry.next()) {
362 auto record = qry.record();
363 quint16 fbTzId = record.value(0).value<quint16>();
364 QByteArray ianaId = record.value(1).toByteArray().simplified();
365 qFbTzIdToIanaIdMap()->insert(fbTzId, ianaId);
366 qIanaIdToFbTzIdMap()->insert(ianaId, fbTzId);
367 }
368 }
369#endif
370
371public:
372 isc_db_handle ibase;
373 isc_tr_handle trans;
374 ISC_STATUS status[20];
375 QMap<QString, QIBaseEventBuffer*> eventBuffers;
376};
377
378typedef QMap<void *, QIBaseDriver *> QIBaseBufferDriverMap;
379Q_GLOBAL_STATIC(QIBaseBufferDriverMap, qBufferDriverMap)
381
383{
384 qMutex()->lock();
385 qBufferDriverMap()->remove(reinterpret_cast<void *>(eBuffer->resultBuffer));
386 qMutex()->unlock();
387 delete eBuffer;
388}
389
391
393{
394 Q_DECLARE_PRIVATE(QIBaseResult)
395
396public:
397 explicit QIBaseResult(const QIBaseDriver* db);
398
399 bool prepare(const QString &query) override;
400 bool exec() override;
401 QVariant handle() const override;
402
403protected:
404 bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) override;
405 bool reset (const QString &query) override;
406 int size() override;
407 int numRowsAffected() override;
408 QSqlRecord record() const override;
409
410 template<typename T>
412 {
413 if (scale >= 0)
414 return QVariant(val);
415
416 switch (numericalPrecisionPolicy()) {
418 return QVariant(qint32(val * pow(10.0, scale)));
420 return QVariant(qint64(val * pow(10.0, scale)));
422 return QVariant(double(val * pow(10.0, scale)));
423 case QSql::HighPrecision: {
424 const bool negative = val < 0;
426 if constexpr (std::is_signed_v<T> || negative)
428 else
430 auto len = number.size();
431 scale *= -1;
432 if (scale >= len) {
433 number = QString(scale - len + 1, u'0') + number;
434 len = number.size();
435 }
436 const auto sepPos = len - scale;
437 number = number.left(sepPos) + u'.' + number.mid(sepPos);
438 if (negative)
439 number = u'-' + number;
440 return QVariant(number);
441 }
442 }
443 return QVariant(val);
444 }
445};
446
448{
449 Q_DECLARE_PUBLIC(QIBaseResult)
450
451public:
453
456
457 void cleanup();
459 {
460 Q_Q(QIBaseResult);
461 QString imsg;
462 ISC_LONG sqlcode;
463 if (!getIBaseError(imsg, status, sqlcode))
464 return false;
465
466 q->setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", msg),
467 imsg, typ,
468 sqlcode != -1 ? QString::number(sqlcode) : QString()));
469 return true;
470 }
471
472 bool transaction();
473 bool commit();
474
475 bool isSelect();
476 QVariant fetchBlob(ISC_QUAD *bId);
477 bool writeBlob(qsizetype iPos, const QByteArray &ba);
478 QVariant fetchArray(int pos, ISC_QUAD *arr);
479 bool writeArray(qsizetype i, const QList<QVariant> &list);
480public:
481 ISC_STATUS status[20];
482 isc_tr_handle trans;
483 //indicator whether we have a local transaction or a transaction on driver level
485 isc_stmt_handle stmt;
486 isc_db_handle ibase;
487 XSQLDA *sqlda; // output sqlda
488 XSQLDA *inda; // input parameters
490};
491
492
495 trans(0),
496 localTransaction(!drv_d_func()->ibase),
497 stmt(0),
498 ibase(drv_d_func()->ibase),
499 sqlda(nullptr),
500 inda(nullptr),
501 queryType(-1)
502{
503}
504
506{
507 Q_Q(QIBaseResult);
508 commit();
509 if (!localTransaction)
510 trans = 0;
511
512 if (stmt) {
513 isc_dsql_free_statement(status, &stmt, DSQL_drop);
514 stmt = 0;
515 }
516
517 delDA(sqlda);
518 delDA(inda);
519
520 queryType = -1;
521 q->cleanup();
522}
523
525{
526 isc_blob_handle handle = 0;
527 ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[iPos].sqldata;
528 isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
529 if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"),
531 qsizetype i = 0;
532 while (i < ba.size()) {
533 isc_put_segment(status, &handle, qMin(ba.size() - i, QIBaseChunkSize),
534 ba.data() + i);
535 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB")))
536 return false;
537 i += qMin(ba.size() - i, QIBaseChunkSize);
538 }
539 }
540 isc_close_blob(status, &handle);
541 return true;
542}
543
545{
546 isc_blob_handle handle = 0;
547
548 isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
549 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to open BLOB"),
551 return QVariant();
552
553 unsigned short len = 0;
555 int chunkSize = QIBaseChunkSize;
556 ba.resize(chunkSize);
557 qsizetype read = 0;
558 while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
559 read += len;
560 ba.resize(read + chunkSize);
561 }
562 ba.resize(read);
563
564 bool isErr = (status[1] == isc_segstr_eof ? false :
565 isError(QT_TRANSLATE_NOOP("QIBaseResult",
566 "Unable to read BLOB"),
568
569 isc_close_blob(status, &handle);
570
571 if (isErr)
572 return QVariant();
573
574 ba.resize(read);
575 return ba;
576}
577
578template<typename T>
579static QList<QVariant> toList(char** buf, int count)
580{
581 QList<QVariant> res;
582 for (int i = 0; i < count; ++i) {
583 res.append(*(T*)(*buf));
584 *buf += sizeof(T);
585 }
586 return res;
587}
588
589static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
590 short* numElements, ISC_ARRAY_DESC *arrayDesc)
591{
592 const short dim = arrayDesc->array_desc_dimensions - 1;
593 const unsigned char dataType = arrayDesc->array_desc_dtype;
594 QList<QVariant> valList;
595 unsigned short strLen = arrayDesc->array_desc_length;
596
597 if (curDim != dim) {
598 for(int i = 0; i < numElements[curDim]; ++i)
599 buffer = readArrayBuffer(list, buffer, curDim + 1, numElements, arrayDesc);
600 } else {
601 switch(dataType) {
602 case blr_varying:
603 case blr_varying2:
604 strLen += 2; // for the two terminating null values
606 case blr_text:
607 case blr_text2: {
608 int o;
609 for (int i = 0; i < numElements[dim]; ++i) {
610 for(o = 0; o < strLen && buffer[o]!=0; ++o )
611 ;
612
613 valList.append(QString::fromUtf8(buffer, o));
614 buffer += strLen;
615 }
616 break; }
617 case blr_long:
618 valList = toList<int>(&buffer, numElements[dim]);
619 break;
620 case blr_short:
621 valList = toList<short>(&buffer, numElements[dim]);
622 break;
623 case blr_int64:
624 valList = toList<qint64>(&buffer, numElements[dim]);
625 break;
626 case blr_float:
627 valList = toList<float>(&buffer, numElements[dim]);
628 break;
629 case blr_double:
630 valList = toList<double>(&buffer, numElements[dim]);
631 break;
632 case blr_timestamp:
633 for(int i = 0; i < numElements[dim]; ++i) {
634 valList.append(fromTimeStamp(buffer));
635 buffer += sizeof(ISC_TIMESTAMP);
636 }
637 break;
638#if (FB_API_VER >= 40)
639 case blr_timestamp_tz:
640 for (int i = 0; i < numElements[dim]; ++i) {
641 valList.append(fromTimeStampTz(buffer));
642 buffer += sizeof(ISC_TIMESTAMP_TZ);
643 }
644 break;
645#endif
646 case blr_sql_time:
647 for (int i = 0; i < numElements[dim]; ++i) {
648 valList.append(fromTime(buffer));
649 buffer += sizeof(ISC_TIME);
650 }
651 break;
652 case blr_sql_date:
653 for(int i = 0; i < numElements[dim]; ++i) {
654 valList.append(fromDate(buffer));
655 buffer += sizeof(ISC_DATE);
656 }
657 break;
659 valList = toList<bool>(&buffer, numElements[dim]);
660 break;
661 }
662 }
663 if (dim > 0)
664 list.append(valList);
665 else
666 list += valList;
667 return buffer;
668}
669
671{
672 QList<QVariant> list;
673 ISC_ARRAY_DESC desc;
674
675 if (!arr)
676 return list;
677
678 QByteArray relname(sqlda->sqlvar[pos].relname, sqlda->sqlvar[pos].relname_length);
679 QByteArray sqlname(sqlda->sqlvar[pos].sqlname, sqlda->sqlvar[pos].sqlname_length);
680
681 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
682 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
684 return list;
685
686
687 int arraySize = 1, subArraySize;
688 short dimensions = desc.array_desc_dimensions;
689 QVarLengthArray<short> numElements(dimensions);
690
691 for(int i = 0; i < dimensions; ++i) {
692 subArraySize = (desc.array_desc_bounds[i].array_bound_upper -
693 desc.array_desc_bounds[i].array_bound_lower + 1);
694 numElements[i] = subArraySize;
695 arraySize = subArraySize * arraySize;
696 }
697
698 ISC_LONG bufLen;
700 /* varying arrayelements are stored with 2 trailing null bytes
701 indicating the length of the string
702 */
703 if (desc.array_desc_dtype == blr_varying
704 || desc.array_desc_dtype == blr_varying2) {
705 desc.array_desc_length += 2;
706 bufLen = desc.array_desc_length * arraySize * sizeof(short);
707 } else {
708 bufLen = desc.array_desc_length * arraySize;
709 }
710
711
712 ba.resize(int(bufLen));
713 isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen);
714 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get array data"),
716 return list;
717
718 readArrayBuffer(list, ba.data(), 0, numElements.data(), &desc);
719
720 return QVariant(list);
721}
722
723template<typename T>
724static char* fillList(char *buffer, const QList<QVariant> &list, T* = nullptr)
725{
726 for (const auto &elem : list) {
727 T val = qvariant_cast<T>(elem);
728 memcpy(buffer, &val, sizeof(T));
729 buffer += sizeof(T);
730 }
731 return buffer;
732}
733
734template<>
735char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
736{
737 for (const auto &elem : list) {
738 double val = qvariant_cast<double>(elem);
739 float val2 = (float)val;
740 memcpy(buffer, &val2, sizeof(float));
741 buffer += sizeof(float);
742 }
743 return buffer;
744}
745
746static char* qFillBufferWithString(char *buffer, const QString& string,
747 short buflen, bool varying, bool array)
748{
749 QByteArray str = string.toUtf8(); // keep a copy of the string alive in this scope
750 if (varying) {
751 short tmpBuflen = buflen;
752 if (str.length() < buflen)
753 buflen = str.length();
754 if (array) { // interbase stores varying arrayelements different than normal varying elements
755 memcpy(buffer, str.data(), buflen);
756 memset(buffer + buflen, 0, tmpBuflen - buflen);
757 } else {
758 *(short*)buffer = buflen; // first two bytes is the length
759 memcpy(buffer + sizeof(short), str.data(), buflen);
760 }
761 buffer += tmpBuflen;
762 } else {
763 str = str.leftJustified(buflen, ' ', true);
764 memcpy(buffer, str.data(), buflen);
765 buffer += buflen;
766 }
767 return buffer;
768}
769
770static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
771 QMetaType::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc,
772 QString& error)
773{
774 ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
775 short dim = arrayDesc->array_desc_dimensions - 1;
776
777 int elements = (bounds[curDim].array_bound_upper -
778 bounds[curDim].array_bound_lower + 1);
779
780 if (list.size() != elements) { // size mismatch
781 error = "Expected size: %1. Supplied size: %2"_L1;
782 error = "Array size mismatch. Fieldname: %1 "_L1
783 + error.arg(elements).arg(list.size());
784 return 0;
785 }
786
787 if (curDim != dim) {
788 for (const auto &elem : list) {
789
790 if (elem.typeId() != QMetaType::QVariantList) { // dimensions mismatch
791 error = "Array dimensons mismatch. Fieldname: %1"_L1;
792 return 0;
793 }
794
795 buffer = createArrayBuffer(buffer, elem.toList(), type, curDim + 1,
796 arrayDesc, error);
797 if (!buffer)
798 return 0;
799 }
800 } else {
801 switch(type) {
802 case QMetaType::Int:
803 case QMetaType::UInt:
804 if (arrayDesc->array_desc_dtype == blr_short)
805 buffer = fillList<short>(buffer, list);
806 else
807 buffer = fillList<int>(buffer, list);
808 break;
809 case QMetaType::Double:
810 if (arrayDesc->array_desc_dtype == blr_float)
811 buffer = fillList<float>(buffer, list, static_cast<float *>(0));
812 else
813 buffer = fillList<double>(buffer, list);
814 break;
815 case QMetaType::LongLong:
816 buffer = fillList<qint64>(buffer, list);
817 break;
818 case QMetaType::ULongLong:
819 buffer = fillList<quint64>(buffer, list);
820 break;
821 case QMetaType::QString:
822 for (const auto &elem : list)
823 buffer = qFillBufferWithString(buffer, elem.toString(),
824 arrayDesc->array_desc_length,
825 arrayDesc->array_desc_dtype == blr_varying,
826 true);
827 break;
828 case QMetaType::QDate:
829 for (const auto &elem : list) {
830 *((ISC_DATE*)buffer) = toDate(elem.toDate());
831 buffer += sizeof(ISC_DATE);
832 }
833 break;
834 case QMetaType::QTime:
835 for (const auto &elem : list) {
836 *((ISC_TIME*)buffer) = toTime(elem.toTime());
837 buffer += sizeof(ISC_TIME);
838 }
839 break;
840 case QMetaType::QDateTime:
841 for (const auto &elem : list) {
842 switch (arrayDesc->array_desc_dtype) {
843 case blr_timestamp:
844 *((ISC_TIMESTAMP*)buffer) = toTimeStamp(elem.toDateTime());
845 buffer += sizeof(ISC_TIMESTAMP);
846 break;
847#if (FB_API_VER >= 40)
848 case blr_timestamp_tz:
849 *((ISC_TIMESTAMP_TZ*)buffer) = toTimeStampTz(elem.toDateTime());
850 buffer += sizeof(ISC_TIMESTAMP_TZ);
851 break;
852#endif
853 default:
854 break;
855 }
856 }
857 break;
858 case QMetaType::Bool:
859 buffer = fillList<bool>(buffer, list);
860 break;
861 default:
862 break;
863 }
864 }
865 return buffer;
866}
867
869{
870 Q_Q(QIBaseResult);
872 ISC_QUAD *arrayId = (ISC_QUAD*) inda->sqlvar[column].sqldata;
873 ISC_ARRAY_DESC desc;
874
875 QByteArray relname(inda->sqlvar[column].relname, inda->sqlvar[column].relname_length);
876 QByteArray sqlname(inda->sqlvar[column].sqlname, inda->sqlvar[column].sqlname_length);
877
878 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
879 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"),
881 return false;
882
883 short arraySize = 1;
884 ISC_LONG bufLen;
885
886 short dimensions = desc.array_desc_dimensions;
887 for(int i = 0; i < dimensions; ++i) {
888 arraySize *= (desc.array_desc_bounds[i].array_bound_upper -
889 desc.array_desc_bounds[i].array_bound_lower + 1);
890 }
891
892 /* varying arrayelements are stored with 2 trailing null bytes
893 indicating the length of the string
894 */
895 if (desc.array_desc_dtype == blr_varying ||
896 desc.array_desc_dtype == blr_varying2)
897 desc.array_desc_length += 2;
898
899 bufLen = desc.array_desc_length * arraySize;
901 ba.resize(int(bufLen));
902
903 if (list.size() > arraySize) {
904 error = "Array size mismatch: size of %1 is %2, size of provided list is %3"_L1;
905 error = error.arg(QLatin1StringView(sqlname)).arg(arraySize).arg(list.size());
906 q->setLastError(QSqlError(error, ""_L1, QSqlError::StatementError));
907 return false;
908 }
909
911 qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0),
912 0, &desc, error)) {
913 q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), ""_L1,
915 return false;
916 }
917
918 /* readjust the buffer size*/
919 if (desc.array_desc_dtype == blr_varying
920 || desc.array_desc_dtype == blr_varying2)
921 desc.array_desc_length -= 2;
922
923 isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen);
924 return true;
925}
926
927
929{
930 char acBuffer[9];
931 char qType = isc_info_sql_stmt_type;
932 isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
933 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get query info"),
935 return false;
936 int iLength = isc_vax_integer(&acBuffer[1], 2);
937 queryType = isc_vax_integer(&acBuffer[3], iLength);
938 return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure);
939}
940
942{
943 if (trans)
944 return true;
945 if (drv_d_func()->trans) {
946 localTransaction = false;
947 trans = drv_d_func()->trans;
948 return true;
949 }
950 localTransaction = true;
951
952 isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
953 if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not start transaction"),
955 return false;
956
957 return true;
958}
959
960// does nothing if the transaction is on the
961// driver level
963{
964 if (!trans)
965 return false;
966 // don't commit driver's transaction, the driver will do it for us
967 if (!localTransaction)
968 return true;
969
970 isc_commit_transaction(status, &trans);
971 trans = 0;
972 return !isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to commit transaction"),
974}
975
977
982
984{
985 Q_D(QIBaseResult);
986// qDebug("prepare: %ls", qUtf16Printable(query));
987 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
988 return false;
989 d->cleanup();
990 setActive(false);
992
993 createDA(d->sqlda);
994 if (d->sqlda == (XSQLDA*)0) {
995 qCWarning(lcIbase) << "QIOBaseResult: createDA(): failed to allocate memory";
996 return false;
997 }
998
999 createDA(d->inda);
1000 if (d->inda == (XSQLDA*)0){
1001 qCWarning(lcIbase) << "QIOBaseResult: createDA(): failed to allocate memory";
1002 return false;
1003 }
1004
1005 if (!d->transaction())
1006 return false;
1007
1008 isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
1009 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not allocate statement"),
1011 return false;
1012 isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0,
1013 const_cast<char*>(query.toUtf8().constData()), FBVERSION, d->sqlda);
1014 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not prepare statement"),
1016 return false;
1017
1018 isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
1019 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
1020 "Could not describe input statement"), QSqlError::StatementError))
1021 return false;
1022 if (d->inda->sqld > d->inda->sqln) {
1023 enlargeDA(d->inda, d->inda->sqld);
1024 if (d->inda == (XSQLDA*)0) {
1025 qCWarning(lcIbase) << "QIOBaseResult: enlargeDA(): failed to allocate memory";
1026 return false;
1027 }
1028
1029 isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
1030 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
1031 "Could not describe input statement"), QSqlError::StatementError))
1032 return false;
1033 }
1034 initDA(d->inda);
1035 if (d->sqlda->sqld > d->sqlda->sqln) {
1036 // need more field descriptors
1037 enlargeDA(d->sqlda, d->sqlda->sqld);
1038 if (d->sqlda == (XSQLDA*)0) {
1039 qCWarning(lcIbase) << "QIOBaseResult: enlargeDA(): failed to allocate memory";
1040 return false;
1041 }
1042
1043 isc_dsql_describe(d->status, &d->stmt, FBVERSION, d->sqlda);
1044 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not describe statement"),
1046 return false;
1047 }
1048 initDA(d->sqlda);
1049
1050 setSelect(d->isSelect());
1051 if (!isSelect()) {
1052 free(d->sqlda);
1053 d->sqlda = 0;
1054 }
1055
1056 return true;
1057}
1058
1060{
1061 Q_D(QIBaseResult);
1062 bool ok = true;
1063
1064 if (!d->trans)
1065 d->transaction();
1066
1067 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
1068 return false;
1069 setActive(false);
1071
1072 if (d->inda) {
1073 const QList<QVariant> &values = boundValues();
1074 if (values.count() > d->inda->sqld) {
1075 qCWarning(lcIbase) << "QIBaseResult::exec: Parameter mismatch, expected"_L1
1076 << d->inda->sqld << ", got"_L1 << values.count()
1077 << "parameters"_L1;
1078 return false;
1079 }
1080 for (qsizetype para = 0; para < values.count(); ++para) {
1081 if (!d->inda->sqlvar[para].sqldata)
1082 // skip unknown datatypes
1083 continue;
1084 const QVariant &val = values[para];
1085 if (d->inda->sqlvar[para].sqltype & 1) {
1087 // set null indicator
1088 *(d->inda->sqlvar[para].sqlind) = -1;
1089 // and set the value to 0, otherwise it would count as empty string.
1090 // it seems to be working with just setting sqlind to -1
1091 //*((char*)d->inda->sqlvar[para].sqldata) = 0;
1092 continue;
1093 }
1094 // a value of 0 means non-null.
1095 *(d->inda->sqlvar[para].sqlind) = 0;
1096 } else {
1098 qCWarning(lcIbase) << "QIBaseResult::exec: Null value replaced by default (zero)"_L1
1099 << "value for type of column"_L1 << d->inda->sqlvar[para].ownname
1100 << ", which is not nullable."_L1;
1101 }
1102 }
1103 switch(d->inda->sqlvar[para].sqltype & ~1) {
1104 case SQL_INT64:
1105 if (d->inda->sqlvar[para].sqlscale < 0)
1106 *((qint64*)d->inda->sqlvar[para].sqldata) =
1107 (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
1108 else
1109 *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
1110 break;
1111 case SQL_LONG:
1112 if (d->inda->sqlvar[para].sqllen == 4) {
1113 if (d->inda->sqlvar[para].sqlscale < 0)
1114 *((qint32*)d->inda->sqlvar[para].sqldata) =
1115 (qint32)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
1116 else
1117 *((qint32*)d->inda->sqlvar[para].sqldata) = (qint32)val.toInt();
1118 } else {
1119 *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
1120 }
1121 break;
1122 case SQL_SHORT:
1123 if (d->inda->sqlvar[para].sqlscale < 0)
1124 *((short*)d->inda->sqlvar[para].sqldata) =
1125 (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
1126 else
1127 *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
1128 break;
1129 case SQL_FLOAT:
1130 *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
1131 break;
1132 case SQL_DOUBLE:
1133 *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
1134 break;
1135 case SQL_TIMESTAMP:
1136 *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
1137 break;
1138#if (FB_API_VER >= 40)
1139 case SQL_TIMESTAMP_TZ:
1140 *((ISC_TIMESTAMP_TZ*)d->inda->sqlvar[para].sqldata) = toTimeStampTz(val.toDateTime());
1141 break;
1142#endif
1143 case SQL_TYPE_TIME:
1144 *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
1145 break;
1146 case SQL_TYPE_DATE:
1147 *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
1148 break;
1149 case SQL_VARYING:
1150 case SQL_TEXT:
1151 qFillBufferWithString(d->inda->sqlvar[para].sqldata, val.toString(),
1152 d->inda->sqlvar[para].sqllen,
1153 (d->inda->sqlvar[para].sqltype & ~1) == SQL_VARYING, false);
1154 break;
1155 case SQL_BLOB:
1156 ok &= d->writeBlob(para, val.toByteArray());
1157 break;
1158 case SQL_ARRAY:
1159 ok &= d->writeArray(para, val.toList());
1160 break;
1161 case SQL_BOOLEAN:
1162 *((bool*)d->inda->sqlvar[para].sqldata) = val.toBool();
1163 break;
1164 default:
1165 qCWarning(lcIbase, "QIBaseResult::exec: Unknown datatype %d",
1166 d->inda->sqlvar[para].sqltype & ~1);
1167 break;
1168 }
1169 }
1170 }
1171
1172 if (ok) {
1173 isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
1174 QString imsg;
1175 ISC_LONG sqlcode;
1176 if (getIBaseError(imsg, d->status, sqlcode) && sqlcode != -501) {
1177 setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", "Unable to close statement"),
1179 sqlcode != -1 ? QString::number(sqlcode) : QString()));
1180 return false;
1181 }
1182 if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) {
1183 cleanup();
1184 }
1185 if (d->queryType == isc_info_sql_stmt_exec_procedure)
1186 isc_dsql_execute2(d->status, &d->trans, &d->stmt, FBVERSION, d->inda, d->sqlda);
1187 else
1188 isc_dsql_execute(d->status, &d->trans, &d->stmt, FBVERSION, d->inda);
1189 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to execute query")))
1190 return false;
1191
1192 // Not all stored procedures necessarily return values.
1193 if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0)
1194 delDA(d->sqlda);
1195
1196 if (d->sqlda)
1197 init(d->sqlda->sqld);
1198
1199 if (!isSelect())
1200 d->commit();
1201
1202 setActive(true);
1203 return true;
1204 }
1205 return false;
1206}
1207
1209{
1210 if (!prepare(query))
1211 return false;
1212 return exec();
1213}
1214
1216{
1217 Q_D(QIBaseResult);
1218 ISC_STATUS stat = 0;
1219
1220 // Stored Procedures are special - they populate our d->sqlda when executing,
1221 // so we don't have to call isc_dsql_fetch
1222 if (d->queryType == isc_info_sql_stmt_exec_procedure) {
1223 // the first "fetch" shall succeed, all consecutive ones will fail since
1224 // we only have one row to fetch for stored procedures
1225 if (rowIdx != 0)
1226 stat = 100;
1227 } else {
1228 stat = isc_dsql_fetch(d->status, &d->stmt, FBVERSION, d->sqlda);
1229 }
1230
1231 if (stat == 100) {
1232 // no more rows
1234 return false;
1235 }
1236 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not fetch next item"),
1238 return false;
1239 if (rowIdx < 0) // not interested in actual values
1240 return true;
1241
1242 for (int i = 0; i < d->sqlda->sqld; ++i) {
1243 int idx = rowIdx + i;
1244 char *buf = d->sqlda->sqlvar[i].sqldata;
1245 int size = d->sqlda->sqlvar[i].sqllen;
1246 Q_ASSERT(buf);
1247
1248 if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
1249 // null value
1250 QVariant v;
1251 v.convert(QMetaType(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype,
1252 d->sqlda->sqlvar[i].sqlscale < 0)));
1253 if (v.userType() == QMetaType::Double) {
1254 switch(numericalPrecisionPolicy()) {
1256 v.convert(QMetaType(QMetaType::Int));
1257 break;
1259 v.convert(QMetaType(QMetaType::LongLong));
1260 break;
1262 v.convert(QMetaType(QMetaType::QString));
1263 break;
1265 // no conversion
1266 break;
1267 }
1268 }
1269 row[idx] = v;
1270 continue;
1271 }
1272
1273 switch(d->sqlda->sqlvar[i].sqltype & ~1) {
1274 case SQL_VARYING:
1275 // pascal strings - a short with a length information followed by the data
1276 row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
1277 break;
1278 case SQL_INT64: {
1279 Q_ASSERT(d->sqlda->sqlvar[i].sqllen == sizeof(qint64));
1280 const auto val = *(qint64 *)buf;
1281 const auto scale = d->sqlda->sqlvar[i].sqlscale;
1282 row[idx] = applyScale(val, scale);
1283 break;
1284 }
1285 case SQL_LONG:
1286 if (d->sqlda->sqlvar[i].sqllen == 4) {
1287 const auto val = *(qint32 *)buf;
1288 const auto scale = d->sqlda->sqlvar[i].sqlscale;
1289 row[idx] = applyScale(val, scale);
1290 } else
1291 row[idx] = QVariant(*(qint64*)buf);
1292 break;
1293 case SQL_SHORT: {
1294 const auto val = *(short *)buf;
1295 const auto scale = d->sqlda->sqlvar[i].sqlscale;
1296 row[idx] = applyScale(val, scale);
1297 break;
1298 }
1299 case SQL_FLOAT:
1300 row[idx] = QVariant(double((*(float*)buf)));
1301 break;
1302 case SQL_DOUBLE:
1303 row[idx] = QVariant(*(double*)buf);
1304 break;
1305 case SQL_TIMESTAMP:
1306 row[idx] = fromTimeStamp(buf);
1307 break;
1308 case SQL_TYPE_TIME:
1309 row[idx] = fromTime(buf);
1310 break;
1311 case SQL_TYPE_DATE:
1312 row[idx] = fromDate(buf);
1313 break;
1314 case SQL_TEXT:
1315 row[idx] = QString::fromUtf8(buf, size);
1316 break;
1317 case SQL_BLOB:
1318 row[idx] = d->fetchBlob((ISC_QUAD*)buf);
1319 break;
1320 case SQL_ARRAY:
1321 row[idx] = d->fetchArray(i, (ISC_QUAD*)buf);
1322 break;
1323 case SQL_BOOLEAN:
1324 row[idx] = QVariant(bool((*(bool*)buf)));
1325 break;
1326#if (FB_API_VER >= 40)
1327 case SQL_TIMESTAMP_TZ:
1328 row[idx] = fromTimeStampTz(buf);
1329 break;
1330#endif
1331 default:
1332 // unknown type - don't even try to fetch
1333 qCWarning(lcIbase, "gotoNext: unknown sqltype: %d",
1334 d->sqlda->sqlvar[i].sqltype & ~1);
1335 row[idx] = QVariant();
1336 break;
1337 }
1338 }
1339
1340 return true;
1341}
1342
1344{
1345 return -1;
1346
1347#if 0
1348 static char sizeInfo[] = {isc_info_sql_records};
1349 char buf[64];
1350
1351 //qDebug() << sizeInfo;
1352 if (!isActive() || !isSelect())
1353 return -1;
1354
1355 char ct;
1356 short len;
1357 int val = 0;
1358// while(val == 0) {
1359 isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
1360// isc_database_info(d->status, &d->ibase, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
1361
1362 for(int i = 0; i < 66; ++i)
1363 qDebug() << QString::number(buf[i]);
1364
1365 for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
1366 ct = *(c++);
1367 len = isc_vax_integer(c, 2);
1368 c += 2;
1369 val = isc_vax_integer(c, len);
1370 c += len;
1371 qDebug() << "size" << val;
1372 if (ct == isc_info_req_select_count)
1373 return val;
1374 }
1375 //qDebug("size -1");
1376 return -1;
1377
1378 unsigned int i, result_size;
1379 if (buf[0] == isc_info_sql_records) {
1380 i = 3;
1381 result_size = isc_vax_integer(&buf[1],2);
1382 while (buf[i] != isc_info_end && i < result_size) {
1383 len = (short)isc_vax_integer(&buf[i+1],2);
1384 if (buf[i] == isc_info_req_select_count)
1385 return (isc_vax_integer(&buf[i+3],len));
1386 i += len+3;
1387 }
1388 }
1389// }
1390 return -1;
1391#endif
1392}
1393
1395{
1396 Q_D(QIBaseResult);
1397 static char acCountInfo[] = {isc_info_sql_records};
1398 char cCountType;
1399 bool bIsProcedure = false;
1400
1401 switch (d->queryType) {
1402 case isc_info_sql_stmt_select:
1403 cCountType = isc_info_req_select_count;
1404 break;
1405 case isc_info_sql_stmt_update:
1406 cCountType = isc_info_req_update_count;
1407 break;
1408 case isc_info_sql_stmt_delete:
1409 cCountType = isc_info_req_delete_count;
1410 break;
1411 case isc_info_sql_stmt_insert:
1412 cCountType = isc_info_req_insert_count;
1413 break;
1414 case isc_info_sql_stmt_exec_procedure:
1415 bIsProcedure = true; // will sum all changes
1416 break;
1417 default:
1418 qCWarning(lcIbase) << "numRowsAffected: Unknown statement type (" << d->queryType << ")";
1419 return -1;
1420 }
1421
1422 char acBuffer[33];
1423 int iResult = -1;
1424 isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
1425 if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get statement info"),
1427 return -1;
1428 for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
1429 char cType = *pcBuf++;
1430 short sLength = isc_vax_integer (pcBuf, 2);
1431 pcBuf += 2;
1432 int iValue = isc_vax_integer (pcBuf, sLength);
1433 pcBuf += sLength;
1434 if (bIsProcedure) {
1435 if (cType == isc_info_req_insert_count || cType == isc_info_req_update_count
1436 || cType == isc_info_req_delete_count) {
1437 if (iResult == -1)
1438 iResult = 0;
1439 iResult += iValue;
1440 }
1441 } else if (cType == cCountType) {
1442 iResult = iValue;
1443 break;
1444 }
1445 }
1446 return iResult;
1447}
1448
1450{
1451 Q_D(const QIBaseResult);
1452 QSqlRecord rec;
1453 if (!isActive() || !d->sqlda)
1454 return rec;
1455
1456 XSQLVAR v;
1457 for (int i = 0; i < d->sqlda->sqld; ++i) {
1458 v = d->sqlda->sqlvar[i];
1459 QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
1460 QMetaType(qIBaseTypeName2(v.sqltype, v.sqlscale < 0)),
1461 QString::fromLatin1(v.relname, v.relname_length));
1462 f.setLength(v.sqllen);
1463 f.setPrecision(qAbs(v.sqlscale));
1464 f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
1465 if (v.sqlscale < 0) {
1466 QSqlQuery q(driver()->createResult());
1467 q.setForwardOnly(true);
1468 q.exec("select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
1469 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1470 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1471 "AND a.RDB$RELATION_NAME = '"_L1 + QString::fromLatin1(v.relname, v.relname_length) + "' "
1472 "AND a.RDB$FIELD_NAME = '"_L1 + QString::fromLatin1(v.sqlname, v.sqlname_length) + "' "_L1);
1473 if (q.first()) {
1474 if (v.sqlscale < 0) {
1475 f.setLength(q.value(0).toInt());
1476 f.setPrecision(qAbs(q.value(1).toInt()));
1477 } else {
1478 f.setLength(q.value(2).toInt());
1479 f.setPrecision(0);
1480 }
1481 f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
1482 }
1483 }
1484 rec.append(f);
1485 }
1486 return rec;
1487}
1488
1490{
1491 Q_D(const QIBaseResult);
1492 return QVariant(QMetaType::fromType<isc_stmt_handle>(), &d->stmt);
1493}
1494
1495/*********************************/
1496
1498 : QSqlDriver(*new QIBaseDriverPrivate, parent)
1499{
1500}
1501
1503 : QSqlDriver(*new QIBaseDriverPrivate, parent)
1504{
1505 Q_D(QIBaseDriver);
1506 d->ibase = connection;
1507 setOpen(true);
1508 setOpenError(false);
1509}
1510
1514
1516{
1517 switch (f) {
1518 case QuerySize:
1519 case NamedPlaceholders:
1520 case LastInsertId:
1521 case BatchOperations:
1522 case SimpleLocking:
1523 case FinishQuery:
1524 case MultipleResultSets:
1525 case CancelQuery:
1526 return false;
1527 case Transactions:
1528 case PreparedQueries:
1530 case Unicode:
1531 case BLOB:
1532 case EventNotifications:
1534 return true;
1535 }
1536 return false;
1537}
1538
1540 const QString &user,
1541 const QString &password,
1542 const QString &host,
1543 int port,
1544 const QString &connOpts)
1545{
1546 Q_D(QIBaseDriver);
1547 if (isOpen())
1548 close();
1549
1550 const auto opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
1551
1552 QByteArray role;
1553 for (const auto &opt : opts) {
1554 const auto tmp(opt.trimmed());
1555 qsizetype idx;
1556 if ((idx = tmp.indexOf(u'=')) != -1) {
1557 const auto val = tmp.mid(idx + 1).trimmed();
1558 const auto opt = tmp.left(idx).trimmed().toString();
1559 if (opt.toUpper() == "ISC_DPB_SQL_ROLE_NAME"_L1) {
1560 role = val.toLocal8Bit();
1561 role.truncate(255);
1562 }
1563 }
1564 }
1565
1566 QByteArray enc = "UTF8";
1567 QByteArray usr = user.toLocal8Bit();
1568 QByteArray pass = password.toLocal8Bit();
1569 usr.truncate(255);
1570 pass.truncate(255);
1571
1572 QByteArray ba;
1573 ba.reserve(usr.length() + pass.length() + enc.length() + role.length() + 9);
1574 ba.append(char(isc_dpb_version1));
1575 ba.append(char(isc_dpb_user_name));
1576 ba.append(char(usr.length()));
1577 ba.append(usr.data(), usr.length());
1578 ba.append(char(isc_dpb_password));
1579 ba.append(char(pass.length()));
1580 ba.append(pass.data(), pass.length());
1581 ba.append(char(isc_dpb_lc_ctype));
1582 ba.append(char(enc.length()));
1583 ba.append(enc.data(), enc.length());
1584
1585 if (!role.isEmpty()) {
1586 ba.append(char(isc_dpb_sql_role_name));
1587 ba.append(char(role.length()));
1588 ba.append(role.data(), role.length());
1589 }
1590
1591 QString portString;
1592 if (port != -1)
1593 portString = QStringLiteral("/%1").arg(port);
1594
1595 QString ldb;
1596 if (!host.isEmpty())
1597 ldb += host + portString + u':';
1598 ldb += db;
1599 isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()),
1600 &d->ibase, ba.size(), ba.data());
1601 if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"),
1603 setOpenError(true);
1604 return false;
1605 }
1606
1607 setOpen(true);
1608 setOpenError(false);
1609#if (FB_API_VER >= 40)
1610 std::call_once(initTZMappingFlag, [d](){ d->initTZMappingCache(); });
1611 if (lastError().isValid())
1612 {
1613 setOpen(true);
1614 return false;
1615 }
1616#endif
1617 return true;
1618}
1619
1621{
1622 Q_D(QIBaseDriver);
1623 if (isOpen()) {
1624
1625 if (d->eventBuffers.size()) {
1626 ISC_STATUS status[20];
1628 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1629 QIBaseEventBuffer *eBuffer = i.value();
1631 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1632 qFreeEventBuffer(eBuffer);
1633 }
1634 d->eventBuffers.clear();
1635 }
1636
1637 isc_detach_database(d->status, &d->ibase);
1638 d->ibase = 0;
1639 setOpen(false);
1640 setOpenError(false);
1641 }
1642}
1643
1645{
1646 return new QIBaseResult(this);
1647}
1648
1650{
1651 Q_D(QIBaseDriver);
1652 if (!isOpen() || isOpenError())
1653 return false;
1654 if (d->trans)
1655 return false;
1656
1657 isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
1658 return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Could not start transaction"),
1660}
1661
1663{
1664 Q_D(QIBaseDriver);
1665 if (!isOpen() || isOpenError())
1666 return false;
1667 if (!d->trans)
1668 return false;
1669
1670 isc_commit_transaction(d->status, &d->trans);
1671 d->trans = 0;
1672 return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to commit transaction"),
1674}
1675
1677{
1678 Q_D(QIBaseDriver);
1679 if (!isOpen() || isOpenError())
1680 return false;
1681 if (!d->trans)
1682 return false;
1683
1684 isc_rollback_transaction(d->status, &d->trans);
1685 d->trans = 0;
1686 return !d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Unable to rollback transaction"),
1688}
1689
1691{
1693 if (!isOpen())
1694 return res;
1695
1696 QString typeFilter;
1697
1698 if (type == QSql::SystemTables) {
1699 typeFilter += "RDB$SYSTEM_FLAG != 0"_L1;
1700 } else if (type == (QSql::SystemTables | QSql::Views)) {
1701 typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"_L1;
1702 } else {
1703 if (!(type & QSql::SystemTables))
1704 typeFilter += "RDB$SYSTEM_FLAG = 0 AND "_L1;
1705 if (!(type & QSql::Views))
1706 typeFilter += "RDB$VIEW_BLR IS NULL AND "_L1;
1707 if (!(type & QSql::Tables))
1708 typeFilter += "RDB$VIEW_BLR IS NOT NULL AND "_L1;
1709 if (!typeFilter.isEmpty())
1710 typeFilter.chop(5);
1711 }
1712 if (!typeFilter.isEmpty())
1713 typeFilter.prepend("where "_L1);
1714
1716 q.setForwardOnly(true);
1717 if (!q.exec("select rdb$relation_name from rdb$relations "_L1 + typeFilter))
1718 return res;
1719 while (q.next())
1720 res << q.value(0).toString().simplified();
1721
1722 return res;
1723}
1724
1726{
1727 QSqlRecord rec;
1728 if (!isOpen())
1729 return rec;
1730
1733 q.setForwardOnly(true);
1734 q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
1735 "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
1736 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1737 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1738 "AND a.RDB$RELATION_NAME = '"_L1 + table + "' "
1739 "ORDER BY a.RDB$FIELD_POSITION"_L1);
1740
1741 while (q.next()) {
1742 int type = q.value(1).toInt();
1743 bool hasScale = q.value(3).toInt() < 0;
1744 QSqlField f(q.value(0).toString().simplified(), QMetaType(qIBaseTypeName(type, hasScale)), tablename);
1745 if (hasScale) {
1746 f.setLength(q.value(4).toInt());
1747 f.setPrecision(qAbs(q.value(3).toInt()));
1748 } else {
1749 f.setLength(q.value(2).toInt());
1750 f.setPrecision(0);
1751 }
1752 f.setRequired(q.value(5).toInt() > 0);
1753
1754 rec.append(f);
1755 }
1756 return rec;
1757}
1758
1760{
1762 if (!isOpen())
1763 return index;
1764
1767 q.setForwardOnly(true);
1768 q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
1769 "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
1770 "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
1771 "AND a.RDB$RELATION_NAME = '"_L1 + tablename +
1772 " 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
1773 "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
1774 "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
1775 "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
1776 "ORDER BY b.RDB$FIELD_POSITION"_L1);
1777
1778 while (q.next()) {
1779 QSqlField field(q.value(1).toString().simplified(),
1780 QMetaType(qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0)),
1781 tablename);
1782 index.append(field); //TODO: asc? desc?
1783 index.setName(q.value(0).toString());
1784 }
1785
1786 return index;
1787}
1788
1789QString QIBaseDriver::formatValue(const QSqlField &field, bool trimStrings) const
1790{
1791 switch (field.metaType().id()) {
1792 case QMetaType::QDateTime: {
1793 QDateTime datetime = field.value().toDateTime();
1794 if (datetime.isValid())
1795 return u'\'' + QString::number(datetime.date().year()) + u'-' +
1796 QString::number(datetime.date().month()) + u'-' +
1797 QString::number(datetime.date().day()) + u' ' +
1798 QString::number(datetime.time().hour()) + u':' +
1799 QString::number(datetime.time().minute()) + u':' +
1800 QString::number(datetime.time().second()) + u'.' +
1801 QString::number(datetime.time().msec()).rightJustified(3, u'0', true) +
1802 u'\'';
1803 else
1804 return "NULL"_L1;
1805 }
1806 case QMetaType::QTime: {
1807 QTime time = field.value().toTime();
1808 if (time.isValid())
1809 return u'\'' + QString::number(time.hour()) + u':' +
1810 QString::number(time.minute()) + u':' +
1811 QString::number(time.second()) + u'.' +
1812 QString::number(time.msec()).rightJustified(3, u'0', true) +
1813 u'\'';
1814 else
1815 return "NULL"_L1;
1816 }
1817 case QMetaType::QDate: {
1818 QDate date = field.value().toDate();
1819 if (date.isValid())
1820 return u'\'' + QString::number(date.year()) + u'-' +
1821 QString::number(date.month()) + u'-' +
1822 QString::number(date.day()) + u'\'';
1823 else
1824 return "NULL"_L1;
1825 }
1826 default:
1827 return QSqlDriver::formatValue(field, trimStrings);
1828 }
1829}
1830
1832{
1833 Q_D(const QIBaseDriver);
1834 return QVariant(QMetaType::fromType<isc_db_handle>(), &d->ibase);
1835}
1836
1837static ISC_EVENT_CALLBACK qEventCallback(char *result, ISC_USHORT length, const ISC_UCHAR *updated)
1838{
1839 if (!updated)
1840 return 0;
1841
1842
1843 memcpy(result, updated, length);
1844 qMutex()->lock();
1845 QIBaseDriver *driver = qBufferDriverMap()->value(result);
1846 qMutex()->unlock();
1847
1848 // We use an asynchronous call (i.e., queued connection) because the event callback
1849 // is executed in a different thread than the one in which the driver lives.
1850 if (driver)
1851 QMetaObject::invokeMethod(driver, "qHandleEventNotification", Qt::QueuedConnection, Q_ARG(void *, reinterpret_cast<void *>(result)));
1852
1853 return 0;
1854}
1855
1857{
1858 Q_D(QIBaseDriver);
1859 if (!isOpen()) {
1860 qCWarning(lcIbase, "QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
1861 return false;
1862 }
1863
1864 if (d->eventBuffers.contains(name)) {
1865 qCWarning(lcIbase, "QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%ls'.",
1867 return false;
1868 }
1869
1870 QIBaseEventBuffer *eBuffer = new QIBaseEventBuffer;
1872 eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer,
1873 &eBuffer->resultBuffer,
1874 1,
1875 name.toLocal8Bit().constData());
1876
1877 qMutex()->lock();
1878 qBufferDriverMap()->insert(eBuffer->resultBuffer, this);
1879 qMutex()->unlock();
1880
1881 d->eventBuffers.insert(name, eBuffer);
1882
1883 ISC_STATUS status[20];
1884 isc_que_events(status,
1885 &d->ibase,
1886 &eBuffer->eventId,
1887 eBuffer->bufferLength,
1888 eBuffer->eventBuffer,
1889 reinterpret_cast<ISC_EVENT_CALLBACK>(reinterpret_cast<void *>
1890 (&qEventCallback)),
1891 eBuffer->resultBuffer);
1892
1893 if (status[0] == 1 && status[1]) {
1894 setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name)));
1895 d->eventBuffers.remove(name);
1896 qFreeEventBuffer(eBuffer);
1897 return false;
1898 }
1899
1900 return true;
1901}
1902
1904{
1905 Q_D(QIBaseDriver);
1906 if (!isOpen()) {
1907 qCWarning(lcIbase, "QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
1908 return false;
1909 }
1910
1911 if (!d->eventBuffers.contains(name)) {
1912 qCWarning(lcIbase, "QIBaseDriver::QIBaseSubscriptionState not subscribed to '%ls'.",
1914 return false;
1915 }
1916
1917 QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name);
1918 ISC_STATUS status[20];
1920 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1921
1922 if (status[0] == 1 && status[1]) {
1923 setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name)));
1924 return false;
1925 }
1926
1927 d->eventBuffers.remove(name);
1928 qFreeEventBuffer(eBuffer);
1929
1930 return true;
1931}
1932
1934{
1935 Q_D(const QIBaseDriver);
1936 return QStringList(d->eventBuffers.keys());
1937}
1938
1939void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
1940{
1941 Q_D(QIBaseDriver);
1943 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1944 QIBaseEventBuffer* eBuffer = i.value();
1945 if (reinterpret_cast<void *>(eBuffer->resultBuffer) != updatedResultBuffer)
1946 continue;
1947
1948 ISC_ULONG counts[20];
1949 memset(counts, 0, sizeof(counts));
1950 isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer);
1951 if (counts[0]) {
1952
1953 if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed)
1955 else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting)
1956 eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed;
1957
1958 ISC_STATUS status[20];
1959 isc_que_events(status,
1960 &d->ibase,
1961 &eBuffer->eventId,
1962 eBuffer->bufferLength,
1963 eBuffer->eventBuffer,
1964 reinterpret_cast<ISC_EVENT_CALLBACK>(reinterpret_cast<void *>
1965 (&qEventCallback)),
1966 eBuffer->resultBuffer);
1967 if (Q_UNLIKELY(status[0] == 1 && status[1])) {
1968 qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%ls'",
1969 qUtf16Printable(i.key()));
1970 }
1971
1972 return;
1973 }
1974 }
1975}
1976
1978{
1979 QString res = identifier;
1980 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
1981 res.replace(u'"', "\"\""_L1);
1982 res.replace(u'.', "\".\""_L1);
1983 res = u'"' + res + u'"';
1984 }
1985 return res;
1986}
1987
1989{
1990 Q_UNUSED(type);
1991 return 31;
1992}
1993
1995
1996#include "moc_qsql_ibase_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:634
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
void truncate(qsizetype pos)
Truncates the byte array at index position pos.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
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...
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\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
qint64 daysTo(QDate d) const
Returns the number of days from this date to d (which is negative if d is earlier than this date).
constexpr bool isValid() const
Returns true if this date is valid; otherwise returns false.
Definition qdatetime.h:71
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...
QDate addDays(qint64 days) const
Returns a QDate object containing a date ndays later than the date of this object (or earlier if nday...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
ISC_STATUS status[20]
bool isError(const char *msg, QSqlError::ErrorType typ=QSqlError::UnknownError)
isc_tr_handle trans
isc_db_handle ibase
QMap< QString, QIBaseEventBuffer * > eventBuffers
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.
QStringList subscribedToNotifications() const override
Returns a list of the names of the event notifications that are currently subscribed to.
QVariant handle() const override
Returns the low-level database handle wrapped in a QVariant or an invalid variant if there is no hand...
QSqlRecord record(const QString &tablename) const override
Returns a QSqlRecord populated with the names of the fields in table tableName.
int maximumIdentifierLength(IdentifierType type) const override
QStringList tables(QSql::TableType) const override
Returns a list of the names of the tables in the database.
bool hasFeature(DriverFeature f) const override
Returns true if the driver supports feature feature; otherwise returns false.
QString formatValue(const QSqlField &field, bool trimStrings) const override
Returns a string representation of the field value for the database.
bool rollbackTransaction() override
This function is called to rollback a transaction.
bool unsubscribeFromNotification(const QString &name) override
This function is called to unsubscribe from event notifications from the database.
QIBaseDriver(QObject *parent=nullptr)
void close() override
Derived classes must reimplement this pure virtual function in order to close the database connection...
bool beginTransaction() override
This function is called to begin a transaction.
bool subscribeToNotification(const QString &name) override
This function is called to subscribe to event notifications from the database.
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...
bool commitTransaction() override
This function is called to commit a transaction.
virtual ~QIBaseDriver()
QSqlIndex primaryIndex(const QString &table) const override
Returns the primary index for table tableName.
QVariant fetchArray(int pos, ISC_QUAD *arr)
bool isError(const char *msg, QSqlError::ErrorType typ=QSqlError::UnknownError)
isc_db_handle ibase
QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *drv)
isc_stmt_handle stmt
isc_tr_handle trans
bool writeBlob(qsizetype iPos, const QByteArray &ba)
QVariant fetchBlob(ISC_QUAD *bId)
ISC_STATUS status[20]
bool writeArray(qsizetype i, const QList< QVariant > &list)
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool gotoNext(QSqlCachedResult::ValueCache &row, int rowIdx) override
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
QVariant applyScale(T val, int scale) const
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
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...
QIBaseResult(const QIBaseDriver *db)
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
qsizetype size() const noexcept
Definition qlist.h:397
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmetatype.h:341
int id(int=0) const
Definition qmetatype.h:475
Type
\macro Q_DECLARE_OPAQUE_POINTER(PointerType)
Definition qmetatype.h:345
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qobject.h:103
void init(int colCount)
QSqlDriver::DbmsType dbmsType
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...
QSqlError lastError() const
Returns a QSqlError object which contains information about the last error that occurred on the datab...
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
bool isOpenError() const
Returns true if the there was an error opening the database connection; otherwise returns false.
virtual void setLastError(const QSqlError &e)
This function is used to set the value of the last error, error, that occurred on the database.
void notification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
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 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
@ UnknownError
Definition qsqlerror.h:24
@ 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
QVariant value
Definition qsqlfield.h:24
The QSqlIndex class provides functions to manipulate and describe database indexes.
Definition qsqlindex.h:18
The QSqlQuery class provides a means of executing and manipulating SQL statements.
Definition qsqlquery.h:24
The QSqlRecord class encapsulates a database record.
Definition qsqlrecord.h:20
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:22
bool isSelect() const
Returns true if the current result is from a SELECT statement; otherwise returns false.
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...
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.
const QSqlDriver * driver() const
Returns the driver associated with the result.
bool isActive() const
Returns true if the result has records to be retrieved; otherwise returns false.
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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 chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
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
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString leftJustified(qsizetype width, QChar fill=u' ', bool trunc=false) const
Returns a string of size width that contains this string padded by the fill character.
Definition qstring.cpp:7022
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
QByteArray toUtf8() const &
Definition qstring.h:634
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
\inmodule QtCore
Definition qtimezone.h:26
\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.
bool isValid() const
Returns true if the time is valid; otherwise returns false.
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
bool convert(QMetaType type)
Casts the variant to the requested type, targetType.
QDateTime toDateTime() const
Returns the variant as a QDateTime if the variant has userType() \l QMetaType::QDateTime,...
QTime toTime() const
Returns the variant as a QTime if the variant has userType() \l QMetaType::QTime, \l QMetaType::QDate...
QDate toDate() const
Returns the variant as a QDate if the variant has userType() \l QMetaType::QDate, \l QMetaType::QDate...
#define this
Definition dialogs.cpp:9
QString str
[2]
QDate date
[1]
QStyleOptionButton opt
@ 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.
Definition qcompare.h:63
@ QueuedConnection
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError * error
DBusConnection * connection
EGLOutputPortEXT port
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCritical
Definition qlogging.h:167
#define qDebug
[1]
Definition qlogging.h:164
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint name
GLfloat n
GLenum GLenum GLsizei void GLsizei void * column
GLboolean reset
GLenum query
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLenum array
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLenum GLsizei len
GLenum GLenum GLenum GLenum GLenum scale
GLenum GLenum GLsizei void * table
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static void split(QT_FT_Vector *b)
static void delDA(XSQLDA *&sqlda)
static void qFreeEventBuffer(QIBaseEventBuffer *eBuffer)
static void initDA(XSQLDA *sqlda)
static ISC_DATE toDate(QDate t)
static bool getIBaseError(QString &msg, const ISC_STATUS *status, ISC_LONG &sqlcode)
static void enlargeDA(XSQLDA *&sqlda, int n)
static QTime fromTime(char *buffer)
static char * createArrayBuffer(char *buffer, const QList< QVariant > &list, QMetaType::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc, QString &error)
static void createDA(XSQLDA *&sqlda)
static char * qFillBufferWithString(char *buffer, const QString &string, short buflen, bool varying, bool array)
#define FBVERSION
static QDateTime fromTimeStamp(char *buffer)
static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
#define blr_boolean_dtype
static char * fillList(char *buffer, const QList< QVariant > &list, T *=nullptr)
static QList< QVariant > toList(char **buf, int count)
constexpr qsizetype QIBaseChunkSize
static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale)
char * fillList< float >(char *buffer, const QList< QVariant > &list, float *)
static ISC_TIME toTime(QTime t)
static ISC_EVENT_CALLBACK qEventCallback(char *result, ISC_USHORT length, const ISC_UCHAR *updated)
#define SQLDA_CURRENT_VERSION
QMap< void *, QIBaseDriver * > QIBaseBufferDriverMap
static QDate fromDate(char *buffer)
static char * readArrayBuffer(QList< QVariant > &list, char *buffer, short curDim, short *numElements, ISC_ARRAY_DESC *arrayDesc)
static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
#define Q_DECLARE_SQLDRIVER_PRIVATE(Class)
SSL_CTX int void * arg
#define qUtf16Printable(string)
Definition qstring.h:1543
#define QStringLiteral(str)
static const QTextHtmlElement elements[Html_NumElements]
#define emit
#define Q_UNUSED(x)
#define QT_TRANSLATE_NOOP(scope, x)
unsigned short quint16
Definition qtypes.h:48
int qint32
Definition qtypes.h:49
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
ReturnedValue read(const char *data)
QList< int > list
[14]
QByteArray ba
[0]
QObject::connect nullptr
QMimeDatabase db
[0]
MyRecord record(int row) const
[0]
QIBaseSubscriptionState subscriptionState
ISC_UCHAR * eventBuffer
ISC_UCHAR * resultBuffer
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...