summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/ibase/qsql_ibase.cpp')
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp217
1 files changed, 142 insertions, 75 deletions
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index 28361e250d..bb2b3d846e 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)
@@ -100,6 +106,9 @@ 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:
@@ -118,6 +127,7 @@ static void initDA(XSQLDA *sqlda)
default:
// not supported - do not bind.
sqlda->sqlvar[i].sqldata = 0;
+ qCWarning(lcIbase, "initDA: unknown sqltype: %d", sqlda->sqlvar[i].sqltype & ~1);
break;
}
if (sqlda->sqlvar[i].sqltype & 1) {
@@ -189,6 +199,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 +222,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)
@@ -344,21 +360,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 +412,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 +527,7 @@ public:
XSQLDA *sqlda; // output sqlda
XSQLDA *inda; // input parameters
int queryType;
+ mutable QSqlRecord cachedRecord;
};
@@ -479,6 +559,7 @@ void QIBaseResultPrivate::cleanup()
delDA(inda);
queryType = -1;
+ cachedRecord.clear();
q->cleanup();
}
@@ -1063,32 +1144,25 @@ bool QIBaseResult::exec()
}
switch(d->inda->sqlvar[para].sqltype & ~1) {
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, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
break;
+#ifdef IBASE_INT128_SUPPORTED
+ case SQL_INT128:
+ setWithScale<qinternalint128>(val, d->inda->sqlvar[para].sqlscale,
+ d->inda->sqlvar[para].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 (d->inda->sqlvar[para].sqllen == 4)
+ setWithScale<qint32>(val, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
+ else
+ setWithScale<qint64>(val, 0, d->inda->sqlvar[para].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, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
break;
case SQL_FLOAT:
- *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
+ *((float*)d->inda->sqlvar[para].sqldata) = val.toFloat();
break;
case SQL_DOUBLE:
*((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
@@ -1236,27 +1310,38 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
// 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(d->sqlda->sqlvar[i].sqllen == sizeof(qint64));
+ const auto val = *(qint64 *)buf;
+ const auto scale = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = applyScale(val, scale);
+ break;
+ }
+#ifdef IBASE_INT128_SUPPORTED
+ case SQL_INT128: {
+ Q_ASSERT(d->sqlda->sqlvar[i].sqllen == sizeof(qinternalint128));
+ const qinternalint128 val128 = qFromUnaligned<qinternalint128>(buf);
+ const auto scale = d->sqlda->sqlvar[i].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 (d->sqlda->sqlvar[i].sqllen == 4) {
+ const auto val = *(qint32 *)buf;
+ const auto scale = d->sqlda->sqlvar[i].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 = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = applyScale(val, scale);
break;
+ }
case SQL_FLOAT:
row[idx] = QVariant(double((*(float*)buf)));
break;
@@ -1291,30 +1376,11 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
#endif
default:
// unknown type - don't even try to fetch
+ qCWarning(lcIbase, "gotoNext: unknown sqltype: %d",
+ d->sqlda->sqlvar[i].sqltype & ~1);
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 +1495,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 +1528,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