6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qendian.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qtimezone.h>
10#include <QtCore/qdeadlinetimer.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qlist.h>
13#include <QtCore/private/qlocale_tools_p.h>
14#include <QtCore/qloggingcategory.h>
15#include <QtCore/qmap.h>
16#include <QtCore/qmutex.h>
17#include <QtCore/qvariant.h>
18#include <QtCore/qvarlengtharray.h>
19#include <QtSql/qsqlerror.h>
20#include <QtSql/qsqlfield.h>
21#include <QtSql/qsqlindex.h>
22#include <QtSql/qsqlquery.h>
23#include <QtSql/private/qsqlcachedresult_p.h>
24#include <QtSql/private/qsqldriver_p.h>
34using namespace Qt::StringLiterals;
36#define FBVERSION SQL_DIALECT_V6
38#ifndef SQLDA_CURRENT_VERSION
39#define SQLDA_CURRENT_VERSION SQLDA_VERSION1
43#ifndef blr_boolean_dtype
44#define blr_boolean_dtype blr_bool
47#if (defined(QT_SUPPORTS_INT128) || defined(QT_USE_MSVC_INT128)) && (FB_API_VER >= 40
)
48#define IBASE_INT128_SUPPORTED
53#define SQL_BOOLEAN 32764
59typedef QMap<quint16, QByteArray> QFbTzIdToIanaIdMap;
60typedef QMap<QByteArray, quint16> QIanaIdToFbTzIdMap;
61Q_GLOBAL_STATIC(QFbTzIdToIanaIdMap, qFbTzIdToIanaIdMap)
62Q_GLOBAL_STATIC(QIanaIdToFbTzIdMap, qIanaIdToFbTzIdMap)
63std::once_flag initTZMappingFlag;
66static bool getIBaseError(QString& msg,
const ISC_STATUS* status, ISC_LONG &sqlcode)
68 if (status[0] != 1 || status[1] <= 0)
72 sqlcode = isc_sqlcode(status);
74 while(fb_interpret(buf, 512, &status)) {
77 msg += QString::fromUtf8(buf);
84 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
85 if (sqlda == (XSQLDA*)0)
return;
89 sqlda->sqlvar[0].sqlind = 0;
90 sqlda->sqlvar[0].sqldata = 0;
95 if (sqlda != (XSQLDA*)0)
97 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
98 if (sqlda == (XSQLDA*)0)
return;
105 for (
int i = 0; i < sqlda->sqld; ++i) {
106 XSQLVAR &sqlvar = sqlda->sqlvar[i];
107 const auto sqltype = (sqlvar.sqltype & ~1);
115#if (FB_API_VER >= 40
)
116 case SQL_TIMESTAMP_TZ:
117#ifdef IBASE_INT128_SUPPORTED
126 sqlvar.sqldata =
new char[sqlvar.sqllen];
129 sqlvar.sqldata =
new char[
sizeof(ISC_QUAD)];
130 memset(sqlvar.sqldata, 0,
sizeof(ISC_QUAD));
133 sqlvar.sqldata =
new char[sqlvar.sqllen +
sizeof(
short)];
138 qCWarning(lcIbase,
"initDA: unknown sqltype: %d", sqltype);
141 if (sqlvar.sqltype & 1) {
142 sqlvar.sqlind =
new short[1];
143 *(sqlvar.sqlind) = 0;
154 for (
int i = 0; i < sqlda->sqld; ++i) {
155 delete [] sqlda->sqlvar[i].sqlind;
156 delete [] sqlda->sqlvar[i].sqldata;
170 return QMetaType::QString;
172 return QMetaType::QTime;
174 return QMetaType::QDate;
176#if (FB_API_VER >= 40
)
177 case blr_timestamp_tz:
179 return QMetaType::QDateTime;
181 return QMetaType::QByteArray;
185 return (hasScale ? QMetaType::Double : QMetaType::Int);
187 return (hasScale ? QMetaType::Double : QMetaType::LongLong);
191 return QMetaType::Double;
193 return QMetaType::Bool;
195 qCWarning(lcIbase,
"qIBaseTypeName: unknown datatype: %d", iType);
196 return QMetaType::UnknownType;
204 return QMetaType::QString;
207 return (hasScale ? QMetaType::Double : QMetaType::Int);
209 return (hasScale ? QMetaType::Double : QMetaType::LongLong);
210#ifdef IBASE_INT128_SUPPORTED
212 return (hasScale ? QMetaType::Double : QMetaType::LongLong);
216 return QMetaType::Double;
218#if (FB_API_VER >= 40
)
219 case SQL_TIMESTAMP_TZ:
221 return QMetaType::QDateTime;
223 return QMetaType::QTime;
225 return QMetaType::QDate;
227 return QMetaType::QVariantList;
229 return QMetaType::QByteArray;
231 return QMetaType::Bool;
235 qCWarning(lcIbase,
"qIBaseTypeName: unknown datatype: %d", iType);
236 return QMetaType::UnknownType;
245 ts.timestamp_time = dt.time().msecsSinceStartOfDay() * 10;
246 ts.timestamp_date = s_ibaseBaseDate.daysTo(dt.date());
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);
260#if (FB_API_VER >= 40
)
261static inline QDateTime fromTimeStampTz(
const char *buffer)
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;
270 QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID);
271 if (!timeZoneName.isEmpty())
273 const auto utc = QDateTime(d, t, QTimeZone(QTimeZone::UTC));
274 return utc.toTimeZone(QTimeZone(timeZoneName));
280static inline ISC_TIMESTAMP_TZ toTimeStampTz(
const QDateTime &dt)
282 const auto dtUtc = dt.toUTC();
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);
293 return t.msecsSinceStartOfDay() * 10;
300 const auto timebuf =
reinterpret_cast<
const ISC_TIME *>(buffer);
301 return QTime::fromMSecsSinceStartOfDay(*timebuf / 10);
306 return s_ibaseBaseDate.daysTo(t);
313 const auto tsbuf =
reinterpret_cast<
const ISC_TIMESTAMP *>(buffer);
314 return s_ibaseBaseDate.addDays(
int(tsbuf->timestamp_date));
329 Q_DECLARE_PUBLIC(QIBaseDriver)
348#if (FB_API_VER >= 40
)
356 qCInfo(
lcIbase) <<
"Table 'RDB$TIME_ZONES' not found - not timezone support available";
377Q_GLOBAL_STATIC(QIBaseBufferDriverMap, qBufferDriverMap)
378Q_GLOBAL_STATIC(QMutex, qMutex);
383 qBufferDriverMap()->remove(
reinterpret_cast<
void *>(eBuffer->resultBuffer));
402 bool gotoNext(QSqlCachedResult::ValueCache& row,
int rowIdx)
override;
403 bool reset (
const QString &query)
override;
411 const bool negative = val < 0;
413#ifdef IBASE_INT128_SUPPORTED
414 if constexpr (std::is_same_v<qinternalint128, T>) {
415 number = negative ? qint128toBasicLatin(val * -1)
416 : qint128toBasicLatin(val);
419 number = negative ? QString::number(qAbs(val))
420 : QString::number(val);
421 auto len = number.size();
424 number = QString(scale - len + 1, u'0') + number;
427 const auto sepPos = len - scale;
428 number = number.left(sepPos) + u'.' + number.mid(sepPos);
430 number = u'-' + number;
438 return QVariant(val);
440 switch (numericalPrecisionPolicy()) {
441 case QSql::LowPrecisionInt32:
442 return QVariant(qint32(val * pow(10.0, scale)));
443 case QSql::LowPrecisionInt64:
444 return QVariant(qint64(val * pow(10.0, scale)));
445 case QSql::LowPrecisionDouble:
446 return QVariant(
double(val * pow(10.0, scale)));
447 case QSql::HighPrecision:
448 return QVariant(numberToHighPrecision(val, scale));
450 return QVariant(val);
456 auto ptr =
reinterpret_cast<T *>(data);
458 double d = floor(0.5 + val.toDouble() * pow(10.0, scale * -1));
459#ifdef IBASE_INT128_SUPPORTED
460 if constexpr (std::is_same_v<qinternalint128, T>) {
461 quint64 lower = quint64(d);
462 quint64 tmp = quint64(std::numeric_limits<quint32>::max()) + 1;
465 quint64 higher = quint64(d);
466 qinternalint128 result = higher;
469 *ptr =
static_cast<T>(result);
472 *ptr =
static_cast<T>(d);
475 *ptr = val.value<T>();
481 Q_DECLARE_PUBLIC(QIBaseResult)
495 if (!getIBaseError(imsg, status, sqlcode))
498 q->setLastError(QSqlError(QCoreApplication::translate(
"QIBaseResult", msg),
500 sqlcode != -1 ? QString::number(sqlcode) : QString()));
527 : QSqlCachedResultPrivate(q, drv),
531 ibase(drv_d_func()->ibase),
542 if (!localTransaction)
546 isc_dsql_free_statement(status, &stmt, DSQL_drop);
554 cachedRecord.clear();
560 isc_blob_handle handle = 0;
561 ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[iPos].sqldata;
562 isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
563 if (!isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to create BLOB"),
564 QSqlError::StatementError)) {
566 while (i < ba.size()) {
567 isc_put_segment(status, &handle, qMin(ba.size() - i, QIBaseChunkSize),
569 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to write BLOB")))
571 i += qMin(ba.size() - i, QIBaseChunkSize);
574 isc_close_blob(status, &handle);
580 isc_blob_handle handle = 0;
582 isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
583 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to open BLOB"),
584 QSqlError::StatementError))
587 unsigned short len = 0;
588 constexpr auto chunkSize = QIBaseChunkSize;
589 QByteArray ba(chunkSize, Qt::Uninitialized);
591 while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
593 ba.resize(read + chunkSize);
597 bool isErr = (status[1] == isc_segstr_eof ?
false :
598 isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
599 "Unable to read BLOB"),
600 QSqlError::StatementError));
602 isc_close_blob(status, &handle);
616 for (
int i = 0; i < count; ++i) {
617 res.append(*(T*)(*buf));
623static const char *
readArrayBuffer(QList<QVariant>& list,
const char *buffer,
short curDim,
624 const short *numElements, ISC_ARRAY_DESC *arrayDesc)
626 const short dim = arrayDesc->array_desc_dimensions - 1;
627 const unsigned char dataType = arrayDesc->array_desc_dtype;
628 QList<QVariant> valList;
629 unsigned short strLen = arrayDesc->array_desc_length;
632 for(
int i = 0; i < numElements[curDim]; ++i)
633 buffer = readArrayBuffer(list, buffer, curDim + 1, numElements, arrayDesc);
643 for (
int i = 0; i < numElements[dim]; ++i) {
644 for(o = 0; o < strLen && buffer[o]!=0; ++o )
647 valList.append(QString::fromUtf8(buffer, o));
652 valList = toList<
int>(&buffer, numElements[dim]);
655 valList = toList<
short>(&buffer, numElements[dim]);
658 valList = toList<qint64>(&buffer, numElements[dim]);
661 valList = toList<
float>(&buffer, numElements[dim]);
664 valList = toList<
double>(&buffer, numElements[dim]);
667 for(
int i = 0; i < numElements[dim]; ++i) {
668 valList.append(fromTimeStamp(buffer));
669 buffer +=
sizeof(ISC_TIMESTAMP);
672#if (FB_API_VER >= 40
)
673 case blr_timestamp_tz:
674 for (
int i = 0; i < numElements[dim]; ++i) {
675 valList.append(fromTimeStampTz(buffer));
676 buffer +=
sizeof(ISC_TIMESTAMP_TZ);
681 for (
int i = 0; i < numElements[dim]; ++i) {
682 valList.append(fromTime(buffer));
683 buffer +=
sizeof(ISC_TIME);
687 for(
int i = 0; i < numElements[dim]; ++i) {
688 valList.append(fromDate(buffer));
689 buffer +=
sizeof(ISC_DATE);
693 valList = toList<
bool>(&buffer, numElements[dim]);
698 list.append(valList);
706 QList<QVariant> list;
712 const XSQLVAR &sqlvar = sqlda->sqlvar[pos];
713 const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length);
714 const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length);
716 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
717 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not find array"),
718 QSqlError::StatementError))
723 short dimensions = desc.array_desc_dimensions;
724 QVarLengthArray<
short> numElements(dimensions);
726 for(
int i = 0; i < dimensions; ++i) {
727 short subArraySize = (desc.array_desc_bounds[i].array_bound_upper -
728 desc.array_desc_bounds[i].array_bound_lower + 1);
729 numElements[i] = subArraySize;
730 arraySize = subArraySize * arraySize;
735
736
737 if (desc.array_desc_dtype == blr_varying
738 || desc.array_desc_dtype == blr_varying2) {
739 desc.array_desc_length += 2;
740 bufLen = desc.array_desc_length * arraySize *
sizeof(
short);
742 bufLen = desc.array_desc_length * arraySize;
745 QByteArray ba(bufLen, Qt::Uninitialized);
746 isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen);
747 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get array data"),
748 QSqlError::StatementError))
751 readArrayBuffer(list, ba.constData(), 0, numElements.constData(), &desc);
753 return QVariant(list);
757static char*
fillList(
char *buffer,
const QList<QVariant> &list, T* =
nullptr)
759 for (
const auto &elem : list) {
760 T val = qvariant_cast<T>(elem);
761 memcpy(buffer, &val,
sizeof(T));
768char*
fillList<
float>(
char *buffer,
const QList<QVariant> &list,
float*)
770 for (
const auto &elem : list) {
771 double val = qvariant_cast<
double>(elem);
772 float val2 = (
float)val;
773 memcpy(buffer, &val2,
sizeof(
float));
774 buffer +=
sizeof(
float);
780 short buflen,
bool varying,
bool array)
782 QByteArray str = string.toUtf8();
784 short tmpBuflen = buflen;
785 if (str.length() < buflen)
786 buflen = str.length();
788 memcpy(buffer, str.constData(), buflen);
789 memset(buffer + buflen, 0, tmpBuflen - buflen);
791 *(
short*)buffer = buflen;
792 memcpy(buffer +
sizeof(
short), str.constData(), buflen);
796 str = str.leftJustified(buflen,
' ',
true);
797 memcpy(buffer, str.constData(), buflen);
804 QMetaType::Type type,
short curDim, ISC_ARRAY_DESC *arrayDesc,
807 ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
808 short dim = arrayDesc->array_desc_dimensions - 1;
810 int elements = (bounds[curDim].array_bound_upper -
811 bounds[curDim].array_bound_lower + 1);
813 if (list.size() != elements) {
814 error = QCoreApplication::translate(
816 "Array size mismatch. Field name: %3, expected size: %1. Supplied size: %2")
817 .arg(elements).arg(list.size());
822 for (
const auto &elem : list) {
824 if (elem.typeId() != QMetaType::QVariantList) {
825 error = QCoreApplication::translate(
827 "Array dimensions mismatch. Field name: %1");
831 buffer = createArrayBuffer(buffer, elem.toList(), type, curDim + 1,
839 case QMetaType::UInt:
840 if (arrayDesc->array_desc_dtype == blr_short)
841 buffer = fillList<
short>(buffer, list);
843 buffer = fillList<
int>(buffer, list);
845 case QMetaType::Double:
846 if (arrayDesc->array_desc_dtype == blr_float)
847 buffer = fillList<
float>(buffer, list,
static_cast<
float *>(0));
849 buffer = fillList<
double>(buffer, list);
851 case QMetaType::LongLong:
852 buffer = fillList<qint64>(buffer, list);
854 case QMetaType::ULongLong:
855 buffer = fillList<quint64>(buffer, list);
857 case QMetaType::QString:
858 for (
const auto &elem : list)
859 buffer = qFillBufferWithString(buffer, elem.toString(),
860 arrayDesc->array_desc_length,
861 arrayDesc->array_desc_dtype == blr_varying,
864 case QMetaType::QDate:
865 for (
const auto &elem : list) {
866 *((ISC_DATE*)buffer) = toDate(elem.toDate());
867 buffer +=
sizeof(ISC_DATE);
870 case QMetaType::QTime:
871 for (
const auto &elem : list) {
872 *((ISC_TIME*)buffer) = toTime(elem.toTime());
873 buffer +=
sizeof(ISC_TIME);
876 case QMetaType::QDateTime:
877 for (
const auto &elem : list) {
878 switch (arrayDesc->array_desc_dtype) {
880 *((ISC_TIMESTAMP*)buffer) = toTimeStamp(elem.toDateTime());
881 buffer +=
sizeof(ISC_TIMESTAMP);
883#if (FB_API_VER >= 40
)
884 case blr_timestamp_tz:
885 *((ISC_TIMESTAMP_TZ*)buffer) = toTimeStampTz(elem.toDateTime());
886 buffer +=
sizeof(ISC_TIMESTAMP_TZ);
894 case QMetaType::Bool:
895 buffer = fillList<
bool>(buffer, list);
910 XSQLVAR &sqlvar = inda->sqlvar[column];
911 ISC_QUAD *arrayId = (ISC_QUAD*) sqlvar.sqldata;
912 const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length);
913 const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length);
915 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
916 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not find array"),
917 QSqlError::StatementError))
923 short dimensions = desc.array_desc_dimensions;
924 for(
int i = 0; i < dimensions; ++i) {
925 arraySize *= (desc.array_desc_bounds[i].array_bound_upper -
926 desc.array_desc_bounds[i].array_bound_lower + 1);
930
931
932 if (desc.array_desc_dtype == blr_varying ||
933 desc.array_desc_dtype == blr_varying2)
934 desc.array_desc_length += 2;
936 bufLen = desc.array_desc_length * arraySize;
937 QByteArray ba(bufLen, Qt::Uninitialized);
939 if (list.size() > arraySize) {
940 error = QCoreApplication::translate(
942 "Array size mismatch: size of %1 is %2, size of provided list is %3");
943 error = error.arg(QLatin1StringView(sqlname)).arg(arraySize).arg(list.size());
944 q->setLastError(QSqlError(error, {}, QSqlError::StatementError));
948 if (!createArrayBuffer(ba.data(), list,
949 qIBaseTypeName(desc.array_desc_dtype, sqlvar.sqlscale < 0),
951 q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), {},
952 QSqlError::StatementError));
957 if (desc.array_desc_dtype == blr_varying
958 || desc.array_desc_dtype == blr_varying2)
959 desc.array_desc_length -= 2;
961 isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen);
969 char qType = isc_info_sql_stmt_type;
970 isc_dsql_sql_info(status, &stmt, 1, &qType,
sizeof(acBuffer), acBuffer);
971 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get query info"),
972 QSqlError::StatementError))
974 int iLength = isc_vax_integer(&acBuffer[1], 2);
975 queryType = isc_vax_integer(&acBuffer[3], iLength);
976 return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure);
983 if (drv_d_func()->trans) {
985 trans = drv_d_func()->trans;
990 isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
991 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not start transaction"),
992 QSqlError::TransactionError))
1008 isc_commit_transaction(status, &trans);
1010 return !isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to commit transaction"),
1011 QSqlError::TransactionError);
1017 : QSqlCachedResult(*
new QIBaseResultPrivate(
this, db))
1025 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
1029 setAt(QSql::BeforeFirstRow);
1032 if (d->sqlda == (XSQLDA*)0) {
1033 qCWarning(lcIbase) <<
"QIOBaseResult: createDA(): failed to allocate memory";
1038 if (d->inda == (XSQLDA*)0){
1039 qCWarning(lcIbase) <<
"QIOBaseResult: createDA(): failed to allocate memory";
1043 if (!d->transaction())
1046 isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
1047 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not allocate statement"),
1048 QSqlError::StatementError))
1050 isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0,
1051 const_cast<
char*>(query.toUtf8().constData()),
FBVERSION, d->sqlda);
1052 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not prepare statement"),
1053 QSqlError::StatementError))
1056 isc_dsql_describe_bind(d->status, &d->stmt,
FBVERSION, d->inda);
1057 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
1058 "Could not describe input statement"), QSqlError::StatementError))
1060 if (d->inda->sqld > d->inda->sqln) {
1061 enlargeDA(d->inda, d->inda->sqld);
1062 if (d->inda == (XSQLDA*)0) {
1063 qCWarning(lcIbase) <<
"QIOBaseResult: enlargeDA(): failed to allocate memory";
1067 isc_dsql_describe_bind(d->status, &d->stmt,
FBVERSION, d->inda);
1068 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
1069 "Could not describe input statement"), QSqlError::StatementError))
1073 if (d->sqlda->sqld > d->sqlda->sqln) {
1075 enlargeDA(d->sqlda, d->sqlda->sqld);
1076 if (d->sqlda == (XSQLDA*)0) {
1077 qCWarning(lcIbase) <<
"QIOBaseResult: enlargeDA(): failed to allocate memory";
1081 isc_dsql_describe(d->status, &d->stmt,
FBVERSION, d->sqlda);
1082 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not describe statement"),
1083 QSqlError::StatementError))
1088 setSelect(d->isSelect());
1105 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
1108 setAt(QSql::BeforeFirstRow);
1111 const QList<QVariant> &values = boundValues();
1112 if (values.count() > d->inda->sqld) {
1113 qCWarning(lcIbase) <<
"QIBaseResult::exec: Parameter mismatch, expected"_L1
1114 << d->inda->sqld <<
", got"_L1 << values.count()
1118 for (qsizetype para = 0; para < values.count(); ++para) {
1119 const XSQLVAR &sqlvar = d->inda->sqlvar[para];
1120 if (!sqlvar.sqldata)
1123 const QVariant &val = values[para];
1124 if (sqlvar.sqltype & 1) {
1125 if (QSqlResultPrivate::isVariantNull(val)) {
1127 *(sqlvar.sqlind) = -1;
1134 *(sqlvar.sqlind) = 0;
1136 if (QSqlResultPrivate::isVariantNull(val)) {
1137 qCWarning(lcIbase) <<
"QIBaseResult::exec: Null value replaced by default (zero)"_L1
1138 <<
"value for type of column"_L1 << sqlvar.ownname
1139 <<
", which is not nullable."_L1;
1142 const auto sqltype = sqlvar.sqltype & ~1;
1145 setWithScale<qint64>(val, sqlvar.sqlscale, sqlvar.sqldata);
1147#ifdef IBASE_INT128_SUPPORTED
1149 setWithScale<qinternalint128>(val, sqlvar.sqlscale,
1154 if (sqlvar.sqllen == 4)
1155 setWithScale<qint32>(val, sqlvar.sqlscale, sqlvar.sqldata);
1157 setWithScale<qint64>(val, 0, sqlvar.sqldata);
1160 setWithScale<qint16>(val, sqlvar.sqlscale, sqlvar.sqldata);
1163 *((
float*)sqlvar.sqldata) = val.toFloat();
1166 *((
double*)sqlvar.sqldata) = val.toDouble();
1169 *((ISC_TIMESTAMP*)sqlvar.sqldata) = toTimeStamp(val.toDateTime());
1171#if (FB_API_VER >= 40
)
1172 case SQL_TIMESTAMP_TZ:
1173 *((ISC_TIMESTAMP_TZ*)sqlvar.sqldata) = toTimeStampTz(val.toDateTime());
1177 *((ISC_TIME*)sqlvar.sqldata) = toTime(val.toTime());
1180 *((ISC_DATE*)sqlvar.sqldata) = toDate(val.toDate());
1184 qFillBufferWithString(sqlvar.sqldata, val.toString(), sqlvar.sqllen,
1185 sqltype == SQL_VARYING,
false);
1188 ok &= d->writeBlob(para, val.toByteArray());
1191 ok &= d->writeArray(para, val.toList());
1194 *((
bool*)sqlvar.sqldata) = val.toBool();
1197 qCWarning(lcIbase,
"QIBaseResult::exec: Unknown datatype %d",
1205 isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
1208 if (getIBaseError(imsg, d->status, sqlcode) && sqlcode != -501) {
1209 setLastError(QSqlError(QCoreApplication::translate(
"QIBaseResult",
"Unable to close statement"),
1210 imsg, QSqlError::UnknownError,
1211 sqlcode != -1 ? QString::number(sqlcode) : QString()));
1214 if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) {
1217 if (d->queryType == isc_info_sql_stmt_exec_procedure)
1218 isc_dsql_execute2(d->status, &d->trans, &d->stmt,
FBVERSION, d->inda, d->sqlda);
1220 isc_dsql_execute(d->status, &d->trans, &d->stmt,
FBVERSION, d->inda);
1221 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to execute query")))
1225 if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0)
1229 init(d->sqlda->sqld);
1242 if (!prepare(query))
1250 ISC_STATUS stat = 0;
1254 if (d->queryType == isc_info_sql_stmt_exec_procedure) {
1260 stat = isc_dsql_fetch(d->status, &d->stmt,
FBVERSION, d->sqlda);
1265 setAt(QSql::AfterLastRow);
1268 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not fetch next item"),
1269 QSqlError::StatementError))
1274 for (
int i = 0; i < d->sqlda->sqld; ++i) {
1275 int idx = rowIdx + i;
1276 const XSQLVAR &sqlvar = d->sqlda->sqlvar[i];
1278 if ((sqlvar.sqltype & 1) && *sqlvar.sqlind) {
1281 v.convert(QMetaType(qIBaseTypeName2(sqlvar.sqltype, sqlvar.sqlscale < 0)));
1282 if (v.userType() == QMetaType::Double) {
1283 switch(numericalPrecisionPolicy()) {
1284 case QSql::LowPrecisionInt32:
1285 v.convert(QMetaType(QMetaType::Int));
1287 case QSql::LowPrecisionInt64:
1288 v.convert(QMetaType(QMetaType::LongLong));
1290 case QSql::HighPrecision:
1291 v.convert(QMetaType(QMetaType::QString));
1293 case QSql::LowPrecisionDouble:
1302 const char *buf = sqlvar.sqldata;
1303 int size = sqlvar.sqllen;
1305 const auto sqltype = sqlvar.sqltype & ~1;
1309 row[idx] = QString::fromUtf8(buf +
sizeof(
short), *(
short*)buf);
1312 Q_ASSERT(sqlvar.sqllen ==
sizeof(qint64));
1313 const auto val = *(qint64 *)buf;
1314 const auto scale = sqlvar.sqlscale;
1315 row[idx] = applyScale(val, scale);
1318#ifdef IBASE_INT128_SUPPORTED
1320 Q_ASSERT(sqlvar.sqllen ==
sizeof(qinternalint128));
1321 const qinternalint128 val128 = qFromUnaligned<qinternalint128>(buf);
1322 const auto scale = sqlvar.sqlscale;
1323 row[idx] = numberToHighPrecision(val128, scale);
1324 if (numericalPrecisionPolicy() != QSql::HighPrecision)
1325 row[idx] = applyScale(row[idx].toDouble(), 0);
1330 if (sqlvar.sqllen == 4) {
1331 const auto val = *(qint32 *)buf;
1332 const auto scale = sqlvar.sqlscale;
1333 row[idx] = applyScale(val, scale);
1335 row[idx] = QVariant(*(qint64*)buf);
1338 const auto val = *(
short *)buf;
1339 const auto scale = sqlvar.sqlscale;
1340 row[idx] = applyScale(val, scale);
1344 row[idx] = QVariant(
double((*(
float*)buf)));
1347 row[idx] = QVariant(*(
double*)buf);
1350 row[idx] = fromTimeStamp(buf);
1353 row[idx] = fromTime(buf);
1356 row[idx] = fromDate(buf);
1359 row[idx] = QString::fromUtf8(buf, size);
1362 row[idx] = d->fetchBlob((ISC_QUAD*)buf);
1365 row[idx] = d->fetchArray(i, (ISC_QUAD*)buf);
1368 row[idx] = QVariant(
bool((*(
bool*)buf)));
1370#if (FB_API_VER >= 40
)
1371 case SQL_TIMESTAMP_TZ:
1372 row[idx] = fromTimeStampTz(buf);
1377 qCWarning(lcIbase,
"gotoNext: unknown sqltype: %d", sqltype);
1378 row[idx] = QVariant();
1391 static char sizeInfo[] = {isc_info_sql_records};
1395 if (!isActive() || !isSelect())
1402 isc_dsql_sql_info(d->status, &d->stmt,
sizeof(sizeInfo), sizeInfo,
sizeof(buf), buf);
1405 for(
int i = 0; i < 66; ++i)
1406 qDebug() << QString::number(buf[i]);
1408 for (
char* c = buf + 3; *c != isc_info_end; ) {
1410 len = isc_vax_integer(c, 2);
1412 val = isc_vax_integer(c, len);
1414 qDebug() <<
"size" << val;
1415 if (ct == isc_info_req_select_count)
1421 unsigned int i, result_size;
1422 if (buf[0] == isc_info_sql_records) {
1424 result_size = isc_vax_integer(&buf[1],2);
1425 while (buf[i] != isc_info_end && i < result_size) {
1426 len = (
short)isc_vax_integer(&buf[i+1],2);
1427 if (buf[i] == isc_info_req_select_count)
1428 return (isc_vax_integer(&buf[i+3],len));
1440 static char acCountInfo[] = {isc_info_sql_records};
1442 bool bIsProcedure =
false;
1444 switch (d->queryType) {
1445 case isc_info_sql_stmt_select:
1446 cCountType = isc_info_req_select_count;
1448 case isc_info_sql_stmt_update:
1449 cCountType = isc_info_req_update_count;
1451 case isc_info_sql_stmt_delete:
1452 cCountType = isc_info_req_delete_count;
1454 case isc_info_sql_stmt_insert:
1455 cCountType = isc_info_req_insert_count;
1457 case isc_info_sql_stmt_exec_procedure:
1458 bIsProcedure =
true;
1461 qCWarning(lcIbase) <<
"numRowsAffected: Unknown statement type (" << d->queryType <<
")";
1467 isc_dsql_sql_info(d->status, &d->stmt,
sizeof(acCountInfo), acCountInfo,
sizeof(acBuffer), acBuffer);
1468 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get statement info"),
1469 QSqlError::StatementError))
1471 for (
char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; ) {
1472 char cType = *pcBuf++;
1473 short sLength = isc_vax_integer (pcBuf, 2);
1475 int iValue = isc_vax_integer (pcBuf, sLength);
1478 if (cType == isc_info_req_insert_count || cType == isc_info_req_update_count
1479 || cType == isc_info_req_delete_count) {
1484 }
else if (cType == cCountType) {
1494 Q_D(
const QIBaseResult);
1495 if (!isActive() || !d->sqlda)
1498 if (!d->cachedRecord.isEmpty())
1499 return d->cachedRecord;
1501 for (
int i = 0; i < d->sqlda->sqld; ++i) {
1502 const XSQLVAR &v = d->sqlda->sqlvar[i];
1503 QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
1504 QMetaType(qIBaseTypeName2(v.sqltype, v.sqlscale < 0)),
1505 QString::fromLatin1(v.relname, v.relname_length));
1506 f.setLength(v.sqllen);
1507 f.setPrecision(qAbs(v.sqlscale));
1508 f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
1509 if (v.sqlscale < 0) {
1510 QSqlQuery q(driver()->createResult());
1511 q.setForwardOnly(
true);
1512 q.exec(
"select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
1513 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1514 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1515 "AND a.RDB$RELATION_NAME = '"_L1 + QString::fromLatin1(v.relname, v.relname_length) +
"' "
1516 "AND a.RDB$FIELD_NAME = '"_L1 + QString::fromLatin1(v.sqlname, v.sqlname_length) +
"' "_L1);
1518 if (v.sqlscale < 0) {
1519 f.setLength(q.value(0).toInt());
1520 f.setPrecision(qAbs(q.value(1).toInt()));
1522 f.setLength(q.value(2).toInt());
1525 f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
1528 d->cachedRecord.append(f);
1530 return d->cachedRecord;
1535 Q_D(
const QIBaseResult);
1536 return QVariant(QMetaType::fromType<isc_stmt_handle>(), &d->stmt);
1541QIBaseDriver::QIBaseDriver(QObject * parent)
1542 : QSqlDriver(*
new QIBaseDriverPrivate, parent)
1546QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent)
1547 : QSqlDriver(*
new QIBaseDriverPrivate, parent)
1550 d->ibase = connection;
1552 setOpenError(
false);
1555QIBaseDriver::~QIBaseDriver()
1559bool QIBaseDriver::hasFeature(DriverFeature f)
const
1563 case NamedPlaceholders:
1565 case BatchOperations:
1568 case MultipleResultSets:
1572 case PreparedQueries:
1573 case PositionalPlaceholders:
1576 case EventNotifications:
1577 case LowPrecisionNumbers:
1583bool QIBaseDriver::open(
const QString &db,
1584 const QString &user,
1585 const QString &password,
1586 const QString &host,
1588 const QString &connOpts)
1594 const auto opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
1597 for (
const auto &opt : opts) {
1598 const auto tmp(opt.trimmed());
1600 if ((idx = tmp.indexOf(u'=')) != -1) {
1601 const auto val = tmp.mid(idx + 1).trimmed();
1602 const auto opt = tmp.left(idx).trimmed().toString();
1603 if (opt.toUpper() ==
"ISC_DPB_SQL_ROLE_NAME"_L1) {
1604 role = val.toLocal8Bit();
1610 QByteArray enc =
"UTF8";
1611 QByteArray usr = user.toLocal8Bit();
1612 QByteArray pass = password.toLocal8Bit();
1617 ba.reserve(usr.length() + pass.length() + enc.length() + role.length() + 9);
1618 ba.append(
char(isc_dpb_version1));
1619 ba.append(
char(isc_dpb_user_name));
1620 ba.append(
char(usr.length()));
1621 ba.append(usr.constData(), usr.length());
1622 ba.append(
char(isc_dpb_password));
1623 ba.append(
char(pass.length()));
1624 ba.append(pass.constData(), pass.length());
1625 ba.append(
char(isc_dpb_lc_ctype));
1626 ba.append(
char(enc.length()));
1627 ba.append(enc.constData(), enc.length());
1629 if (!role.isEmpty()) {
1630 ba.append(
char(isc_dpb_sql_role_name));
1631 ba.append(
char(role.length()));
1632 ba.append(role.constData(), role.length());
1637 portString = QStringLiteral(
"/%1").arg(port);
1640 if (!host.isEmpty())
1641 ldb += host + portString + u':';
1643 isc_attach_database(d->status, 0,
const_cast<
char *>(ldb.toLocal8Bit().constData()),
1644 &d->ibase, ba.size(), ba.constData());
1645 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Error opening database"),
1646 QSqlError::ConnectionError)) {
1652 setOpenError(
false);
1653#if (FB_API_VER >= 40
)
1654 std::call_once(initTZMappingFlag, [d](){ d->initTZMappingCache(); });
1659void QIBaseDriver::close()
1664 if (d->eventBuffers.size()) {
1665 ISC_STATUS status[20];
1666 QMap<QString, QIBaseEventBuffer *>::const_iterator i;
1667 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1668 QIBaseEventBuffer *eBuffer = i.value();
1669 eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
1670 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1671 qFreeEventBuffer(eBuffer);
1673 d->eventBuffers.clear();
1676 isc_detach_database(d->status, &d->ibase);
1679 setOpenError(
false);
1683QSqlResult *QIBaseDriver::createResult()
const
1685 return new QIBaseResult(
this);
1688bool QIBaseDriver::beginTransaction()
1691 if (!isOpen() || isOpenError())
1696 isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
1697 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Could not start transaction"),
1698 QSqlError::TransactionError);
1701bool QIBaseDriver::commitTransaction()
1704 if (!isOpen() || isOpenError())
1709 isc_commit_transaction(d->status, &d->trans);
1711 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Unable to commit transaction"),
1712 QSqlError::TransactionError);
1715bool QIBaseDriver::rollbackTransaction()
1718 if (!isOpen() || isOpenError())
1723 isc_rollback_transaction(d->status, &d->trans);
1725 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Unable to rollback transaction"),
1726 QSqlError::TransactionError);
1729QStringList QIBaseDriver::tables(QSql::TableType type)
const
1737 if (type == QSql::SystemTables) {
1738 typeFilter +=
"RDB$SYSTEM_FLAG != 0"_L1;
1739 }
else if (type == (QSql::SystemTables | QSql::Views)) {
1740 typeFilter +=
"RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"_L1;
1742 if (!(type & QSql::SystemTables))
1743 typeFilter +=
"RDB$SYSTEM_FLAG = 0 AND "_L1;
1744 if (!(type & QSql::Views))
1745 typeFilter +=
"RDB$VIEW_BLR IS NULL AND "_L1;
1746 if (!(type & QSql::Tables))
1747 typeFilter +=
"RDB$VIEW_BLR IS NOT NULL AND "_L1;
1748 if (!typeFilter.isEmpty())
1751 if (!typeFilter.isEmpty())
1752 typeFilter.prepend(
"where "_L1);
1754 QSqlQuery q(createResult());
1755 q.setForwardOnly(
true);
1756 if (!q.exec(
"select rdb$relation_name from rdb$relations "_L1 + typeFilter))
1759 res << q.value(0).toString().simplified();
1764QSqlRecord QIBaseDriver::record(
const QString& tablename)
const
1770 const QString table = stripDelimiters(tablename, QSqlDriver::TableName);
1771 QSqlQuery q(createResult());
1772 q.setForwardOnly(
true);
1773 q.exec(
"SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
1774 "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
1775 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1776 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1777 "AND a.RDB$RELATION_NAME = '"_L1 + table +
"' "
1778 "ORDER BY a.RDB$FIELD_POSITION"_L1);
1781 int type = q.value(1).toInt();
1782 bool hasScale = q.value(3).toInt() < 0;
1783 QSqlField f(q.value(0).toString().simplified(), QMetaType(qIBaseTypeName(type, hasScale)), tablename);
1785 f.setLength(q.value(4).toInt());
1786 f.setPrecision(qAbs(q.value(3).toInt()));
1788 f.setLength(q.value(2).toInt());
1791 f.setRequired(q.value(5).toInt() > 0);
1798QSqlIndex QIBaseDriver::primaryIndex(
const QString &table)
const
1800 QSqlIndex index(table);
1804 const QString tablename = stripDelimiters(table, QSqlDriver::TableName);
1805 QSqlQuery q(createResult());
1806 q.setForwardOnly(
true);
1807 q.exec(
"SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
1808 "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
1809 "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
1810 "AND a.RDB$RELATION_NAME = '"_L1 + tablename +
1811 " 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
1812 "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
1813 "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
1814 "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
1815 "ORDER BY b.RDB$FIELD_POSITION"_L1);
1818 QSqlField field(q.value(1).toString().simplified(),
1819 QMetaType(qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0)),
1821 index.append(field);
1822 index.setName(q.value(0).toString());
1828QString QIBaseDriver::formatValue(
const QSqlField &field,
bool trimStrings)
const
1830 switch (field.metaType().id()) {
1831 case QMetaType::QDateTime: {
1832 QDateTime datetime = field.value().toDateTime();
1833 if (datetime.isValid())
1834 return u'\'' + QString::number(datetime.date().year()) + u'-' +
1835 QString::number(datetime.date().month()) + u'-' +
1836 QString::number(datetime.date().day()) + u' ' +
1837 QString::number(datetime.time().hour()) + u':' +
1838 QString::number(datetime.time().minute()) + u':' +
1839 QString::number(datetime.time().second()) + u'.' +
1840 QString::number(datetime.time().msec()).rightJustified(3, u'0',
true) +
1845 case QMetaType::QTime: {
1846 QTime time = field.value().toTime();
1848 return u'\'' + QString::number(time.hour()) + u':' +
1849 QString::number(time.minute()) + u':' +
1850 QString::number(time.second()) + u'.' +
1851 QString::number(time.msec()).rightJustified(3, u'0',
true) +
1856 case QMetaType::QDate: {
1857 QDate date = field.value().toDate();
1859 return u'\'' + QString::number(date.year()) + u'-' +
1860 QString::number(date.month()) + u'-' +
1861 QString::number(date.day()) + u'\'';
1866 return QSqlDriver::formatValue(field, trimStrings);
1870QVariant QIBaseDriver::handle()
const
1872 Q_D(
const QIBaseDriver);
1873 return QVariant(QMetaType::fromType<isc_db_handle>(), &d->ibase);
1882 memcpy(result, updated, length);
1884 QIBaseDriver *driver = qBufferDriverMap()->value(result);
1890 QMetaObject::invokeMethod(driver,
"qHandleEventNotification", Qt::QueuedConnection, Q_ARG(
void *,
reinterpret_cast<
void *>(result)));
1895bool QIBaseDriver::subscribeToNotification(
const QString &name)
1899 qCWarning(lcIbase,
"QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
1903 if (d->eventBuffers.contains(name)) {
1904 qCWarning(lcIbase,
"QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%ls'.",
1905 qUtf16Printable(name));
1909 QIBaseEventBuffer *eBuffer =
new QIBaseEventBuffer;
1910 eBuffer->subscriptionState = QIBaseEventBuffer::Starting;
1911 eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer,
1912 &eBuffer->resultBuffer,
1914 name.toLocal8Bit().constData());
1917 qBufferDriverMap()->insert(eBuffer->resultBuffer,
this);
1920 d->eventBuffers.insert(name, eBuffer);
1922 ISC_STATUS status[20];
1923 isc_que_events(status,
1926 eBuffer->bufferLength,
1927 eBuffer->eventBuffer,
1928 reinterpret_cast<ISC_EVENT_CALLBACK>(
reinterpret_cast<
void *>
1930 eBuffer->resultBuffer);
1932 if (status[0] == 1 && status[1]) {
1933 setLastError(QSqlError(tr(
"Could not subscribe to event notifications for %1.").arg(name)));
1934 d->eventBuffers.remove(name);
1935 qFreeEventBuffer(eBuffer);
1942bool QIBaseDriver::unsubscribeFromNotification(
const QString &name)
1946 qCWarning(lcIbase,
"QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
1950 if (!d->eventBuffers.contains(name)) {
1951 qCWarning(lcIbase,
"QIBaseDriver::QIBaseSubscriptionState not subscribed to '%ls'.",
1952 qUtf16Printable(name));
1956 QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name);
1957 ISC_STATUS status[20];
1958 eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
1959 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1961 if (status[0] == 1 && status[1]) {
1962 setLastError(QSqlError(tr(
"Could not unsubscribe from event notifications for %1.").arg(name)));
1966 d->eventBuffers.remove(name);
1967 qFreeEventBuffer(eBuffer);
1972QStringList QIBaseDriver::subscribedToNotifications()
const
1974 Q_D(
const QIBaseDriver);
1975 return QStringList(d->eventBuffers.keys());
1978void QIBaseDriver::qHandleEventNotification(
void *updatedResultBuffer)
1981 QMap<QString, QIBaseEventBuffer *>::const_iterator i;
1982 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1983 QIBaseEventBuffer* eBuffer = i.value();
1984 if (
reinterpret_cast<
void *>(eBuffer->resultBuffer) != updatedResultBuffer)
1987 ISC_ULONG counts[20];
1988 memset(counts, 0,
sizeof(counts));
1989 isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer);
1992 if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed)
1993 emit notification(i.key(), QSqlDriver::UnknownSource, QVariant());
1994 else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting)
1995 eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed;
1997 ISC_STATUS status[20];
1998 isc_que_events(status,
2001 eBuffer->bufferLength,
2002 eBuffer->eventBuffer,
2003 reinterpret_cast<ISC_EVENT_CALLBACK>(
reinterpret_cast<
void *>
2005 eBuffer->resultBuffer);
2006 if (Q_UNLIKELY(status[0] == 1 && status[1])) {
2007 qCritical(
"QIBaseDriver::qHandleEventNotification: could not resubscribe to '%ls'",
2008 qUtf16Printable(i.key()));
2016QString QIBaseDriver::escapeIdentifier(
const QString &identifier, IdentifierType)
const
2018 QString res = identifier;
2019 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
2020 res.replace(u'"',
"\"\""_L1);
2021 res.replace(u'.',
"\".\""_L1);
2022 res = u'"' + res + u'"';
2027int QIBaseDriver::maximumIdentifierLength(IdentifierType type)
const
2035#include "moc_qsql_ibase_p.cpp"
QMap< QString, QIBaseEventBuffer * > eventBuffers
QVariant fetchArray(int pos, ISC_QUAD *arr)
bool isError(const char *msg, QSqlError::ErrorType typ=QSqlError::UnknownError)
bool writeBlob(qsizetype iPos, const QByteArray &ba)
QVariant fetchBlob(ISC_QUAD *bId)
bool writeArray(qsizetype i, const QList< QVariant > &list)
void setWithScale(const QVariant &val, int scale, char *data)
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool gotoNext(QSqlCachedResult::ValueCache &row, int rowIdx) override
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
QVariant applyScale(T val, int scale) const
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
int size() override
Returns the size of the SELECT result, or -1 if it cannot be determined or if the query is not a SELE...
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
static QString numberToHighPrecision(T val, int scale)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
QMap< void *, QIBaseDriver * > QIBaseBufferDriverMap
static void delDA(XSQLDA *&sqlda)
static void qFreeEventBuffer(QIBaseEventBuffer *eBuffer)
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)
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