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