29 QList<ColumnInfo> cols;
30 unsigned count = meta->getCount(&st);
32 for (
unsigned i = 0; i < count; ++i) {
34 ci.name = QString::fromUtf8(meta->getAlias(&st, i));
35 if (ci.name.isEmpty())
36 ci.name = QString::fromUtf8(meta->getField(&st, i));
37 ci.relation = QString::fromUtf8(meta->getRelation(&st, i));
38 ci.field = QString::fromUtf8(meta->getField(&st, i));
39 ci
.fbType =
static_cast<
int>(meta->getType(&st, i));
41 ci
.scale = meta->getScale(&st, i);
42 ci
.length = meta->getLength(&st, i);
43 ci
.offset = meta->getOffset(&st, i);
45 ci
.nullable = (meta->isNullable(&st, i) != FB_FALSE);
46 ci.qtType = fbTypeToQt(ci.fbType, ci.scale);
280 Q_D(QFirebirdResult);
284
285
286 if (
const TxnOp op = classifyTxnControl(query); op != TxnOp::None) {
292 const QByteArray utf8 = query.toUtf8();
294 ThrowStatusWrapper st(d->status());
295 ITransaction *tr = d->ensureTransaction();
296 d->stmt = d->att()->prepare(&st, tr, 0, utf8.constData(),
298 IStatement::PREPARE_PREFETCH_METADATA);
299 unsigned flags = d->stmt->getFlags(&st);
300 d->isSelect = (flags & IStatement::FLAG_HAS_CURSOR) != 0;
301 setSelect(d->isSelect);
303 d->inMeta = d->stmt->getInputMetadata(&st);
304 if (d->inMeta && d->inMeta->getCount(&st) == 0) {
305 d->inMeta->release();
308 d->outMeta = d->stmt->getOutputMetadata(&st);
310 if (d->outMeta->getCount(&st) == 0) {
311 d->outMeta->release();
312 d->outMeta =
nullptr;
314 d->cols = buildColumns(d->outMeta, st);
315 d->outBuffer.resize(
static_cast<qsizetype>(d->outMeta->getAlignedLength(&st)));
318 if (!d->isSelect && d->outMeta) {
320 d->isProcExec =
true;
324 d->inBuffer.resize(
static_cast<qsizetype>(d->inMeta->getAlignedLength(&st)));
325 d->inCols = buildColumns(d->inMeta, st);
327 }
catch (
const FbException &e) {
328 const QSqlError err = fbError(d->master(), e.getStatus(), QSqlError::StatementError);
329 qCInfo(lcFirebird) <<
"prepare:" << fbErrorLog(err);
331
332
416 BlobWriter blobWriter)
421 const qsizetype count = inCols.size();
424
425
426
427 if (vals.size() < count) {
428 errorText = u"Parameter count mismatch: statement expects %1, %2 bound"_s
429 .arg(count).arg(vals.size());
433 for (qsizetype i = 0; i < count; ++i) {
435 short *nullFlag =
reinterpret_cast<
short *>(inBuffer.data() + ci.nullOffset);
436 const QVariant &val = vals[i];
439
440
441
442 bool valueIsNull = val.isNull();
444 switch (val.metaType().id()) {
445 case QMetaType::QString: valueIsNull = val.toString().isNull();
break;
446 case QMetaType::QByteArray: valueIsNull = val.toByteArray().isNull();
break;
447 case QMetaType::QDateTime: valueIsNull = !val.toDateTime().isValid();
break;
448 case QMetaType::QDate: valueIsNull = !val.toDate().isValid();
break;
449 case QMetaType::QTime: valueIsNull = !val.toTime().isValid();
break;
455
456
457
458
459
466 char *data = inBuffer.data() + ci.offset;
469 if (ci.scale != 0 || fbBaseType(ci.fbType) == SQL_INT128) {
471 if (!encodeScaledNumeric(data, ci.fbType, ci.scale, val, numError)) {
472 errorText = u"Parameter %1: %2"_s.arg(QString::number(i + 1), numError);
480 *
reinterpret_cast<qint16 *>(data) =
static_cast<qint16>(val.toInt());
483 *
reinterpret_cast<qint32 *>(data) =
static_cast<qint32>(val.toInt());
486 *
reinterpret_cast<qint64 *>(data) = val.toLongLong();
489 *
reinterpret_cast<
float *>(data) =
static_cast<
float>(val.toDouble());
492 *
reinterpret_cast<
double *>(data) = val.toDouble();
495 ThrowStatusWrapper innerSt(status());
496 decFloatFromString(master()->getUtilInterface()->getDecFloat16(&innerSt), innerSt,
497 QtPrivate::sqlNumericInputString(val),
reinterpret_cast<FB_DEC16 *>(data));
501 ThrowStatusWrapper innerSt(status());
502 decFloatFromString(master()->getUtilInterface()->getDecFloat34(&innerSt), innerSt,
503 QtPrivate::sqlNumericInputString(val),
reinterpret_cast<FB_DEC34 *>(data));
507 *
reinterpret_cast<FB_BOOLEAN *>(data) =
508 val.toBool() ? FB_TRUE : FB_FALSE;
511 *
reinterpret_cast<ISC_DATE *>(data) =
512 encodeQDate(master()->getUtilInterface(), val.toDate());
515 *
reinterpret_cast<ISC_TIME *>(data) =
516 encodeQTime(master()->getUtilInterface(), val.toTime());
519 *
reinterpret_cast<ISC_TIMESTAMP *>(data) =
520 encodeQDateTime(master()->getUtilInterface(), val.toDateTime());
522 case SQL_TIMESTAMP_TZ:
523 *
reinterpret_cast<ISC_TIMESTAMP_TZ *>(data) =
524 encodeQDateTimeTz(status(), master()->getUtilInterface(), val.toDateTime());
527 *
reinterpret_cast<ISC_TIME_TZ *>(data) =
528 encodeQTimeTz(status(), master()->getUtilInterface(), val.toDateTime());
532 encodeTextValue(data, ci
.fbType, ci
.length, val.toString().toUtf8());
535 const QByteArray blobData = val.toByteArray();
536 ISC_QUAD &blobId = *
reinterpret_cast<ISC_QUAD *>(data);
539
540 if (!blobWriter(blobId, blobData)) {
541 errorText = u"Failed to write BLOB for parameter %1"_s.arg(i + 1);
545 writeInlineBlob(blobId, blobData);
550 if (val.typeId() != QMetaType::QVariantList) {
551 errorText = u"Cannot bind non-list value to ARRAY parameter %1"_s.arg(i + 1);
554 ISC_QUAD &arrId = *
reinterpret_cast<ISC_QUAD *>(data);
555 ITransaction *tr = ensureTransaction();
556 if (!writeArray(att(), tr, status(), master(), arrayDescCache,
557 &arrId, ci.relation, ci.field, val.toList())) {
558 errorText = u"Failed to write ARRAY parameter %1"_s.arg(i + 1);
597 Q_D(QFirebirdResult);
600
601
602
603 if (d->txnOp != TxnOp::None) {
604 ITransaction *&iTrans = d->drv_d_func()->iTrans;
605 ITransaction *tr = iTrans ? iTrans : d->autoTrans;
608 ThrowStatusWrapper st(d->status());
609 if (d->txnOp == TxnOp::Commit)
613 }
catch (
const FbException &e) {
614 const QSqlError err = fbError(d->master(), e.getStatus(), QSqlError::TransactionError);
615 qCInfo(lcFirebird) <<
"exec(txn):" << fbErrorLog(err);
622 d->autoTrans =
nullptr;
625 setAt(QSql::AfterLastRow);
630 setLastError(QSqlError(u"Statement not prepared"_s, QString(), QSqlError::StatementError));
635
636
637
639 const qsizetype paramCount = d->inMeta ? d->inCols.size() : 0;
641 for (
int p = 0, bc = boundValueCount(); p < bc; ++p) {
642 if (bindValueType(p).testFlag(QSql::In))
645 if (inputCount > paramCount) {
646 setLastError(QSqlError(
647 u"Parameter count mismatch: statement expects %1 input parameter(s), %2 bound"_s
648 .arg(paramCount).arg(inputCount),
649 QString(), QSqlError::StatementError));
656 d->affectedRows = -1;
657 d->resetOverflowWarnings();
660 ThrowStatusWrapper st(d->status());
661 ITransaction *tr = d->ensureTransaction();
663 if (!d->buildInputMessage(bindError)) {
664 setLastError(QSqlError(bindError, QString(), QSqlError::StatementError));
669 d->cursor = d->stmt->openCursor(&st, tr,
671 d->inBuffer.isEmpty() ?
nullptr : d->inBuffer.data(),
673 isForwardOnly() ? 0 : IStatement::CURSOR_TYPE_SCROLLABLE);
676 d->cacheComplete =
false;
677 d->useRowCache = d->drv_d_func()->cacheScrollableResults && !isForwardOnly();
678 setAt(QSql::BeforeFirstRow);
681 d->isProcExec =
false;
682 d->procRowFetched =
false;
684
685
686 if (!d->outBuffer.isEmpty())
687 d->outBuffer.fill(0);
688 ITransaction *newTr = d->stmt->execute(&st, tr,
690 d->inBuffer.isEmpty() ?
nullptr : d->inBuffer.data(),
692 d->outBuffer.isEmpty() ?
nullptr : d->outBuffer.data());
694
695 d->adoptReplacementTransaction(tr, newTr);
696 d->affectedRows = QFirebirdResultPrivate::AffectedPending;
699
700
701 d->isProcExec =
true;
702 setAt(QSql::BeforeFirstRow);
706 if (d->autoTrans && !d->drv_d_func()->iTrans)
708 setAt(QSql::AfterLastRow);
712 }
catch (
const FbException &e) {
713 const QSqlError err = fbError(d->master(), e.getStatus(), QSqlError::StatementError);
714 qCInfo(lcFirebird) <<
"exec:" << fbErrorLog(err);
723 Q_D(QFirebirdResult);
725 if (!d->stmt || !d->inMeta) {
727 return QSqlResult::execBatch(arrayBind);
730 const QList<QVariant> &batchValues = d->values;
731 if (batchValues.isEmpty()) {
732 setLastError(QSqlError(u"No values bound for batch execution"_s,
733 QString(), QSqlError::StatementError));
737 const qsizetype paramCount = batchValues.size();
738 const QVariantList firstList = batchValues.at(0).toList();
739 const qsizetype batchCount = firstList.size();
745 d->affectedRows = -1;
746 d->resetOverflowWarnings();
748 IUtil *utl = d->master()->getUtilInterface();
751 ThrowStatusWrapper st(d->status());
752 ITransaction *tr = d->ensureTransaction();
755
756
757 FbGuard<IXpbBuilder> pb(utl->getXpbBuilder(&st, IXpbBuilder::BATCH,
nullptr, 0),
758 fbDispose<IXpbBuilder>);
759 pb->insertInt(&st, IBatch::TAG_RECORD_COUNTS, 1);
762 bool hasBlobs =
false;
763 for (
const ColumnInfo &ci : std::as_const(d->inCols)) {
764 if (fbBaseType(ci.fbType) == SQL_BLOB) {
770 pb->insertInt(&st, IBatch::TAG_BLOB_POLICY, IBatch::BLOB_ID_ENGINE);
773
774 FbGuard<IBatch> batch(d->stmt->createBatch(&st, d->inMeta,
775 pb->getBufferLength(&st), pb->getBuffer(&st)),
779
780
781 QVarLengthArray<QVariantList, 16> paramLists;
782 paramLists.reserve(paramCount);
783 for (qsizetype p = 0; p < paramCount; ++p)
784 paramLists.append(batchValues.at(p).toList());
786 for (qsizetype row = 0; row < batchCount; ++row) {
787 QList<QVariant> rowValues;
788 rowValues.reserve(paramCount);
789 for (qsizetype p = 0; p < paramCount; ++p) {
790 const QVariantList &col = paramLists[p];
791 rowValues.append(row < col.size() ? col.at(row) : QVariant());
797 auto blobWriter = [&](ISC_QUAD &blobId,
const QByteArray &data) ->
bool {
799 batch->addBlob(&st,
static_cast<
unsigned>(data.size()),
800 data.constData(), &blobId, 0,
nullptr);
802 }
catch (
const FbException &e) {
803 const auto berr = fbError(d->master(), e.getStatus(), QSqlError::StatementError);
804 qCWarning(lcFirebird) <<
"batch addBlob:" << fbErrorLog(berr);
810 if (!d->fillInputBuffer(rowValues, bindError,
811 hasBlobs ? blobWriter : QFirebirdResultPrivate::BlobWriter{})) {
812 setLastError(QSqlError(bindError, QString(), QSqlError::StatementError));
816 batch->add(&st, 1, d->inBuffer.data());
819 FbGuard<IBatchCompletionState> cs(batch->execute(&st, tr),
820 fbDispose<IBatchCompletionState>);
822 unsigned total = cs->getSize(&st);
823 int totalAffected = 0;
824 bool hadError =
false;
825 for (
unsigned p = 0; p < total; ++p) {
826 int state = cs->getState(&st, p);
827 if (state == IBatchCompletionState::EXECUTE_FAILED) {
829 }
else if (state != IBatchCompletionState::SUCCESS_NO_INFO) {
830 totalAffected += state;
836 d->affectedRows = totalAffected;
839 unsigned errPos = cs->findError(&st, 0);
840 if (errPos != IBatchCompletionState::NO_MORE_ERRORS) {
841 IStatus *errStatus = d->master()->getStatus();
843 cs->getStatus(&st, errStatus, errPos);
844 setLastError(fbError(d->master(), errStatus, QSqlError::StatementError));
846 setLastError(QSqlError(u"Batch execution failed at message %1"_s
848 QString(), QSqlError::StatementError));
850 errStatus->dispose();
853
854 setLastError(QSqlError(u"Batch execution failed"_s,
855 QString(), QSqlError::StatementError));
860
861
862 batch.closeWith([&](IBatch *b) { b->close(&st); });
865 if (d->autoTrans && !d->drv_d_func()->iTrans)
868 setAt(QSql::AfterLastRow);
872 }
catch (
const FbException &e) {
873 const QSqlError err = fbError(d->master(), e.getStatus(), QSqlError::StatementError);
874 qCInfo(lcFirebird) <<
"execBatch:" << fbErrorLog(err);
1127 Q_D(
const QFirebirdResult);
1129 if (field < 0 || field >= d->cols.size()) {
1130 qCDebug(lcFirebird,
"data(%d): out of range, cols.size()=%d", field,
int(d->cols.size()));
1137
1138
1140 qCDebug(lcFirebird,
"data(%d): SQL NULL, fbType=%d qtType=%d", field, ci
.fbType,
int(ci.qtType));
1141 return QVariant(QMetaType(ci.qtType));
1144 qCDebug(lcFirebird,
"data(%d): fbType=%d qtType=%d", field, ci
.fbType,
int(ci.qtType));
1145 const char *ptr = d->outBuffer.constData() + ci.offset;
1148
1149
1150
1154 const auto policy = numericalPrecisionPolicy();
1157 return QtPrivate::sqlApplyScale(*
reinterpret_cast<
const qint16 *>(ptr), ci.scale, policy);
1159 return QtPrivate::sqlApplyScale(*
reinterpret_cast<
const qint32 *>(ptr), ci.scale, policy);
1161 QString overflowText;
1162 const QVariant value = decodeInt128Slot(ptr, ci.scale, policy, d->master(),
1163 d->status(), &overflowText);
1167 const int row = d->isProcExec ? 0 : at();
1168 if (!overflowText.isEmpty() && d->firstOverflowWarning(row, field))
1169 QtPrivate::sqlWarnLowPrecisionOverflow(lcFirebird, overflowText, policy);
1173 return QtPrivate::sqlApplyScale(*
reinterpret_cast<
const qint64 *>(ptr), ci.scale, policy);
1179 return static_cast<
int>(*
reinterpret_cast<
const qint16 *>(ptr));
1181 return static_cast<
int>(*
reinterpret_cast<
const qint32 *>(ptr));
1183 return *
reinterpret_cast<
const qint64 *>(ptr);
1186 return decodeInt128Slot(ptr, 0, QSql::HighPrecision, d->master(), d->status());
1188 return static_cast<
double>(*
reinterpret_cast<
const float *>(ptr));
1190 return *
reinterpret_cast<
const double *>(ptr);
1192 ThrowStatusWrapper st(d->status());
1193 return decFloatToString(d->master()->getUtilInterface()->getDecFloat16(&st), st,
1194 reinterpret_cast<
const FB_DEC16 *>(ptr));
1197 ThrowStatusWrapper st(d->status());
1198 return decFloatToString(d->master()->getUtilInterface()->getDecFloat34(&st), st,
1199 reinterpret_cast<
const FB_DEC34 *>(ptr));
1202 return *
reinterpret_cast<
const FB_BOOLEAN *>(ptr) != FB_FALSE;
1204 return decodeFirebirdDate(d->master()->getUtilInterface(),
1205 *
reinterpret_cast<
const ISC_DATE *>(ptr));
1207 return decodeFirebirdTime(d->master()->getUtilInterface(),
1208 *
reinterpret_cast<
const ISC_TIME *>(ptr));
1210 return decodeFirebirdTimestamp(d->master()->getUtilInterface(),
1211 *
reinterpret_cast<
const ISC_TIMESTAMP *>(ptr));
1212 case SQL_TIMESTAMP_TZ:
1213 return decodeFirebirdTimestampTz(d->status(), d->master()->getUtilInterface(),
1214 *
reinterpret_cast<
const ISC_TIMESTAMP_TZ *>(ptr));
1216 return decodeFirebirdTimeTz(d->status(), d->master()->getUtilInterface(),
1217 *
reinterpret_cast<
const ISC_TIME_TZ *>(ptr));
1219 const ISC_QUAD &blobId = *
reinterpret_cast<
const ISC_QUAD *>(ptr);
1220 QByteArray ba = readBlob(d->att(), d->activeTransaction(),
1221 d->status(), blobId);
1223 if (ci.fbSubType == 1)
1224 return QString::fromUtf8(ba);
1228 const ISC_QUAD &arrayId = *
reinterpret_cast<
const ISC_QUAD *>(ptr);
1229 ITransaction *tra = d->activeTransaction();
1230 return fetchArray(d->att(), tra, d->status(), d->master(),
1231 d->arrayDescCache, arrayId, ci.relation, ci.field);
1235
1236
1237 const unsigned short len = *
reinterpret_cast<
const unsigned short *>(ptr);
1238 const qsizetype maxLen =
1239 std::max(qsizetype(0),
static_cast<qsizetype>(ci
.length) - 2);
1240 return QString::fromUtf8(ptr + 2, std::min(
static_cast<qsizetype>(len), maxLen));
1244
1245 QString s = QString::fromUtf8(ptr,
static_cast<qsizetype>(ci.length));
1246 while (s.endsWith(u' '))
1251 qCWarning(lcFirebird,
"data: unhandled Firebird type %d for field %d", ci
.fbType, field);
1252 return QString::fromUtf8(ptr);
1254 }
catch (
const FbException &e) {
1255 setLastError(fbError(d->master(), e.getStatus(), QSqlError::StatementError));