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