Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsql_mimer.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2022 Mimer Information Technology
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include <qcoreapplication.h>
7#include <qvariant.h>
8#include <qmetatype.h>
9#include <qdatetime.h>
10#include <qloggingcategory.h>
11#include <qsqlerror.h>
12#include <qsqlfield.h>
13#include <qsqlindex.h>
14#include <qsqlrecord.h>
15#include <qsqlquery.h>
16#include <qsocketnotifier.h>
17#include <qstringlist.h>
18#include <qlocale.h>
19#if defined(Q_OS_WIN32)
20# include <QtCore/qt_windows.h>
21#endif
22#include <QtSql/private/qsqlresult_p.h>
23#include <QtSql/private/qsqldriver_p.h>
24#include "qsql_mimer.h"
25
26#define MIMER_DEFAULT_DATATYPE 1000
27
28Q_DECLARE_OPAQUE_POINTER(MimerSession)
29Q_DECLARE_METATYPE(MimerSession)
30
31Q_DECLARE_OPAQUE_POINTER(MimerStatement)
32Q_DECLARE_METATYPE(MimerStatement)
33
35
36Q_STATIC_LOGGING_CATEGORY(lcMimer, "qt.sql.mimer")
37
55
56using namespace Qt::StringLiterals;
57
59
60class QMimerSQLResult final : public QSqlResult
61{
63public:
65 virtual ~QMimerSQLResult() override;
66 QVariant handle() const override;
67 static constexpr int genericError = -1;
68 static constexpr int lobChunkMaxSizeSet = 1048500;
69 static constexpr int lobChunkMaxSizeFetch = 65536;
70 static constexpr int maxStackStringSize = 200;
71 static constexpr int maxTimeStringSize = 18;
72 static constexpr int maxDateStringSize = 10;
73 static constexpr int maxTimestampStringSize = 29;
74
75private:
76 void cleanup();
77 bool fetch(int i) override;
81 QVariant data(int i) override;
82 bool isNull(int index) override;
83 bool reset(const QString &query) override;
86 QSqlRecord record() const override;
87 bool prepare(const QString &query) override;
88 bool execBatch(bool arrayBind = false) override;
89 bool exec() override;
90 qint64 currentRow();
91 QVariant lastInsertId() const override;
92};
93
95{
96 Q_DECLARE_PUBLIC(QMimerSQLDriver)
97public:
100 QString dbName;
101 QString dbUser;
102 void splitTableQualifier(const QString &qualifier, QString *schema, QString *table) const;
103};
104
137
138static QSqlError qMakeError(const QString &err, const int errCode, QSqlError::ErrorType type,
139 const QMimerSQLDriverPrivate *p)
140{
141 QString msg;
142 if (p) {
143 size_t str_len;
144 int e_code;
145 int rc;
146 str_len = (rc = MimerGetError(p->sessionhandle, &e_code, NULL, 0)) + 1;
147 if (!MIMER_SUCCEEDED(rc)) {
148 msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
149 .arg(errCode);
150 } else {
151 QVarLengthArray<wchar_t> tmp_buff((qsizetype)str_len);
152 if (!MIMER_SUCCEEDED(
153 rc = MimerGetError(p->sessionhandle, &e_code, tmp_buff.data(), str_len)))
154 msg = QCoreApplication::translate("QMimerSQL", "No Mimer SQL error for code %1")
155 .arg(errCode);
156 else
157 msg = QString::fromWCharArray(tmp_buff.data());
158 }
159 } else {
160 msg = QCoreApplication::translate("QMimerSQL", "Generic Mimer SQL error");
161 }
162
163 return QSqlError("QMIMER: "_L1 + err, msg, type, QString::number(errCode));
164}
165
166static QString msgCouldNotGet(const char *type, int column)
167{
168 //: Data type, column
169 return QCoreApplication::translate("QMimerSQLResult",
170 "Could not get %1, column %2").arg(QLatin1StringView(type)).arg(column);
171}
172
173static QString msgCouldNotSet(const char *type, int column)
174{
175 //: Data type, parameter
176 return QCoreApplication::translate("QMimerSQLResult",
177 "Could not set %1, parameter %2").arg(QLatin1StringView(type)).arg(column);
178}
179
180QMimerSQLDriver::QMimerSQLDriver(QObject *parent) : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
181{
182}
183
184QMimerSQLDriver::QMimerSQLDriver(MimerSession *conn, QObject *parent)
185 : QSqlDriver(*new QMimerSQLDriverPrivate, parent)
186{
187 Q_D(QMimerSQLDriver);
188 if (conn)
189 d->sessionhandle = *conn;
190}
191
192QMimerSQLDriver::~QMimerSQLDriver()
193{
194 close();
195}
196
197QMimerSQLResult::QMimerSQLResult(const QMimerSQLDriver *db)
198 : QSqlResult(*new QMimerSQLResultPrivate(this, db))
199{
200 Q_D(QMimerSQLResult);
201 d->preparedQuery = db->hasFeature(QSqlDriver::PreparedQueries);
202}
203
205{
206 cleanup();
207}
208
210{
211 switch (t) {
212 case MIMER_BINARY:
213 case MIMER_BINARY_VARYING:
215 case MIMER_BLOB:
216 case MIMER_NATIVE_BLOB:
218 case MIMER_CLOB:
219 case MIMER_NCLOB:
220 case MIMER_NATIVE_CLOB:
221 case MIMER_NATIVE_NCLOB:
223 case MIMER_DATE:
225 case MIMER_TIME:
227 case MIMER_TIMESTAMP:
229 case MIMER_INTERVAL_DAY:
230 case MIMER_INTERVAL_DAY_TO_HOUR:
231 case MIMER_INTERVAL_DAY_TO_MINUTE:
232 case MIMER_INTERVAL_DAY_TO_SECOND:
233 case MIMER_INTERVAL_HOUR:
234 case MIMER_INTERVAL_HOUR_TO_MINUTE:
235 case MIMER_INTERVAL_HOUR_TO_SECOND:
236 case MIMER_INTERVAL_MINUTE:
237 case MIMER_INTERVAL_MINUTE_TO_SECOND:
238 case MIMER_INTERVAL_MONTH:
239 case MIMER_INTERVAL_SECOND:
240 case MIMER_INTERVAL_YEAR:
241 case MIMER_INTERVAL_YEAR_TO_MONTH:
242 case MIMER_NCHAR:
243 case MIMER_CHARACTER:
244 case MIMER_CHARACTER_VARYING:
245 case MIMER_NCHAR_VARYING:
246 case MIMER_UTF8:
249 case MIMER_INTEGER:
250 case MIMER_DECIMAL:
251 case MIMER_FLOAT:
253 case MIMER_BOOLEAN:
255 case MIMER_T_BIGINT:
256 case MIMER_T_UNSIGNED_BIGINT:
257 case MIMER_NATIVE_BIGINT_NULLABLE:
258 case MIMER_NATIVE_BIGINT:
260 case MIMER_NATIVE_REAL_NULLABLE:
261 case MIMER_NATIVE_REAL:
262 case MIMER_T_REAL:
264 case MIMER_T_FLOAT:
265 case MIMER_NATIVE_DOUBLE_NULLABLE:
266 case MIMER_NATIVE_DOUBLE:
267 case MIMER_T_DOUBLE:
269 case MIMER_NATIVE_INTEGER:
270 case MIMER_NATIVE_INTEGER_NULLABLE:
271 case MIMER_NATIVE_SMALLINT_NULLABLE:
272 case MIMER_NATIVE_SMALLINT:
273 case MIMER_T_INTEGER:
274 case MIMER_T_SMALLINT:
276 case MIMER_UUID:
278 default:
279 qCWarning(lcMimer) << "QMimerSQLDriver::mimerMapColumnTypes: Unknown data type:" << t;
280 }
282}
283
285{
286 switch (t) {
287 case MIMER_BINARY:
288 case MIMER_BINARY_VARYING:
289 case MIMER_BLOB:
290 case MIMER_NATIVE_BLOB:
291 return QMetaType::QByteArray;
292 case MIMER_CLOB:
293 case MIMER_NCLOB:
294 case MIMER_NATIVE_CLOB:
295 case MIMER_NATIVE_NCLOB:
296 case MIMER_INTERVAL_DAY:
297 case MIMER_DECIMAL:
298 case MIMER_INTERVAL_DAY_TO_HOUR:
299 case MIMER_INTERVAL_DAY_TO_MINUTE:
300 case MIMER_INTERVAL_DAY_TO_SECOND:
301 case MIMER_INTERVAL_HOUR:
302 case MIMER_INTERVAL_HOUR_TO_MINUTE:
303 case MIMER_INTERVAL_HOUR_TO_SECOND:
304 case MIMER_INTERVAL_MINUTE:
305 case MIMER_INTERVAL_MINUTE_TO_SECOND:
306 case MIMER_INTERVAL_MONTH:
307 case MIMER_INTERVAL_SECOND:
308 case MIMER_INTERVAL_YEAR:
309 case MIMER_INTERVAL_YEAR_TO_MONTH:
310 case MIMER_NCHAR:
311 case MIMER_CHARACTER:
312 case MIMER_CHARACTER_VARYING:
313 case MIMER_NCHAR_VARYING:
314 case MIMER_UTF8:
316 case MIMER_INTEGER:
317 case MIMER_FLOAT:
318 return QMetaType::QString;
319 case MIMER_BOOLEAN:
320 return QMetaType::Bool;
321 case MIMER_T_BIGINT:
322 case MIMER_T_UNSIGNED_BIGINT:
323 case MIMER_NATIVE_BIGINT_NULLABLE:
324 case MIMER_NATIVE_BIGINT:
325 return QMetaType::LongLong;
326 case MIMER_NATIVE_REAL_NULLABLE:
327 case MIMER_NATIVE_REAL:
328 case MIMER_T_REAL:
329 return QMetaType::Float;
330 case MIMER_T_FLOAT:
331 case MIMER_NATIVE_DOUBLE_NULLABLE:
332 case MIMER_NATIVE_DOUBLE:
333 case MIMER_T_DOUBLE:
334 return QMetaType::Double;
335 case MIMER_NATIVE_INTEGER_NULLABLE:
336 case MIMER_T_INTEGER:
337 case MIMER_NATIVE_INTEGER:
338 return QMetaType::Int;
339 case MIMER_NATIVE_SMALLINT_NULLABLE:
340 case MIMER_T_SMALLINT:
341 return QMetaType::Int;
342 case MIMER_DATE:
343 return QMetaType::QDate;
344 case MIMER_TIME:
345 return QMetaType::QTime;
346 break;
347 case MIMER_TIMESTAMP:
348 return QMetaType::QDateTime;
349 case MIMER_UUID:
350 return QMetaType::QUuid;
351 default:
352 qCWarning(lcMimer) << "QMimerSQLDriver::qDecodeMSQLType: Unknown data type:" << t;
353 return QMetaType::UnknownType;
354 }
355}
356
357static int32_t qLookupMimDataType(QStringView s)
358{
359 if (s == u"BINARY")
360 return MIMER_BINARY;
361 if (s == u"BINARY VARYING")
362 return MIMER_BINARY_VARYING;
363 if (s == u"BINARY LARGE OBJECT")
364 return MIMER_BLOB;
365 if (s == u"CHARACTER LARGE OBJECT")
366 return MIMER_CLOB;
367 if (s == u"NATIONAL CHAR LARGE OBJECT")
368 return MIMER_NCLOB;
369 if (s == u"INTERVAL DAY")
370 return MIMER_INTERVAL_DAY;
371 if (s == u"DECIMAL")
372 return MIMER_DECIMAL;
373 if (s == u"INTERVAL DAY TO HOUR")
374 return MIMER_INTERVAL_DAY_TO_HOUR;
375 if (s == u"INTERVAL DAY TO MINUTE")
376 return MIMER_INTERVAL_DAY_TO_MINUTE;
377 if (s == u"INTERVAL DAY TO SECOND")
378 return MIMER_INTERVAL_DAY_TO_SECOND;
379 if (s == u"INTERVAL HOUR")
380 return MIMER_INTERVAL_HOUR;
381 if (s == u"INTERVAL HOUR TO MINUTE")
382 return MIMER_INTERVAL_HOUR_TO_MINUTE;
383 if (s == u"INTERVAL HOUR TO SECOND")
384 return MIMER_INTERVAL_HOUR_TO_SECOND;
385 if (s == u"INTERVAL MINUTE")
386 return MIMER_INTERVAL_MINUTE;
387 if (s == u"INTERVAL MINUTE TO SECOND")
388 return MIMER_INTERVAL_MINUTE_TO_SECOND;
389 if (s == u"INTERVAL MONTH")
390 return MIMER_INTERVAL_MONTH;
391 if (s == u"INTERVAL SECOND")
392 return MIMER_INTERVAL_SECOND;
393 if (s == u"INTERVAL YEAR")
394 return MIMER_INTERVAL_YEAR;
395 if (s == u"INTERVAL YEAR TO MONTH")
396 return MIMER_INTERVAL_YEAR_TO_MONTH;
397 if (s == u"NATIONAL CHARACTER")
398 return MIMER_NCHAR;
399 if (s == u"CHARACTER")
400 return MIMER_CHARACTER;
401 if (s == u"CHARACTER VARYING")
402 return MIMER_CHARACTER_VARYING;
403 if (s == u"NATIONAL CHARACTER VARYING")
404 return MIMER_NCHAR_VARYING;
405 if (s == u"UTF-8")
406 return MIMER_UTF8;
407 if (s == u"BOOLEAN")
408 return MIMER_BOOLEAN;
409 if (s == u"BIGINT")
410 return MIMER_T_BIGINT;
411 if (s == u"REAL")
412 return MIMER_T_REAL;
413 if (s == u"FLOAT")
414 return MIMER_T_FLOAT;
415 if (s == u"DOUBLE PRECISION")
416 return MIMER_T_DOUBLE;
417 if (s == u"INTEGER")
418 return MIMER_T_INTEGER;
419 if (s == u"SMALLINT")
420 return MIMER_T_SMALLINT;
421 if (s == u"DATE")
422 return MIMER_DATE;
423 if (s == u"TIME")
424 return MIMER_TIME;
425 if (s == u"TIMESTAMP")
426 return MIMER_TIMESTAMP;
427 if (s == u"BUILTIN.UUID")
428 return MIMER_UUID;
429 if (s == u"USER-DEFINED")
431 qCWarning(lcMimer) << "QMimerSQLDriver::qLookupMimDataType: Unhandled data type:" << s;
433}
434
435QVariant QMimerSQLResult::handle() const
436{
437 Q_D(const QMimerSQLResult);
438 return QVariant::fromValue(d->statementhandle);
439}
440
441void QMimerSQLResult::cleanup()
442{
443 Q_D(QMimerSQLResult);
444 if (!driver() || !driver()->isOpen()) {
445 d->openCursor = false;
446 d->openStatement = false;
447 return;
448 }
449 if (d->openCursor) {
450 const int32_t err = MimerCloseCursor(d->statementhandle);
451 if (!MIMER_SUCCEEDED(err))
452 setLastError(qMakeError(
453 QCoreApplication::translate("QMimerSQLResult", "Could not close cursor"), err,
454 QSqlError::StatementError, d->drv_d_func()));
455 d->openCursor = false;
456 }
457 if (d->openStatement) {
458 const int32_t err = MimerEndStatement(&d->statementhandle);
459 if (!MIMER_SUCCEEDED(err))
460 setLastError(qMakeError(
461 QCoreApplication::translate("QMimerSQLResult", "Could not close statement"),
462 err, QSqlError::StatementError, d->drv_d_func()));
463 d->openStatement = false;
464 }
465 d->currentSize = -1;
466}
467
468qint64 QMimerSQLResult::currentRow()
469{
470 Q_D(const QMimerSQLResult);
471 return d->currentRow;
472}
473
475{
476 Q_D(const QMimerSQLResult);
477 int32_t err = 0;
478 if (!isActive() || !isSelect())
479 return false;
480 if (i == at())
481 return true;
482 if (i < 0)
483 return false;
484
485 if (isForwardOnly() && i < at())
486 return false;
487
488 if (isForwardOnly()) {
489 bool rc;
490 do {
491 rc = fetchNext();
492 } while (rc && currentRow() < i);
493 return rc;
494 } else {
495 err = MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, i + 1);
496 if (err == MIMER_NO_DATA)
497 return false;
498 }
499 if (!MIMER_SUCCEEDED(err)) {
500 setLastError(
501 qMakeError(QCoreApplication::translate("QMimerSQLResult", "Fetch did not succeed"),
502 err, QSqlError::StatementError, d->drv_d_func()));
503 return false;
504 }
505 setAt(MimerCurrentRow(d->statementhandle) - 1);
506 return true;
507}
508
510{
511 Q_D(const QMimerSQLResult);
512 int32_t err = 0;
513 if (!isActive() || !isSelect())
514 return false;
515 if (isForwardOnly()) {
516 if (currentRow() < 0)
517 return fetchNext();
518 else if (currentRow() == 0)
519 setAt(0);
520 else
521 return false;
522 } else {
523 err = MimerFetchScroll(d->statementhandle, MIMER_FIRST, 0);
524 if (MIMER_SUCCEEDED(err) && err != MIMER_NO_DATA)
525 setAt(0);
526 }
527 if (!MIMER_SUCCEEDED(err)) {
528 setLastError(qMakeError(
529 QCoreApplication::translate("QMimerSQLResult", "Fetch first did not succeed"), err,
530 QSqlError::StatementError, d->drv_d_func()));
531 return false;
532 }
533 if (err == MIMER_NO_DATA)
534 return false;
535 return true;
536}
537
539{
540 Q_D(const QMimerSQLResult);
541 int32_t err = 0;
542 int row = 0;
543 if (!isActive() || !isSelect())
544 return false;
545 if (isForwardOnly()) {
546 bool rc;
547 do {
548 rc = fetchNext();
549 } while (rc);
550
551 return currentRow() >= 0;
552 } else {
553 err = MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
554 if (err == MIMER_NO_DATA)
555 return false;
556 if (MIMER_SUCCEEDED(err)) {
557 row = MimerCurrentRow(d->statementhandle) - 1;
558 } else {
559 setLastError(qMakeError(
560 QCoreApplication::translate("QMimerSQLResult:", "Fetch last did not succeed"),
561 err, QSqlError::StatementError, d->drv_d_func()));
562 return false;
563 }
564 }
565
566 if (row < 0) {
567 setAt(QSql::BeforeFirstRow);
568 return false;
569 } else {
570 setAt(row);
571 return true;
572 }
573}
574
576{
577 Q_D(QMimerSQLResult);
578 int32_t err = 0;
579 if (!isActive() || !isSelect())
580 return false;
581 if (isForwardOnly())
582 err = MimerFetch(d->statementhandle);
583 else
584 err = MimerFetchScroll(d->statementhandle, MIMER_NEXT, 0);
585 if (!MIMER_SUCCEEDED(err)) {
586 setLastError(qMakeError(
587 QCoreApplication::translate("QMimerSQLResult", "Could not fetch next row"), err,
588 QSqlError::StatementError, d->drv_d_func()));
589 if (isForwardOnly())
590 d->currentRow = QSql::BeforeFirstRow;
591 return false;
592 }
593 if (err == MIMER_NO_DATA)
594 return false;
595 if (isForwardOnly())
596 setAt(++d->currentRow);
597 else
598 setAt(MimerCurrentRow(d->statementhandle) - 1);
599 return true;
600}
601
602QVariant QMimerSQLResult::data(int i)
603{
604 Q_D(QMimerSQLResult);
605 int32_t err;
606 int32_t mType;
607 if (d->callWithOut) {
608 if (i >= MimerParameterCount(d->statementhandle)) {
609 setLastError(qMakeError(
610 QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
611 .arg(i),
612 genericError, QSqlError::StatementError, nullptr));
613 return QVariant();
614 }
615 mType = MimerParameterType(d->statementhandle, static_cast<std::int16_t>(i + 1));
616 } else {
617 if (i >= MimerColumnCount(d->statementhandle)) {
618 setLastError(qMakeError(
619 QCoreApplication::translate("QMimerSQLResult:", "Column %1 out of range")
620 .arg(i),
621 genericError, QSqlError::StatementError, nullptr));
622 return QVariant();
623 }
624 mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i + 1));
625 }
626 const QMetaType::Type type = qDecodeMSQLType(mType);
627 const MimerColumnTypes mimDataType = mimerMapColumnTypes(mType);
628 err = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(i + 1));
629 if (err > 0) {
630 return QVariant(QMetaType(type), nullptr);
631 } else {
632 switch (mimDataType) {
634 wchar_t dateString_w[maxDateStringSize + 1];
635 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), dateString_w,
636 sizeof(dateString_w) / sizeof(dateString_w[0]));
637 if (!MIMER_SUCCEEDED(err)) {
638 setLastError(qMakeError(msgCouldNotGet("date", i),
639 err, QSqlError::StatementError, d->drv_d_func()));
640 return QVariant(QMetaType(type), nullptr);
641 }
642 return QDate::fromString(QString::fromWCharArray(dateString_w), "yyyy-MM-dd"_L1);
643 }
645 wchar_t timeString_w[maxTimeStringSize + 1];
646 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), timeString_w,
647 sizeof(timeString_w) / sizeof(timeString_w[0]));
648 if (!MIMER_SUCCEEDED(err)) {
649 setLastError(qMakeError(msgCouldNotGet("time", i),
650 err, QSqlError::StatementError, d->drv_d_func()));
651 return QVariant(QMetaType(type), nullptr);
652 }
653 QString timeString = QString::fromWCharArray(timeString_w);
654 QString timeFormatString = "HH:mm:ss"_L1;
655 if (timeString.size() > 8) {
656 timeFormatString.append(".zzz"_L1);
657 timeString = timeString.left(12);
658 }
659 return QTime::fromString(timeString, timeFormatString);
660 }
662 wchar_t dateTimeString_w[maxTimestampStringSize + 1];
663 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
664 dateTimeString_w,
665 sizeof(dateTimeString_w) / sizeof(dateTimeString_w[0]));
666 if (!MIMER_SUCCEEDED(err)) {
667 setLastError(
668 qMakeError(msgCouldNotGet("date time", i),
669 err, QSqlError::StatementError, d->drv_d_func()));
670 return QVariant(QMetaType(type), nullptr);
671 }
672 QString dateTimeString = QString::fromWCharArray(dateTimeString_w);
673 QString dateTimeFormatString = "yyyy-MM-dd HH:mm:ss"_L1;
674 if (dateTimeString.size() > 19) {
675 dateTimeFormatString.append(".zzz"_L1);
676 dateTimeString = dateTimeString.left(23);
677 }
678 return QDateTime::fromString(dateTimeString, dateTimeFormatString);
679 }
681 int resInt;
682 err = MimerGetInt32(d->statementhandle, static_cast<std::int16_t>(i + 1), &resInt);
683 if (!MIMER_SUCCEEDED(err)) {
684 setLastError(qMakeError(msgCouldNotGet("int32", i),
685 err, QSqlError::StatementError, d->drv_d_func()));
686 return QVariant(QMetaType(type), nullptr);
687 }
688 return resInt;
689 }
691 int64_t resLongLong;
692 err = MimerGetInt64(d->statementhandle, static_cast<std::int16_t>(i + 1), &resLongLong);
693 if (!MIMER_SUCCEEDED(err)) {
694 setLastError(qMakeError(msgCouldNotGet("int64", i),
695 err, QSqlError::StatementError, d->drv_d_func()));
696 return QVariant(QMetaType(type), nullptr);
697 }
698 return (qlonglong)resLongLong;
699 }
701 err = MimerGetBoolean(d->statementhandle, static_cast<std::int16_t>(i + 1));
702 if (!MIMER_SUCCEEDED(err)) {
703 setLastError(
704 qMakeError(msgCouldNotGet("boolean", i),
705 err, QSqlError::StatementError, d->drv_d_func()));
706 return QVariant(QMetaType(type), nullptr);
707 }
708 return err == 1;
709 }
711 float resFloat;
712 err = MimerGetFloat(d->statementhandle, static_cast<std::int16_t>(i + 1), &resFloat);
713 if (!MIMER_SUCCEEDED(err)) {
714 setLastError(qMakeError(msgCouldNotGet("float", i),
715 err, QSqlError::StatementError, d->drv_d_func()));
716 return QVariant(QMetaType(type), nullptr);
717 }
718 return resFloat;
719 }
721 double resDouble;
722 err = MimerGetDouble(d->statementhandle, static_cast<std::int16_t>(i + 1), &resDouble);
723 if (!MIMER_SUCCEEDED(err)) {
724 setLastError(
725 qMakeError(msgCouldNotGet("double", i),
726 err, QSqlError::StatementError, d->drv_d_func()));
727 return QVariant(QMetaType(type), nullptr);
728 }
729 switch (numericalPrecisionPolicy()) {
730 case QSql::LowPrecisionInt32:
731 return static_cast<std::int32_t>(resDouble);
732 case QSql::LowPrecisionInt64:
733 return static_cast<qint64>(resDouble);
734 case QSql::LowPrecisionDouble:
735 return static_cast<qreal>(resDouble);
736 case QSql::HighPrecision:
737 return QString::number(resDouble, 'g', 17);
738 }
739 return QVariant(QMetaType(type), nullptr);
740 }
742 QByteArray byteArray;
743 // Get size
744 err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i + 1), NULL, 0);
745 if (MIMER_SUCCEEDED(err)) {
746 byteArray.resize(err);
747 err = MimerGetBinary(d->statementhandle, static_cast<std::int16_t>(i + 1),
748 byteArray.data(), err);
749 }
750 if (!MIMER_SUCCEEDED(err)) {
751 setLastError(
752 qMakeError(msgCouldNotGet("binary", i),
753 err, QSqlError::StatementError, d->drv_d_func()));
754 return QVariant(QMetaType(type), nullptr);
755 }
756 return byteArray;
757 }
759 QByteArray byteArray;
760 size_t size;
761 err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i + 1), &size,
762 &d->lobhandle);
763 if (MIMER_SUCCEEDED(err)) {
764 constexpr size_t maxSize = lobChunkMaxSizeFetch;
765 QVarLengthArray<char> blobchar(lobChunkMaxSizeFetch);
766 byteArray.reserve(size);
767 size_t left_to_return = size;
768 while (left_to_return > 0) {
769 const size_t bytesToReceive =
770 left_to_return <= maxSize ? left_to_return : maxSize;
771 err = MimerGetBlobData(&d->lobhandle, blobchar.data(), bytesToReceive);
772 byteArray.append(QByteArray::fromRawData(blobchar.data(), bytesToReceive));
773 left_to_return -= bytesToReceive;
774 if (!MIMER_SUCCEEDED(err)) {
775 setLastError(qMakeError(msgCouldNotGet("BLOB", i),
776 err, QSqlError::StatementError, d->drv_d_func()));
777 return QVariant(QMetaType(type), nullptr);
778 }
779 }
780 } else {
781 setLastError(qMakeError(msgCouldNotGet("BLOB", i),
782 err, QSqlError::StatementError, d->drv_d_func()));
783 return QVariant(QMetaType(type), nullptr);
784 }
785 return byteArray;
786 }
789 wchar_t resString_w[maxStackStringSize + 1];
790 // Get size
791 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1), resString_w,
792 0);
793 if (MIMER_SUCCEEDED(err)) {
794 int size = err;
795 if (err <= maxStackStringSize) { // For smaller strings, use a small buffer for
796 // efficiency
797 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
798 resString_w, maxStackStringSize + 1);
799 if (MIMER_SUCCEEDED(err))
800 return QString::fromWCharArray(resString_w);
801 } else { // For larger strings, dynamically allocate memory
802 QVarLengthArray<wchar_t> largeResString_w(size + 1);
803 err = MimerGetString(d->statementhandle, static_cast<std::int16_t>(i + 1),
804 largeResString_w.data(), size + 1);
805 if (MIMER_SUCCEEDED(err))
806 return QString::fromWCharArray(largeResString_w.data());
807 }
808 }
809 setLastError(qMakeError(msgCouldNotGet(
810 mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
811 err, QSqlError::StatementError, d->drv_d_func()));
812 return QVariant(QMetaType(type), nullptr);
813 }
815 size_t size;
816 err = MimerGetLob(d->statementhandle, static_cast<std::int16_t>(i + 1), &size,
817 &d->lobhandle);
818 if (MIMER_SUCCEEDED(err)) {
819 constexpr size_t maxSize = lobChunkMaxSizeFetch;
820 QVarLengthArray<wchar_t> clobstring_w(lobChunkMaxSizeFetch + 1);
821
822 size_t left_to_return = size;
823 QString returnString;
824 while (left_to_return > 0) {
825 const size_t bytesToReceive =
826 left_to_return <= maxSize ? left_to_return : maxSize;
827 err = MimerGetNclobData(&d->lobhandle, clobstring_w.data(), bytesToReceive + 1);
828 returnString.append(QString::fromWCharArray(clobstring_w.data()));
829 left_to_return -= bytesToReceive;
830 if (!MIMER_SUCCEEDED(err)) {
831 setLastError(qMakeError(msgCouldNotGet("CLOB", i),
832 err, QSqlError::StatementError, d->drv_d_func()));
833 return QVariant(QMetaType(type), nullptr);
834 }
835 }
836 return returnString;
837 }
838 setLastError(qMakeError(msgCouldNotGet("CLOB", i),
839 err, QSqlError::StatementError, d->drv_d_func()));
840 return QVariant(QMetaType(type), nullptr);
841 }
843 unsigned char uuidChar[16];
844 err = MimerGetUUID(d->statementhandle, static_cast<std::int16_t>(i + 1), uuidChar);
845 if (!MIMER_SUCCEEDED(err)) {
846 setLastError(qMakeError(msgCouldNotGet("UUID", i),
847 err, QSqlError::StatementError, d->drv_d_func()));
848 return QVariant(QMetaType(type), nullptr);
849 }
850 const auto uuidByteArray = QByteArrayView(reinterpret_cast<char *>(uuidChar), 16);
851 return QUuid::fromRfc4122(uuidByteArray);
852 }
854 default:
855 setLastError(qMakeError(
856 QCoreApplication::translate("QMimerSQLResult", "Unknown data type %1").arg(i),
857 genericError, QSqlError::StatementError, nullptr));
858 }
859 return QVariant(QMetaType(type), nullptr);
860 }
861}
862
863bool QMimerSQLResult::isNull(int index)
864{
865 Q_D(const QMimerSQLResult);
866 const int32_t rc = MimerIsNull(d->statementhandle, static_cast<std::int16_t>(index + 1));
867 if (!MIMER_SUCCEEDED(rc)) {
868 setLastError(qMakeError(
869 QCoreApplication::translate("QMimerSQLResult", "Could not check null, column %1")
870 .arg(index),
871 rc, QSqlError::StatementError, d->drv_d_func()));
872 return false;
873 }
874 return rc != 0;
875}
876
877bool QMimerSQLResult::reset(const QString &query)
878{
879 if (!prepare(query))
880 return false;
881 return exec();
882}
883
885{
886 Q_D(QMimerSQLResult);
887 if (!isActive() || !isSelect() || isForwardOnly())
888 return -1;
889
890 if (d->currentSize != -1)
891 return d->currentSize;
892
893 const int currentRow = MimerCurrentRow(d->statementhandle);
894 MimerFetchScroll(d->statementhandle, static_cast<std::int32_t>(MIMER_LAST), 0);
895 int size = MimerCurrentRow(d->statementhandle);
896 if (!MIMER_SUCCEEDED(size))
897 size = -1;
898 MimerFetchScroll(d->statementhandle, MIMER_ABSOLUTE, currentRow);
899 d->currentSize = size;
900 return size;
901}
902
904{
905 Q_D(const QMimerSQLResult);
906 return d->rowsAffected;
907}
908
909QSqlRecord QMimerSQLResult::record() const
910{
911 Q_D(const QMimerSQLResult);
912 QSqlRecord rec;
913 if (!isActive() || !isSelect() || !driver())
914 return rec;
915 QSqlField field;
916 const int colSize = MimerColumnCount(d->statementhandle);
917 for (int i = 0; i < colSize; i++) {
918 wchar_t colName_w[100];
919 MimerColumnName(d->statementhandle, static_cast<std::int16_t>(i + 1), colName_w,
920 sizeof(colName_w) / sizeof(colName_w[0]));
921 field.setName(QString::fromWCharArray(colName_w));
922 const int32_t mType = MimerColumnType(d->statementhandle, static_cast<std::int16_t>(i + 1));
923 const QMetaType::Type type = qDecodeMSQLType(mType);
924 field.setMetaType(QMetaType(type));
925 field.setValue(QVariant(field.metaType()));
926 // field.setPrecision(); Should be implemented once the Mimer API can give this
927 // information.
928 // field.setLength(); Should be implemented once the Mimer API can give
929 // this information.
930 rec.insert(i, field);
931 }
932 return rec;
933}
934
935bool QMimerSQLResult::prepare(const QString &query)
936{
937 Q_D(QMimerSQLResult);
938 int32_t err;
939 if (!driver())
940 return false;
941 if (!d->preparedQuery)
942 return QSqlResult::prepare(query);
943 if (query.isEmpty())
944 return false;
945 cleanup();
946 const int option = isForwardOnly() ? MIMER_FORWARD_ONLY : MIMER_SCROLLABLE;
947 err = MimerBeginStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData(), option,
948 &d->statementhandle);
949 if (err == MIMER_STATEMENT_CANNOT_BE_PREPARED) {
950 err = MimerExecuteStatement8(d->drv_d_func()->sessionhandle, query.toUtf8().constData());
951 if (MIMER_SUCCEEDED(err)) {
952 d->executedStatement = true;
953 d->openCursor = false;
954 d->openStatement = false;
955 return true;
956 }
957 }
958 if (!MIMER_SUCCEEDED(err)) {
959 setLastError(qMakeError(QCoreApplication::translate("QMimerSQLResult",
960 "Could not prepare/execute statement"),
961 err, QSqlError::StatementError, d->drv_d_func()));
962 return false;
963 }
964 d->openStatement = true;
965 return true;
966}
967
969{
970 Q_D(QMimerSQLResult);
971 int32_t err;
972 if (!driver())
973 return false;
974 if (!d->preparedQuery)
975 return QSqlResult::exec();
976 if (d->executedStatement) {
977 d->executedStatement = false;
978 return true;
979 }
980 if (d->openCursor) {
981 setAt(QSql::BeforeFirstRow);
982 err = MimerCloseCursor(d->statementhandle);
983 d->openCursor = false;
984 d->currentSize = -1;
985 }
986 QVector<QVariant> &values = boundValues();
987 if (d->execBatch)
988 values = d->batch_vector;
989 int mimParamCount = MimerParameterCount(d->statementhandle);
990 if (!MIMER_SUCCEEDED(mimParamCount))
991 mimParamCount = 0;
992 if (mimParamCount != values.size()) {
993 setLastError(qMakeError(
994 QCoreApplication::translate("QMimerSQLResult", "Wrong number of parameters"),
995 genericError, QSqlError::StatementError, nullptr));
996 return false;
997 }
998 for (int i = 0; i < mimParamCount; i++) {
999 if (bindValueType(i) == QSql::Out) {
1000 d->callWithOut = true;
1001 continue;
1002 }
1003 const QVariant &val = values.at(i);
1004 if (QSqlResultPrivate::isVariantNull(val) || val.isNull() || val.toString().isNull()) {
1005 err = MimerSetNull(d->statementhandle, i + 1);
1006 if (!MIMER_SUCCEEDED(err)) {
1007 setLastError(
1008 qMakeError(msgCouldNotSet("null", i),
1009 err, QSqlError::StatementError, d->drv_d_func()));
1010 return false;
1011 }
1012 continue;
1013 }
1014
1015 const int mimParamType = MimerParameterType(d->statementhandle, i + 1);
1016 const MimerColumnTypes mimDataType = mimerMapColumnTypes(mimParamType);
1017 switch (mimDataType) {
1018 case MimerColumnTypes::Int: {
1019 bool convertOk;
1020 err = MimerSetInt32(d->statementhandle, i + 1, val.toInt(&convertOk));
1021 if (!convertOk || !MIMER_SUCCEEDED(err)) {
1022 setLastError(
1023 qMakeError(msgCouldNotSet("int32", i),
1024 convertOk ? err : genericError, QSqlError::StatementError,
1025 convertOk ? d->drv_d_func() : nullptr));
1026 return false;
1027 }
1028 break;
1029 }
1031 bool convertOk;
1032 err = MimerSetInt64(d->statementhandle, i + 1, val.toLongLong(&convertOk));
1033 if (!convertOk || !MIMER_SUCCEEDED(err)) {
1034 setLastError(
1035 qMakeError(msgCouldNotSet("int64", i),
1036 convertOk ? err : genericError, QSqlError::StatementError,
1037 convertOk ? d->drv_d_func() : nullptr));
1038 return false;
1039 }
1040 break;
1041 }
1043 bool convertOk;
1044 err = MimerSetFloat(d->statementhandle, i + 1, val.toFloat(&convertOk));
1045 if (!convertOk || !MIMER_SUCCEEDED(err)) {
1046 setLastError(
1047 qMakeError(msgCouldNotSet("float", i),
1048 convertOk ? err : genericError, QSqlError::StatementError,
1049 convertOk ? d->drv_d_func() : nullptr));
1050 return false;
1051 }
1052 break;
1053 }
1055 bool convertOk;
1056 err = MimerSetDouble(d->statementhandle, i + 1, val.toDouble(&convertOk));
1057 if (!convertOk || !MIMER_SUCCEEDED(err)) {
1058 setLastError(
1059 qMakeError(msgCouldNotSet("double", i),
1060 convertOk ? err : genericError, QSqlError::StatementError,
1061 convertOk ? d->drv_d_func() : nullptr));
1062 return false;
1063 }
1064 break;
1065 }
1067 const QByteArray binArr = val.toByteArray();
1068 size_t size = static_cast<std::size_t>(binArr.size());
1069 err = MimerSetBinary(d->statementhandle, i + 1, binArr.data(), size);
1070 if (!MIMER_SUCCEEDED(err)) {
1071 setLastError(
1072 qMakeError(msgCouldNotSet("binary", i),
1073 err, QSqlError::StatementError, d->drv_d_func()));
1074 return false;
1075 }
1076 break;
1077 }
1079 err = MimerSetBoolean(d->statementhandle, i + 1, val.toBool() == true ? 1 : 0);
1080 if (!MIMER_SUCCEEDED(err)) {
1081 setLastError(
1082 qMakeError(msgCouldNotSet("boolean", i),
1083 err, QSqlError::StatementError, d->drv_d_func()));
1084 return false;
1085 }
1086 break;
1087 }
1089 const QByteArray uuidArray = val.toUuid().toRfc4122();
1090 const unsigned char *uuid =
1091 reinterpret_cast<const unsigned char *>(uuidArray.constData());
1092 err = MimerSetUUID(d->statementhandle, i + 1, uuid);
1093 if (!MIMER_SUCCEEDED(err)) {
1094 setLastError(
1095 qMakeError(msgCouldNotSet("UUID", i),
1096 err, QSqlError::StatementError, d->drv_d_func()));
1097 return false;
1098 }
1099 break;
1100 }
1103 QByteArray string_b = val.toString().trimmed().toUtf8();
1104 const char *string_u = string_b.constData();
1105 err = MimerSetString8(d->statementhandle, i + 1, string_u);
1106 if (!MIMER_SUCCEEDED(err)) {
1107 setLastError(
1108 qMakeError(msgCouldNotSet(
1109 mimDataType == MimerColumnTypes::Numeric ? "numeric" : "string", i),
1110 err, QSqlError::StatementError, d->drv_d_func()));
1111 return false;
1112 }
1113 break;
1114 }
1116 err = MimerSetString8(d->statementhandle, i + 1, val.toString().toUtf8().constData());
1117 if (!MIMER_SUCCEEDED(err)) {
1118 setLastError(
1119 qMakeError(msgCouldNotSet("date", i),
1120 err, QSqlError::StatementError, d->drv_d_func()));
1121 return false;
1122 }
1123 break;
1124 }
1126 QString timeFormatString = "hh:mm:ss"_L1;
1127 const QTime timeVal = val.toTime();
1128 if (timeVal.msec() > 0)
1129 timeFormatString.append(".zzz"_L1);
1130 err = MimerSetString8(d->statementhandle, i + 1,
1131 timeVal.toString(timeFormatString).toUtf8().constData());
1132 if (!MIMER_SUCCEEDED(err)) {
1133 setLastError(
1134 qMakeError(msgCouldNotSet("time", i),
1135 err, QSqlError::StatementError, d->drv_d_func()));
1136 return false;
1137 }
1138 break;
1139 }
1141 QString dateTimeFormatString = "yyyy-MM-dd hh:mm:ss"_L1;
1142 const QDateTime dateTimeVal = val.toDateTime();
1143 if (dateTimeVal.time().msec() > 0)
1144 dateTimeFormatString.append(".zzz"_L1);
1145 err = MimerSetString8(
1146 d->statementhandle, i + 1,
1147 val.toDateTime().toString(dateTimeFormatString).toUtf8().constData());
1148 if (!MIMER_SUCCEEDED(err)) {
1149 setLastError(qMakeError(msgCouldNotSet("datetime", i),
1150 err, QSqlError::StatementError, d->drv_d_func()));
1151 return false;
1152 }
1153 break;
1154 }
1156 const QByteArray blobArr = val.toByteArray();
1157 const char *blobData = blobArr.constData();
1158 qsizetype size = blobArr.size();
1159 err = MimerSetLob(d->statementhandle, i + 1, size, &d->lobhandle);
1160 if (MIMER_SUCCEEDED(err)) {
1161 qsizetype maxSize = lobChunkMaxSizeSet;
1162 if (size > maxSize) {
1163 qsizetype left_to_send = size;
1164 for (qsizetype k = 0; left_to_send > 0; k++) {
1165 if (left_to_send <= maxSize) {
1166 err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize],
1167 left_to_send);
1168 left_to_send = 0;
1169 } else {
1170 err = MimerSetBlobData(&d->lobhandle, &blobData[k * maxSize], maxSize);
1171 left_to_send = left_to_send - maxSize;
1172 }
1173 }
1174 if (!MIMER_SUCCEEDED(err)) {
1175 setLastError(
1176 qMakeError(msgCouldNotSet("BLOB byte array", i),
1177 err, QSqlError::StatementError, d->drv_d_func()));
1178 return false;
1179 }
1180 } else {
1181 err = MimerSetBlobData(&d->lobhandle, blobArr, size);
1182 }
1183 }
1184 if (!MIMER_SUCCEEDED(err)) {
1185 setLastError(qMakeError(msgCouldNotSet("BLOB byte array", i),
1186 err, QSqlError::StatementError, d->drv_d_func()));
1187 return false;
1188 }
1189 break;
1190 }
1192 QByteArray string_b = val.toString().trimmed().toUtf8();
1193 const char *string_u = string_b.constData();
1194 size_t size_c = 1;
1195 size_t size = 0;
1196 while (string_u[size++])
1197 if ((string_u[size] & 0xc0) != 0x80)
1198 size_c++;
1199 err = MimerSetLob(d->statementhandle, i + 1, size_c, &d->lobhandle);
1200 if (MIMER_SUCCEEDED(err)) {
1201 constexpr size_t maxSize = lobChunkMaxSizeSet;
1202 if (size > maxSize) {
1203 size_t left_to_send = size;
1204 size_t pos = 0;
1205 uint step_back = 0;
1206 while (left_to_send > 0 && step_back < maxSize) {
1207 step_back = 0;
1208 if (left_to_send <= maxSize) {
1209 err = MimerSetNclobData8(&d->lobhandle, &string_u[pos], left_to_send);
1210 left_to_send = 0;
1211 } else {
1212 // Check that we don't split a multi-byte utf-8 characters
1213 while (pos + maxSize - step_back > 0
1214 && (string_u[pos + maxSize - step_back] & 0xc0) == 0x80)
1215 step_back++;
1216 err = MimerSetNclobData8(&d->lobhandle, &string_u[pos],
1217 maxSize - step_back);
1218 left_to_send = left_to_send - maxSize + step_back;
1219 pos += maxSize - step_back;
1220 }
1221 if (!MIMER_SUCCEEDED(err)) {
1222 setLastError(qMakeError(msgCouldNotSet("CLOB", i),
1223 err, QSqlError::StatementError, d->drv_d_func()));
1224 return false;
1225 }
1226 }
1227 } else {
1228 err = MimerSetNclobData8(&d->lobhandle, string_u, size);
1229 }
1230 }
1231 if (!MIMER_SUCCEEDED(err)) {
1232 setLastError(
1233 qMakeError(msgCouldNotSet("CLOB", i),
1234 err, QSqlError::StatementError, d->drv_d_func()));
1235 return false;
1236 }
1237 break;
1238 }
1240 default:
1241 setLastError(qMakeError(
1242 QCoreApplication::translate("QMimerSQLResult", "Unknown datatype, parameter %1")
1243 .arg(i),
1244 genericError, QSqlError::StatementError, nullptr));
1245 return false;
1246 }
1247 }
1248 if (d->execBatch)
1249 return true;
1250 err = MimerExecute(d->statementhandle);
1251 if (MIMER_SUCCEEDED(err)) {
1252 d->rowsAffected = err;
1253 int k = 0;
1254 for (qsizetype i = 0; i < values.size(); i++) {
1255 if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
1256 bindValue(i, data(k), QSql::In);
1257 k++;
1258 }
1259 }
1260 d->callWithOut = false;
1261 }
1262 setSelect(false);
1263 if (MIMER_SEQUENCE_ERROR == err) {
1264 err = MimerOpenCursor(d->statementhandle);
1265 d->rowsAffected = err;
1266 d->openCursor = true;
1267 d->currentRow = QSql::BeforeFirstRow;
1268 setSelect(true);
1269 }
1270 if (!MIMER_SUCCEEDED(err)) {
1271 setLastError(
1272 qMakeError(QCoreApplication::translate("QMimerSQLResult",
1273 "Could not execute statement/open cursor"),
1274 err, QSqlError::StatementError, d->drv_d_func()));
1275 return false;
1276 }
1277 setActive(true);
1278 return true;
1279}
1280
1281bool QMimerSQLResult::execBatch(bool arrayBind)
1282{
1283 Q_D(QMimerSQLResult);
1284 Q_UNUSED(arrayBind);
1285 int32_t err;
1286 const QVector<QVariant> values = boundValues();
1287
1288 // Check that we only have input parameters. Currently
1289 // we can only handle batch operations without output parameters.
1290 for (qsizetype i = 0; i < values.first().toList().size(); i++)
1291 if (bindValueType(i) == QSql::Out || bindValueType(i) == QSql::InOut) {
1292 setLastError(qMakeError(QCoreApplication::translate(
1293 "QMimerSQLResult",
1294 "Only input parameters can be used in batch operations"),
1295 genericError, QSqlError::StatementError, nullptr));
1296 d->execBatch = false;
1297 return false;
1298 }
1299 d->execBatch = true;
1300 for (qsizetype i = 0; i < values.first().toList().size(); i++) {
1301 for (qsizetype j = 0; j < values.size(); j++)
1302 d->batch_vector.append(values.at(j).toList().at(i));
1303 exec();
1304 if (i != (values.at(0).toList().size() - 1)) {
1305 err = MimerAddBatch(d->statementhandle);
1306 if (!MIMER_SUCCEEDED(err)) {
1307 setLastError(qMakeError(
1308 //: %1 is the batch number
1309 QCoreApplication::translate("QMimerSQLResult", "Could not add batch %1")
1310 .arg(i),
1311 err, QSqlError::StatementError, d->drv_d_func()));
1312 d->execBatch = false;
1313 return false;
1314 }
1315 }
1316 d->batch_vector.clear();
1317 }
1318 d->execBatch = false;
1319 err = MimerExecute(d->statementhandle);
1320 if (!MIMER_SUCCEEDED(err)) {
1321 setLastError(qMakeError(
1322 QCoreApplication::translate("QMimerSQLResult", "Could not execute batch"), err,
1323 QSqlError::StatementError, d->drv_d_func()));
1324 return false;
1325 }
1326 return true;
1327}
1328
1330{
1331 Q_D(const QMimerSQLResult);
1332 int64_t lastSequence;
1333 const int32_t err = MimerGetSequenceInt64(d->statementhandle, &lastSequence);
1334 if (!MIMER_SUCCEEDED(err))
1335 return QVariant(QMetaType(QMetaType::LongLong), nullptr);
1336 return QVariant(qint64(lastSequence));
1337}
1338
1339bool QMimerSQLDriver::hasFeature(DriverFeature f) const
1340{
1341 switch (f) {
1342 case NamedPlaceholders: // Is true in reality but Qt parses Sql statement...
1343 case EventNotifications:
1344 case LowPrecisionNumbers:
1345 case MultipleResultSets:
1346 case SimpleLocking:
1347 case CancelQuery:
1348 return false;
1349 case FinishQuery:
1350 case LastInsertId:
1351 case Transactions:
1352 case QuerySize:
1353 case BLOB:
1354 case Unicode:
1355 case PreparedQueries:
1356 case PositionalPlaceholders:
1357 case BatchOperations:
1358 return true;
1359 }
1360 return true;
1361}
1362
1363bool QMimerSQLDriver::open(const QString &db, const QString &user, const QString &password,
1364 const QString &host, int port, const QString &connOpts)
1365{
1366 Q_D(QMimerSQLDriver);
1367 Q_UNUSED(host);
1368 Q_UNUSED(port);
1369 Q_UNUSED(connOpts);
1370 if (isOpen())
1371 close();
1372 const int32_t err = MimerBeginSession8(db.toUtf8().constData(), user.toUtf8().constData(),
1373 password.toUtf8().constData(), &d->sessionhandle);
1374 if (!MIMER_SUCCEEDED(err)) {
1375 setLastError(qMakeError(
1376 QCoreApplication::translate("QMimerSQLDriver", "Could not connect to database")
1377 + " "_L1 + db,
1378 err, QSqlError::ConnectionError, nullptr));
1379 setOpenError(true);
1380 return false;
1381 }
1382 d->dbUser = user;
1383 d->dbName = db;
1384 setOpen(true);
1385 setOpenError(false);
1386 return true;
1387}
1388
1389void QMimerSQLDriver::close()
1390{
1391 Q_D(QMimerSQLDriver);
1392 if (isOpen()) {
1393 const int end_err = MimerEndSession(&d->sessionhandle);
1394 if (MIMER_SUCCEEDED(end_err)) {
1395 setOpen(false);
1396 setOpenError(false);
1397 }
1398 }
1399}
1400
1401QSqlResult *QMimerSQLDriver::createResult() const
1402{
1403 return new QMimerSQLResult(this);
1404}
1405
1406QStringList QMimerSQLDriver::tables(QSql::TableType type) const
1407{
1408 QStringList tl;
1409 if (!isOpen())
1410 return tl;
1411 QSqlQuery t(createResult());
1412 QString sql;
1413 switch (type) {
1414 case QSql::Tables: {
1415 sql = "select table_name from information_schema.tables where "
1416 "table_type=\'BASE TABLE\' AND table_schema = CURRENT_USER"_L1;
1417 break;
1418 }
1419 case QSql::SystemTables: {
1420 sql = "select table_name from information_schema.tables where "
1421 "table_type=\'BASE TABLE\' AND table_schema = \'SYSTEM\'"_L1;
1422 break;
1423 }
1424 case QSql::Views: {
1425 sql = "select table_name from information_schema.tables where "
1426 "table_type=\'VIEW\' AND table_schema = CURRENT_USER"_L1;
1427 break;
1428 }
1429 case QSql::AllTables: {
1430 sql = "select table_name from information_schema.tables where "
1431 "(table_type=\'VIEW\' or table_type=\'BASE TABLE\')"
1432 " AND (table_schema = CURRENT_USER OR table_schema =\'SYSTEM\')"_L1;
1433 break;
1434 }
1435 default:
1436 break;
1437 }
1438 if (sql.length() > 0) {
1439 t.exec(sql);
1440 while (t.next())
1441 tl.append(t.value(0).toString());
1442 }
1443 return tl;
1444}
1445
1446QSqlIndex QMimerSQLDriver::primaryIndex(const QString &tablename) const
1447{
1448 Q_D(const QMimerSQLDriver);
1449 if (!isOpen())
1450 return QSqlIndex();
1451 QString table = tablename;
1452 if (isIdentifierEscaped(table, QSqlDriver::TableName))
1453 table = stripDelimiters(table, QSqlDriver::TableName);
1454 QSqlIndex index(tablename);
1455 QSqlQuery t(createResult());
1456 QString schema;
1457 QString qualifiedName = table;
1458 d->splitTableQualifier(qualifiedName, &schema, &table);
1459 QString sql =
1460 "select information_schema.ext_access_paths.column_name,"
1461 "case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
1462 "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
1463 "when data_type = 'INTEGER' and numeric_precision <= 10 AND NUMERIC_PRECISION > 5 "
1464 "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
1465 "else upper(data_type) end as data_type "
1466 "from information_schema.ext_access_paths full outer join "
1467 "information_schema.columns on information_schema.ext_access_paths.column_name = "
1468 "information_schema.columns.column_name and "
1469 "information_schema.ext_access_paths.table_name = "
1470 "information_schema.columns.table_name where "
1471 "information_schema.ext_access_paths.table_name = \'"_L1;
1472 sql.append(table)
1473 .append("\' and index_type = \'PRIMARY KEY\'"_L1);
1474 if (schema.length() == 0)
1475 sql.append(" and table_schema = CURRENT_USER"_L1);
1476 else
1477 sql.append(" and table_schema = \'"_L1).append(schema).append("\'"_L1);
1478
1479 if (!t.exec(sql))
1480 return QSqlIndex();
1481 int i = 0;
1482 while (t.next()) {
1483 QSqlField field(t.value(0).toString(),
1484 QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
1485 tablename);
1486 index.insert(i, field);
1487 index.setName(t.value(0).toString());
1488 i++;
1489 }
1490 return index;
1491}
1492
1493QSqlRecord QMimerSQLDriver::record(const QString &tablename) const
1494{
1495 Q_D(const QMimerSQLDriver);
1496 if (!isOpen())
1497 return QSqlRecord();
1498 QSqlRecord rec;
1499 QSqlQuery t(createResult());
1500 QString qualifiedName = tablename;
1501 if (isIdentifierEscaped(qualifiedName, QSqlDriver::TableName))
1502 qualifiedName = stripDelimiters(qualifiedName, QSqlDriver::TableName);
1503 QString schema, table;
1504 d->splitTableQualifier(qualifiedName, &schema, &table);
1505
1506 QString sql =
1507 "select column_name, case when data_type = 'INTERVAL' then 'INTERVAL '|| interval_type "
1508 "when data_type = 'INTEGER' and numeric_precision > 10 then 'BIGINT' "
1509 "when data_type = 'INTEGER' and numeric_precision <= 10 AND numeric_precision > 5 "
1510 "then 'INTEGER' when data_type = 'INTEGER' and numeric_precision <= 5 then 'SMALLINT' "
1511 "else UPPER(data_type) end as data_type, case when is_nullable = 'YES' then false else "
1512 "true end as required, "
1513 "coalesce(numeric_precision, coalesce(datetime_precision,coalesce(interval_precision, "
1514 "-1))) as prec from information_schema.columns where table_name = \'"_L1;
1515 if (schema.length() == 0)
1516 sql.append(table).append("\' and table_schema = CURRENT_USER"_L1);
1517 else
1518 sql.append(table).append("\' and table_schema = \'"_L1).append(schema).append("\'"_L1);
1519 sql.append(" order by ordinal_position"_L1);
1520 if (!t.exec(sql))
1521 return QSqlRecord();
1522
1523 while (t.next()) {
1524 QSqlField field(t.value(0).toString(),
1525 QMetaType(qDecodeMSQLType(qLookupMimDataType(t.value(1).toString()))),
1526 tablename);
1527 field.setRequired(t.value(3).toBool());
1528 if (t.value(3).toInt() != -1)
1529 field.setPrecision(t.value(3).toInt());
1530 rec.append(field);
1531 }
1532
1533 return rec;
1534}
1535
1536QVariant QMimerSQLDriver::handle() const
1537{
1538 Q_D(const QMimerSQLDriver);
1539 return QVariant::fromValue(d->sessionhandle);
1540}
1541
1542QString QMimerSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
1543{
1544 Q_UNUSED(type);
1545 QString res = identifier;
1546 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"')) {
1547 res.replace(u'"', "\"\""_L1);
1548 res = u'"' + res + u'"';
1549 res.replace(u'.', "\".\""_L1);
1550 }
1551 return res;
1552}
1553
1554bool QMimerSQLDriver::beginTransaction()
1555{
1556 Q_D(const QMimerSQLDriver);
1557 const int32_t err = MimerBeginTransaction(d->sessionhandle, MIMER_TRANS_READWRITE);
1558 if (!MIMER_SUCCEEDED(err)) {
1559 setLastError(qMakeError(
1560 QCoreApplication::translate("QMimerSQLDriver", "Could not start transaction"), err,
1561 QSqlError::TransactionError, d));
1562 return false;
1563 }
1564 return true;
1565}
1566
1567bool QMimerSQLDriver::commitTransaction()
1568{
1569 Q_D(const QMimerSQLDriver);
1570 const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_COMMIT);
1571 if (!MIMER_SUCCEEDED(err)) {
1572 setLastError(qMakeError(
1573 QCoreApplication::translate("QMimerSQLDriver", "Could not commit transaction"), err,
1574 QSqlError::TransactionError, d));
1575 return false;
1576 }
1577 return true;
1578}
1579
1580bool QMimerSQLDriver::rollbackTransaction()
1581{
1582 Q_D(const QMimerSQLDriver);
1583 const int32_t err = MimerEndTransaction(d->sessionhandle, MIMER_ROLLBACK);
1584 if (!MIMER_SUCCEEDED(err)) {
1585 setLastError(qMakeError(
1586 QCoreApplication::translate("QMimerSQLDriver", "Could not roll back transaction"),
1587 err, QSqlError::TransactionError, d));
1588 return false;
1589 }
1590 return true;
1591}
1592
1593void QMimerSQLDriverPrivate::splitTableQualifier(const QString &qualifiedName, QString *schema,
1594 QString *table) const
1595{
1596 const QList<QStringView> l = QStringView(qualifiedName).split(u'.');
1597 int n = l.count();
1598 if (n > 2) {
1599 return; // can't possibly be a valid table qualifier
1600 } else if (n == 1) {
1601 *schema = QString();
1602 *table = l.at(0).toString();
1603 } else {
1604 *schema = l.at(0).toString();
1605 *table = l.at(1).toString();
1606 }
1607}
1608
1609QT_END_NAMESPACE
1610
1611#include "moc_qsql_mimer.cpp"
MimerSession sessionhandle
void splitTableQualifier(const QString &qualifier, QString *schema, QString *table) const
QVector< QVariant > batch_vector
static constexpr int maxStackStringSize
static constexpr int lobChunkMaxSizeSet
bool fetchNext() override
Positions the result to the next available record (row) in the result.
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QVariant data(int i) override
Returns the data for field index in the current row as a QVariant.
static constexpr int maxDateStringSize
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
int size() override
Returns the size of the SELECT result, or -1 if it cannot be determined or if the query is not a SELE...
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
static constexpr int maxTimeStringSize
static constexpr int genericError
bool fetchLast() override
Positions the result to the last record (last row) in the result.
static constexpr int lobChunkMaxSizeFetch
bool fetch(int i) override
Positions the result to an arbitrary (zero-based) row index.
bool execBatch(bool arrayBind=false) override
bool isNull(int index) override
Returns true if the field at position index in the current row is null; otherwise returns false.
bool fetchFirst() override
Positions the result to the first record (row 0) in the result.
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
QMimerSQLResult(const QMimerSQLDriver *db)
static constexpr int maxTimestampStringSize
virtual ~QMimerSQLResult() override
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
QVariant lastInsertId() const override
Returns the object ID of the most recent inserted row if the database supports it.
The QSqlField class manipulates the fields in SQL database tables and views.
Definition qsqlfield.h:20
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
#define Q_DECLARE_OPAQUE_POINTER(POINTER)
Definition qmetatype.h:1499
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1507
static QSqlError qMakeError(const QString &err, const int errCode, QSqlError::ErrorType type, const QMimerSQLDriverPrivate *p)
static MimerColumnTypes mimerMapColumnTypes(int32_t t)
static QString msgCouldNotGet(const char *type, int column)
static QMetaType::Type qDecodeMSQLType(int32_t t)
static QString msgCouldNotSet(const char *type, int column)
static int32_t qLookupMimDataType(QStringView s)
MimerColumnTypes
#define MIMER_DEFAULT_DATATYPE