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#include <ibase.h> // SQL type constants (SQL_TEXT, SQL_BLOB, …)
28#include <firebird/Interface.h>
29
30#include <cmath>
31
32QT_BEGIN_NAMESPACE
33
34Q_DECLARE_LOGGING_CATEGORY(lcFirebird)
35
36/*! \internal
37 RAII guard for Firebird OO-API handles
38
39 The Firebird OO API splits cleanup across two families: reference-counted
40 interfaces freed with release() (IStatement, IResultSet, IBlob, …) and
41 IDisposable ones freed with dispose() (IXpbBuilder, IBatchCompletionState).
42 FbGuard centralises the "free on every error path" bookkeeping so it is not
43 repeated at every call site.
44
45 FbGuard owns one handle and frees it on scope exit with the action supplied
46 at construction, unless it has been dismissed. closeWith() performs the
47 type-specific graceful shutdown (IResultSet::close, IStatement::free,
48 ITransaction::commit, …) which itself consumes the interface reference on
49 success — so on success the guard is dismissed (no extra free), and only on a
50 failed close does the destructor release the handle. This mirrors exactly the
51 "close on the happy path, release on the error path" idiom used throughout.
52*/
53
54template <typename T> static void fbRelease(T *h) { h->release(); }
55template <typename T> static void fbDispose(T *h) { h->dispose(); }
56
57template <typename T>
58class FbGuard
59{
60public:
61 using FreeFn = void (*)(T *);
62 /*! \internal
63 A discarded unnamed temporary would free the handle immediately while
64 later code keeps using it — force the guard to be a named local.
65 */
66 Q_NODISCARD_CTOR FbGuard(T *handle, FreeFn freeFn) : m_handle(handle), m_free(freeFn) {}
67 ~FbGuard() { if (m_handle) m_free(m_handle); }
68 /*! \internal
69 Intentionally scope-pinned: not copyable, and no move operations —
70 ownership transfer goes through take() instead.
71 */
72 FbGuard(const FbGuard &) = delete;
73 FbGuard &operator=(const FbGuard &) = delete;
74
75 T *get() const { return m_handle; }
76 T *operator->() const { return m_handle; }
77 explicit operator bool() const { return m_handle != nullptr; }
78
79 // Relinquish ownership; the destructor will no longer free the handle.
80 T *take() { T *h = m_handle; m_handle = nullptr; return h; }
81 void dismiss() { m_handle = nullptr; }
82
83 /*! \internal
84 Graceful close. On success the guard is dismissed (close already released
85 the interface). If closeFn throws, the destructor still frees the handle;
86 the exception is rethrown unless rethrow == false (for noexcept cleanup
87 paths that must not propagate).
88 */
89 template <typename CloseFn>
90 void closeWith(CloseFn closeFn, bool rethrow = true)
91 {
92 if (!m_handle)
93 return;
94 try {
95 closeFn(m_handle);
96 } catch (...) {
97 m_free(m_handle);
98 m_handle = nullptr;
99 if (rethrow)
100 throw;
101 return;
102 }
103 m_handle = nullptr;
104 }
105
106private:
107 T *m_handle;
108 FreeFn m_free;
109};
110
111// Strip nullable bit from Firebird SQL type
112inline int fbBaseType(int t) { return t & ~1; }
113
114// Status/error helpers
115
116QString fbErrorString(Firebird::IMaster *master, Firebird::IStatus *status);
117QSqlError fbError(Firebird::IMaster *master, Firebird::IStatus *status,
118 QSqlError::ErrorType type);
119QString fbErrorLog(const QSqlError &err);
120
121// Transaction helpers
122
123// How to finish a transaction
124enum class TxnEnd : quint8 {
127};
128
129// Lock-conflict behaviour of a transaction: isc_tpb_wait vs isc_tpb_nowait
130enum class TxnWait : quint8 {
133};
134
135void finishAndClear(Firebird::ThrowStatusWrapper &st,
136 Firebird::ITransaction *&tr, TxnEnd end);
137Firebird::ITransaction *startDefaultTransaction(Firebird::IAttachment *att,
138 Firebird::ThrowStatusWrapper &st,
139 TxnWait wait);
140
141// Map BLR element type (from ISC_ARRAY_DESC) to QMetaType
142QMetaType::Type blrTypeToQt(unsigned char blrType, bool hasScale);
143
144// Encode a UTF-8 string into a CHAR/VARCHAR message slot
145void encodeTextValue(char *data, int fbType, qsizetype length, const QByteArray &bytes);
146
147// Numeric codecs
148
149// 10^n as a 64-bit integer (n >= 0) — see qsql_firebird_helpers.cpp.
150qint64 pow10i(int n);
151
152/*! \internal
153 Format a scaled integer as a high-precision decimal string.
154 e.g. val=12345, scale=-2 → "123.45"; val=12, scale=2 → "1200"
155*/
156template<typename T>
157static QString numberToHighPrecision(T val, int scale)
158{
159 if (scale == 0)
160 return QString::number(static_cast<qint64>(val));
161 if (scale > 0) {
162 // actual = stored * 10^scale → an integer with `scale` trailing zeros
163 return QString::number(static_cast<qint64>(val)) + QString(scale, u'0');
164 }
165 const bool negative = val < 0;
166 QString number = QString::number(negative ? -static_cast<qint64>(val)
167 : static_cast<qint64>(val));
168 const int absScale = -scale;
169 if (absScale >= number.size())
170 number = QString(absScale - number.size() + 1, u'0') + number;
171 const int sepPos = number.size() - absScale;
172 number = number.left(sepPos) + u'.' + number.mid(sepPos);
173 if (negative)
174 number = u'-' + number;
175 return number;
176}
177
178
179// Return a scaled integer value as the type dictated by numericalPrecisionPolicy.
180template<typename T>
181static QVariant applyScale(T val, int scale, QSql::NumericalPrecisionPolicy policy)
182{
183 if (scale == 0)
184 return QVariant(static_cast<qint64>(val));
185 switch (policy) {
186 case QSql::LowPrecisionInt32:
187 case QSql::LowPrecisionInt64: {
188 /* actual = stored * 10^scale. Scale with integer arithmetic so large
189 INT64-backed values keep full precision — a double multiply would
190 drop bits above 2^53. */
191 const qint64 iv = static_cast<qint64>(val);
192 const qint64 scaled = scale < 0 ? iv / pow10i(-scale) : iv * pow10i(scale);
193 return policy == QSql::LowPrecisionInt32 ? QVariant(static_cast<qint32>(scaled))
194 : QVariant(scaled);
195 }
196 case QSql::LowPrecisionDouble:
197 return QVariant(static_cast<double>(val) * std::pow(10.0, scale));
198 case QSql::HighPrecision:
199 return QVariant(numberToHighPrecision(val, scale));
200 }
201 return QVariant(static_cast<double>(val) * std::pow(10.0, scale));
202}
203
204QMetaType::Type fbTypeToQt(int fbType, int scale);
205QByteArray numericInputString(const QVariant &val);
206bool decimalToScaledInt64(const QByteArray &decimal, int scale, qint64 *out);
207
208/*! \internal
209 ---- INT128 / DECFLOAT string conversion helpers ----
210
211 Firebird's IInt128/IDecFloat interfaces convert to and from a canonical
212 decimal string via a fixed-size caller buffer. These wrappers own the buffer
213 (64 chars is ample for INT128's 39 digits and DECFLOAT's 34) so the value
214 conversions read as one line at each call site.
215*/
216
217
218QString int128ToString(Firebird::IUtil *util, Firebird::ThrowStatusWrapper &st,
219 const FB_I128 *value, int scale);
220
221/*! \internal
222 IDecFloat16 and IDecFloat34 share identical toString()/fromString() shapes;
223 templated on the interface so one helper serves both DECFLOAT widths.
224*/
225template <typename Iface, typename Raw>
226static QString decFloatToString(Iface *iface, Firebird::ThrowStatusWrapper &st, const Raw *value)
227{
228 char buf[64];
229 iface->toString(&st, value, sizeof(buf), buf);
230 return QString::fromLatin1(buf);
231}
232
233template <typename Iface, typename Raw>
234static void decFloatFromString(Iface *iface, Firebird::ThrowStatusWrapper &st,
235 const QByteArray &s, Raw *out)
236{
237 iface->fromString(&st, s.constData(), out);
238}
239
240// Date/time codecs
241
242ISC_DATE encodeQDate(Firebird::IUtil *util, const QDate &d);
243ISC_TIME encodeQTime(Firebird::IUtil *util, const QTime &t);
244ISC_TIMESTAMP encodeQDateTime(Firebird::IUtil *util, const QDateTime &dt);
245ISC_TIMESTAMP_TZ encodeQDateTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
246 const QDateTime &dt);
247ISC_TIME_TZ encodeQTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
248 const QDateTime &dt);
249QDate decodeFirebirdDate(Firebird::IUtil *util, ISC_DATE date);
250QTime decodeFirebirdTime(Firebird::IUtil *util, ISC_TIME time);
251QDateTime decodeFirebirdTimestamp(Firebird::IUtil *util, const ISC_TIMESTAMP &ts);
252QDateTime decodeFirebirdTimestampTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
253 const ISC_TIMESTAMP_TZ &tstz);
254QDateTime decodeFirebirdTimeTz(Firebird::IStatus *iStatus, Firebird::IUtil *util,
255 const ISC_TIME_TZ &ttz);
256
257QT_END_NAMESPACE
258
259#endif // QSQL_FIREBIRD_HELPERS_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)
QFirebirdResult(const QFirebirdDriver *driver)
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)