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