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_helpers_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_HELPERS_P_H
6#define QSQL_FIREBIRD_HELPERS_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
19#include <QtCore/qbytearray.h>
20#include <QtCore/qdatetime.h>
21#include <QtCore/qloggingcategory.h>
22#include <QtCore/qmetatype.h>
23#include <QtCore/qvariant.h>
24#include <QtSql/qtsqlglobal.h>
25#include <QtSql/qsqlerror.h>
26
27// Scaled-numeric / INT128 codec shared with QIBASE; see the INT128 slot adapters below.
28#include <QtSql/private/qsqlnumeric_p.h>
29
30#include <ibase.h> // SQL type constants (SQL_TEXT, SQL_BLOB, …)
31#include <firebird/Interface.h>
32
34
35Q_DECLARE_LOGGING_CATEGORY(lcFirebird)
36
37/*! \internal
38 RAII guard for Firebird OO-API handles
39
40 The Firebird OO API splits cleanup across two families: reference-counted
41 interfaces freed with release() (IStatement, IResultSet, IBlob, …) and
42 IDisposable ones freed with dispose() (IXpbBuilder, IBatchCompletionState).
43 FbGuard centralises the "free on every error path" bookkeeping so it is not
44 repeated at every call site.
45
46 FbGuard owns one handle and frees it on scope exit with the action supplied
47 at construction, unless it has been dismissed. closeWith() performs the
48 type-specific graceful shutdown (IResultSet::close, IStatement::free,
49 ITransaction::commit, …) which itself consumes the interface reference on
50 success — so on success the guard is dismissed (no extra free), and only on a
51 failed close does the destructor release the handle. This mirrors exactly the
52 "close on the happy path, release on the error path" idiom used throughout.
53*/
54
55template <typename T> static void fbRelease(T *h) { h->release(); }
56template <typename T> static void fbDispose(T *h) { h->dispose(); }
57
58template <typename T>
60{
61public:
62 using FreeFn = void (*)(T *);
63 /*! \internal
64 A discarded unnamed temporary would free the handle immediately while
65 later code keeps using it — force the guard to be a named local.
66 */
68 ~FbGuard() { if (m_handle) m_free(m_handle); }
69 /*! \internal
70 Intentionally scope-pinned: not copyable, and no move operations —
71 ownership transfer goes through take() instead.
72 */
73 FbGuard(const FbGuard &) = delete;
74 FbGuard &operator=(const FbGuard &) = delete;
75
76 T *get() const { return m_handle; }
77 T *operator->() const { return m_handle; }
78 explicit operator bool() const { return m_handle != nullptr; }
79
80 // Relinquish ownership; the destructor will no longer free the handle.
81 T *take() { T *h = m_handle; m_handle = nullptr; return h; }
82 void dismiss() { m_handle = nullptr; }
83
84 /*! \internal
85 Graceful close. On success the guard is dismissed (close already released
86 the interface). If closeFn throws, the destructor still frees the handle;
87 the exception is rethrown unless rethrow == false (for noexcept cleanup
88 paths that must not propagate).
89 */
90 template <typename CloseFn>
91 void closeWith(CloseFn closeFn, bool rethrow = true)
92 {
93 if (!m_handle)
94 return;
95 try {
96 closeFn(m_handle);
97 } catch (...) {
98 m_free(m_handle);
99 m_handle = nullptr;
100 if (rethrow)
101 throw;
102 return;
103 }
104 m_handle = nullptr;
105 }
106
107private:
108 T *m_handle;
109 FreeFn m_free;
110};
111
112// Strip nullable bit from Firebird SQL type
113inline int fbBaseType(int t) { return t & ~1; }
114
115// Status/error helpers
116
117QString fbErrorString(Firebird::IMaster *master, Firebird::IStatus *status);
118QSqlError fbError(Firebird::IMaster *master, Firebird::IStatus *status,
119 QSqlError::ErrorType type);
120QString fbErrorLog(const QSqlError &err);
121
122// Transaction helpers
123
124// How to finish a transaction
125enum class TxnEnd : quint8 {
128};
129
130// Lock-conflict behaviour of a transaction: isc_tpb_wait vs isc_tpb_nowait
131enum class TxnWait : quint8 {
134};
135
136void finishAndClear(Firebird::ThrowStatusWrapper &st,
137 Firebird::ITransaction *&tr, TxnEnd end);
138Firebird::ITransaction *startDefaultTransaction(Firebird::IAttachment *att,
139 Firebird::ThrowStatusWrapper &st,
140 TxnWait wait);
141
142// Map BLR element type (from ISC_ARRAY_DESC) to QMetaType
143QMetaType::Type blrTypeToQt(unsigned char blrType, bool hasScale);
144
145// Encode a UTF-8 string into a CHAR/VARCHAR message slot
146void encodeTextValue(char *data, int fbType, qsizetype length, const QByteArray &bytes);
147
148// Numeric codecs
149//
150// The policy-aware scaling, exact decimal parsing/formatting and the 128-bit
151// word helpers are the generic QtPrivate::sql* functions in QtSql's private
152// qsqlnumeric_p.h; only the Firebird-specific adapters live here.
153
154QMetaType::Type fbTypeToQt(int fbType, int scale);
155
156/*! \internal
157 INT128 slot adapters
158
159 The two adapters own the single native/fallback switch for a 16-byte INT128
160 message slot (scale 0 for a plain INT128 parameter or array element), so the
161 scalar paths and the array codec cannot diverge. With a 128-bit integer type
162 (QSQL_HAS_INT128 from qsqlnumeric_p.h: qint128 on GCC/Clang, std::_Signed128
163 on MSVC via Qt's private qinternalint128 alias) the slot is packed and
164 unpacked word-wise and converted with the QtPrivate::sql* helpers; without
165 one, Firebird's IInt128 interface converts to and from the same canonical
166 decimal string. Behaviour is identical either way.
167
168 Encoding reports a failure through errorText/false in both builds — never as
169 FbException; callers wrap errorText with their own context. Decoding applies
170 the numerical precision policy; when an integer part does not fit a
171 LowPrecisionInt target it returns a double instead and, if overflowText is
172 given, stores the value's decimal text there so the caller can warn at the
173 rate it sees fit (QFirebirdResult::data() runs on every access of a cell).
174*/
175bool encodeInt128Slot(const QVariant &val, int scale, char *slot,
176 Firebird::IMaster *master, Firebird::IStatus *status,
177 QString &errorText);
178QVariant decodeInt128Slot(const char *slot, int scale, QSql::NumericalPrecisionPolicy policy,
179 Firebird::IMaster *master, Firebird::IStatus *status,
180 QString *overflowText = nullptr);
181
182/*! \internal
183 IDecFloat16 and IDecFloat34 share identical toString()/fromString() shapes;
184 templated on the interface so one helper serves both DECFLOAT widths. The
185 64-char caller buffer is ample for DECFLOAT's 34 digits.
186*/
187template <typename Iface, typename Raw>
188static QString decFloatToString(Iface *iface, Firebird::ThrowStatusWrapper &st, const Raw *value)
189{
190 char buf[64];
191 iface->toString(&st, value, sizeof(buf), buf);
192 return QString::fromLatin1(buf);
193}
194
195template <typename Iface, typename Raw>
196static void decFloatFromString(Iface *iface, Firebird::ThrowStatusWrapper &st,
197 const QByteArray &s, Raw *out)
198{
199 iface->fromString(&st, s.constData(), out);
200}
201
202// Date/time codecs
203
204ISC_DATE encodeQDate(Firebird::IUtil *util, const QDate &d);
205ISC_TIME encodeQTime(Firebird::IUtil *util, const QTime &t);
206ISC_TIMESTAMP encodeQDateTime(Firebird::IUtil *util, const QDateTime &dt);
207ISC_TIMESTAMP_TZ encodeQDateTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
208 const QDateTime &dt);
209ISC_TIME_TZ encodeQTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
210 const QDateTime &dt);
211QDate decodeFirebirdDate(Firebird::IUtil *util, ISC_DATE date);
212QTime decodeFirebirdTime(Firebird::IUtil *util, ISC_TIME time);
213QDateTime decodeFirebirdTimestamp(Firebird::IUtil *util, const ISC_TIMESTAMP &ts);
214QDateTime decodeFirebirdTimestampTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
215 const ISC_TIMESTAMP_TZ &tstz);
216QDateTime decodeFirebirdTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
217 const ISC_TIME_TZ &ttz);
218
219QT_END_NAMESPACE
220
221#endif // QSQL_FIREBIRD_HELPERS_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)
QFirebirdResult(const QFirebirdDriver *driver)
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)