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 <QtCore/qset.h>
26#include <QtSql/qsqlrecord.h>
27#include <QtSql/private/qsqlresult_p.h>
28
29#include <ibase.h> // ISC_QUAD, ISC_ARRAY_DESC
30#include <firebird/Interface.h>
31
32#include <functional>
33#include <limits>
34
35QT_BEGIN_NAMESPACE
36
37/*! \internal
38 Transaction-control statement kind (bare COMMIT/ROLLBACK), handled via the
39 ITransaction API rather than executed as DSQL.
40*/
41enum class TxnOp : quint8 {
42 None,
43 Commit,
44 Rollback,
45};
46
47struct ColumnInfo {
49 QString relation; // table name (for array lookup)
50 QString field; // column name in table (for array lookup)
51 int fbType = 0;
52 int fbSubType = 0;
53 int scale = 0;
54 unsigned length = 0; // byte length from metadata
55 unsigned offset = 0;
56 unsigned nullOffset = 0;
57 bool nullable = true;
59};
60
61/*! \internal
62 Defined ahead of QFirebirdResultPrivate: Q_DECLARE_PUBLIC below static_casts
63 QSqlResult* to QFirebirdResult*, which needs the complete type here.
64*/
66{
68 friend class QFirebirdDriver;
70
71public:
72 explicit QFirebirdResult(const QFirebirdDriver *driver);
74
75 QVariant handle() const override;
76
77protected:
78 bool prepare(const QString &query) override;
79 bool exec() override;
80 bool execBatch(bool arrayBind = false) override;
81
82 QVariant data(int field) override;
83 bool isNull(int field) override;
84 bool reset(const QString &query) override;
85 int size() override;
88
89 bool fetch(int i) override;
90 bool fetchFirst() override;
91 bool fetchLast() override;
92 bool fetchNext() override;
94
95 QSqlRecord record() const override;
96
97private:
98 void cleanup();
99 /*! \internal
100 After EXECUTE PROCEDURE, copy output columns back into bound QSql::Out
101 parameters so they can be read via QSqlQuery::boundValue().
102 */
103 void writeOutValues();
104 /*! \internal
105 Cache-mode (FB_SCROLLABLE_CACHE) fetch: serve absolute row `target` from
106 the client-side row cache, populating it from the server as needed.
107 */
108 bool fetchCached(int target);
109};
110
112{
113 Q_DECLARE_PUBLIC(QFirebirdResult)
115public:
117
119 {
120 cleanup();
121 /* Deregister from the driver, unless the driver has already been
122 destroyed (which sets driverAlive = false and clears its list). */
123 if (driverAlive)
124 drv_d_func()->activeResults.removeOne(this);
125 }
126
127 void cleanup();
128
129 /*! \internal
130 Close the open cursor (if any), releasing the handle if the close throws.
131 Assumes the attachment is alive — only exec()/execBatch() call it, and only
132 while executing. cleanup() handles the detached case inline.
133 */
134 void closeCursor();
135
136 /*! \internal
137 Set to false by ~QFirebirdDriver if the driver object is destroyed while
138 this result is still alive. Once false, cleanup() must not dereference the
139 (now-freed) driver private.
140 */
141 bool driverAlive = true;
142
143 /*! \internal
144 Returns the driver's master/status/attachment/transaction without copying
145 them. These are const accessors that hand back mutable Firebird interface
146 pointers by design: the Firebird OO API has no const-qualified interfaces,
147 and result fetching (e.g. data() const reading a BLOB) is inherently a
148 mutating server operation, so logical const cannot propagate here.
149 */
150 Firebird::IMaster *master() const { return drv_d_func()->master; }
151 Firebird::IStatus *status() const { return drv_d_func()->iStatus; }
152 Firebird::IAttachment *att() const { return drv_d_func()->iAtt; }
154 { return drv_d_func()->iTrans ? drv_d_func()->iTrans : autoTrans; }
155
156 // Ensure a transaction is active; auto-starts one if needed.
158
159 // Build input message from bound values and fill inBuffer.
160 bool buildInputMessage(QString &errorText);
161
162 /*! \internal
163 Core helper: fill inBuffer from a flat list of values (one per parameter,
164 metadata taken from the inCols cache). blobWriter, when set, is called
165 for SQL_BLOB parameters instead of using att->createBlob().
166 Signature: bool(ISC_QUAD &blobId, const QByteArray &data).
167 Returns false on error.
168 */
169 using BlobWriter = std::function<bool(ISC_QUAD &, const QByteArray &)>;
170 bool fillInputBuffer(const QList<QVariant> &vals,
171 QString &errorText, BlobWriter blobWriter = {});
172
173 /*! \internal
174 Per-value encoders, factored out of fillInputBuffer so it stays focused
175 on parameter iteration and NULL handling. Both may throw FbException,
176 which exec()/execBatch translate into a QSqlError.
177 encodeScaledNumeric handles every scaled NUMERIC/DECIMAL parameter and
178 plain INT128 (scale 0); it returns false (with errorText set) for values
179 that cannot be represented exactly in the column's range.
180 */
181 bool encodeScaledNumeric(char *data, int fbType, int scale, const QVariant &val,
182 QString &errorText);
183 void writeInlineBlob(ISC_QUAD &blobId, const QByteArray &blobData);
184
185 /*! \internal
186 Adopt a transaction handle that IStatement::execute() returned in place of
187 the one it was given, releasing the replaced handle exactly once.
188 */
189 void adoptReplacementTransaction(Firebird::ITransaction *executedTr,
190 Firebird::ITransaction *newTr);
191
194 Firebird::ITransaction *autoTrans = nullptr; // auto-started transaction (non-user)
195
198
199 QList<ColumnInfo> cols; // output columns, cached at prepare
200 QList<ColumnInfo> inCols; // input parameters, cached at prepare (metadata
201 // is immutable after prepare; avoids re-reading
202 // it per parameter on every exec/batch row)
203 QByteArray outBuffer; // row buffer for output
204 QByteArray inBuffer; // parameter message buffer
205
206 /*! \internal
207 Array descriptors resolved by cachedArrayDesc, keyed by relation/field.
208 Valid while the statement lives; cleared in cleanup(). mutable: data()
209 reads through a const private pointer and only populates the cache.
210 */
212
213 /*! \internal
214 getAffectedRecords() costs a statement-info round trip, so exec() only
215 marks the count as pending and numRowsAffected() resolves it on demand
216 (QIBASE defers the same way). -1 = unknown/none.
217 */
218 static constexpr int AffectedPending = -2;
219 int affectedRows = -1;
220 bool isSelect = false;
221 bool isProcExec = false; // EXECUTE PROCEDURE with output params
222 bool procRowFetched = false; // the single proc output row has been consumed
223
224 TxnOp txnOp = TxnOp::None; // see TxnOp
225
226 /*! \internal
227 ---- Opt-in client-side row cache (FB_SCROLLABLE_CACHE) ----
228 Enabled in exec() for scrollable SELECTs when the driver flag is set.
229 rowCache holds a raw snapshot of outBuffer per absolute row index; on a
230 cached fetch the snapshot is copied back into outBuffer so data() works
231 unchanged (BLOB/array columns cache their blob id, valid for the txn).
232 */
233 bool useRowCache = false;
234 bool cacheComplete = false; // true once the cursor has been read to EOF
236
237 /*! \internal
238 Pull rows from the server cursor (sequential fetchNext, which benefits
239 from fbclient read-ahead) into rowCache until it holds at least index+1
240 rows or the cursor is exhausted. Returns true if rowCache[index] exists.
241 May throw FbException (callers wrap in try/catch as elsewhere).
242 */
243 bool ensureCachedRow(int index);
244
245 /*! \internal
246 data() decodes from outBuffer on every call, so the LowPrecisionInt
247 overflow warning for an INT128 cell would repeat on each access of the
248 same value. Remember the fields already reported for the current row.
249 Every exec() and cleanup() starts afresh (resetOverflowWarnings), for
250 SELECTs and EXECUTE PROCEDURE alike. NoWarnedRow lies below every
251 position data() can be called at: the procedure output row is read at
252 QSql::BeforeFirstRow (-1) by writeOutValues() and at 0 after next().
253 mutable, like arrayDescCache: data() reads through a const private
254 pointer and this is bookkeeping about what it has reported, not state
255 that changes what it returns.
256 */
257 static constexpr int NoWarnedRow = (std::numeric_limits<int>::min)();
260 void resetOverflowWarnings() const;
261 bool firstOverflowWarning(int row, int field) const;
262
263 /*! \internal
264 QSqlRecord is derived solely from the (immutable-after-prepare) column
265 metadata, so build it once and hand back copies — see record().
266 */
268 mutable bool recordCached = false;
269
270 QString formatFbError(const QString &ctx);
271};
272
273QT_END_NAMESPACE
274
275#endif // QSQL_FIREBIRD_RESULT_P_H
FbGuard(const FbGuard &)=delete
void closeWith(CloseFn closeFn, bool rethrow=true)
FbGuard & operator=(const FbGuard &)=delete
T * operator->() const
void(*)(T *) FreeFn
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
static constexpr int NoWarnedRow
Firebird::IMessageMetadata * outMeta
Firebird::ITransaction * activeTransaction() const
Firebird::IResultSet * cursor
bool firstOverflowWarning(int row, int field) const
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...
Combined button and popup list for selecting options.
QDateTime decodeFirebirdTimestamp(Firebird::IUtil *util, const ISC_TIMESTAMP &ts)
int fbBaseType(int t)
bool encodeInt128Slot(const QVariant &val, int scale, char *slot, Firebird::IMaster *master, Firebird::IStatus *status, QString &errorText)
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)
ISC_TIMESTAMP encodeQDateTime(Firebird::IUtil *util, const QDateTime &dt)
ISC_TIME encodeQTime(Firebird::IUtil *util, const QTime &t)
static void fbDispose(T *h)
QDate decodeFirebirdDate(Firebird::IUtil *util, ISC_DATE date)
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)
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)
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)
QVariant decodeInt128Slot(const char *slot, int scale, QSql::NumericalPrecisionPolicy policy, Firebird::IMaster *master, Firebird::IStatus *status, QString *overflowText=nullptr)
QMetaType::Type qtType