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.cpp386
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;
}