diff options
Diffstat (limited to 'src/plugins/sqldrivers/ibase/qsql_ibase.cpp')
-rw-r--r-- | src/plugins/sqldrivers/ibase/qsql_ibase.cpp | 386 |
1 files changed, 231 insertions, 155 deletions
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp index 28361e250d..7cec986883 100644 --- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp +++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp @@ -3,11 +3,13 @@ #include "qsql_ibase_p.h" #include <QtCore/qcoreapplication.h> +#include <QtCore/qendian.h> #include <QtCore/qdatetime.h> #include <QtCore/qtimezone.h> #include <QtCore/qdeadlinetimer.h> #include <QtCore/qdebug.h> #include <QtCore/qlist.h> +#include <QtCore/private/qlocale_tools_p.h> #include <QtCore/qloggingcategory.h> #include <QtCore/qmap.h> #include <QtCore/qmutex.h> @@ -41,6 +43,10 @@ using namespace Qt::StringLiterals; #define blr_boolean_dtype blr_bool #endif +#if (defined(QT_SUPPORTS_INT128) || defined(QT_USE_MSVC_INT128)) && (FB_API_VER >= 40) +#define IBASE_INT128_SUPPORTED +#endif + constexpr qsizetype QIBaseChunkSize = SHRT_MAX / 2; #if (FB_API_VER >= 40) @@ -91,7 +97,9 @@ static void enlargeDA(XSQLDA *&sqlda, int n) static void initDA(XSQLDA *sqlda) { for (int i = 0; i < sqlda->sqld; ++i) { - switch (sqlda->sqlvar[i].sqltype & ~1) { + XSQLVAR &sqlvar = sqlda->sqlvar[i]; + const auto sqltype = (sqlvar.sqltype & ~1); + switch (sqltype) { case SQL_INT64: case SQL_LONG: case SQL_SHORT: @@ -100,31 +108,35 @@ static void initDA(XSQLDA *sqlda) case SQL_TIMESTAMP: #if (FB_API_VER >= 40) case SQL_TIMESTAMP_TZ: +#ifdef IBASE_INT128_SUPPORTED + case SQL_INT128: +#endif #endif case SQL_TYPE_TIME: case SQL_TYPE_DATE: case SQL_TEXT: case SQL_BLOB: case SQL_BOOLEAN: - sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen]; + sqlvar.sqldata = new char[sqlvar.sqllen]; break; case SQL_ARRAY: - sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)]; - memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD)); + sqlvar.sqldata = new char[sizeof(ISC_QUAD)]; + memset(sqlvar.sqldata, 0, sizeof(ISC_QUAD)); break; case SQL_VARYING: - sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)]; + sqlvar.sqldata = new char[sqlvar.sqllen + sizeof(short)]; break; default: // not supported - do not bind. - sqlda->sqlvar[i].sqldata = 0; + sqlvar.sqldata = 0; + qCWarning(lcIbase, "initDA: unknown sqltype: %d", sqltype); break; } - if (sqlda->sqlvar[i].sqltype & 1) { - sqlda->sqlvar[i].sqlind = new short[1]; - *(sqlda->sqlvar[i].sqlind) = 0; + if (sqlvar.sqltype & 1) { + sqlvar.sqlind = new short[1]; + *(sqlvar.sqlind) = 0; } else { - sqlda->sqlvar[i].sqlind = 0; + sqlvar.sqlind = 0; } } } @@ -189,6 +201,10 @@ static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale) return (hasScale ? QMetaType::Double : QMetaType::Int); case SQL_INT64: return (hasScale ? QMetaType::Double : QMetaType::LongLong); +#ifdef IBASE_INT128_SUPPORTED + case SQL_INT128: + return (hasScale ? QMetaType::Double : QMetaType::LongLong); +#endif case SQL_FLOAT: case SQL_DOUBLE: return QMetaType::Double; @@ -208,8 +224,10 @@ static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale) case SQL_BOOLEAN: return QMetaType::Bool; default: - return QMetaType::UnknownType; + break; } + qCWarning(lcIbase, "qIBaseTypeName: unknown datatype: %d", iType); + return QMetaType::UnknownType; } static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt) @@ -222,7 +240,7 @@ static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt) return ts; } -static QDateTime fromTimeStamp(char *buffer) +static QDateTime fromTimeStamp(const char *buffer) { static const QDate bd(1858, 11, 17); QTime t(0, 0); @@ -230,14 +248,14 @@ static QDateTime fromTimeStamp(char *buffer) // have to demangle the structure ourselves because isc_decode_time // strips the msecs - auto timebuf = reinterpret_cast<ISC_TIMESTAMP*>(buffer); + auto timebuf = reinterpret_cast<const ISC_TIMESTAMP *>(buffer); t = t.addMSecs(static_cast<int>(timebuf->timestamp_time / 10)); d = bd.addDays(timebuf->timestamp_date); return QDateTime(d, t); } #if (FB_API_VER >= 40) -QDateTime fromTimeStampTz(char *buffer) +QDateTime fromTimeStampTz(const char *buffer) { static const QDate bd(1858, 11, 17); QTime t(0, 0); @@ -245,7 +263,7 @@ QDateTime fromTimeStampTz(char *buffer) // have to demangle the structure ourselves because isc_decode_time // strips the msecs - auto timebuf = reinterpret_cast<ISC_TIMESTAMP_TZ*>(buffer); + auto timebuf = reinterpret_cast<const ISC_TIMESTAMP_TZ *>(buffer); t = t.addMSecs(static_cast<int>(timebuf->utc_timestamp.timestamp_time / 10)); d = bd.addDays(timebuf->utc_timestamp.timestamp_date); quint16 fpTzID = timebuf->time_zone; @@ -275,12 +293,13 @@ static ISC_TIME toTime(QTime t) return (ISC_TIME)midnight.msecsTo(t) * 10; } -static QTime fromTime(char *buffer) +static QTime fromTime(const char *buffer) { QTime t(0, 0); // have to demangle the structure ourselves because isc_decode_time // strips the msecs - t = t.addMSecs(int((*(ISC_TIME*)buffer) / 10)); + const auto timebuf = reinterpret_cast<const ISC_TIME *>(buffer); + t = t.addMSecs(int((*timebuf) / 10)); return t; } @@ -294,14 +313,15 @@ static ISC_DATE toDate(QDate t) return date; } -static QDate fromDate(char *buffer) +static QDate fromDate(const char *buffer) { static const QDate bd(1858, 11, 17); QDate d; // have to demangle the structure ourselves because isc_decode_time // strips the msecs - d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date)); + const auto tsbuf = reinterpret_cast<const ISC_TIMESTAMP *>(buffer); + d = bd.addDays(int(tsbuf->timestamp_date)); return d; } @@ -344,21 +364,14 @@ public: QSqlQuery qry(q->createResult()); qry.setForwardOnly(true); qry.exec(QString("select * from RDB$TIME_ZONES"_L1)); - if (qry.lastError().type()) { - q->setLastError(QSqlError( - QCoreApplication::translate("QIBaseDriver", - "failed to query time zone mapping from system table"), - qry.lastError().databaseText(), - QSqlError::StatementError, - qry.lastError().nativeErrorCode())); - + if (qry.lastError().isValid()) { + qCInfo(lcIbase) << "Table 'RDB$TIME_ZONES' not found - not timezone support available"; return; } while (qry.next()) { - auto record = qry.record(); - quint16 fbTzId = record.value(0).value<quint16>(); - QByteArray ianaId = record.value(1).toByteArray().simplified(); + quint16 fbTzId = qry.value(0).value<quint16>(); + QByteArray ianaId = qry.value(1).toByteArray().simplified(); qFbTzIdToIanaIdMap()->insert(fbTzId, ianaId); qIanaIdToFbTzIdMap()->insert(ianaId, fbTzId); } @@ -403,6 +416,76 @@ protected: int size() override; int numRowsAffected() override; QSqlRecord record() const override; + + template<typename T> + static QString numberToHighPrecision(T val, int scale) + { + const bool negative = val < 0; + QString number; +#ifdef IBASE_INT128_SUPPORTED + if constexpr (std::is_same_v<qinternalint128, T>) { + number = negative ? qint128toBasicLatin(val * -1) + : qint128toBasicLatin(val); + } else +#endif + number = negative ? QString::number(qAbs(val)) + : QString::number(val); + auto len = number.size(); + scale *= -1; + if (scale >= len) { + number = QString(scale - len + 1, u'0') + number; + len = number.size(); + } + const auto sepPos = len - scale; + number = number.left(sepPos) + u'.' + number.mid(sepPos); + if (negative) + number = u'-' + number; + return number; + } + + template<typename T> + QVariant applyScale(T val, int scale) const + { + if (scale >= 0) + return QVariant(val); + + switch (numericalPrecisionPolicy()) { + case QSql::LowPrecisionInt32: + return QVariant(qint32(val * pow(10.0, scale))); + case QSql::LowPrecisionInt64: + return QVariant(qint64(val * pow(10.0, scale))); + case QSql::LowPrecisionDouble: + return QVariant(double(val * pow(10.0, scale))); + case QSql::HighPrecision: + return QVariant(numberToHighPrecision(val, scale)); + } + return QVariant(val); + } + + template<typename T> + void setWithScale(const QVariant &val, int scale, char *data) + { + auto ptr = reinterpret_cast<T *>(data); + if (scale < 0) { + double d = floor(0.5 + val.toDouble() * pow(10.0, scale * -1)); +#ifdef IBASE_INT128_SUPPORTED + if constexpr (std::is_same_v<qinternalint128, T>) { + quint64 lower = quint64(d); + quint64 tmp = quint64(std::numeric_limits<quint32>::max()) + 1; + d /= tmp; + d /= tmp; + quint64 higher = quint64(d); + qinternalint128 result = higher; + result <<= 64; + result += lower; + *ptr = static_cast<T>(result); + } else +#endif + *ptr = static_cast<T>(d); + } + else + *ptr = val.value<T>(); + } }; class QIBaseResultPrivate: public QSqlCachedResultPrivate @@ -448,6 +531,7 @@ public: XSQLDA *sqlda; // output sqlda XSQLDA *inda; // input parameters int queryType; + mutable QSqlRecord cachedRecord; }; @@ -479,6 +563,7 @@ void QIBaseResultPrivate::cleanup() delDA(inda); queryType = -1; + cachedRecord.clear(); q->cleanup(); } @@ -512,9 +597,8 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId) return QVariant(); unsigned short len = 0; - QByteArray ba; - int chunkSize = QIBaseChunkSize; - ba.resize(chunkSize); + constexpr auto chunkSize = QIBaseChunkSize; + QByteArray ba(chunkSize, Qt::Uninitialized); qsizetype read = 0; while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) { read += len; @@ -537,9 +621,10 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId) } template<typename T> -static QList<QVariant> toList(char** buf, int count) +static QList<QVariant> toList(const char **buf, int count) { QList<QVariant> res; + res.reserve(count); for (int i = 0; i < count; ++i) { res.append(*(T*)(*buf)); *buf += sizeof(T); @@ -547,8 +632,8 @@ static QList<QVariant> toList(char** buf, int count) return res; } -static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim, - short* numElements, ISC_ARRAY_DESC *arrayDesc) +static const char *readArrayBuffer(QList<QVariant>& list, const char *buffer, short curDim, + const short *numElements, ISC_ARRAY_DESC *arrayDesc) { const short dim = arrayDesc->array_desc_dimensions - 1; const unsigned char dataType = arrayDesc->array_desc_dtype; @@ -636,8 +721,9 @@ QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr) if (!arr) return list; - QByteArray relname(sqlda->sqlvar[pos].relname, sqlda->sqlvar[pos].relname_length); - QByteArray sqlname(sqlda->sqlvar[pos].sqlname, sqlda->sqlvar[pos].sqlname_length); + const XSQLVAR &sqlvar = sqlda->sqlvar[pos]; + const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length); + const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length); isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc); if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"), @@ -645,19 +731,18 @@ QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr) return list; - int arraySize = 1, subArraySize; + int arraySize = 1; short dimensions = desc.array_desc_dimensions; QVarLengthArray<short> numElements(dimensions); for(int i = 0; i < dimensions; ++i) { - subArraySize = (desc.array_desc_bounds[i].array_bound_upper - - desc.array_desc_bounds[i].array_bound_lower + 1); + short subArraySize = (desc.array_desc_bounds[i].array_bound_upper - + desc.array_desc_bounds[i].array_bound_lower + 1); numElements[i] = subArraySize; arraySize = subArraySize * arraySize; } ISC_LONG bufLen; - QByteArray ba; /* varying arrayelements are stored with 2 trailing null bytes indicating the length of the string */ @@ -669,14 +754,13 @@ QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr) bufLen = desc.array_desc_length * arraySize; } - - ba.resize(int(bufLen)); + QByteArray ba(bufLen, Qt::Uninitialized); isc_array_get_slice(status, &ibase, &trans, arr, &desc, ba.data(), &bufLen); if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not get array data"), QSqlError::StatementError)) return list; - readArrayBuffer(list, ba.data(), 0, numElements.data(), &desc); + readArrayBuffer(list, ba.constData(), 0, numElements.constData(), &desc); return QVariant(list); } @@ -713,16 +797,16 @@ static char* qFillBufferWithString(char *buffer, const QString& string, if (str.length() < buflen) buflen = str.length(); if (array) { // interbase stores varying arrayelements different than normal varying elements - memcpy(buffer, str.data(), buflen); + memcpy(buffer, str.constData(), buflen); memset(buffer + buflen, 0, tmpBuflen - buflen); } else { *(short*)buffer = buflen; // first two bytes is the length - memcpy(buffer + sizeof(short), str.data(), buflen); + memcpy(buffer + sizeof(short), str.constData(), buflen); } buffer += tmpBuflen; } else { str = str.leftJustified(buflen, ' ', true); - memcpy(buffer, str.data(), buflen); + memcpy(buffer, str.constData(), buflen); buffer += buflen; } return buffer; @@ -739,9 +823,10 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list, bounds[curDim].array_bound_lower + 1); if (list.size() != elements) { // size mismatch - error = "Expected size: %1. Supplied size: %2"_L1; - error = "Array size mismatch. Fieldname: %1 "_L1 - + error.arg(elements).arg(list.size()); + error = QCoreApplication::translate( + "QIBaseResult", + "Array size mismatch. Fieldname: %3, Expected size: %1. Supplied size: %2") + .arg(elements).arg(list.size()); return 0; } @@ -749,7 +834,9 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list, for (const auto &elem : list) { if (elem.typeId() != QMetaType::QVariantList) { // dimensions mismatch - error = "Array dimensons mismatch. Fieldname: %1"_L1; + error = QCoreApplication::translate( + "QIBaseResult", + "Array dimensons mismatch. Fieldname: %1"); return 0; } @@ -830,11 +917,12 @@ bool QIBaseResultPrivate::writeArray(qsizetype column, const QList<QVariant> &li { Q_Q(QIBaseResult); QString error; - ISC_QUAD *arrayId = (ISC_QUAD*) inda->sqlvar[column].sqldata; ISC_ARRAY_DESC desc; - QByteArray relname(inda->sqlvar[column].relname, inda->sqlvar[column].relname_length); - QByteArray sqlname(inda->sqlvar[column].sqlname, inda->sqlvar[column].sqlname_length); + XSQLVAR &sqlvar = inda->sqlvar[column]; + ISC_QUAD *arrayId = (ISC_QUAD*) sqlvar.sqldata; + const auto relname = QByteArray::fromRawData(sqlvar.relname, sqlvar.relname_length); + const auto sqlname = QByteArray::fromRawData(sqlvar.sqlname, sqlvar.sqlname_length); isc_array_lookup_bounds(status, &ibase, &trans, relname.data(), sqlname.data(), &desc); if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not find array"), @@ -858,20 +946,21 @@ bool QIBaseResultPrivate::writeArray(qsizetype column, const QList<QVariant> &li desc.array_desc_length += 2; bufLen = desc.array_desc_length * arraySize; - QByteArray ba; - ba.resize(int(bufLen)); + QByteArray ba(bufLen, Qt::Uninitialized); if (list.size() > arraySize) { - error = "Array size mismatch: size of %1 is %2, size of provided list is %3"_L1; + error = QCoreApplication::translate( + "QIBaseResult", + "Array size mismatch: size of %1 is %2, size of provided list is %3"); error = error.arg(QLatin1StringView(sqlname)).arg(arraySize).arg(list.size()); - q->setLastError(QSqlError(error, ""_L1, QSqlError::StatementError)); + q->setLastError(QSqlError(error, {}, QSqlError::StatementError)); return false; } if (!createArrayBuffer(ba.data(), list, - qIBaseTypeName(desc.array_desc_dtype, inda->sqlvar[column].sqlscale < 0), + qIBaseTypeName(desc.array_desc_dtype, sqlvar.sqlscale < 0), 0, &desc, error)) { - q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), ""_L1, + q->setLastError(QSqlError(error.arg(QLatin1StringView(sqlname)), {}, QSqlError::StatementError)); return false; } @@ -1039,79 +1128,73 @@ bool QIBaseResult::exec() return false; } for (qsizetype para = 0; para < values.count(); ++para) { - if (!d->inda->sqlvar[para].sqldata) + const XSQLVAR &sqlvar = d->inda->sqlvar[para]; + if (!sqlvar.sqldata) // skip unknown datatypes continue; const QVariant &val = values[para]; - if (d->inda->sqlvar[para].sqltype & 1) { + if (sqlvar.sqltype & 1) { if (QSqlResultPrivate::isVariantNull(val)) { // set null indicator - *(d->inda->sqlvar[para].sqlind) = -1; + *(sqlvar.sqlind) = -1; // and set the value to 0, otherwise it would count as empty string. // it seems to be working with just setting sqlind to -1 - //*((char*)d->inda->sqlvar[para].sqldata) = 0; + //*((char*)sqlvar.sqldata) = 0; continue; } // a value of 0 means non-null. - *(d->inda->sqlvar[para].sqlind) = 0; + *(sqlvar.sqlind) = 0; } else { if (QSqlResultPrivate::isVariantNull(val)) { qCWarning(lcIbase) << "QIBaseResult::exec: Null value replaced by default (zero)"_L1 - << "value for type of column"_L1 << d->inda->sqlvar[para].ownname + << "value for type of column"_L1 << sqlvar.ownname << ", which is not nullable."_L1; } } - switch(d->inda->sqlvar[para].sqltype & ~1) { + const auto sqltype = sqlvar.sqltype & ~1; + switch (sqltype) { case SQL_INT64: - if (d->inda->sqlvar[para].sqlscale < 0) - *((qint64*)d->inda->sqlvar[para].sqldata) = - (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); + setWithScale<qint64>(val, sqlvar.sqlscale, sqlvar.sqldata); + break; +#ifdef IBASE_INT128_SUPPORTED + case SQL_INT128: + setWithScale<qinternalint128>(val, sqlvar.sqlscale, + sqlvar.sqldata); break; +#endif case SQL_LONG: - if (d->inda->sqlvar[para].sqllen == 4) { - if (d->inda->sqlvar[para].sqlscale < 0) - *((qint32*)d->inda->sqlvar[para].sqldata) = - (qint32)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((qint32*)d->inda->sqlvar[para].sqldata) = (qint32)val.toInt(); - } else { - *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); - } + if (sqlvar.sqllen == 4) + setWithScale<qint32>(val, sqlvar.sqlscale, sqlvar.sqldata); + else + setWithScale<qint64>(val, 0, sqlvar.sqldata); break; case SQL_SHORT: - if (d->inda->sqlvar[para].sqlscale < 0) - *((short*)d->inda->sqlvar[para].sqldata) = - (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); - else - *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt(); + setWithScale<qint16>(val, sqlvar.sqlscale, sqlvar.sqldata); break; case SQL_FLOAT: - *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble(); + *((float*)sqlvar.sqldata) = val.toFloat(); break; case SQL_DOUBLE: - *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble(); + *((double*)sqlvar.sqldata) = val.toDouble(); break; case SQL_TIMESTAMP: - *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime()); + *((ISC_TIMESTAMP*)sqlvar.sqldata) = toTimeStamp(val.toDateTime()); break; #if (FB_API_VER >= 40) case SQL_TIMESTAMP_TZ: - *((ISC_TIMESTAMP_TZ*)d->inda->sqlvar[para].sqldata) = toTimeStampTz(val.toDateTime()); + *((ISC_TIMESTAMP_TZ*)sqlvar.sqldata) = toTimeStampTz(val.toDateTime()); break; #endif case SQL_TYPE_TIME: - *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime()); + *((ISC_TIME*)sqlvar.sqldata) = toTime(val.toTime()); break; case SQL_TYPE_DATE: - *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate()); + *((ISC_DATE*)sqlvar.sqldata) = toDate(val.toDate()); break; case SQL_VARYING: case SQL_TEXT: - qFillBufferWithString(d->inda->sqlvar[para].sqldata, val.toString(), - d->inda->sqlvar[para].sqllen, - (d->inda->sqlvar[para].sqltype & ~1) == SQL_VARYING, false); + qFillBufferWithString(sqlvar.sqldata, val.toString(), sqlvar.sqllen, + sqltype == SQL_VARYING, false); break; case SQL_BLOB: ok &= d->writeBlob(para, val.toByteArray()); @@ -1120,11 +1203,11 @@ bool QIBaseResult::exec() ok &= d->writeArray(para, val.toList()); break; case SQL_BOOLEAN: - *((bool*)d->inda->sqlvar[para].sqldata) = val.toBool(); + *((bool*)sqlvar.sqldata) = val.toBool(); break; default: qCWarning(lcIbase, "QIBaseResult::exec: Unknown datatype %d", - d->inda->sqlvar[para].sqltype & ~1); + sqltype); break; } } @@ -1202,15 +1285,12 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) for (int i = 0; i < d->sqlda->sqld; ++i) { int idx = rowIdx + i; - char *buf = d->sqlda->sqlvar[i].sqldata; - int size = d->sqlda->sqlvar[i].sqllen; - Q_ASSERT(buf); + const XSQLVAR &sqlvar = d->sqlda->sqlvar[i]; - if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) { + if ((sqlvar.sqltype & 1) && *sqlvar.sqlind) { // null value QVariant v; - v.convert(QMetaType(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype, - d->sqlda->sqlvar[i].sqlscale < 0))); + v.convert(QMetaType(qIBaseTypeName2(sqlvar.sqltype, sqlvar.sqlscale < 0))); if (v.userType() == QMetaType::Double) { switch(numericalPrecisionPolicy()) { case QSql::LowPrecisionInt32: @@ -1231,32 +1311,47 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) continue; } - switch(d->sqlda->sqlvar[i].sqltype & ~1) { + const char *buf = sqlvar.sqldata; + int size = sqlvar.sqllen; + Q_ASSERT(buf); + const auto sqltype = sqlvar.sqltype & ~1; + switch (sqltype) { case SQL_VARYING: // pascal strings - a short with a length information followed by the data row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf); break; - case SQL_INT64: - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = *(qint64*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale); - else - row[idx] = QVariant(*(qint64*)buf); + case SQL_INT64: { + Q_ASSERT(sqlvar.sqllen == sizeof(qint64)); + const auto val = *(qint64 *)buf; + const auto scale = sqlvar.sqlscale; + row[idx] = applyScale(val, scale); + break; + } +#ifdef IBASE_INT128_SUPPORTED + case SQL_INT128: { + Q_ASSERT(sqlvar.sqllen == sizeof(qinternalint128)); + const qinternalint128 val128 = qFromUnaligned<qinternalint128>(buf); + const auto scale = sqlvar.sqlscale; + row[idx] = numberToHighPrecision(val128, scale); + if (numericalPrecisionPolicy() != QSql::HighPrecision) + row[idx] = applyScale(row[idx].toDouble(), 0); break; + } +#endif case SQL_LONG: - if (d->sqlda->sqlvar[i].sqllen == 4) - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = QVariant(*(qint32*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale)); - else - row[idx] = QVariant(*(qint32*)buf); - else + if (sqlvar.sqllen == 4) { + const auto val = *(qint32 *)buf; + const auto scale = sqlvar.sqlscale; + row[idx] = applyScale(val, scale); + } else row[idx] = QVariant(*(qint64*)buf); break; - case SQL_SHORT: - if (d->sqlda->sqlvar[i].sqlscale < 0) - row[idx] = QVariant(long((*(short*)buf)) * pow(10.0, d->sqlda->sqlvar[i].sqlscale)); - else - row[idx] = QVariant(int((*(short*)buf))); + case SQL_SHORT: { + const auto val = *(short *)buf; + const auto scale = sqlvar.sqlscale; + row[idx] = applyScale(val, scale); break; + } case SQL_FLOAT: row[idx] = QVariant(double((*(float*)buf))); break; @@ -1291,30 +1386,10 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) #endif default: // unknown type - don't even try to fetch + qCWarning(lcIbase, "gotoNext: unknown sqltype: %d", sqltype); row[idx] = QVariant(); break; } - if (d->sqlda->sqlvar[i].sqlscale < 0) { - QVariant v = row[idx]; - switch(numericalPrecisionPolicy()) { - case QSql::LowPrecisionInt32: - if (v.convert(QMetaType(QMetaType::Int))) - row[idx]=v; - break; - case QSql::LowPrecisionInt64: - if (v.convert(QMetaType(QMetaType::LongLong))) - row[idx]=v; - break; - case QSql::LowPrecisionDouble: - if (v.convert(QMetaType(QMetaType::Double))) - row[idx]=v; - break; - case QSql::HighPrecision: - if (v.convert(QMetaType(QMetaType::QString))) - row[idx]=v; - break; - } - } } return true; @@ -1429,13 +1504,14 @@ int QIBaseResult::numRowsAffected() QSqlRecord QIBaseResult::record() const { Q_D(const QIBaseResult); - QSqlRecord rec; if (!isActive() || !d->sqlda) - return rec; + return {}; + + if (!d->cachedRecord.isEmpty()) + return d->cachedRecord; - XSQLVAR v; for (int i = 0; i < d->sqlda->sqld; ++i) { - v = d->sqlda->sqlvar[i]; + const XSQLVAR &v = d->sqlda->sqlvar[i]; QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(), QMetaType(qIBaseTypeName2(v.sqltype, v.sqlscale < 0)), QString::fromLatin1(v.relname, v.relname_length)); @@ -1461,9 +1537,9 @@ QSqlRecord QIBaseResult::record() const f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional); } } - rec.append(f); + d->cachedRecord.append(f); } - return rec; + return d->cachedRecord; } QVariant QIBaseResult::handle() const @@ -1554,18 +1630,18 @@ bool QIBaseDriver::open(const QString &db, ba.append(char(isc_dpb_version1)); ba.append(char(isc_dpb_user_name)); ba.append(char(usr.length())); - ba.append(usr.data(), usr.length()); + ba.append(usr.constData(), usr.length()); ba.append(char(isc_dpb_password)); ba.append(char(pass.length())); - ba.append(pass.data(), pass.length()); + ba.append(pass.constData(), pass.length()); ba.append(char(isc_dpb_lc_ctype)); ba.append(char(enc.length())); - ba.append(enc.data(), enc.length()); + ba.append(enc.constData(), enc.length()); if (!role.isEmpty()) { ba.append(char(isc_dpb_sql_role_name)); ba.append(char(role.length())); - ba.append(role.data(), role.length()); + ba.append(role.constData(), role.length()); } QString portString; @@ -1577,7 +1653,7 @@ bool QIBaseDriver::open(const QString &db, ldb += host + portString + u':'; ldb += db; isc_attach_database(d->status, 0, const_cast<char *>(ldb.toLocal8Bit().constData()), - &d->ibase, ba.size(), ba.data()); + &d->ibase, ba.size(), ba.constData()); if (d->isError(QT_TRANSLATE_NOOP("QIBaseDriver", "Error opening database"), QSqlError::ConnectionError)) { setOpenError(true); @@ -1871,7 +1947,7 @@ bool QIBaseDriver::subscribeToNotification(const QString &name) eBuffer->resultBuffer); if (status[0] == 1 && status[1]) { - setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name))); + setLastError(QSqlError(tr("Could not subscribe to event notifications for %1.").arg(name))); d->eventBuffers.remove(name); qFreeEventBuffer(eBuffer); return false; @@ -1900,7 +1976,7 @@ bool QIBaseDriver::unsubscribeFromNotification(const QString &name) isc_cancel_events(status, &d->ibase, &eBuffer->eventId); if (status[0] == 1 && status[1]) { - setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name))); + setLastError(QSqlError(tr("Could not unsubscribe from event notifications for %1.").arg(name))); return false; } |