summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/ibase
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/ibase')
-rw-r--r--src/plugins/sqldrivers/ibase/CMakeLists.txt4
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp402
2 files changed, 265 insertions, 141 deletions
diff --git a/src/plugins/sqldrivers/ibase/CMakeLists.txt b/src/plugins/sqldrivers/ibase/CMakeLists.txt
index 8cd5c24dfc..b8f2d2561f 100644
--- a/src/plugins/sqldrivers/ibase/CMakeLists.txt
+++ b/src/plugins/sqldrivers/ibase/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_plugin(QIBaseDriverPlugin
OUTPUT_NAME qsqlibase
PLUGIN_TYPE sqldrivers
@@ -7,6 +10,7 @@ qt_internal_add_plugin(QIBaseDriverPlugin
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
LIBRARIES
Interbase::Interbase
Qt::Core
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index d228628c08..7b5f7e8eb7 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -4,9 +4,11 @@
#include "qsql_ibase_p.h"
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdatetime.h>
+#include <QtCore/qtimezone.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qmap.h>
#include <QtCore/qmutex.h>
#include <QtCore/qvariant.h>
@@ -20,9 +22,12 @@
#include <stdlib.h>
#include <limits.h>
#include <math.h>
+#include <mutex>
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(lcIbase, "qt.sql.ibase")
+
using namespace Qt::StringLiterals;
#define FBVERSION SQL_DIALECT_V6
@@ -36,7 +41,15 @@ using namespace Qt::StringLiterals;
#define blr_boolean_dtype blr_bool
#endif
-enum { QIBaseChunkSize = SHRT_MAX / 2 };
+constexpr qsizetype QIBaseChunkSize = SHRT_MAX / 2;
+
+#if (FB_API_VER >= 40)
+typedef QMap<quint16, QByteArray> QFbTzIdToIanaIdMap;
+typedef QMap<QByteArray, quint16> QIanaIdToFbTzIdMap;
+Q_GLOBAL_STATIC(QFbTzIdToIanaIdMap, qFbTzIdToIanaIdMap)
+Q_GLOBAL_STATIC(QIanaIdToFbTzIdMap, qIanaIdToFbTzIdMap)
+std::once_flag initTZMappingFlag;
+#endif
static bool getIBaseError(QString& msg, const ISC_STATUS* status, ISC_LONG &sqlcode)
{
@@ -85,6 +98,9 @@ static void initDA(XSQLDA *sqlda)
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_TIMESTAMP:
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+#endif
case SQL_TYPE_TIME:
case SQL_TYPE_DATE:
case SQL_TEXT:
@@ -102,6 +118,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) {
@@ -122,7 +139,7 @@ static void delDA(XSQLDA *&sqlda)
delete [] sqlda->sqlvar[i].sqldata;
}
free(sqlda);
- sqlda = 0;
+ sqlda = nullptr;
}
static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
@@ -139,6 +156,9 @@ static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
case blr_sql_date:
return QMetaType::QDate;
case blr_timestamp:
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+#endif
return QMetaType::QDateTime;
case blr_blob:
return QMetaType::QByteArray;
@@ -155,7 +175,7 @@ static QMetaType::Type qIBaseTypeName(int iType, bool hasScale)
case blr_boolean_dtype:
return QMetaType::Bool;
}
- qWarning("qIBaseTypeName: unknown datatype: %d", iType);
+ qCWarning(lcIbase, "qIBaseTypeName: unknown datatype: %d", iType);
return QMetaType::UnknownType;
}
@@ -174,6 +194,9 @@ static QMetaType::Type qIBaseTypeName2(int iType, bool hasScale)
case SQL_DOUBLE:
return QMetaType::Double;
case SQL_TIMESTAMP:
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+#endif
return QMetaType::QDateTime;
case SQL_TYPE_TIME:
return QMetaType::QTime;
@@ -186,8 +209,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)
@@ -208,12 +233,45 @@ static QDateTime fromTimeStamp(char *buffer)
// have to demangle the structure ourselves because isc_decode_time
// strips the msecs
- t = t.addMSecs(int(((ISC_TIMESTAMP*)buffer)->timestamp_time / 10));
- d = bd.addDays(int(((ISC_TIMESTAMP*)buffer)->timestamp_date));
-
+ auto timebuf = reinterpret_cast<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)
+{
+ 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);
+ quint16 fpTzID = timebuf->time_zone;
+
+ QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID);
+ if (!timeZoneName.isEmpty())
+ return QDateTime(d, t, QTimeZone(timeZoneName));
+ else
+ return {};
+}
+
+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.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
+ return ts;
+}
+#endif
+
static ISC_TIME toTime(QTime t)
{
static const QTime midnight(0, 0, 0, 0);
@@ -282,6 +340,34 @@ public:
return true;
}
+#if (FB_API_VER >= 40)
+ void initTZMappingCache()
+ {
+ Q_Q(QIBaseDriver);
+ 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()));
+
+ return;
+ }
+
+ while (qry.next()) {
+ auto record = qry.record();
+ quint16 fbTzId = record.value(0).value<quint16>();
+ QByteArray ianaId = record.value(1).toByteArray().simplified();
+ qFbTzIdToIanaIdMap()->insert(fbTzId, ianaId);
+ qIanaIdToFbTzIdMap()->insert(ianaId, fbTzId);
+ }
+ }
+#endif
+
public:
isc_db_handle ibase;
isc_tr_handle trans;
@@ -320,6 +406,42 @@ protected:
int size() override;
int numRowsAffected() override;
QSqlRecord record() const override;
+
+ 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: {
+ const bool negative = val < 0;
+ QString number;
+ if constexpr (std::is_signed_v<T> || negative)
+ number = QString::number(qAbs(val));
+ else
+ number = 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 QVariant(number);
+ }
+ }
+ return QVariant(val);
+ }
};
class QIBaseResultPrivate: public QSqlCachedResultPrivate
@@ -352,9 +474,9 @@ public:
bool isSelect();
QVariant fetchBlob(ISC_QUAD *bId);
- bool writeBlob(int i, const QByteArray &ba);
+ bool writeBlob(qsizetype iPos, const QByteArray &ba);
QVariant fetchArray(int pos, ISC_QUAD *arr);
- bool writeArray(int i, const QList<QVariant> &list);
+ bool writeArray(qsizetype i, const QList<QVariant> &list);
public:
ISC_STATUS status[20];
isc_tr_handle trans;
@@ -374,8 +496,8 @@ QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *q, const QIBaseDriver *dr
localTransaction(!drv_d_func()->ibase),
stmt(0),
ibase(drv_d_func()->ibase),
- sqlda(0),
- inda(0),
+ sqlda(nullptr),
+ inda(nullptr),
queryType(-1)
{
}
@@ -399,20 +521,20 @@ void QIBaseResultPrivate::cleanup()
q->cleanup();
}
-bool QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
+bool QIBaseResultPrivate::writeBlob(qsizetype iPos, const QByteArray &ba)
{
isc_blob_handle handle = 0;
- ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
+ ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[iPos].sqldata;
isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
if (!isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to create BLOB"),
QSqlError::StatementError)) {
- int i = 0;
+ qsizetype i = 0;
while (i < ba.size()) {
- isc_put_segment(status, &handle, qMin(ba.size() - i, int(QIBaseChunkSize)),
- const_cast<char*>(ba.data()) + i);
+ isc_put_segment(status, &handle, qMin(ba.size() - i, QIBaseChunkSize),
+ ba.data() + i);
if (isError(QT_TRANSLATE_NOOP("QIBaseResult", "Unable to write BLOB")))
return false;
- i += qMin(ba.size() - i, int(QIBaseChunkSize));
+ i += qMin(ba.size() - i, QIBaseChunkSize);
}
}
isc_close_blob(status, &handle);
@@ -432,7 +554,7 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
QByteArray ba;
int chunkSize = QIBaseChunkSize;
ba.resize(chunkSize);
- int read = 0;
+ qsizetype read = 0;
while (isc_get_segment(status, &handle, &len, chunkSize, ba.data() + read) == 0 || status[1] == isc_segment) {
read += len;
ba.resize(read + chunkSize);
@@ -454,7 +576,7 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
}
template<typename T>
-static QList<QVariant> toList(char** buf, int count, T* = nullptr)
+static QList<QVariant> toList(char** buf, int count)
{
QList<QVariant> res;
for (int i = 0; i < count; ++i) {
@@ -493,7 +615,7 @@ static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
}
break; }
case blr_long:
- valList = toList<int>(&buffer, numElements[dim], static_cast<int *>(0));
+ valList = toList<int>(&buffer, numElements[dim]);
break;
case blr_short:
valList = toList<short>(&buffer, numElements[dim]);
@@ -513,8 +635,16 @@ static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
buffer += sizeof(ISC_TIMESTAMP);
}
break;
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+ for (int i = 0; i < numElements[dim]; ++i) {
+ valList.append(fromTimeStampTz(buffer));
+ buffer += sizeof(ISC_TIMESTAMP_TZ);
+ }
+ break;
+#endif
case blr_sql_time:
- for(int i = 0; i < numElements[dim]; ++i) {
+ for (int i = 0; i < numElements[dim]; ++i) {
valList.append(fromTime(buffer));
buffer += sizeof(ISC_TIME);
}
@@ -593,9 +723,8 @@ QVariant QIBaseResultPrivate::fetchArray(int pos, ISC_QUAD *arr)
template<typename T>
static char* fillList(char *buffer, const QList<QVariant> &list, T* = nullptr)
{
- for (int i = 0; i < list.size(); ++i) {
- T val;
- val = qvariant_cast<T>(list.at(i));
+ for (const auto &elem : list) {
+ T val = qvariant_cast<T>(elem);
memcpy(buffer, &val, sizeof(T));
buffer += sizeof(T);
}
@@ -605,11 +734,9 @@ static char* fillList(char *buffer, const QList<QVariant> &list, T* = nullptr)
template<>
char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
{
- for (int i = 0; i < list.size(); ++i) {
- double val;
- float val2 = 0;
- val = qvariant_cast<double>(list.at(i));
- val2 = (float)val;
+ for (const auto &elem : list) {
+ double val = qvariant_cast<double>(elem);
+ float val2 = (float)val;
memcpy(buffer, &val2, sizeof(float));
buffer += sizeof(float);
}
@@ -644,7 +771,6 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
QMetaType::Type type, short curDim, ISC_ARRAY_DESC *arrayDesc,
QString& error)
{
- int i;
ISC_ARRAY_BOUND *bounds = arrayDesc->array_desc_bounds;
short dim = arrayDesc->array_desc_dimensions - 1;
@@ -659,14 +785,14 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
}
if (curDim != dim) {
- for(i = 0; i < list.size(); ++i) {
+ for (const auto &elem : list) {
- if (list.at(i).typeId() != QMetaType::QVariantList) { // dimensions mismatch
+ if (elem.typeId() != QMetaType::QVariantList) { // dimensions mismatch
error = "Array dimensons mismatch. Fieldname: %1"_L1;
return 0;
}
- buffer = createArrayBuffer(buffer, list.at(i).toList(), type, curDim + 1,
+ buffer = createArrayBuffer(buffer, elem.toList(), type, curDim + 1,
arrayDesc, error);
if (!buffer)
return 0;
@@ -693,29 +819,40 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
buffer = fillList<quint64>(buffer, list);
break;
case QMetaType::QString:
- for (i = 0; i < list.size(); ++i)
- buffer = qFillBufferWithString(buffer, list.at(i).toString(),
+ for (const auto &elem : list)
+ buffer = qFillBufferWithString(buffer, elem.toString(),
arrayDesc->array_desc_length,
arrayDesc->array_desc_dtype == blr_varying,
true);
break;
case QMetaType::QDate:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_DATE*)buffer) = toDate(list.at(i).toDate());
+ for (const auto &elem : list) {
+ *((ISC_DATE*)buffer) = toDate(elem.toDate());
buffer += sizeof(ISC_DATE);
}
break;
case QMetaType::QTime:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_TIME*)buffer) = toTime(list.at(i).toTime());
+ for (const auto &elem : list) {
+ *((ISC_TIME*)buffer) = toTime(elem.toTime());
buffer += sizeof(ISC_TIME);
}
break;
-
case QMetaType::QDateTime:
- for (i = 0; i < list.size(); ++i) {
- *((ISC_TIMESTAMP*)buffer) = toTimeStamp(list.at(i).toDateTime());
- buffer += sizeof(ISC_TIMESTAMP);
+ for (const auto &elem : list) {
+ switch (arrayDesc->array_desc_dtype) {
+ case blr_timestamp:
+ *((ISC_TIMESTAMP*)buffer) = toTimeStamp(elem.toDateTime());
+ buffer += sizeof(ISC_TIMESTAMP);
+ break;
+#if (FB_API_VER >= 40)
+ case blr_timestamp_tz:
+ *((ISC_TIMESTAMP_TZ*)buffer) = toTimeStampTz(elem.toDateTime());
+ buffer += sizeof(ISC_TIMESTAMP_TZ);
+ break;
+#endif
+ default:
+ break;
+ }
}
break;
case QMetaType::Bool:
@@ -728,7 +865,7 @@ static char* createArrayBuffer(char *buffer, const QList<QVariant> &list,
return buffer;
}
-bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
+bool QIBaseResultPrivate::writeArray(qsizetype column, const QList<QVariant> &list)
{
Q_Q(QIBaseResult);
QString error;
@@ -745,7 +882,6 @@ bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
short arraySize = 1;
ISC_LONG bufLen;
- QList<QVariant> subList = list;
short dimensions = desc.array_desc_dimensions;
for(int i = 0; i < dimensions; ++i) {
@@ -847,7 +983,7 @@ QIBaseResult::QIBaseResult(const QIBaseDriver *db)
bool QIBaseResult::prepare(const QString& query)
{
Q_D(QIBaseResult);
-// qDebug("prepare: %s", qPrintable(query));
+// qDebug("prepare: %ls", qUtf16Printable(query));
if (!driver() || !driver()->isOpen() || driver()->isOpenError())
return false;
d->cleanup();
@@ -856,13 +992,13 @@ bool QIBaseResult::prepare(const QString& query)
createDA(d->sqlda);
if (d->sqlda == (XSQLDA*)0) {
- qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ qCWarning(lcIbase) << "QIOBaseResult: createDA(): failed to allocate memory";
return false;
}
createDA(d->inda);
if (d->inda == (XSQLDA*)0){
- qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ qCWarning(lcIbase) << "QIOBaseResult: createDA(): failed to allocate memory";
return false;
}
@@ -886,7 +1022,7 @@ bool QIBaseResult::prepare(const QString& query)
if (d->inda->sqld > d->inda->sqln) {
enlargeDA(d->inda, d->inda->sqld);
if (d->inda == (XSQLDA*)0) {
- qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ qCWarning(lcIbase) << "QIOBaseResult: enlargeDA(): failed to allocate memory";
return false;
}
@@ -900,7 +1036,7 @@ bool QIBaseResult::prepare(const QString& query)
// need more field descriptors
enlargeDA(d->sqlda, d->sqlda->sqld);
if (d->sqlda == (XSQLDA*)0) {
- qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ qCWarning(lcIbase) << "QIOBaseResult: enlargeDA(): failed to allocate memory";
return false;
}
@@ -920,7 +1056,6 @@ bool QIBaseResult::prepare(const QString& query)
return true;
}
-
bool QIBaseResult::exec()
{
Q_D(QIBaseResult);
@@ -936,20 +1071,17 @@ bool QIBaseResult::exec()
if (d->inda) {
const QList<QVariant> &values = boundValues();
- int i;
if (values.count() > d->inda->sqld) {
- qWarning() << "QIBaseResult::exec: Parameter mismatch, expected"_L1 <<
- d->inda->sqld << ", got"_L1 << values.count() <<
- "parameters"_L1;
+ qCWarning(lcIbase) << "QIBaseResult::exec: Parameter mismatch, expected"_L1
+ << d->inda->sqld << ", got"_L1 << values.count()
+ << "parameters"_L1;
return false;
}
- int para = 0;
- for (i = 0; i < values.count(); ++i) {
- para = i;
+ for (qsizetype para = 0; para < values.count(); ++para) {
if (!d->inda->sqlvar[para].sqldata)
// skip unknown datatypes
continue;
- const QVariant val(values[i]);
+ const QVariant &val = values[para];
if (d->inda->sqlvar[para].sqltype & 1) {
if (QSqlResultPrivate::isVariantNull(val)) {
// set null indicator
@@ -961,6 +1093,12 @@ bool QIBaseResult::exec()
}
// a value of 0 means non-null.
*(d->inda->sqlvar[para].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
+ << ", which is not nullable."_L1;
+ }
}
switch(d->inda->sqlvar[para].sqltype & ~1) {
case SQL_INT64:
@@ -997,6 +1135,11 @@ bool QIBaseResult::exec()
case SQL_TIMESTAMP:
*((ISC_TIMESTAMP*)d->inda->sqlvar[para].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());
+ break;
+#endif
case SQL_TYPE_TIME:
*((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
break;
@@ -1019,8 +1162,8 @@ bool QIBaseResult::exec()
*((bool*)d->inda->sqlvar[para].sqldata) = val.toBool();
break;
default:
- qWarning("QIBaseResult::exec: Unknown datatype %d",
- d->inda->sqlvar[para].sqltype & ~1);
+ qCWarning(lcIbase, "QIBaseResult::exec: Unknown datatype %d",
+ d->inda->sqlvar[para].sqltype & ~1);
break;
}
}
@@ -1132,27 +1275,26 @@ 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;
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;
@@ -1180,32 +1322,18 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
case SQL_BOOLEAN:
row[idx] = QVariant(bool((*(bool*)buf)));
break;
+#if (FB_API_VER >= 40)
+ case SQL_TIMESTAMP_TZ:
+ row[idx] = fromTimeStampTz(buf);
+ break;
+#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;
@@ -1286,7 +1414,7 @@ int QIBaseResult::numRowsAffected()
bIsProcedure = true; // will sum all changes
break;
default:
- qWarning() << "numRowsAffected: Unknown statement type (" << d->queryType << ")";
+ qCWarning(lcIbase) << "numRowsAffected: Unknown statement type (" << d->queryType << ")";
return -1;
}
@@ -1352,7 +1480,6 @@ QSqlRecord QIBaseResult::record() const
f.setRequiredStatus(q.value(3).toBool() ? QSqlField::Required : QSqlField::Optional);
}
}
- f.setSqlType(v.sqltype);
rec.append(f);
}
return rec;
@@ -1408,26 +1535,26 @@ bool QIBaseDriver::hasFeature(DriverFeature f) const
return false;
}
-bool QIBaseDriver::open(const QString & db,
- const QString & user,
- const QString & password,
- const QString & host,
- int port,
- const QString & connOpts)
+bool QIBaseDriver::open(const QString &db,
+ const QString &user,
+ const QString &password,
+ const QString &host,
+ int port,
+ const QString &connOpts)
{
Q_D(QIBaseDriver);
if (isOpen())
close();
- const QStringList opts(connOpts.split(u';', Qt::SkipEmptyParts));
+ const auto opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
QByteArray role;
- for (int i = 0; i < opts.count(); ++i) {
- QString tmp(opts.at(i).simplified());
+ for (const auto &opt : opts) {
+ const auto tmp(opt.trimmed());
qsizetype idx;
if ((idx = tmp.indexOf(u'=')) != -1) {
- QString val = tmp.mid(idx + 1).simplified();
- QString opt = tmp.left(idx).simplified();
+ const auto val = tmp.mid(idx + 1).trimmed();
+ const auto opt = tmp.left(idx).trimmed().toString();
if (opt.toUpper() == "ISC_DPB_SQL_ROLE_NAME"_L1) {
role = val.toLocal8Bit();
role.truncate(255);
@@ -1478,6 +1605,14 @@ bool QIBaseDriver::open(const QString & db,
setOpen(true);
setOpenError(false);
+#if (FB_API_VER >= 40)
+ std::call_once(initTZMappingFlag, [d](){ d->initTZMappingCache(); });
+ if (lastError().isValid())
+ {
+ setOpen(true);
+ return false;
+ }
+#endif
return true;
}
@@ -1496,13 +1631,6 @@ void QIBaseDriver::close()
qFreeEventBuffer(eBuffer);
}
d->eventBuffers.clear();
-
-#if defined(FB_API_VER)
- // TODO check whether this workaround for Firebird crash is still needed
- QDeadlineTimer timer(500);
- while (!timer.hasExpired())
- QCoreApplication::processEvents();
-#endif
}
isc_detach_database(d->status, &d->ibase);
@@ -1587,8 +1715,8 @@ QStringList QIBaseDriver::tables(QSql::TableType type) const
q.setForwardOnly(true);
if (!q.exec("select rdb$relation_name from rdb$relations "_L1 + typeFilter))
return res;
- while(q.next())
- res << q.value(0).toString().simplified();
+ while (q.next())
+ res << q.value(0).toString().simplified();
return res;
}
@@ -1599,13 +1727,9 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
if (!isOpen())
return rec;
+ const QString table = stripDelimiters(tablename, QSqlDriver::TableName);
QSqlQuery q(createResult());
q.setForwardOnly(true);
- QString table = tablename;
- if (isIdentifierEscaped(table, QSqlDriver::TableName))
- table = stripDelimiters(table, QSqlDriver::TableName);
- else
- table = table.toUpper();
q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
"b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
"FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
@@ -1625,7 +1749,6 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
f.setPrecision(0);
}
f.setRequired(q.value(5).toInt() > 0);
- f.setSqlType(type);
rec.append(f);
}
@@ -1638,12 +1761,7 @@ QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
if (!isOpen())
return index;
- QString tablename = table;
- if (isIdentifierEscaped(tablename, QSqlDriver::TableName))
- tablename = stripDelimiters(tablename, QSqlDriver::TableName);
- else
- tablename = tablename.toUpper();
-
+ const QString tablename = stripDelimiters(table, QSqlDriver::TableName);
QSqlQuery q(createResult());
q.setForwardOnly(true);
q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
@@ -1738,13 +1856,13 @@ bool QIBaseDriver::subscribeToNotification(const QString &name)
{
Q_D(QIBaseDriver);
if (!isOpen()) {
- qWarning("QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
+ qCWarning(lcIbase, "QIBaseDriver::subscribeFromNotificationImplementation: database not open.");
return false;
}
if (d->eventBuffers.contains(name)) {
- qWarning("QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%s'.",
- qPrintable(name));
+ qCWarning(lcIbase, "QIBaseDriver::subscribeToNotificationImplementation: already subscribing to '%ls'.",
+ qUtf16Printable(name));
return false;
}
@@ -1785,13 +1903,13 @@ bool QIBaseDriver::unsubscribeFromNotification(const QString &name)
{
Q_D(QIBaseDriver);
if (!isOpen()) {
- qWarning("QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
+ qCWarning(lcIbase, "QIBaseDriver::unsubscribeFromNotificationImplementation: database not open.");
return false;
}
if (!d->eventBuffers.contains(name)) {
- qWarning("QIBaseDriver::QIBaseSubscriptionState not subscribed to '%s'.",
- qPrintable(name));
+ qCWarning(lcIbase, "QIBaseDriver::QIBaseSubscriptionState not subscribed to '%ls'.",
+ qUtf16Printable(name));
return false;
}
@@ -1846,8 +1964,8 @@ void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
(&qEventCallback)),
eBuffer->resultBuffer);
if (Q_UNLIKELY(status[0] == 1 && status[1])) {
- qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%s'",
- qPrintable(i.key()));
+ qCritical("QIBaseDriver::qHandleEventNotification: could not resubscribe to '%ls'",
+ qUtf16Printable(i.key()));
}
return;
@@ -1860,8 +1978,8 @@ QString QIBaseDriver::escapeIdentifier(const QString &identifier, IdentifierType
QString res = identifier;
if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"') ) {
res.replace(u'"', "\"\""_L1);
- res.prepend(u'"').append(u'"');
res.replace(u'.', "\".\""_L1);
+ res = u'"' + res + u'"';
}
return res;
}
@@ -1873,3 +1991,5 @@ int QIBaseDriver::maximumIdentifierLength(IdentifierType type) const
}
QT_END_NAMESPACE
+
+#include "moc_qsql_ibase_p.cpp"