summaryrefslogtreecommitdiffstats
path: root/src/plugins/sqldrivers/mimer/qsql_mimer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sqldrivers/mimer/qsql_mimer.cpp')
-rw-r--r--src/plugins/sqldrivers/mimer/qsql_mimer.cpp1610
1 files changed, 1610 insertions, 0 deletions
diff --git a/src/plugins/sqldrivers/mimer/qsql_mimer.cpp b/src/plugins/sqldrivers/mimer/qsql_mimer.cpp
new file mode 100644
index 0000000000..7f89e0a0d5
--- /dev/null
+++ b/src/plugins/sqldrivers/mimer/qsql_mimer.cpp
@@ -0,0 +1,1610 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Mimer Information Technology
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <qcoreapplication.h>
+#include <qvariant.h>
+#include <qmetatype.h>
+#include <qdatetime.h>
+#include <qloggingcategory.h>
+#include <qsqlerror.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+#include <qsqlrecord.h>
+#include <qsqlquery.h>
+#include <qsocketnotifier.h>
+#include <qstringlist.h>
+#include <qlocale.h>
+#if defined(Q_OS_WIN32)
+# include <QtCore/qt_windows.h>
+#endif
+#include <QtSql/private/qsqlresult_p.h>
+#include <QtSql/private/qsqldriver_p.h>
+#include "qsql_mimer.h"
+
+#define MIMER_DEFAULT_DATATYPE 1000
+
+Q_DECLARE_OPAQUE_POINTER(MimerSession)
+Q_DECLARE_METATYPE(MimerSession)
+
+Q_DECLARE_OPAQUE_POINTER(MimerStatement)
+Q_DECLARE_METATYPE(MimerStatement)
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcMimer, "qt.sql.mimer")
+
+enum class MimerColumnTypes {
+ Binary,
+ Clob,
+ Blob,
+ String,
+ Int,
+ Numeric,
+ Long,
+ Float,
+ Double,
+ Boolean,
+ Uuid,
+ Date,
+ Time,
+ Timestamp,
+ Unknown
+};
+
+using namespace Qt::StringLiterals;
+
+class QMimerSQLResultPrivate;
+
+class QMimerSQLResult final : public QSqlResult
+{
+ Q_DECLARE_PRIVATE(QMimerSQLResult)
+public:
+ QMimerSQLResult(const QMimerSQLDriver *db);
+ virtual ~QMimerSQLResult() override;
+ QVariant handle() const override;
+ static constexpr int genericError = -1;
+ static constexpr int lobChunkMaxSizeSet = 1048500;
+ static constexpr int lobChunkMaxSizeFetch = 65536;
+ static constexpr int maxStackStringSize = 200;
+ static constexpr int maxTimeStringSize = 18;
+ static constexpr int maxDateStringSize = 10;
+ static constexpr int maxTimestampStringSize = 29;
+
+private:
+ void cleanup();
+ bool fetch(int i) override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool fetchNext() override;
+ QVariant data(int i) override;
+ bool isNull(int index) override;
+ bool reset(const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ bool prepare(const QString &query) override;
+ bool execBatch(bool arrayBind = false) override;
+ bool exec() override;
+ qint64 currentRow();
+ QVariant lastInsertId() const override;
+};
+
+class QMimerSQLDriverPrivate final : public QSqlDriverPrivate
+{
+ Q_DECLARE_PUBLIC(QMimerSQLDriver)
+public:
+ QMimerSQLDriverPrivate() : QSqlDriverPrivate(QSqlDriver::MimerSQL), sessionhandle(nullptr) { }
+ MimerSession sessionhandle;
+ QString dbName;
+ QString dbUser;
+ void splitTableQualifier(const QString &qualifier, QString *schema, QString *table) const;
+};
+
+class QMimerSQLResultPrivate : public QSqlResultPrivate
+{
+ Q_DECLARE_PUBLIC(QMimerSQLResult)
+public:
+ Q_DECLARE_SQLDRIVER_PRIVATE(QMimerSQLDriver)
+ QMimerSQLResultPrivate(QMimerSQLResult *q, const QMimerSQLDriver *drv)
+ : QSqlResultPrivate(q, drv),
+ statementhandle(nullptr),
+ lobhandle(nullptr),
+ rowsAffected(0),
+ preparedQuery(false),
+ openCursor(false),
+ openStatement(false),
+ executedStatement(false),
+ callWithOut(false),
+ execBatch(false),
+ currentRow(QSql::BeforeFirstRow)
+ {
+ }
+ MimerStatement statementhandle;
+ MimerLob lobhandle;
+ int rowsAffected;
+ bool preparedQuery;
+ bool openCursor;
+ bool openStatement;
+ bool executedStatement;
+ bool callWithOut;
+ bool execBatch;
+ qint64 currentSize = -1;
+ qint64 currentRow; // Only used when forwardOnly()
+ QVector<QVariant> batch_vector;
+};
+
+static QSqlError qMakeError(const QString &err, const int errCode, QSqlError::ErrorType type,
+ const QMimerSQLDriverPrivate *p)
+{
+ QString msg;
+ if (p) {
+ size_t str_len;
+ int e_code;
+ int rc;
+ str_len = (rc = MimerGetError(p->sessionhandle, &e_code, NULL, 0)) + 1;
+ if (!MIMER_SUCCEEDED(rc)) {
+ msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
+ .arg(errCode);
+ } else {
+ QVarLengthArray<wchar_t> tmp_buff((qsizetype)str_len);
+ if (!MIMER_SUCCEEDED(
+ rc = MimerGetError(p->sessionhandle, &e_code, tmp_buff.data(), str_len)))
+ msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
+ .arg(errCode);
+ else
+ msg = QString::fromWCharArray(tmp_buff.data());
+ }
+ } else {
+ msg = QCoreApplication::translate("QMimerSQL", "Generic Mimer SQL error");
+ }
+
+ return QSqlError("QMIMER: "_L1 + err, msg, type, QString::number(errCode));
+}
+
+static QString msgCouldNotGet(const char *type, int column)
+{
+ //: Data type, column
+ return QCoreApplication::translate("QMimerSQLResult",
+ "Could not get %1, column %2").arg(QLatin1StringView(type)).arg(column);
+}
+
+static QString msgCouldNotSet(const char *type, int column)
+{
+ //: Data type, parameter
+ return QCoreApplication::translate("QMimerSQLResult",
+ "Could not set %1, parameter %2").arg(QLatin1StringView(type)).arg(column);
+}
+
+QMimerSQLDriver::QMimerSQLDriver(QObject *parent) : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
+{
+}
+
+QMimerSQLDriver::QMimerSQLDriver(MimerSession *conn, QObject *parent)
+ : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
+{
+ Q_D(QMimerSQLDriver);
+ if (conn)
+ d->sessionhandle = *conn;
+}
+
+QMimerSQLDriver::~QMimerSQLDriver()
+{
+ close();
+}
+
+QMimerSQLResult::QMimerSQLResult(const QMimerSQLDriver *db)
+ : QSqlResult(*new QMimerSQLResultPrivate(this, db))
+{
+ Q_D(QMimerSQLResult);
+ d->preparedQuery = db->hasFeature(QSqlDriver::PreparedQueries);
+}
+
+QMimerSQLResult::~QMimerSQLResult()
+{
+ cleanup();
+}
+
+static MimerColumnTypes mimerMapColumnTypes(int32_t t)
+{
+ switch (t) {
+ case MIMER_BINARY:
+ case MIMER_BINARY_VARYING:
+ return MimerColumnTypes::Binary;
+ case MIMER_BLOB:
+ case MIMER_NATIVE_BLOB:
+ return MimerColumnTypes::Blob;
+ case MIMER_CLOB:
+ case MIMER_NCLOB:
+ case MIMER_NATIVE_CLOB:
+ case MIMER_NATIVE_NCLOB:
+ return MimerColumnTypes::Clob;
+ case MIMER_DATE:
+ return MimerColumnTypes::Date;
+ case MIMER_TIME:
+ return MimerColumnTypes::Time;
+ case MIMER_TIMESTAMP:
+ return MimerColumnTypes::Timestamp;
+ case MIMER_INTERVAL_DAY:
+ case MIMER_INTERVAL_DAY_TO_HOUR:
+ case MIMER_INTERVAL_DAY_TO_MINUTE:
+ case MIMER_INTERVAL_DAY_TO_SECOND:
+ case MIMER_INTERVAL_HOUR:
+ case MIMER_INTERVAL_HOUR_TO_MINUTE:
+ case MIMER_INTERVAL_HOUR_TO_SECOND:
+ case MIMER_INTERVAL_MINUTE:
+ case MIMER_INTERVAL_MINUTE_TO_SECOND:
+ case MIMER_INTERVAL_MONTH:
+ case MIMER_INTERVAL_SECOND:
+ case MIMER_INTERVAL_YEAR:
+ case MIMER_INTERVAL_YEAR_TO_MONTH:
+ case MIMER_NCHAR:
+ case MIMER_CHARACTER:
+ case MIMER_CHARACTER_VARYING:
+ case MIMER_NCHAR_VARYING:
+ case MIMER_UTF8:
+ case MIMER_DEFAULT_DATATYPE:
+ return MimerColumnTypes::String;
+ case MIMER_INTEGER:
+ case MIMER_DECIMAL:
+ case MIMER_FLOAT:
+ return MimerColumnTypes::Numeric;
+ case MIMER_BOOLEAN:
+ return MimerColumnTypes::Boolean;
+ case MIMER_T_BIGINT:
+ case MIMER_T_UNSIGNED_BIGINT:
+ case MIMER_NATIVE_BIGINT_NULLABLE:
+ case MIMER_NATIVE_BIGINT:
+ return MimerColumnTypes::Long;
+ case MIMER_NATIVE_REAL_NULLABLE:
+ case MIMER_NATIVE_REAL:
+ case MIMER_T_REAL:
+ return MimerColumnTypes::Float;
+ case MIMER_T_FLOAT:
+ case MIMER_NATIVE_DOUBLE_NULLABLE:
+ case MIMER_NATIVE_DOUBLE:
+ case MIMER_T_DOUBLE:
+ return MimerColumnTypes::Double;
+ case MIMER_NATIVE_INTEGER:
+ case MIMER_NATIVE_INTEGER_NULLABLE:
+ case MIMER_NATIVE_SMALLINT_NULLABLE:
+ case MIMER_NATIVE_SMALLINT:
+ case MIMER_T_INTEGER:
+ case MIMER_T_SMALLINT:
+ return MimerColumnTypes::Int;
+ case MIMER_UUID:
+ return MimerColumnTypes::Uuid;
+ default:
+ qCWarning(lcMimer) << "QMimerSQLDriver::mimerMapColumnTypes: Unknown data type:" << t;
+ }
+ return MimerColumnTypes::Unknown;
+}
+
+static QMetaType::Type qDecodeMSQLType(int32_t t)
+{
+ switch (t) {
+ case MIMER_BINARY:
+ case MIMER_BINARY_VARYING:
+ case MIMER_BLOB:
+ case MIMER_NATIVE_BLOB:
+ return QMetaType::QByteArray;
+ case MIMER_CLOB:
+ case MIMER_NCLOB:
+ case MIMER_NATIVE_CLOB:
+ case MIMER_NATIVE_NCLOB:
+ case MIMER_INTERVAL_DAY:
+ case MIMER_DECIMAL:
+ case MIMER_INTERVAL_DAY_TO_HOUR:
+ case MIMER_INTERVAL_DAY_TO_MINUTE:
+ case MIMER_INTERVAL_DAY_TO_SECOND:
+ case MIMER_INTERVAL_HOUR:
+ case MIMER_INTERVAL_HOUR_TO_MINUTE:
+ case MIMER_INTERVAL_HOUR_TO_SECOND:
+ case MIMER_INTERVAL_MINUTE:
+ case MIMER_INTERVAL_MINUTE_TO_SECOND:
+ case MIMER_INTERVAL_MONTH:
+ case MIMER_INTERVAL_SECOND:
+ case MIMER_INTERVAL_YEAR:
+ case MIMER_INTERVAL_YEAR_TO_MONTH:
+ case MIMER_NCHAR:
+ case MIMER_CHARACTER:
+ case MIMER_CHARACTER_VARYING:
+ case MIMER_NCHAR_VARYING:
+ case MIMER_UTF8:
+ case MIMER_DEFAULT_DATATYPE:
+ case MIMER_INTEGER:
+ case MIMER_FLOAT:
+ return QMetaType::QString;
+ case MIMER_BOOLEAN:
+ return QMetaType::Bool;
+ case MIMER_T_BIGINT:
+ case MIMER_T_UNSIGNED_BIGINT:
+ case MIMER_NATIVE_BIGINT_NULLABLE:
+ case MIMER_NATIVE_BIGINT:
+ return QMetaType::LongLong;
+ case MIMER_NATIVE_REAL_NULLABLE:
+ case MIMER_NATIVE_REAL:
+ case MIMER_T_REAL:
+ return QMetaType::Float;
+ case MIMER_T_FLOAT:
+ case MIMER_NATIVE_DOUBLE_NULLABLE:
+ case MIMER_NATIVE_DOUBLE:
+ case MIMER_T_DOUBLE:
+ return QMetaType::Double;
+ case MIMER_NATIVE_INTEGER_NULLABLE:
+ case MIMER_T_INTEGER:
+ case MIMER_NATIVE_INTEGER:
+ return QMetaType::Int;
+ case MIMER_NATIVE_SMALLINT_NULLABLE:
+ case MIMER_T_SMALLINT:
+ return QMetaType::Int;
+ case MIMER_DATE:
+ return QMetaType::QDate;
+ case MIMER_TIME:
+ return QMetaType::QTime;
+ break;
+ case MIMER_TIMESTAMP:
+ return QMetaType::QDateTime;
+ case MIMER_UUID:
+ return QMetaType::QUuid;
+ default:
+ qCWarning(lcMimer) << "QMimerSQLDriver::qDecodeMSQLType: Unknown data type:" << t;
+ return QMetaType::UnknownType;
+ }
+}
+
+static int32_t qLookupMimDataType(QStringView s)
+{
+ if (s == u"BINARY")
+ return MIMER_BINARY;
+ if (s == u"BINARY VARYING")
+ return MIMER_BINARY_VARYING;
+ if (s == u"BINARY LARGE OBJECT")
+ return MIMER_BLOB;
+ if (s == u"CHARACTER LARGE OBJECT")
+ return MIMER_CLOB;
+ if (s == u"NATIONAL CHAR LARGE OBJECT")
+ return MIMER_NCLOB;
+ if (s == u"INTERVAL DAY")
+ return MIMER_INTERVAL_DAY;
+ if (s == u"DECIMAL")
+ return MIMER_DECIMAL;
+ if (s == u"INTERVAL DAY TO HOUR")
+ return MIMER_INTERVAL_DAY_TO_HOUR;
+ if (s == u"INTERVAL DAY TO MINUTE")
+ return MIMER_INTERVAL_DAY_TO_MINUTE;
+ if (s == u"INTERVAL DAY TO SECOND")
+ return MIMER_INTERVAL_DAY_TO_SECOND;
+ if (s == u"INTERVAL HOUR")
+ return MIMER_INTERVAL_HOUR;
+ if (s == u"INTERVAL HOUR TO MINUTE")
+ return MIMER_INTERVAL_HOUR_TO_MINUTE;
+ if (s == u"INTERVAL HOUR TO SECOND")
+ return MIMER_INTERVAL_HOUR_TO_SECOND;
+ if (s == u"INTERVAL MINUTE")
+ return MIMER_INTERVAL_MINUTE;
+ if (s == u"INTERVAL MINUTE TO SECOND")
+ return MIMER_INTERVAL_MINUTE_TO_SECOND;
+ if (s == u"INTERVAL MONTH")
+ return MIMER_INTERVAL_MONTH;
+ if (s == u"INTERVAL SECOND")
+ return MIMER_INTERVAL_SECOND;
+ if (s == u"INTERVAL YEAR")
+ return MIMER_INTERVAL_YEAR;
+ if (s == u"INTERVAL YEAR TO MONTH")
+ return MIMER_INTERVAL_YEAR_TO_MONTH;
+ if (s == u"NATIONAL CHARACTER")
+ return MIMER_NCHAR;
+ if (s == u"CHARACTER")
+ return MIMER_CHARACTER;
+ if (s == u"CHARACTER VARYING")
+ return MIMER_CHARACTER_VARYING;
+ if (s == u"NATIONAL CHARACTER VARYING")
+ return MIMER_NCHAR_VARYING;
+ if (s == u"UTF-8")
+ return MIMER_UTF8;
+ if (s == u"BOOLEAN")
+ return MIMER_BOOLEAN;
+ if (s == u"BIGINT")
+ return MIMER_T_BIGINT;
+ if (s == u"REAL")
+ return MIMER_T_REAL;
+ if (s == u"FLOAT")
+ return MIMER_T_FLOAT;
+ if (s == u"DOUBLE PRECISION")
+ return MIMER_T_DOUBLE;
+ if (s == u"INTEGER")
+ return MIMER_T_INTEGER;
+ if (s == u"SMALLINT")
+ return MIMER_T_SMALLINT;
+ if (s == u"DATE")
+ return MIMER_DATE;
+ if (s == u"TIME")
+ return MIMER_TIME;
+ if (s == u"TIMESTAMP")
+ return MIMER_TIMESTAMP;
+ if (s == u"BUILTIN.UUID")
+ return MIMER_UUID;
+ if (s == u"USER-DEFINED")
+ return MIMER_DEFAULT_DATATYPE;
+ qCWarning(lcMimer) << "QMimerSQLDriver::qLookupMimDataType: Unhandled data type:" << s;
+ return MIMER_DEFAULT_DATATYPE;
+}
+
+QVariant QMimerSQLResult::handle() const
+{
+ Q_D(const QMimerSQLResult);
+ return QVariant::fromValue(d->statementhandle);
+}
+
+void QMimerSQLResult::cleanup()
+{
+ Q_D(QMimerSQLResult);
+ if (!driver() || !driver()->isOpen()) {
+ d->openCursor = false;
+ d->openStatement = false;
+ return;
+ }
+ if (d->openCursor) {
+ const int32_t err = MimerCloseCursor(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err))
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not close cursor"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ d->openCursor = false;
+ }
+ if (d->openStatement) {
+ const int32_t err = MimerEndStatement(&d->statementhandle);
+ if (!MIMER_SUCCEEDED(err))
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not close statement"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ d->openStatement = false;
+ }
+ d->currentSize = -1;
+}
+
+qint64 QMimerSQLResult::currentRow()
+{
+ Q_D(const QMimerSQLResult);
+ return d->currentRow;
+}
+
+bool QMimerSQLResult::fetch(int i)
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (i == at())
+ return true;
+ if (i < 0)
+ return false;
+
+ if (isForwardOnly() && i < at())
+ return false;
+
+ if (isForwardOnly()) {
+ bool rc;
+ do {
+ rc = fetchNext();
+ } while (rc && currentRow() < i);
+ return rc;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, i + 1);
+ if (err == MIMER_NO_DATA)
+ return false;
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(QCoreApplication::translate("QMimerSQLResult", "Fetch did not succeed"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ setAt(MimerCurrentRow(d->statementhandle) - 1);
+ return true;
+}
+
+bool QMimerSQLResult::fetchFirst()
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly()) {
+ if (currentRow() < 0)
+ return fetchNext();
+ else if (currentRow() == 0)
+ setAt(0);
+ else
+ return false;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, MIMER_FIRST, 0);
+ if (MIMER_SUCCEEDED(err) && err != MIMER_NO_DATA)
+ setAt(0);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Fetch first did not succeed"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ if (err == MIMER_NO_DATA)
+ return false;
+ return true;
+}
+
+bool QMimerSQLResult::fetchLast()
+{
+ Q_D(const QMimerSQLResult);
+ int32_t err = 0;
+ int row = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly()) {
+ bool rc;
+ do {
+ rc = fetchNext();
+ } while (rc);
+
+ return currentRow() >= 0;
+ } else {
+ err = MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
+ if (err == MIMER_NO_DATA)
+ return false;
+ if (MIMER_SUCCEEDED(err)) {
+ row = MimerCurrentRow(d->statementhandle) - 1;
+ } else {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Fetch last did not succeed"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ }
+
+ if (row < 0) {
+ setAt(QSql::BeforeFirstRow);
+ return false;
+ } else {
+ setAt(row);
+ return true;
+ }
+}
+
+bool QMimerSQLResult::fetchNext()
+{
+ Q_D(QMimerSQLResult);
+ int32_t err = 0;
+ if (!isActive() || !isSelect())
+ return false;
+ if (isForwardOnly())
+ err = MimerFetch(d->statementhandle);
+ else
+ err = MimerFetchScroll(d->statementhandle, MIMER_NEXT, 0);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not fetch next row"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ if (isForwardOnly())
+ d->currentRow = QSql::BeforeFirstRow;
+ return false;
+ }
+ if (err == MIMER_NO_DATA)
+ return false;
+ if (isForwardOnly())
+ setAt(++d->currentRow);
+ else
+ setAt(MimerCurrentRow(d->statementhandle) - 1);
+ return true;
+}
+
+QVariant QMimerSQLResult::data(int i)
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ int32_t mType;
+ if (d->callWithOut) {
+ if (i >= MimerParameterCount(d->statementhandle)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return QVariant();
+ }
+ mType = MimerParameterType(d->statementhandle, static_cast<std::int16_t>(i + 1));
+ } else {
+ if (i >= MimerColumnCount(d->statementhandle)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return QVariant();
+ }
+ mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i + 1));
+ }
+ const QMetaType::Type type = qDecodeMSQLType(mType);
+ const MimerColumnTypes mimDataType = mimerMapColumnTypes(mType);
+ err = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(i + 1));
+ if (err > 0) {
+ return QVariant(QMetaType(type), nullptr);
+ } else {
+ switch (mimDataType) {
+ case MimerColumnTypes::Date: {
+ wchar_t dateString_w[maxDateStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), dateString_w,
+ sizeof(dateString_w) / sizeof(dateString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("date", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return QDate::fromString(QString::fromWCharArray(dateString_w), "yyyy-MM-dd"_L1);
+ }
+ case MimerColumnTypes::Time: {
+ wchar_t timeString_w[maxTimeStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), timeString_w,
+ sizeof(timeString_w) / sizeof(timeString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ QString timeString = QString::fromWCharArray(timeString_w);
+ QString timeFormatString = "HH:mm:ss"_L1;
+ if (timeString.size() > 8) {
+ timeFormatString.append(".zzz"_L1);
+ timeString = timeString.left(12);
+ }
+ return QTime::fromString(timeString, timeFormatString);
+ }
+ case MimerColumnTypes::Timestamp: {
+ wchar_t dateTimeString_w[maxTimestampStringSize + 1];
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
+ dateTimeString_w,
+ sizeof(dateTimeString_w) / sizeof(dateTimeString_w[0]));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("date time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ QString dateTimeString = QString::fromWCharArray(dateTimeString_w);
+ QString dateTimeFormatString = "yyyy-MM-dd HH:mm:ss"_L1;
+ if (dateTimeString.size() > 19) {
+ dateTimeFormatString.append(".zzz"_L1);
+ dateTimeString = dateTimeString.left(23);
+ }
+ return QDateTime::fromString(dateTimeString, dateTimeFormatString);
+ }
+ case MimerColumnTypes::Int: {
+ int resInt;
+ err = MimerGetInt32(d->statementhandle, static_cast<std::int16_t>(i + 1), &resInt);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("int32", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return resInt;
+ }
+ case MimerColumnTypes::Long: {
+ int64_t resLongLong;
+ err = MimerGetInt64(d->statementhandle, static_cast<std::int16_t>(i + 1), &resLongLong);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("int64", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return (qlonglong)resLongLong;
+ }
+ case MimerColumnTypes::Boolean: {
+ err = MimerGetBoolean(d->statementhandle, static_cast<std::int16_t>(i + 1));
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("boolean", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return err == 1;
+ }
+ case MimerColumnTypes::Float: {
+ float resFloat;
+ err = MimerGetFloat(d->statementhandle, static_cast<std::int16_t>(i + 1), &resFloat);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("float", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return resFloat;
+ }
+ case MimerColumnTypes::Double: {
+ double resDouble;
+ err = MimerGetDouble(d->statementhandle, static_cast<std::int16_t>(i + 1), &resDouble);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("double", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ switch (numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ return static_cast<std::int32_t>(resDouble);
+ case QSql::LowPrecisionInt64:
+ return static_cast<qint64>(resDouble);
+ case QSql::LowPrecisionDouble:
+ return static_cast<qreal>(resDouble);
+ case QSql::HighPrecision:
+ return QString::number(resDouble, 'g', 17);
+ }
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Binary: {
+ QByteArray byteArray;
+ // Get size
+ err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i + 1), NULL, 0);
+ if (MIMER_SUCCEEDED(err)) {
+ byteArray.resize(err);
+ err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i + 1),
+ byteArray.data(), err);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotGet("binary", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return byteArray;
+ }
+ case MimerColumnTypes::Blob: {
+ QByteArray byteArray;
+ size_t size;
+ err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i + 1), &size,
+ &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeFetch;
+ QVarLengthArray<char> blobchar(lobChunkMaxSizeFetch);
+ byteArray.reserve(size);
+ size_t left_to_return = size;
+ while (left_to_return > 0) {
+ const size_t bytesToReceive =
+ left_to_return <= maxSize ? left_to_return : maxSize;
+ err = MimerGetBlobData(&d->lobhandle, blobchar.data(), bytesToReceive);
+ byteArray.append(QByteArray::fromRawData(blobchar.data(), bytesToReceive));
+ left_to_return -= bytesToReceive;
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("BLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ }
+ } else {
+ setLastError(qMakeError(msgCouldNotGet("BLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ return byteArray;
+ }
+ case MimerColumnTypes::Numeric:
+ case MimerColumnTypes::String: {
+ wchar_t resString_w[maxStackStringSize + 1];
+ // Get size
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), resString_w,
+ 0);
+ if (MIMER_SUCCEEDED(err)) {
+ int size = err;
+ if (err <= maxStackStringSize) { // For smaller strings, use a small buffer for
+ // efficiency
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
+ resString_w, maxStackStringSize + 1);
+ if (MIMER_SUCCEEDED(err))
+ return QString::fromWCharArray(resString_w);
+ } else { // For larger strings, dynamically allocate memory
+ QVarLengthArray<wchar_t> largeResString_w(size + 1);
+ err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
+ largeResString_w.data(), size + 1);
+ if (MIMER_SUCCEEDED(err))
+ return QString::fromWCharArray(largeResString_w.data());
+ }
+ }
+ setLastError(qMakeError(msgCouldNotGet(
+ mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Clob: {
+ size_t size;
+ err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i + 1), &size,
+ &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeFetch;
+ QVarLengthArray<wchar_t> clobstring_w(lobChunkMaxSizeFetch + 1);
+
+ size_t left_to_return = size;
+ QString returnString;
+ while (left_to_return > 0) {
+ const size_t bytesToReceive =
+ left_to_return <= maxSize ? left_to_return : maxSize;
+ err = MimerGetNclobData(&d->lobhandle, clobstring_w.data(), bytesToReceive + 1);
+ returnString.append(QString::fromWCharArray(clobstring_w.data()));
+ left_to_return -= bytesToReceive;
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ }
+ return returnString;
+ }
+ setLastError(qMakeError(msgCouldNotGet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ case MimerColumnTypes::Uuid: {
+ unsigned char uuidChar[16];
+ err = MimerGetUUID(d->statementhandle, static_cast<std::int16_t>(i + 1), uuidChar);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotGet("UUID", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return QVariant(QMetaType(type), nullptr);
+ }
+ const QByteArray uuidByteArray = QByteArray(reinterpret_cast<char *>(uuidChar), 16);
+ return QUuid::fromRfc4122(uuidByteArray);
+ }
+ case MimerColumnTypes::Unknown:
+ default:
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Unknown data type %1").arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ }
+ return QVariant(QMetaType(type), nullptr);
+ }
+}
+
+bool QMimerSQLResult::isNull(int index)
+{
+ Q_D(const QMimerSQLResult);
+ const int32_t rc = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(index + 1));
+ if (!MIMER_SUCCEEDED(rc)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not check null, column %1")
+ .arg(index),
+ rc, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ return rc != 0;
+}
+
+bool QMimerSQLResult::reset(const QString &query)
+{
+ if (!prepare(query))
+ return false;
+ return exec();
+}
+
+int QMimerSQLResult::size()
+{
+ Q_D(QMimerSQLResult);
+ if (!isActive() || !isSelect() || isForwardOnly())
+ return -1;
+
+ if (d->currentSize != -1)
+ return d->currentSize;
+
+ const int currentRow = MimerCurrentRow(d->statementhandle);
+ MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
+ int size = MimerCurrentRow(d->statementhandle);
+ if (!MIMER_SUCCEEDED(size))
+ size = -1;
+ MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, currentRow);
+ d->currentSize = size;
+ return size;
+}
+
+int QMimerSQLResult::numRowsAffected()
+{
+ Q_D(const QMimerSQLResult);
+ return d->rowsAffected;
+}
+
+QSqlRecord QMimerSQLResult::record() const
+{
+ Q_D(const QMimerSQLResult);
+ QSqlRecord rec;
+ if (!isActive() || !isSelect() || !driver())
+ return rec;
+ QSqlField field;
+ const int colSize = MimerColumnCount(d->statementhandle);
+ for (int i = 0; i < colSize; i++) {
+ wchar_t colName_w[100];
+ MimerColumnName(d->statementhandle, static_cast<std::int16_t>(i + 1), colName_w,
+ sizeof(colName_w) / sizeof(colName_w[0]));
+ field.setName(QString::fromWCharArray(colName_w));
+ const int32_t mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i + 1));
+ const QMetaType::Type type = qDecodeMSQLType(mType);
+ field.setMetaType(QMetaType(type));
+ field.setValue(QVariant(field.metaType()));
+ // field.setPrecision(); Should be implemented once the Mimer API can give this
+ // information.
+ // field.setLength(); Should be implemented once the Mimer API can give
+ // this information.
+ rec.insert(i, field);
+ }
+ return rec;
+}
+
+bool QMimerSQLResult::prepare(const QString &query)
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ if (!driver())
+ return false;
+ if (!d->preparedQuery)
+ return QSqlResult::prepare(query);
+ if (query.isEmpty())
+ return false;
+ cleanup();
+ const int option = isForwardOnly() ? MIMER_FORWARD_ONLY : MIMER_SCROLLABLE;
+ err = MimerBeginStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData(), option,
+ &d->statementhandle);
+ if (err == MIMER_STATEMENT_CANNOT_BE_PREPARED) {
+ err = MimerExecuteStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData());
+ if (MIMER_SUCCEEDED(err)) {
+ d->executedStatement = true;
+ d->openCursor = false;
+ d->openStatement = false;
+ return true;
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(QCoreApplication::translate("QMimerSQLResult",
+ "Could not prepare/execute statement"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ d->openStatement = true;
+ return true;
+}
+
+bool QMimerSQLResult::exec()
+{
+ Q_D(QMimerSQLResult);
+ int32_t err;
+ if (!driver())
+ return false;
+ if (!d->preparedQuery)
+ return QSqlResult::exec();
+ if (d->executedStatement) {
+ d->executedStatement = false;
+ return true;
+ }
+ if (d->openCursor) {
+ setAt(QSql::BeforeFirstRow);
+ err = MimerCloseCursor(d->statementhandle);
+ d->openCursor = false;
+ d->currentSize = -1;
+ }
+ QVector<QVariant> &values = boundValues();
+ if (d->execBatch)
+ values = d->batch_vector;
+ int mimParamCount = MimerParameterCount(d->statementhandle);
+ if (!MIMER_SUCCEEDED(mimParamCount))
+ mimParamCount = 0;
+ if (mimParamCount != values.size()) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Wrong number of parameters"),
+ genericError, QSqlError::StatementError, nullptr));
+ return false;
+ }
+ for (int i = 0; i < mimParamCount; i++) {
+ if (bindValueType(i) == QSql::Out) {
+ d->callWithOut = true;
+ continue;
+ }
+ const QVariant &val = values.at(i);
+ if (QSqlResultPrivate::isVariantNull(val) || val.isNull() || val.toString().isNull()) {
+ err = MimerSetNull(d->statementhandle, i + 1);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("null", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ continue;
+ }
+
+ const int mimParamType = MimerParameterType(d->statementhandle, i + 1);
+ const MimerColumnTypes mimDataType = mimerMapColumnTypes(mimParamType);
+ switch (mimDataType) {
+ case MimerColumnTypes::Int: {
+ bool convertOk;
+ err = MimerSetInt32(d->statementhandle, i + 1, val.toInt(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("int32", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Long: {
+ bool convertOk;
+ err = MimerSetInt64(d->statementhandle, i + 1, val.toLongLong(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("int64", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Float: {
+ bool convertOk;
+ err = MimerSetFloat(d->statementhandle, i + 1, val.toFloat(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("float", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Double: {
+ bool convertOk;
+ err = MimerSetDouble(d->statementhandle, i + 1, val.toDouble(&convertOk));
+ if (!convertOk || !MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("double", i),
+ convertOk ? err : genericError, QSqlError::StatementError,
+ convertOk ? d->drv_d_func() : nullptr));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Binary: {
+ const QByteArray binArr = val.toByteArray();
+ size_t size = static_cast<std::size_t>(binArr.size());
+ err = MimerSetBinary(d->statementhandle, i + 1, binArr.data(), size);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("binary", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Boolean: {
+ err = MimerSetBoolean(d->statementhandle, i + 1, val.toBool() == true ? 1 : 0);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("boolean", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Uuid: {
+ const QByteArray uuidArray =
+ QByteArray::fromHex(val.toUuid().toString(QUuid::WithoutBraces).toLatin1());
+ const unsigned char *uuid =
+ reinterpret_cast<const unsigned char *>(uuidArray.constData());
+ err = MimerSetUUID(d->statementhandle, i + 1, uuid);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("UUID", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Numeric:
+ case MimerColumnTypes::String: {
+ QByteArray string_b = val.toString().trimmed().toUtf8();
+ const char *string_u = string_b.constData();
+ err = MimerSetString8(d->statementhandle, i + 1, string_u);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet(
+ mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Date: {
+ err = MimerSetString8(d->statementhandle, i + 1, val.toString().toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("date", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Time: {
+ QString timeFormatString = "hh:mm:ss"_L1;
+ const QTime timeVal = val.toTime();
+ if (timeVal.msec() > 0)
+ timeFormatString.append(".zzz"_L1);
+ err = MimerSetString8(d->statementhandle, i + 1,
+ timeVal.toString(timeFormatString).toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("time", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Timestamp: {
+ QString dateTimeFormatString = "yyyy-MM-dd hh:mm:ss"_L1;
+ const QDateTime dateTimeVal = val.toDateTime();
+ if (dateTimeVal.time().msec() > 0)
+ dateTimeFormatString.append(".zzz"_L1);
+ err = MimerSetString8(
+ d->statementhandle, i + 1,
+ val.toDateTime().toString(dateTimeFormatString).toUtf8().constData());
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("datetime", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Blob: {
+ QByteArray blobArr = val.toByteArray();
+ const char *blobData = blobArr.constData();
+ qsizetype size = blobArr.size();
+ err = MimerSetLob(d->statementhandle, i + 1, size, &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ qsizetype maxSize = lobChunkMaxSizeSet;
+ if (size > maxSize) {
+ qsizetype left_to_send = size;
+ for (qsizetype k = 0; left_to_send > 0; k++) {
+ if (left_to_send <= maxSize) {
+ err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize],
+ left_to_send);
+ left_to_send = 0;
+ } else {
+ err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize], maxSize);
+ left_to_send = left_to_send - maxSize;
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("BLOB byte array", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ } else {
+ err = MimerSetBlobData(&d->lobhandle, blobArr, size);
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("BLOB byte array", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Clob: {
+ QByteArray string_b = val.toString().trimmed().toUtf8();
+ const char *string_u = string_b.constData();
+ size_t size_c = 1;
+ size_t size = 0;
+ while (string_u[size++])
+ if ((string_u[size] & 0xc0) != 0x80)
+ size_c++;
+ err = MimerSetLob(d->statementhandle, i + 1, size_c, &d->lobhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ constexpr size_t maxSize = lobChunkMaxSizeSet;
+ if (size > maxSize) {
+ size_t left_to_send = size;
+ size_t pos = 0;
+ uint step_back = 0;
+ while (left_to_send > 0 && step_back < maxSize) {
+ step_back = 0;
+ if (left_to_send <= maxSize) {
+ err = MimerSetNclobData8(&d->lobhandle, &string_u[pos], left_to_send);
+ left_to_send = 0;
+ } else {
+ // Check that we don't split a multi-byte utf-8 characters
+ while (pos + maxSize - step_back > 0
+ && (string_u[pos + maxSize - step_back] & 0xc0) == 0x80)
+ step_back++;
+ err = MimerSetNclobData8(&d->lobhandle, &string_u[pos],
+ maxSize - step_back);
+ left_to_send = left_to_send - maxSize + step_back;
+ pos += maxSize - step_back;
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(msgCouldNotSet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ }
+ } else {
+ err = MimerSetNclobData8(&d->lobhandle, string_u, size);
+ }
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(msgCouldNotSet("CLOB", i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ break;
+ }
+ case MimerColumnTypes::Unknown:
+ default:
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Unknown datatype, parameter %1")
+ .arg(i),
+ genericError, QSqlError::StatementError, nullptr));
+ return false;
+ }
+ }
+ if (d->execBatch)
+ return true;
+ err = MimerExecute(d->statementhandle);
+ if (MIMER_SUCCEEDED(err)) {
+ d->rowsAffected = err;
+ int k = 0;
+ for (qsizetype i = 0; i < values.size(); i++) {
+ if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
+ bindValue(i, data(k), QSql::In);
+ k++;
+ }
+ }
+ d->callWithOut = false;
+ }
+ setSelect(false);
+ if (MIMER_SEQUENCE_ERROR == err) {
+ err = MimerOpenCursor(d->statementhandle);
+ d->rowsAffected = err;
+ d->openCursor = true;
+ d->currentRow = QSql::BeforeFirstRow;
+ setSelect(true);
+ }
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(
+ qMakeError(QCoreApplication::translate("QMimerSQLResult",
+ "Could not execute statement/open cursor"),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ setActive(true);
+ return true;
+}
+
+bool QMimerSQLResult::execBatch(bool arrayBind)
+{
+ Q_D(QMimerSQLResult);
+ Q_UNUSED(arrayBind);
+ int32_t err;
+ const QVector<QVariant> values = boundValues();
+
+ // Check that we only have input parameters. Currently
+ // we can only handle batch operations without output parameters.
+ for (qsizetype i = 0; i < values.first().toList().size(); i++)
+ if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
+ setLastError(qMakeError(QCoreApplication::translate(
+ "QMimerSQLResult",
+ "Only input parameters can be used in batch operations"),
+ genericError, QSqlError::StatementError, nullptr));
+ d->execBatch = false;
+ return false;
+ }
+ d->execBatch = true;
+ for (qsizetype i = 0; i < values.first().toList().size(); i++) {
+ for (qsizetype j = 0; j < values.size(); j++)
+ d->batch_vector.append(values.at(j).toList().at(i));
+ exec();
+ if (i != (values.at(0).toList().size() - 1)) {
+ err = MimerAddBatch(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ //: %1 is the batch number
+ QCoreApplication::translate("QMimerSQLResult", "Could not add batch %1")
+ .arg(i),
+ err, QSqlError::StatementError, d->drv_d_func()));
+ d->execBatch = false;
+ return false;
+ }
+ }
+ d->batch_vector.clear();
+ }
+ d->execBatch = false;
+ err = MimerExecute(d->statementhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLResult", "Could not execute batch"), err,
+ QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+ return true;
+}
+
+QVariant QMimerSQLResult::lastInsertId() const
+{
+ Q_D(const QMimerSQLResult);
+ int64_t lastSequence;
+ const int32_t err = MimerGetSequenceInt64(d->statementhandle, &lastSequence);
+ if (!MIMER_SUCCEEDED(err))
+ return QVariant(QMetaType(QMetaType::LongLong), nullptr);
+ return QVariant(qint64(lastSequence));
+}
+
+bool QMimerSQLDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case NamedPlaceholders: // Is true in reality but Qt parses Sql statement...
+ case EventNotifications:
+ case LowPrecisionNumbers:
+ case MultipleResultSets:
+ case SimpleLocking:
+ case CancelQuery:
+ return false;
+ case FinishQuery:
+ case LastInsertId:
+ case Transactions:
+ case QuerySize:
+ case BLOB:
+ case Unicode:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case BatchOperations:
+ return true;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::open(const QString &db, const QString &user, const QString &password,
+ const QString &host, int port, const QString &connOpts)
+{
+ Q_D(QMimerSQLDriver);
+ Q_UNUSED(host);
+ Q_UNUSED(port);
+ Q_UNUSED(connOpts);
+ if (isOpen())
+ close();
+ const int32_t err = MimerBeginSession8(db.toUtf8().constData(), user.toUtf8().constData(),
+ password.toUtf8().constData(), &d->sessionhandle);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not connect to database")
+ + " "_L1 + db,
+ err, QSqlError::ConnectionError, nullptr));
+ setOpenError(true);
+ return false;
+ }
+ d->dbUser = user;
+ d->dbName = db;
+ setOpen(true);
+ setOpenError(false);
+ return true;
+}
+
+void QMimerSQLDriver::close()
+{
+ Q_D(QMimerSQLDriver);
+ if (isOpen()) {
+ const int end_err = MimerEndSession(&d->sessionhandle);
+ if (MIMER_SUCCEEDED(end_err)) {
+ setOpen(false);
+ setOpenError(false);
+ }
+ }
+}
+
+QSqlResult *QMimerSQLDriver::createResult() const
+{
+ return new QMimerSQLResult(this);
+}
+
+QStringList QMimerSQLDriver::tables(QSql::TableType type) const
+{
+ QStringList tl;
+ if (!isOpen())
+ return tl;
+ QSqlQuery t(createResult());
+ QString sql;
+ switch (type) {
+ case QSql::Tables: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'BASE TABLE\' AND table_schema = CURRENT_USER"_L1;
+ break;
+ }
+ case QSql::SystemTables: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'BASE TABLE\' AND table_schema = \'SYSTEM\'"_L1;
+ break;
+ }
+ case QSql::Views: {
+ sql = "select table_name from information_schema.tables where "
+ "table_type=\'VIEW\' AND table_schema = CURRENT_USER"_L1;
+ break;
+ }
+ case QSql::AllTables: {
+ sql = "select table_name from information_schema.tables where "
+ "(table_type=\'VIEW\' or table_type=\'BASE TABLE\')"
+ " AND (table_schema = CURRENT_USER OR table_schema =\'SYSTEM\')"_L1;
+ break;
+ }
+ default:
+ break;
+ }
+ if (sql.length() > 0) {
+ t.exec(sql);
+ while (t.next())
+ tl.append(t.value(0).toString());
+ }
+ return tl;
+}
+
+QSqlIndex QMimerSQLDriver::primaryIndex(const QString &tablename) const
+{
+ Q_D(const QMimerSQLDriver);
+ if (!isOpen())
+ return QSqlIndex();
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ QSqlIndex index(tablename);
+ QSqlQuery t(createResult());
+ QString schema;
+ QString qualifiedName = table;
+ d->splitTableQualifier(qualifiedName, &schema, &table);
+ QString sql =
+ "select information_schema.ext_access_paths.column_name,"
+ "case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
+ "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
+ "when data_type = 'INTEGER' and numeric_precision <= 10 AND NUMERIC_PRECISION > 5 "
+ "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
+ "else upper(data_type) end as data_type "
+ "from information_schema.ext_access_paths full outer join "
+ "information_schema.columns on information_schema.ext_access_paths.column_name = "
+ "information_schema.columns.column_name and "
+ "information_schema.ext_access_paths.table_name = "
+ "information_schema.columns.table_name where "
+ "information_schema.ext_access_paths.table_name = \'"_L1;
+ sql.append(table)
+ .append("\' and index_type = \'PRIMARY KEY\'"_L1);
+ if (schema.length() == 0)
+ sql.append(" and table_schema = CURRENT_USER"_L1);
+ else
+ sql.append(" and table_schema = \'"_L1).append(schema).append("\'"_L1);
+
+ if (!t.exec(sql))
+ return QSqlIndex();
+ int i = 0;
+ while (t.next()) {
+ QSqlField field(t.value(0).toString(),
+ QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
+ tablename);
+ index.insert(i, field);
+ index.setName(t.value(0).toString());
+ i++;
+ }
+ return index;
+}
+
+QSqlRecord QMimerSQLDriver::record(const QString &tablename) const
+{
+ Q_D(const QMimerSQLDriver);
+ if (!isOpen())
+ return QSqlRecord();
+ QSqlRecord rec;
+ QSqlQuery t(createResult());
+ QString qualifiedName = tablename;
+ if (isIdentifierEscaped(qualifiedName, QSqlDriver::TableName))
+ qualifiedName = stripDelimiters(qualifiedName, QSqlDriver::TableName);
+ QString schema, table;
+ d->splitTableQualifier(qualifiedName, &schema, &table);
+
+ QString sql =
+ "select column_name, case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
+ "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
+ "when data_type = 'INTEGER' and numeric_precision <= 10 AND numeric_precision > 5 "
+ "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
+ "else UPPER(data_type) end as data_type, case when is_nullable = 'YES' then false else "
+ "true end as required, "
+ "coalesce(numeric_precision, coalesce(datetime_precision,coalesce(interval_precision, "
+ "-1))) as prec from information_schema.columns where table_name = \'"_L1;
+ if (schema.length() == 0)
+ sql.append(table).append("\' and table_schema = CURRENT_USER"_L1);
+ else
+ sql.append(table).append("\' and table_schema = \'"_L1).append(schema).append("\'"_L1);
+ sql.append(" order by ordinal_position"_L1);
+ if (!t.exec(sql))
+ return QSqlRecord();
+
+ while (t.next()) {
+ QSqlField field(t.value(0).toString(),
+ QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
+ tablename);
+ field.setRequired(t.value(3).toBool());
+ if (t.value(3).toInt() != -1)
+ field.setPrecision(t.value(3).toInt());
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QVariant QMimerSQLDriver::handle() const
+{
+ Q_D(const QMimerSQLDriver);
+ return QVariant::fromValue(d->sessionhandle);
+}
+
+QString QMimerSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
+{
+ Q_UNUSED(type);
+ QString res = identifier;
+ if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"')) {
+ res.replace(u'"', "\"\""_L1);
+ res = u'"' + res + u'"';
+ res.replace(u'.', "\".\""_L1);
+ }
+ return res;
+}
+
+bool QMimerSQLDriver::beginTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerBeginTransaction(d->sessionhandle, MIMER_TRANS_READWRITE);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not start transaction"), err,
+ QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::commitTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_COMMIT);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not commit transaction"), err,
+ QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+bool QMimerSQLDriver::rollbackTransaction()
+{
+ Q_D(const QMimerSQLDriver);
+ const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_ROLLBACK);
+ if (!MIMER_SUCCEEDED(err)) {
+ setLastError(qMakeError(
+ QCoreApplication::translate("QMimerSQLDriver", "Could not roll back transaction"),
+ err, QSqlError::TransactionError, d));
+ return false;
+ }
+ return true;
+}
+
+void QMimerSQLDriverPrivate::splitTableQualifier(const QString &qualifiedName, QString *schema,
+ QString *table) const
+{
+ const QList<QStringView> l = QStringView(qualifiedName).split(u'.');
+ int n = l.count();
+ if (n > 2) {
+ return; // can't possibly be a valid table qualifier
+ } else if (n == 1) {
+ *schema = QString();
+ *table = l.at(0).toString();
+ } else {
+ *schema = l.at(0).toString();
+ *table = l.at(1).toString();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsql_mimer.cpp"