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_firebird_result_p.h
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// Copyright (C) 2026 Andreas Bacher
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:significant reason:default
5#ifndef QSQL_FIREBIRD_RESULT_P_H
6#define QSQL_FIREBIRD_RESULT_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
21
22#include <QtCore/qbytearray.h>
23#include <QtCore/qhash.h>
24#include <QtCore/qlist.h>
25#include <QtSql/qsqlrecord.h>
26#include <QtSql/private/qsqlresult_p.h>
27
28#include <ibase.h> // ISC_QUAD, ISC_ARRAY_DESC
29#include <firebird/Interface.h>
30
31#include <functional>
32
33QT_BEGIN_NAMESPACE
34
35/*! \internal
36 Transaction-control statement kind (bare COMMIT/ROLLBACK), handled via the
37 ITransaction API rather than executed as DSQL.
38*/
39enum class TxnOp : quint8 {
40 None,
41 Commit,
42 Rollback,
43};
44
45struct ColumnInfo {
47 QString relation; // table name (for array lookup)
48 QString field; // column name in table (for array lookup)
49 int fbType = 0;
50 int fbSubType = 0;
51 int scale = 0;
52 unsigned length = 0; // byte length from metadata
53 unsigned offset = 0;
54 unsigned nullOffset = 0;
55 bool nullable = true;
57};
58
59/*! \internal
60 Defined ahead of QFirebirdResultPrivate: Q_DECLARE_PUBLIC below static_casts
61 QSqlResult* to QFirebirdResult*, which needs the complete type here.
62*/
64{
66 friend class QFirebirdDriver;
68
69public:
70 explicit QFirebirdResult(const QFirebirdDriver *driver);
72
73 QVariant handle() const override;
74
75protected:
76 bool prepare(const QString &query) override;
77 bool exec() override;
78 bool execBatch(bool arrayBind = false) override;
79
80 QVariant data(int field) override;
81 bool isNull(int field) override;
82 bool reset(const QString &query) override;
83 int size() override;
86
87 bool fetch(int i) override;
88 bool fetchFirst() override;
89 bool fetchLast() override;
90 bool fetchNext() override;
92
93 QSqlRecord record() const override;
94
95private:
96 void cleanup();
97 /*! \internal
98 After EXECUTE PROCEDURE, copy output columns back into bound QSql::Out
99 parameters so they can be read via QSqlQuery::boundValue().
100 */
101 void writeOutValues();
102 /*! \internal
103 Cache-mode (FB_SCROLLABLE_CACHE) fetch: serve absolute row `target` from
104 the client-side row cache, populating it from the server as needed.
105 */
106 bool fetchCached(int target);
107};
108
110{
111 Q_DECLARE_PUBLIC(QFirebirdResult)
113public:
115
117 {
118 cleanup();
119 /* Deregister from the driver, unless the driver has already been
120 destroyed (which sets driverAlive = false and clears its list). */
121 if (driverAlive)
122 drv_d_func()->activeResults.removeOne(this);
123 }
124
125 void cleanup();
126
127 /*! \internal
128 Close the open cursor (if any), releasing the handle if the close throws.
129 Assumes the attachment is alive — only exec()/execBatch() call it, and only
130 while executing. cleanup() handles the detached case inline.
131 */
132 void closeCursor();
133
134 /*! \internal
135 Set to false by ~QFirebirdDriver if the driver object is destroyed while
136 this result is still alive. Once false, cleanup() must not dereference the
137 (now-freed) driver private.
138 */
139 bool driverAlive = true;
140
141 /*! \internal
142 Returns the driver's master/status/attachment/transaction without copying
143 them. These are const accessors that hand back mutable Firebird interface
144 pointers by design: the Firebird OO API has no const-qualified interfaces,
145 and result fetching (e.g. data() const reading a BLOB) is inherently a
146 mutating server operation, so logical const cannot propagate here.
147 */
148 Firebird::IMaster *master() const { return drv_d_func()->master; }
149 Firebird::IStatus *status() const { return drv_d_func()->iStatus; }
150 Firebird::IAttachment *att() const { return drv_d_func()->iAtt; }
152 { return drv_d_func()->iTrans ? drv_d_func()->iTrans : autoTrans; }
153
154 // Ensure a transaction is active; auto-starts one if needed.
156
157 // Build input message from bound values and fill inBuffer.
158 bool buildInputMessage(QString &errorText);
159
160 /*! \internal
161 Core helper: fill inBuffer from a flat list of values (one per parameter,
162 metadata taken from the inCols cache). blobWriter, when set, is called
163 for SQL_BLOB parameters instead of using att->createBlob().
164 Signature: bool(ISC_QUAD &blobId, const QByteArray &data).
165 Returns false on error.
166 */
167 using BlobWriter = std::function<bool(ISC_QUAD &, const QByteArray &)>;
168 bool fillInputBuffer(const QList<QVariant> &vals,
169 QString &errorText, BlobWriter blobWriter = {});
170
171 /*! \internal
172 Per-value encoders, factored out of fillInputBuffer so it stays focused
173 on parameter iteration and NULL handling. Both may throw FbException,
174 which exec()/execBatch translate into a QSqlError.
175 encodeScaledNumeric returns false (with errorText set) for values that
176 cannot be represented exactly in the column's range.
177 */
178 bool encodeScaledNumeric(char *data, int fbType, int scale, const QVariant &val,
179 QString &errorText);
180 void writeInlineBlob(ISC_QUAD &blobId, const QByteArray &blobData);
181
182 /*! \internal
183 Adopt a transaction handle that IStatement::execute() returned in place of
184 the one it was given, releasing the replaced handle exactly once.
185 */
186 void adoptReplacementTransaction(Firebird::ITransaction *executedTr,
187 Firebird::ITransaction *newTr);
188
191 Firebird::ITransaction *autoTrans = nullptr; // auto-started transaction (non-user)
192
195
196 QList<ColumnInfo> cols; // output columns, cached at prepare
197 QList<ColumnInfo> inCols; // input parameters, cached at prepare (metadata
198 // is immutable after prepare; avoids re-reading
199 // it per parameter on every exec/batch row)
200 QByteArray outBuffer; // row buffer for output
201 QByteArray inBuffer; // parameter message buffer
202
203 /*! \internal
204 Array descriptors resolved by cachedArrayDesc, keyed by relation/field.
205 Valid while the statement lives; cleared in cleanup(). mutable: data()
206 reads through a const private pointer and only populates the cache.
207 */
209
210 /*! \internal
211 getAffectedRecords() costs a statement-info round trip, so exec() only
212 marks the count as pending and numRowsAffected() resolves it on demand
213 (QIBASE defers the same way). -1 = unknown/none.
214 */
215 static constexpr int AffectedPending = -2;
216 int affectedRows = -1;
217 bool isSelect = false;
218 bool isProcExec = false; // EXECUTE PROCEDURE with output params
219 bool procRowFetched = false; // the single proc output row has been consumed
220
221 TxnOp txnOp = TxnOp::None; // see TxnOp
222
223 /*! \internal
224 ---- Opt-in client-side row cache (FB_SCROLLABLE_CACHE) ----
225 Enabled in exec() for scrollable SELECTs when the driver flag is set.
226 rowCache holds a raw snapshot of outBuffer per absolute row index; on a
227 cached fetch the snapshot is copied back into outBuffer so data() works
228 unchanged (BLOB/array columns cache their blob id, valid for the txn).
229 */
230 bool useRowCache = false;
231 bool cacheComplete = false; // true once the cursor has been read to EOF
233
234 /*! \internal
235 Pull rows from the server cursor (sequential fetchNext, which benefits
236 from fbclient read-ahead) into rowCache until it holds at least index+1
237 rows or the cursor is exhausted. Returns true if rowCache[index] exists.
238 May throw FbException (callers wrap in try/catch as elsewhere).
239 */
240 bool ensureCachedRow(int index);
241
242 /*! \internal
243 QSqlRecord is derived solely from the (immutable-after-prepare) column
244 metadata, so build it once and hand back copies — see record().
245 */
247 mutable bool recordCached = false;
248
249 QString formatFbError(const QString &ctx);
250};
251
252QT_END_NAMESPACE
253
254#endif // QSQL_FIREBIRD_RESULT_P_H
void setError(const QString &msg, QSqlError::ErrorType type, const QString &code={})
QHash< QString, QFirebirdEventSubscription * > eventSubscriptions
Firebird::ITransaction * iTrans
Firebird::IAttachment * iAtt
QList< QFirebirdResultPrivate * > activeResults
bool finishTransaction(TxnEnd end)
void setFbError(const QString &context, QSqlError::ErrorType type=QSqlError::UnknownError)
bool commitTransaction() override
This function is called to commit a transaction.
QSqlResult * createResult() const override
Creates an empty SQL result on the database.
bool cancelQuery() override
bool hasFeature(DriverFeature feature) const override
Returns true if the driver supports feature feature; otherwise returns false.
QString escapeIdentifier(const QString &identifier, IdentifierType type) const override
Returns the identifier escaped according to the database rules.
QSqlRecord record(const QString &tableName) const override
Returns a QSqlRecord populated with the names of the fields in table tableName.
QFirebirdDriver(QObject *parent=nullptr)
bool rollbackTransaction() override
This function is called to rollback a transaction.
QSqlIndex primaryIndex(const QString &tableName) const override
Returns the primary index for table tableName.
int maximumIdentifierLength(IdentifierType type) const override
QVariant handle() const override
Returns the low-level database handle wrapped in a QVariant or an invalid variant if there is no hand...
bool open(const QString &db, const QString &user, const QString &password, const QString &host, int port, const QString &connOpts) override
Derived classes must reimplement this pure virtual function to open a database connection on database...
QStringList subscribedToNotifications() const override
Returns a list of the names of the event notifications that are currently subscribed to.
bool unsubscribeFromNotification(const QString &name) override
This function is called to unsubscribe from event notifications from the database.
QString formatValue(const QSqlField &field, bool trimStrings) const override
Returns a string representation of the field value for the database.
bool beginTransaction() override
This function is called to begin a transaction.
bool subscribeToNotification(const QString &name) override
This function is called to subscribe to event notifications from the database.
~QFirebirdDriver() override
void close() override
Derived classes must reimplement this pure virtual function in order to close the database connection...
void eventCallbackFunction(unsigned length, const unsigned char *data) override
QFirebirdEventSubscription(QFirebirdDriver *drv, IAttachment *att, const QString &eventName)
Firebird::IStatement * stmt
Firebird::IAttachment * att() const
QString formatFbError(const QString &ctx)
Firebird::IMessageMetadata * inMeta
void adoptReplacementTransaction(Firebird::ITransaction *executedTr, Firebird::ITransaction *newTr)
bool encodeScaledNumeric(char *data, int fbType, int scale, const QVariant &val, QString &errorText)
bool fillInputBuffer(const QList< QVariant > &vals, QString &errorText, BlobWriter blobWriter={})
Firebird::ITransaction * autoTrans
Firebird::IMessageMetadata * outMeta
Firebird::ITransaction * activeTransaction() const
Firebird::IResultSet * cursor
static constexpr int AffectedPending
Firebird::IStatus * status() const
Firebird::ITransaction * ensureTransaction()
bool buildInputMessage(QString &errorText)
void writeInlineBlob(ISC_QUAD &blobId, const QByteArray &blobData)
Firebird::IMaster * master() const
bool execBatch(bool arrayBind=false) override
bool fetchNext() override
Positions the result to the next available record (row) in the result.
bool fetchLast() override
Positions the result to the last record (last row) in the result.
QFirebirdResult(const QFirebirdDriver *driver)
bool isNull(int field) override
Returns true if the field at position index in the current row is null; otherwise returns false.
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
void detachFromResultSet() override
bool fetchFirst() override
Positions the result to the first record (row 0) in the result.
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
bool fetchPrevious() override
Positions the result to the previous record (row) in the result.
bool fetch(int i) override
Positions the result to an arbitrary (zero-based) row index.
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...
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
QVariant data(int field) override
Returns the data for field index in the current row as a QVariant.
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QDateTime decodeFirebirdTimestamp(Firebird::IUtil *util, const ISC_TIMESTAMP &ts)
int fbBaseType(int t)
QString fbErrorLog(const QSqlError &err)
static void decFloatFromString(Iface *iface, Firebird::ThrowStatusWrapper &st, const QByteArray &s, Raw *out)
Firebird::ITransaction * startDefaultTransaction(Firebird::IAttachment *att, Firebird::ThrowStatusWrapper &st, TxnWait wait)
QString fbErrorString(Firebird::IMaster *master, Firebird::IStatus *status)
bool decimalToScaledInt64(const QByteArray &decimal, int scale, qint64 *out)
ISC_TIMESTAMP encodeQDateTime(Firebird::IUtil *util, const QDateTime &dt)
ISC_TIME encodeQTime(Firebird::IUtil *util, const QTime &t)
QDate decodeFirebirdDate(Firebird::IUtil *util, ISC_DATE date)
static QVariant applyScale(T val, int scale, QSql::NumericalPrecisionPolicy policy)
QDateTime decodeFirebirdTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util, const ISC_TIME_TZ &ttz)
void encodeTextValue(char *data, int fbType, qsizetype length, const QByteArray &bytes)
void finishAndClear(Firebird::ThrowStatusWrapper &st, Firebird::ITransaction *&tr, TxnEnd end)
QDateTime decodeFirebirdTimestampTz(Firebird::IStatus *iStatus, Firebird::IUtil *util, const ISC_TIMESTAMP_TZ &tstz)
QByteArray numericInputString(const QVariant &val)
QMetaType::Type blrTypeToQt(unsigned char blrType, bool hasScale)
QTime decodeFirebirdTime(Firebird::IUtil *util, ISC_TIME time)
ISC_TIMESTAMP_TZ encodeQDateTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util, const QDateTime &dt)
static QString decFloatToString(Iface *iface, Firebird::ThrowStatusWrapper &st, const Raw *value)
ISC_TIME_TZ encodeQTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util, const QDateTime &dt)
QString int128ToString(Firebird::IUtil *util, Firebird::ThrowStatusWrapper &st, const FB_I128 *value, int scale)
QSqlError fbError(Firebird::IMaster *master, Firebird::IStatus *status, QSqlError::ErrorType type)
ISC_DATE encodeQDate(Firebird::IUtil *util, const QDate &d)
QMetaType::Type fbTypeToQt(int fbType, int scale)
static QString numberToHighPrecision(T val, int scale)
qint64 pow10i(int n)
QMetaType::Type qtType