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.cpp444
1 files changed, 249 insertions, 195 deletions
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index 28361e250d..44443aaacb 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,46 +224,41 @@ 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)
+// InterBase and FireBird use the modified Julian Date with 1858-11-17 as day 0.
+static constexpr auto s_ibaseBaseDate = QDate::fromJulianDay(2400001);
+
+static inline ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
{
- static const QTime midnight(0, 0, 0, 0);
- static const QDate basedate(1858, 11, 17);
ISC_TIMESTAMP ts;
- ts.timestamp_time = midnight.msecsTo(dt.time()) * 10;
- ts.timestamp_date = basedate.daysTo(dt.date());
+ ts.timestamp_time = dt.time().msecsSinceStartOfDay() * 10;
+ ts.timestamp_date = s_ibaseBaseDate.daysTo(dt.date());
return ts;
}
-static QDateTime fromTimeStamp(char *buffer)
+static inline QDateTime fromTimeStamp(const char *buffer)
{
- static const QDate bd(1858, 11, 17);
- QTime t(0, 0);
- QDate d;
-
// have to demangle the structure ourselves because isc_decode_time
// strips the msecs
- auto timebuf = reinterpret_cast<ISC_TIMESTAMP*>(buffer);
- t = t.addMSecs(static_cast<int>(timebuf->timestamp_time / 10));
- d = bd.addDays(timebuf->timestamp_date);
+ auto timebuf = reinterpret_cast<const ISC_TIMESTAMP *>(buffer);
+ const QTime t = QTime::fromMSecsSinceStartOfDay(timebuf->timestamp_time / 10);
+ const QDate d = s_ibaseBaseDate.addDays(timebuf->timestamp_date);
return QDateTime(d, t);
}
#if (FB_API_VER >= 40)
-QDateTime fromTimeStampTz(char *buffer)
+static inline QDateTime fromTimeStampTz(const char *buffer)
{
- static const QDate bd(1858, 11, 17);
- QTime t(0, 0);
- QDate d;
-
// have to demangle the structure ourselves because isc_decode_time
// strips the msecs
- auto timebuf = reinterpret_cast<ISC_TIMESTAMP_TZ*>(buffer);
- t = t.addMSecs(static_cast<int>(timebuf->utc_timestamp.timestamp_time / 10));
- d = bd.addDays(timebuf->utc_timestamp.timestamp_date);
+ auto timebuf = reinterpret_cast<const ISC_TIMESTAMP_TZ *>(buffer);
+ const QTime t = QTime::fromMSecsSinceStartOfDay(timebuf->utc_timestamp.timestamp_time / 10);
+ const QDate d = s_ibaseBaseDate.addDays(timebuf->utc_timestamp.timestamp_date);
quint16 fpTzID = timebuf->time_zone;
QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID);
@@ -257,53 +268,40 @@ QDateTime fromTimeStampTz(char *buffer)
return {};
}
-ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
+static inline ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
{
- static const QTime midnight(0, 0, 0, 0);
- static const QDate basedate(1858, 11, 17);
ISC_TIMESTAMP_TZ ts;
- ts.utc_timestamp.timestamp_time = midnight.msecsTo(dt.time()) * 10;
- ts.utc_timestamp.timestamp_date = basedate.daysTo(dt.date());
+ ts.utc_timestamp.timestamp_time = dt.time().msecsSinceStartOfDay() * 10;
+ ts.utc_timestamp.timestamp_date = s_ibaseBaseDate.daysTo(dt.date());
ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
return ts;
}
#endif
-static ISC_TIME toTime(QTime t)
+static inline ISC_TIME toTime(QTime t)
{
- static const QTime midnight(0, 0, 0, 0);
- return (ISC_TIME)midnight.msecsTo(t) * 10;
+ return t.msecsSinceStartOfDay() * 10;
}
-static QTime fromTime(char *buffer)
+static inline 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));
-
- return t;
+ const auto timebuf = reinterpret_cast<const ISC_TIME *>(buffer);
+ return QTime::fromMSecsSinceStartOfDay(*timebuf / 10);
}
-static ISC_DATE toDate(QDate t)
+static inline ISC_DATE toDate(QDate t)
{
- static const QDate basedate(1858, 11, 17);
- ISC_DATE date;
-
- date = basedate.daysTo(t);
- return date;
-}
+ return s_ibaseBaseDate.daysTo(t);
+ }
-static QDate fromDate(char *buffer)
+static inline 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));
-
- return d;
+ const auto tsbuf = reinterpret_cast<const ISC_TIMESTAMP *>(buffer);
+ return s_ibaseBaseDate.addDays(int(tsbuf->timestamp_date));
}
struct QIBaseEventBuffer {
@@ -344,21 +342,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 +394,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 +509,7 @@ public:
XSQLDA *sqlda; // output sqlda
XSQLDA *inda; // input parameters
int queryType;
+ mutable QSqlRecord cachedRecord;
};
@@ -479,6 +541,7 @@ void QIBaseResultPrivate::cleanup()
delDA(inda);
queryType = -1;
+ cachedRecord.clear();
q->cleanup();
}
@@ -512,9 +575,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 +599,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 +610,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 +699,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 +709,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 +732,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 +775,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 +801,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. Field name: %3, expected size: %1. Supplied size: %2")
+ .arg(elements).arg(list.size());
return 0;
}
@@ -749,7 +812,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 dimensions mismatch. Field name: %1");
return 0;
}
@@ -830,11 +895,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 +924,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 +1106,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 +1181,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 +1263,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 +1289,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 +1364,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 +1482,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 +1515,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 +1608,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 +1631,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 +1925,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 +1954,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;
}