6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qendian.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qtimezone.h>
10#include <QtCore/qdeadlinetimer.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qlist.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qmap.h>
15#include <QtCore/qmutex.h>
16#include <QtCore/qvariant.h>
17#include <QtCore/qvarlengtharray.h>
18#include <QtSql/qsqlerror.h>
19#include <QtSql/qsqlfield.h>
20#include <QtSql/qsqlindex.h>
21#include <QtSql/qsqlquery.h>
22#include <QtSql/private/qsqlcachedresult_p.h>
23#include <QtSql/private/qsqldriver_p.h>
24#include <QtSql/private/qsqlnumeric_p.h>
33using namespace Qt::StringLiterals;
35#define FBVERSION SQL_DIALECT_V6
37#ifndef SQLDA_CURRENT_VERSION
38#define SQLDA_CURRENT_VERSION SQLDA_VERSION1
42#ifndef blr_boolean_dtype
43#define blr_boolean_dtype blr_bool
47#if defined(QSQL_HAS_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;
412 bool bindScaled(
const QVariant &val,
int scale,
char *data, qsizetype paramIndex)
416 if (!QtPrivate::sqlScaledFromVariant(val, scale, &v, &detail)) {
417 setLastError(QSqlError(QCoreApplication::translate(
"QIBaseResult",
418 "Unable to bind parameter %1")
419 .arg(paramIndex + 1),
420 detail, QSqlError::StatementError));
423#ifdef IBASE_INT128_SUPPORTED
424 if constexpr (std::is_same_v<T, qinternalint128>) {
426 quint64 low = 0, high = 0;
427 QtPrivate::sqlInt128ToWords(v, &low, &high);
428 qToUnaligned(low, data);
429 qToUnaligned(high, data +
sizeof(quint64));
433 qToUnaligned(v, data);
440 Q_DECLARE_PUBLIC(QIBaseResult)
454 if (!getIBaseError(imsg, status, sqlcode))
457 q->setLastError(QSqlError(QCoreApplication::translate(
"QIBaseResult", msg),
459 sqlcode != -1 ? QString::number(sqlcode) : QString()));
486 : QSqlCachedResultPrivate(q, drv),
490 ibase(drv_d_func()->ibase),
501 if (!localTransaction)
505 isc_dsql_free_statement(status, &stmt, DSQL_drop);
513 cachedRecord.clear();
519 isc_blob_handle handle = 0;
520 ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[iPos].sqldata;
521 isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
522 if (!isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to create BLOB"),
523 QSqlError::StatementError)) {
525 while (i < ba.size()) {
526 isc_put_segment(status, &handle, qMin(ba.size() - i, QIBaseChunkSize),
528 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to write BLOB")))
530 i += qMin(ba.size() - i, QIBaseChunkSize);
533 isc_close_blob(status, &handle);
539 isc_blob_handle handle = 0;
541 isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
542 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to open BLOB"),
543 QSqlError::StatementError))
546 unsigned short len = 0;
547 constexpr auto chunkSize = QIBaseChunkSize;
548 QByteArray ba(chunkSize, Qt::Uninitialized);
550 while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
552 ba.resize(read + chunkSize);
556 bool isErr = (status[1] == isc_segstr_eof ?
false :
557 isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
558 "Unable to read BLOB"),
559 QSqlError::StatementError));
561 isc_close_blob(status, &handle);
575 for (
int i = 0; i < count; ++i) {
576 res.append(*(T*)(*buf));
582static const char *
readArrayBuffer(QList<QVariant>& list,
const char *buffer,
short curDim,
583 const short *numElements, ISC_ARRAY_DESC *arrayDesc)
585 const short dim = arrayDesc->array_desc_dimensions - 1;
586 const unsigned char dataType = arrayDesc->array_desc_dtype;
587 QList<QVariant> valList;
588 unsigned short strLen = arrayDesc->array_desc_length;
591 for(
int i = 0; i < numElements[curDim]; ++i)
592 buffer = readArrayBuffer(list, buffer, curDim + 1, numElements, arrayDesc);
602 for (
int i = 0; i < numElements[dim]; ++i) {
603 for(o = 0; o < strLen && buffer[o]!=0; ++o )
606 valList.append(QString::fromUtf8(buffer, o));
611 valList = toList<
int>(&buffer, numElements[dim]);
614 valList = toList<
short>(&buffer, numElements[dim]);
617 valList = toList<qint64>(&buffer, numElements[dim]);
620 valList = toList<
float>(&buffer, numElements[dim]);
623 valList = toList<
double>(&buffer, numElements[dim]);
626 for(
int i = 0; i < numElements[dim]; ++i) {
627 valList.append(fromTimeStamp(buffer));
628 buffer +=
sizeof(ISC_TIMESTAMP);
631#if (FB_API_VER >= 40
)
632 case blr_timestamp_tz:
633 for (
int i = 0; i < numElements[dim]; ++i) {
634 valList.append(fromTimeStampTz(buffer));
635 buffer +=
sizeof(ISC_TIMESTAMP_TZ);
640 for (
int i = 0; i < numElements[dim]; ++i) {
641 valList.append(fromTime(buffer));
642 buffer +=
sizeof(ISC_TIME);
646 for(
int i = 0; i < numElements[dim]; ++i) {
647 valList.append(fromDate(buffer));
648 buffer +=
sizeof(ISC_DATE);
652 valList = toList<
bool>(&buffer, numElements[dim]);
657 list.append(valList);
665 QList<QVariant> list;
671 const XSQLVAR &sqlvar = sqlda->sqlvar[pos];
672 const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length);
673 const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length);
675 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
676 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not find array"),
677 QSqlError::StatementError))
682 short dimensions = desc.array_desc_dimensions;
683 QVarLengthArray<
short> numElements(dimensions);
685 for(
int i = 0; i < dimensions; ++i) {
686 short subArraySize = (desc.array_desc_bounds[i].array_bound_upper -
687 desc.array_desc_bounds[i].array_bound_lower + 1);
688 numElements[i] = subArraySize;
689 arraySize = subArraySize * arraySize;
694
695
696 if (desc.array_desc_dtype == blr_varying
697 || desc.array_desc_dtype == blr_varying2) {
698 desc.array_desc_length += 2;
699 bufLen = desc.array_desc_length * arraySize *
sizeof(
short);
701 bufLen = desc.array_desc_length * arraySize;
704 QByteArray ba(bufLen, Qt::Uninitialized);
705 isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen);
706 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get array data"),
707 QSqlError::StatementError))
710 readArrayBuffer(list, ba.constData(), 0, numElements.constData(), &desc);
712 return QVariant(list);
716static char*
fillList(
char *buffer,
const QList<QVariant> &list, T* =
nullptr)
718 for (
const auto &elem : list) {
719 T val = qvariant_cast<T>(elem);
720 memcpy(buffer, &val,
sizeof(T));
727char*
fillList<
float>(
char *buffer,
const QList<QVariant> &list,
float*)
729 for (
const auto &elem : list) {
730 double val = qvariant_cast<
double>(elem);
731 float val2 = (
float)val;
732 memcpy(buffer, &val2,
sizeof(
float));
733 buffer +=
sizeof(
float);
739 short buflen,
bool varying,
bool array)
741 QByteArray str = string.toUtf8();
743 short tmpBuflen = buflen;
744 if (str.length() < buflen)
745 buflen = str.length();
747 memcpy(buffer, str.constData(), buflen);
748 memset(buffer + buflen, 0, tmpBuflen - buflen);
750 *(
short*)buffer = buflen;
751 memcpy(buffer +
sizeof(
short), str.constData(), buflen);
755 str = str.leftJustified(buflen,
' ',
true);
756 memcpy(buffer, str.constData(), buflen);
763 QMetaType::Type type,
short curDim, ISC_ARRAY_DESC *arrayDesc,
766 ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
767 short dim = arrayDesc->array_desc_dimensions - 1;
769 int elements = (bounds[curDim].array_bound_upper -
770 bounds[curDim].array_bound_lower + 1);
772 if (list.size() != elements) {
773 error = QCoreApplication::translate(
775 "Array size mismatch. Field name: %3, expected size: %1. Supplied size: %2")
776 .arg(elements).arg(list.size());
781 for (
const auto &elem : list) {
783 if (elem.typeId() != QMetaType::QVariantList) {
784 error = QCoreApplication::translate(
786 "Array dimensions mismatch. Field name: %1");
790 buffer = createArrayBuffer(buffer, elem.toList(), type, curDim + 1,
798 case QMetaType::UInt:
799 if (arrayDesc->array_desc_dtype == blr_short)
800 buffer = fillList<
short>(buffer, list);
802 buffer = fillList<
int>(buffer, list);
804 case QMetaType::Double:
805 if (arrayDesc->array_desc_dtype == blr_float)
806 buffer = fillList<
float>(buffer, list,
static_cast<
float *>(0));
808 buffer = fillList<
double>(buffer, list);
810 case QMetaType::LongLong:
811 buffer = fillList<qint64>(buffer, list);
813 case QMetaType::ULongLong:
814 buffer = fillList<quint64>(buffer, list);
816 case QMetaType::QString:
817 for (
const auto &elem : list)
818 buffer = qFillBufferWithString(buffer, elem.toString(),
819 arrayDesc->array_desc_length,
820 arrayDesc->array_desc_dtype == blr_varying,
823 case QMetaType::QDate:
824 for (
const auto &elem : list) {
825 *((ISC_DATE*)buffer) = toDate(elem.toDate());
826 buffer +=
sizeof(ISC_DATE);
829 case QMetaType::QTime:
830 for (
const auto &elem : list) {
831 *((ISC_TIME*)buffer) = toTime(elem.toTime());
832 buffer +=
sizeof(ISC_TIME);
835 case QMetaType::QDateTime:
836 for (
const auto &elem : list) {
837 switch (arrayDesc->array_desc_dtype) {
839 *((ISC_TIMESTAMP*)buffer) = toTimeStamp(elem.toDateTime());
840 buffer +=
sizeof(ISC_TIMESTAMP);
842#if (FB_API_VER >= 40
)
843 case blr_timestamp_tz:
844 *((ISC_TIMESTAMP_TZ*)buffer) = toTimeStampTz(elem.toDateTime());
845 buffer +=
sizeof(ISC_TIMESTAMP_TZ);
853 case QMetaType::Bool:
854 buffer = fillList<
bool>(buffer, list);
869 XSQLVAR &sqlvar = inda->sqlvar[column];
870 ISC_QUAD *arrayId = (ISC_QUAD*) sqlvar.sqldata;
871 const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length);
872 const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length);
874 isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc);
875 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not find array"),
876 QSqlError::StatementError))
882 short dimensions = desc.array_desc_dimensions;
883 for(
int i = 0; i < dimensions; ++i) {
884 arraySize *= (desc.array_desc_bounds[i].array_bound_upper -
885 desc.array_desc_bounds[i].array_bound_lower + 1);
889
890
891 if (desc.array_desc_dtype == blr_varying ||
892 desc.array_desc_dtype == blr_varying2)
893 desc.array_desc_length += 2;
895 bufLen = desc.array_desc_length * arraySize;
896 QByteArray ba(bufLen, Qt::Uninitialized);
898 if (list.size() > arraySize) {
899 error = QCoreApplication::translate(
901 "Array size mismatch: size of %1 is %2, size of provided list is %3");
902 error = error.arg(QLatin1StringView(sqlname)).arg(arraySize).arg(list.size());
903 q->setLastError(QSqlError(error, {}, QSqlError::StatementError));
907 if (!createArrayBuffer(ba.data(), list,
908 qIBaseTypeName(desc.array_desc_dtype, sqlvar.sqlscale < 0),
910 q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), {},
911 QSqlError::StatementError));
916 if (desc.array_desc_dtype == blr_varying
917 || desc.array_desc_dtype == blr_varying2)
918 desc.array_desc_length -= 2;
920 isc_array_put_slice(status, &ibase, &trans, arrayId, &desc, ba.data(), &bufLen);
928 char qType = isc_info_sql_stmt_type;
929 isc_dsql_sql_info(status, &stmt, 1, &qType,
sizeof(acBuffer), acBuffer);
930 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get query info"),
931 QSqlError::StatementError))
933 int iLength = isc_vax_integer(&acBuffer[1], 2);
934 queryType = isc_vax_integer(&acBuffer[3], iLength);
935 return (queryType == isc_info_sql_stmt_select || queryType == isc_info_sql_stmt_exec_procedure);
942 if (drv_d_func()->trans) {
944 trans = drv_d_func()->trans;
949 isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
950 if (isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not start transaction"),
951 QSqlError::TransactionError))
967 isc_commit_transaction(status, &trans);
969 return !isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to commit transaction"),
970 QSqlError::TransactionError);
976 : QSqlCachedResult(*
new QIBaseResultPrivate(
this, db))
984 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
988 setAt(QSql::BeforeFirstRow);
991 if (d->sqlda == (XSQLDA*)0) {
992 qCWarning(lcIbase) <<
"QIOBaseResult: createDA(): failed to allocate memory";
997 if (d->inda == (XSQLDA*)0){
998 qCWarning(lcIbase) <<
"QIOBaseResult: createDA(): failed to allocate memory";
1002 if (!d->transaction())
1005 isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
1006 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not allocate statement"),
1007 QSqlError::StatementError))
1009 isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0,
1010 const_cast<
char*>(query.toUtf8().constData()),
FBVERSION, d->sqlda);
1011 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not prepare statement"),
1012 QSqlError::StatementError))
1015 isc_dsql_describe_bind(d->status, &d->stmt,
FBVERSION, d->inda);
1016 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
1017 "Could not describe input statement"), QSqlError::StatementError))
1019 if (d->inda->sqld > d->inda->sqln) {
1020 enlargeDA(d->inda, d->inda->sqld);
1021 if (d->inda == (XSQLDA*)0) {
1022 qCWarning(lcIbase) <<
"QIOBaseResult: enlargeDA(): failed to allocate memory";
1026 isc_dsql_describe_bind(d->status, &d->stmt,
FBVERSION, d->inda);
1027 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
1028 "Could not describe input statement"), QSqlError::StatementError))
1032 if (d->sqlda->sqld > d->sqlda->sqln) {
1034 enlargeDA(d->sqlda, d->sqlda->sqld);
1035 if (d->sqlda == (XSQLDA*)0) {
1036 qCWarning(lcIbase) <<
"QIOBaseResult: enlargeDA(): failed to allocate memory";
1040 isc_dsql_describe(d->status, &d->stmt,
FBVERSION, d->sqlda);
1041 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not describe statement"),
1042 QSqlError::StatementError))
1047 setSelect(d->isSelect());
1064 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
1067 setAt(QSql::BeforeFirstRow);
1070 const QList<QVariant> &values = boundValues();
1071 if (values.count() > d->inda->sqld) {
1072 qCWarning(lcIbase) <<
"QIBaseResult::exec: Parameter mismatch, expected"_L1
1073 << d->inda->sqld <<
", got"_L1 << values.count()
1077 for (qsizetype para = 0; para < values.count(); ++para) {
1078 const XSQLVAR &sqlvar = d->inda->sqlvar[para];
1079 if (!sqlvar.sqldata)
1082 const QVariant &val = values[para];
1083 if (sqlvar.sqltype & 1) {
1084 if (QSqlResultPrivate::isVariantNull(val)) {
1086 *(sqlvar.sqlind) = -1;
1093 *(sqlvar.sqlind) = 0;
1095 if (QSqlResultPrivate::isVariantNull(val)) {
1096 qCWarning(lcIbase) <<
"QIBaseResult::exec: Null value replaced by default (zero)"_L1
1097 <<
"value for type of column"_L1 << sqlvar.ownname
1098 <<
", which is not nullable."_L1;
1101 const auto sqltype = sqlvar.sqltype & ~1;
1104 if (!bindScaled<qint64>(val, sqlvar.sqlscale, sqlvar.sqldata, para))
1107#ifdef IBASE_INT128_SUPPORTED
1109 if (!bindScaled<qinternalint128>(val, sqlvar.sqlscale, sqlvar.sqldata, para))
1114 if (sqlvar.sqllen == 4) {
1115 if (!bindScaled<qint32>(val, sqlvar.sqlscale, sqlvar.sqldata, para))
1118 if (!bindScaled<qint64>(val, 0, sqlvar.sqldata, para))
1123 if (!bindScaled<qint16>(val, sqlvar.sqlscale, sqlvar.sqldata, para))
1127 *((
float*)sqlvar.sqldata) = val.toFloat();
1130 *((
double*)sqlvar.sqldata) = val.toDouble();
1133 *((ISC_TIMESTAMP*)sqlvar.sqldata) = toTimeStamp(val.toDateTime());
1135#if (FB_API_VER >= 40
)
1136 case SQL_TIMESTAMP_TZ:
1137 *((ISC_TIMESTAMP_TZ*)sqlvar.sqldata) = toTimeStampTz(val.toDateTime());
1141 *((ISC_TIME*)sqlvar.sqldata) = toTime(val.toTime());
1144 *((ISC_DATE*)sqlvar.sqldata) = toDate(val.toDate());
1148 qFillBufferWithString(sqlvar.sqldata, val.toString(), sqlvar.sqllen,
1149 sqltype == SQL_VARYING,
false);
1152 ok &= d->writeBlob(para, val.toByteArray());
1155 ok &= d->writeArray(para, val.toList());
1158 *((
bool*)sqlvar.sqldata) = val.toBool();
1161 qCWarning(lcIbase,
"QIBaseResult::exec: Unknown datatype %d",
1169 isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
1172 if (getIBaseError(imsg, d->status, sqlcode) && sqlcode != -501) {
1173 setLastError(QSqlError(QCoreApplication::translate(
"QIBaseResult",
"Unable to close statement"),
1174 imsg, QSqlError::UnknownError,
1175 sqlcode != -1 ? QString::number(sqlcode) : QString()));
1178 if (colCount() && d->queryType != isc_info_sql_stmt_exec_procedure) {
1181 if (d->queryType == isc_info_sql_stmt_exec_procedure)
1182 isc_dsql_execute2(d->status, &d->trans, &d->stmt,
FBVERSION, d->inda, d->sqlda);
1184 isc_dsql_execute(d->status, &d->trans, &d->stmt,
FBVERSION, d->inda);
1185 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Unable to execute query")))
1189 if (d->queryType == isc_info_sql_stmt_exec_procedure && d->sqlda && d->sqlda->sqld == 0)
1193 init(d->sqlda->sqld);
1206 if (!prepare(query))
1214 ISC_STATUS stat = 0;
1218 if (d->queryType == isc_info_sql_stmt_exec_procedure) {
1224 stat = isc_dsql_fetch(d->status, &d->stmt,
FBVERSION, d->sqlda);
1229 setAt(QSql::AfterLastRow);
1232 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not fetch next item"),
1233 QSqlError::StatementError))
1238 for (
int i = 0; i < d->sqlda->sqld; ++i) {
1239 int idx = rowIdx + i;
1240 const XSQLVAR &sqlvar = d->sqlda->sqlvar[i];
1242 if ((sqlvar.sqltype & 1) && *sqlvar.sqlind) {
1245 v.convert(QMetaType(qIBaseTypeName2(sqlvar.sqltype, sqlvar.sqlscale < 0)));
1246 if (v.userType() == QMetaType::Double) {
1247 switch(numericalPrecisionPolicy()) {
1248 case QSql::LowPrecisionInt32:
1249 v.convert(QMetaType(QMetaType::Int));
1251 case QSql::LowPrecisionInt64:
1252 v.convert(QMetaType(QMetaType::LongLong));
1254 case QSql::HighPrecision:
1255 v.convert(QMetaType(QMetaType::QString));
1257 case QSql::LowPrecisionDouble:
1266 const char *buf = sqlvar.sqldata;
1267 int size = sqlvar.sqllen;
1269 const auto sqltype = sqlvar.sqltype & ~1;
1273 row[idx] = QString::fromUtf8(buf +
sizeof(
short), *(
short*)buf);
1276 Q_ASSERT(sqlvar.sqllen ==
sizeof(qint64));
1277 const auto val = *(qint64 *)buf;
1278 const auto scale = sqlvar.sqlscale;
1279 row[idx] = QtPrivate::sqlApplyScale(val, scale, numericalPrecisionPolicy());
1282#ifdef IBASE_INT128_SUPPORTED
1284 Q_ASSERT(sqlvar.sqllen ==
sizeof(qinternalint128));
1285 const qinternalint128 val128 = QtPrivate::sqlInt128FromWords(
1286 qFromUnaligned<quint64>(buf), qFromUnaligned<quint64>(buf +
sizeof(quint64)));
1287 const auto scale = sqlvar.sqlscale;
1288 const auto policy = numericalPrecisionPolicy();
1289 bool overflow =
false;
1290 row[idx] = QtPrivate::sqlApplyScale(val128, scale, policy, &overflow);
1294 QtPrivate::sqlWarnLowPrecisionOverflow(
1295 lcIbase, QtPrivate::sqlScaledToString(val128, scale), policy);
1301 if (sqlvar.sqllen == 4) {
1302 const auto val = *(qint32 *)buf;
1303 const auto scale = sqlvar.sqlscale;
1304 row[idx] = QtPrivate::sqlApplyScale(val, scale, numericalPrecisionPolicy());
1306 row[idx] = QVariant(*(qint64*)buf);
1309 const auto val = *(
short *)buf;
1310 const auto scale = sqlvar.sqlscale;
1311 row[idx] = QtPrivate::sqlApplyScale(val, scale, numericalPrecisionPolicy());
1315 row[idx] = QVariant(
double((*(
float*)buf)));
1318 row[idx] = QVariant(*(
double*)buf);
1321 row[idx] = fromTimeStamp(buf);
1324 row[idx] = fromTime(buf);
1327 row[idx] = fromDate(buf);
1330 row[idx] = QString::fromUtf8(buf, size);
1333 row[idx] = d->fetchBlob((ISC_QUAD*)buf);
1336 row[idx] = d->fetchArray(i, (ISC_QUAD*)buf);
1339 row[idx] = QVariant(
bool((*(
bool*)buf)));
1341#if (FB_API_VER >= 40
)
1342 case SQL_TIMESTAMP_TZ:
1343 row[idx] = fromTimeStampTz(buf);
1348 qCWarning(lcIbase,
"gotoNext: unknown sqltype: %d", sqltype);
1349 row[idx] = QVariant();
1362 static char sizeInfo[] = {isc_info_sql_records};
1366 if (!isActive() || !isSelect())
1373 isc_dsql_sql_info(d->status, &d->stmt,
sizeof(sizeInfo), sizeInfo,
sizeof(buf), buf);
1376 for(
int i = 0; i < 66; ++i)
1377 qDebug() << QString::number(buf[i]);
1379 for (
char* c = buf + 3; *c != isc_info_end; ) {
1381 len = isc_vax_integer(c, 2);
1383 val = isc_vax_integer(c, len);
1385 qDebug() <<
"size" << val;
1386 if (ct == isc_info_req_select_count)
1392 unsigned int i, result_size;
1393 if (buf[0] == isc_info_sql_records) {
1395 result_size = isc_vax_integer(&buf[1],2);
1396 while (buf[i] != isc_info_end && i < result_size) {
1397 len = (
short)isc_vax_integer(&buf[i+1],2);
1398 if (buf[i] == isc_info_req_select_count)
1399 return (isc_vax_integer(&buf[i+3],len));
1411 static char acCountInfo[] = {isc_info_sql_records};
1413 bool bIsProcedure =
false;
1415 switch (d->queryType) {
1416 case isc_info_sql_stmt_select:
1417 cCountType = isc_info_req_select_count;
1419 case isc_info_sql_stmt_update:
1420 cCountType = isc_info_req_update_count;
1422 case isc_info_sql_stmt_delete:
1423 cCountType = isc_info_req_delete_count;
1425 case isc_info_sql_stmt_insert:
1426 cCountType = isc_info_req_insert_count;
1428 case isc_info_sql_stmt_exec_procedure:
1429 bIsProcedure =
true;
1432 qCWarning(lcIbase) <<
"numRowsAffected: Unknown statement type (" << d->queryType <<
")";
1438 isc_dsql_sql_info(d->status, &d->stmt,
sizeof(acCountInfo), acCountInfo,
sizeof(acBuffer), acBuffer);
1439 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseResult",
"Could not get statement info"),
1440 QSqlError::StatementError))
1442 for (
char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; ) {
1443 char cType = *pcBuf++;
1444 short sLength = isc_vax_integer (pcBuf, 2);
1446 int iValue = isc_vax_integer (pcBuf, sLength);
1449 if (cType == isc_info_req_insert_count || cType == isc_info_req_update_count
1450 || cType == isc_info_req_delete_count) {
1455 }
else if (cType == cCountType) {
1465 Q_D(
const QIBaseResult);
1466 if (!isActive() || !d->sqlda)
1469 if (!d->cachedRecord.isEmpty())
1470 return d->cachedRecord;
1472 for (
int i = 0; i < d->sqlda->sqld; ++i) {
1473 const XSQLVAR &v = d->sqlda->sqlvar[i];
1474 QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
1475 QMetaType(qIBaseTypeName2(v.sqltype, v.sqlscale < 0)),
1476 QString::fromLatin1(v.relname, v.relname_length));
1477 f.setLength(v.sqllen);
1478 f.setPrecision(qAbs(v.sqlscale));
1479 f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
1480 if (v.sqlscale < 0) {
1481 QSqlQuery q(driver()->createResult());
1482 q.setForwardOnly(
true);
1483 q.exec(
"select b.RDB$FIELD_PRECISION, b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, a.RDB$NULL_FLAG "
1484 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1485 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1486 "AND a.RDB$RELATION_NAME = '"_L1 + QString::fromLatin1(v.relname, v.relname_length) +
"' "
1487 "AND a.RDB$FIELD_NAME = '"_L1 + QString::fromLatin1(v.sqlname, v.sqlname_length) +
"' "_L1);
1489 if (v.sqlscale < 0) {
1490 f.setLength(q.value(0).toInt());
1491 f.setPrecision(qAbs(q.value(1).toInt()));
1493 f.setLength(q.value(2).toInt());
1496 f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
1499 d->cachedRecord.append(f);
1501 return d->cachedRecord;
1506 Q_D(
const QIBaseResult);
1507 return QVariant(QMetaType::fromType<isc_stmt_handle>(), &d->stmt);
1512QIBaseDriver::QIBaseDriver(QObject * parent)
1513 : QSqlDriver(*
new QIBaseDriverPrivate, parent)
1517QIBaseDriver::QIBaseDriver(isc_db_handle connection, QObject *parent)
1518 : QSqlDriver(*
new QIBaseDriverPrivate, parent)
1521 d->ibase = connection;
1523 setOpenError(
false);
1526QIBaseDriver::~QIBaseDriver()
1530bool QIBaseDriver::hasFeature(DriverFeature f)
const
1534 case NamedPlaceholders:
1536 case BatchOperations:
1539 case MultipleResultSets:
1543 case PreparedQueries:
1544 case PositionalPlaceholders:
1547 case EventNotifications:
1548 case LowPrecisionNumbers:
1554bool QIBaseDriver::open(
const QString &db,
1555 const QString &user,
1556 const QString &password,
1557 const QString &host,
1559 const QString &connOpts)
1565 const auto opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
1568 for (
const auto &opt : opts) {
1569 const auto tmp(opt.trimmed());
1571 if ((idx = tmp.indexOf(u'=')) != -1) {
1572 const auto val = tmp.mid(idx + 1).trimmed();
1573 const auto opt = tmp.left(idx).trimmed().toString();
1574 if (opt.toUpper() ==
"ISC_DPB_SQL_ROLE_NAME"_L1) {
1575 role = val.toLocal8Bit();
1581 QByteArray enc =
"UTF8";
1582 QByteArray usr = user.toLocal8Bit();
1583 QByteArray pass = password.toLocal8Bit();
1588 ba.reserve(usr.length() + pass.length() + enc.length() + role.length() + 9);
1589 ba.append(
char(isc_dpb_version1));
1590 ba.append(
char(isc_dpb_user_name));
1591 ba.append(
char(usr.length()));
1592 ba.append(usr.constData(), usr.length());
1593 ba.append(
char(isc_dpb_password));
1594 ba.append(
char(pass.length()));
1595 ba.append(pass.constData(), pass.length());
1596 ba.append(
char(isc_dpb_lc_ctype));
1597 ba.append(
char(enc.length()));
1598 ba.append(enc.constData(), enc.length());
1600 if (!role.isEmpty()) {
1601 ba.append(
char(isc_dpb_sql_role_name));
1602 ba.append(
char(role.length()));
1603 ba.append(role.constData(), role.length());
1608 portString = QStringLiteral(
"/%1").arg(port);
1611 if (!host.isEmpty())
1612 ldb += host + portString + u':';
1614 isc_attach_database(d->status, 0,
const_cast<
char *>(ldb.toLocal8Bit().constData()),
1615 &d->ibase, ba.size(), ba.constData());
1616 if (d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Error opening database"),
1617 QSqlError::ConnectionError)) {
1623 setOpenError(
false);
1624#if (FB_API_VER >= 40
)
1625 std::call_once(initTZMappingFlag, [d](){ d->initTZMappingCache(); });
1630void QIBaseDriver::close()
1635 if (d->eventBuffers.size()) {
1636 ISC_STATUS status[20];
1637 QMap<QString, QIBaseEventBuffer *>::const_iterator i;
1638 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1639 QIBaseEventBuffer *eBuffer = i.value();
1640 eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
1641 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1642 qFreeEventBuffer(eBuffer);
1644 d->eventBuffers.clear();
1647 isc_detach_database(d->status, &d->ibase);
1650 setOpenError(
false);
1654QSqlResult *QIBaseDriver::createResult()
const
1656 return new QIBaseResult(
this);
1659bool QIBaseDriver::beginTransaction()
1662 if (!isOpen() || isOpenError())
1667 isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
1668 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Could not start transaction"),
1669 QSqlError::TransactionError);
1672bool QIBaseDriver::commitTransaction()
1675 if (!isOpen() || isOpenError())
1680 isc_commit_transaction(d->status, &d->trans);
1682 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Unable to commit transaction"),
1683 QSqlError::TransactionError);
1686bool QIBaseDriver::rollbackTransaction()
1689 if (!isOpen() || isOpenError())
1694 isc_rollback_transaction(d->status, &d->trans);
1696 return !d->isError(QT_TRANSLATE_NOOP(
"QIBaseDriver",
"Unable to rollback transaction"),
1697 QSqlError::TransactionError);
1700QStringList QIBaseDriver::tables(QSql::TableType type)
const
1708 if (type == QSql::SystemTables) {
1709 typeFilter +=
"RDB$SYSTEM_FLAG != 0"_L1;
1710 }
else if (type == (QSql::SystemTables | QSql::Views)) {
1711 typeFilter +=
"RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"_L1;
1713 if (!(type & QSql::SystemTables))
1714 typeFilter +=
"RDB$SYSTEM_FLAG = 0 AND "_L1;
1715 if (!(type & QSql::Views))
1716 typeFilter +=
"RDB$VIEW_BLR IS NULL AND "_L1;
1717 if (!(type & QSql::Tables))
1718 typeFilter +=
"RDB$VIEW_BLR IS NOT NULL AND "_L1;
1719 if (!typeFilter.isEmpty())
1722 if (!typeFilter.isEmpty())
1723 typeFilter.prepend(
"where "_L1);
1725 QSqlQuery q(createResult());
1726 q.setForwardOnly(
true);
1727 if (!q.exec(
"select rdb$relation_name from rdb$relations "_L1 + typeFilter))
1730 res << q.value(0).toString().simplified();
1735QSqlRecord QIBaseDriver::record(
const QString& tablename)
const
1741 const QString table = stripDelimiters(tablename, QSqlDriver::TableName);
1742 QSqlQuery q(createResult());
1743 q.setForwardOnly(
true);
1744 q.exec(
"SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
1745 "b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
1746 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
1747 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
1748 "AND a.RDB$RELATION_NAME = '"_L1 + table +
"' "
1749 "ORDER BY a.RDB$FIELD_POSITION"_L1);
1752 int type = q.value(1).toInt();
1753 bool hasScale = q.value(3).toInt() < 0;
1754 QSqlField f(q.value(0).toString().simplified(), QMetaType(qIBaseTypeName(type, hasScale)), tablename);
1756 f.setLength(q.value(4).toInt());
1757 f.setPrecision(qAbs(q.value(3).toInt()));
1759 f.setLength(q.value(2).toInt());
1762 f.setRequired(q.value(5).toInt() > 0);
1769QSqlIndex QIBaseDriver::primaryIndex(
const QString &table)
const
1771 QSqlIndex index(table);
1775 const QString tablename = stripDelimiters(table, QSqlDriver::TableName);
1776 QSqlQuery q(createResult());
1777 q.setForwardOnly(
true);
1778 q.exec(
"SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
1779 "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
1780 "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
1781 "AND a.RDB$RELATION_NAME = '"_L1 + tablename +
1782 " 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
1783 "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
1784 "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
1785 "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
1786 "ORDER BY b.RDB$FIELD_POSITION"_L1);
1789 QSqlField field(q.value(1).toString().simplified(),
1790 QMetaType(qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0)),
1792 index.append(field);
1793 index.setName(q.value(0).toString());
1799QString QIBaseDriver::formatValue(
const QSqlField &field,
bool trimStrings)
const
1801 switch (field.metaType().id()) {
1802 case QMetaType::QDateTime: {
1803 QDateTime datetime = field.value().toDateTime();
1804 if (datetime.isValid())
1805 return u'\'' + QString::number(datetime.date().year()) + u'-' +
1806 QString::number(datetime.date().month()) + u'-' +
1807 QString::number(datetime.date().day()) + u' ' +
1808 QString::number(datetime.time().hour()) + u':' +
1809 QString::number(datetime.time().minute()) + u':' +
1810 QString::number(datetime.time().second()) + u'.' +
1811 QString::number(datetime.time().msec()).rightJustified(3, u'0',
true) +
1816 case QMetaType::QTime: {
1817 QTime time = field.value().toTime();
1819 return u'\'' + QString::number(time.hour()) + u':' +
1820 QString::number(time.minute()) + u':' +
1821 QString::number(time.second()) + u'.' +
1822 QString::number(time.msec()).rightJustified(3, u'0',
true) +
1827 case QMetaType::QDate: {
1828 QDate date = field.value().toDate();
1830 return u'\'' + QString::number(date.year()) + u'-' +
1831 QString::number(date.month()) + u'-' +
1832 QString::number(date.day()) + u'\'';
1837 return QSqlDriver::formatValue(field, trimStrings);
1841QVariant QIBaseDriver::handle()
const
1843 Q_D(
const QIBaseDriver);
1844 return QVariant(QMetaType::fromType<isc_db_handle>(), &d->ibase);
1853 memcpy(result, updated, length);
1855 QIBaseDriver *driver = qBufferDriverMap()->value(result);
1861 QMetaObject::invokeMethod(driver,
"qHandleEventNotification", Qt::QueuedConnection, Q_ARG(
void *,
reinterpret_cast<
void *>(result)));
1866bool QIBaseDriver::subscribeToNotification(
const QString &name)
1870 qCWarning(lcIbase,
"QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
1874 if (d->eventBuffers.contains(name)) {
1875 qCWarning(lcIbase,
"QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%ls'.",
1876 qUtf16Printable(name));
1880 QIBaseEventBuffer *eBuffer =
new QIBaseEventBuffer;
1881 eBuffer->subscriptionState = QIBaseEventBuffer::Starting;
1882 eBuffer->bufferLength = isc_event_block(&eBuffer->eventBuffer,
1883 &eBuffer->resultBuffer,
1885 name.toLocal8Bit().constData());
1888 qBufferDriverMap()->insert(eBuffer->resultBuffer,
this);
1891 d->eventBuffers.insert(name, eBuffer);
1893 ISC_STATUS status[20];
1894 isc_que_events(status,
1897 eBuffer->bufferLength,
1898 eBuffer->eventBuffer,
1899 reinterpret_cast<ISC_EVENT_CALLBACK>(
reinterpret_cast<
void *>
1901 eBuffer->resultBuffer);
1903 if (status[0] == 1 && status[1]) {
1904 setLastError(QSqlError(tr(
"Could not subscribe to event notifications for %1.").arg(name)));
1905 d->eventBuffers.remove(name);
1906 qFreeEventBuffer(eBuffer);
1913bool QIBaseDriver::unsubscribeFromNotification(
const QString &name)
1917 qCWarning(lcIbase,
"QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
1921 if (!d->eventBuffers.contains(name)) {
1922 qCWarning(lcIbase,
"QIBaseDriver::QIBaseSubscriptionState not subscribed to '%ls'.",
1923 qUtf16Printable(name));
1927 QIBaseEventBuffer *eBuffer = d->eventBuffers.value(name);
1928 ISC_STATUS status[20];
1929 eBuffer->subscriptionState = QIBaseEventBuffer::Finished;
1930 isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
1932 if (status[0] == 1 && status[1]) {
1933 setLastError(QSqlError(tr(
"Could not unsubscribe from event notifications for %1.").arg(name)));
1937 d->eventBuffers.remove(name);
1938 qFreeEventBuffer(eBuffer);
1943QStringList QIBaseDriver::subscribedToNotifications()
const
1945 Q_D(
const QIBaseDriver);
1946 return QStringList(d->eventBuffers.keys());
1949void QIBaseDriver::qHandleEventNotification(
void *updatedResultBuffer)
1952 QMap<QString, QIBaseEventBuffer *>::const_iterator i;
1953 for (i = d->eventBuffers.constBegin(); i != d->eventBuffers.constEnd(); ++i) {
1954 QIBaseEventBuffer* eBuffer = i.value();
1955 if (
reinterpret_cast<
void *>(eBuffer->resultBuffer) != updatedResultBuffer)
1958 ISC_ULONG counts[20];
1959 memset(counts, 0,
sizeof(counts));
1960 isc_event_counts(counts, eBuffer->bufferLength, eBuffer->eventBuffer, eBuffer->resultBuffer);
1963 if (eBuffer->subscriptionState == QIBaseEventBuffer::Subscribed)
1964 emit notification(i.key(), QSqlDriver::UnknownSource, QVariant());
1965 else if (eBuffer->subscriptionState == QIBaseEventBuffer::Starting)
1966 eBuffer->subscriptionState = QIBaseEventBuffer::Subscribed;
1968 ISC_STATUS status[20];
1969 isc_que_events(status,
1972 eBuffer->bufferLength,
1973 eBuffer->eventBuffer,
1974 reinterpret_cast<ISC_EVENT_CALLBACK>(
reinterpret_cast<
void *>
1976 eBuffer->resultBuffer);
1977 if (Q_UNLIKELY(status[0] == 1 && status[1])) {
1978 qCritical(
"QIBaseDriver::qHandleEventNotification: could not resubscribe to '%ls'",
1979 qUtf16Printable(i.key()));
1987QString QIBaseDriver::escapeIdentifier(
const QString &identifier, IdentifierType)
const
1989 QString res = identifier;
1990 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
1991 res.replace(u'"',
"\"\""_L1);
1992 res.replace(u'.',
"\".\""_L1);
1993 res = u'"' + res + u'"';
1998int QIBaseDriver::maximumIdentifierLength(IdentifierType type)
const
2006#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)
bool bindScaled(const QVariant &val, int scale, char *data, qsizetype paramIndex)
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool gotoNext(QSqlCachedResult::ValueCache &row, int rowIdx) override
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
int size() override
Returns the size of the SELECT result, or -1 if it cannot be determined or if the query is not a SELE...
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
QMap< void *, QIBaseDriver * > QIBaseBufferDriverMap
static void delDA(XSQLDA *&sqlda)
static void qFreeEventBuffer(QIBaseEventBuffer *eBuffer)
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