1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsqldriver.h"
5
6#include "qdatetime.h"
7#include "qsqlerror.h"
8#include "qsqlfield.h"
9#include "qsqlindex.h"
10#include "private/qsqldriver_p.h"
11#include "private/qtools_p.h"
12
13#include <limits.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace Qt::StringLiterals;
18
19static QString prepareIdentifier(const QString &identifier,
20 QSqlDriver::IdentifierType type, const QSqlDriver *driver)
21{
22 Q_ASSERT(driver != nullptr);
23 QString ret = identifier;
24 if (!driver->isIdentifierEscaped(identifier, type))
25 ret = driver->escapeIdentifier(identifier, type);
26 return ret;
27}
28
29/*!
30 \class QSqlDriver
31 \brief The QSqlDriver class is an abstract base class for accessing
32 specific SQL databases.
33
34 \ingroup database
35 \inmodule QtSql
36
37 This class should not be used directly. Use QSqlDatabase instead.
38
39 If you want to create your own SQL drivers, you can subclass this
40 class and reimplement its pure virtual functions and those
41 virtual functions that you need. See \l{How to Write Your Own
42 Database Driver} for more information.
43
44 \sa QSqlDatabase, QSqlResult
45*/
46
47/*!
48 Constructs a new driver with the given \a parent.
49*/
50
51QSqlDriver::QSqlDriver(QObject *parent)
52 : QObject(*new QSqlDriverPrivate, parent)
53{
54}
55
56/*! \internal
57*/
58QSqlDriver::QSqlDriver(QSqlDriverPrivate &dd, QObject *parent)
59 : QObject(dd, parent)
60{
61}
62
63/*!
64 Destroys the object and frees any allocated resources.
65*/
66
67QSqlDriver::~QSqlDriver()
68{
69}
70
71/*!
72 \since 5.0
73
74 \fn QSqlDriver::notification(const QString &name, QSqlDriver::NotificationSource source, const QVariant & payload)
75
76 This signal is emitted when the database posts an event notification
77 that the driver subscribes to. \a name identifies the event notification, \a source indicates the signal source,
78 \a payload holds the extra data optionally delivered with the notification.
79
80 \sa subscribeToNotification()
81*/
82
83/*!
84 \fn bool QSqlDriver::open(const QString &db, const QString &user, const QString& password,
85 const QString &host, int port, const QString &options)
86
87 Derived classes must reimplement this pure virtual function to
88 open a database connection on database \a db, using user name \a
89 user, password \a password, host \a host, port \a port and
90 connection options \a options.
91
92 The function must return true on success and false on failure.
93
94 \sa setOpen()
95*/
96
97/*!
98 \fn bool QSqlDriver::close()
99
100 Derived classes must reimplement this pure virtual function in
101 order to close the database connection. Return true on success,
102 false on failure.
103
104 \sa open(), setOpen()
105*/
106
107/*!
108 \fn QSqlResult *QSqlDriver::createResult() const
109
110 Creates an empty SQL result on the database. Derived classes must
111 reimplement this function and return a QSqlResult object
112 appropriate for their database to the caller.
113*/
114
115/*!
116 Returns \c true if the database connection is open; otherwise returns
117 false.
118*/
119
120bool QSqlDriver::isOpen() const
121{
122 Q_D(const QSqlDriver);
123 return d->isOpen;
124}
125
126/*!
127 Returns \c true if the there was an error opening the database
128 connection; otherwise returns \c false.
129*/
130
131bool QSqlDriver::isOpenError() const
132{
133 Q_D(const QSqlDriver);
134 return d->isOpenError;
135}
136
137/*!
138 \enum QSqlDriver::DriverFeature
139
140 This enum contains a list of features a driver might support. Use
141 hasFeature() to query whether a feature is supported or not. Some features
142 depend on the database server so they can only properly determined after
143 the database connection is successfully opened with QSqlDatabase::open().
144
145 \value Transactions Whether the driver supports SQL transactions.
146 \value QuerySize Whether the database is capable of reporting the size
147 of a query. Note that some databases do not support returning the size
148 (i.e. number of rows returned) of a query, in which case
149 QSqlQuery::size() will return -1.
150 \value BLOB Whether the driver supports Binary Large Object fields.
151 \value Unicode Whether the driver supports Unicode strings if the
152 database server does.
153 \value PreparedQueries Whether the driver supports prepared query execution.
154 \value NamedPlaceholders Whether the driver supports the use of named placeholders.
155 \value PositionalPlaceholders Whether the driver supports the use of positional placeholders.
156 \value LastInsertId Whether the driver supports returning the Id of the last touched row.
157 \value BatchOperations Whether the driver supports batched operations, see QSqlQuery::execBatch()
158 \value SimpleLocking Whether the driver disallows a write lock on a table while other queries have a read lock on it.
159 \value LowPrecisionNumbers Whether the driver allows fetching numerical values with low precision.
160 \value EventNotifications Whether the driver supports database event notifications.
161 \value FinishQuery Whether the driver can do any low-level resource cleanup when QSqlQuery::finish() is called.
162 \value MultipleResultSets Whether the driver can access multiple result sets returned from batched statements or stored procedures.
163 \value CancelQuery Whether the driver allows cancelling a running query.
164
165 More information about supported features can be found in the
166 \l{sql-driver.html}{Qt SQL driver} documentation.
167
168 \sa hasFeature()
169*/
170
171/*!
172 \enum QSqlDriver::StatementType
173
174 This enum contains a list of SQL statement (or clause) types the
175 driver can create.
176
177 \value WhereStatement An SQL \c WHERE statement (e.g., \c{WHERE f = 5}).
178 \value SelectStatement An SQL \c SELECT statement (e.g., \c{SELECT f FROM t}).
179 \value UpdateStatement An SQL \c UPDATE statement (e.g., \c{UPDATE TABLE t set f = 1}).
180 \value InsertStatement An SQL \c INSERT statement (e.g., \c{INSERT INTO t (f) values (1)}).
181 \value DeleteStatement An SQL \c DELETE statement (e.g., \c{DELETE FROM t}).
182
183 \sa sqlStatement()
184*/
185
186/*!
187 \enum QSqlDriver::IdentifierType
188
189 This enum contains a list of SQL identifier types.
190
191 \value FieldName A SQL field name
192 \value TableName A SQL table name
193*/
194
195/*!
196 \enum QSqlDriver::NotificationSource
197
198 This enum contains a list of SQL notification sources.
199
200 \value UnknownSource The notification source is unknown
201 \value SelfSource The notification source is this connection
202 \value OtherSource The notification source is another connection
203*/
204
205/*!
206 \enum QSqlDriver::DbmsType
207 \internal
208
209 This enum contains DBMS types.
210
211 \value UnknownDbms
212 \value MSSqlServer
213 \value MySqlServer
214 \value PostgreSQL
215 \value Oracle
216 \value Sybase
217 \value SQLite
218 \value Interbase
219 \value DB2
220 \value [since 6.6] MimerSQL
221*/
222
223/*!
224 \fn bool QSqlDriver::hasFeature(DriverFeature feature) const
225
226 Returns \c true if the driver supports feature \a feature; otherwise
227 returns \c false.
228
229 Note that some databases need to be open() before this can be
230 determined.
231
232 \sa DriverFeature
233*/
234
235/*!
236 This function sets the open state of the database to \a open.
237 Derived classes can use this function to report the status of
238 open().
239
240 \sa open(), setOpenError()
241*/
242
243void QSqlDriver::setOpen(bool open)
244{
245 Q_D(QSqlDriver);
246 d->isOpen = open;
247}
248
249/*!
250 This function sets the open error state of the database to \a
251 error. Derived classes can use this function to report the status
252 of open(). Note that if \a error is true the open state of the
253 database is set to closed (i.e., isOpen() returns \c false).
254
255 \sa open(), setOpen()
256*/
257
258void QSqlDriver::setOpenError(bool error)
259{
260 Q_D(QSqlDriver);
261 d->isOpenError = error;
262 if (error)
263 d->isOpen = false;
264}
265
266/*!
267 This function is called to begin a transaction. If successful,
268 return true, otherwise return false. The default implementation
269 does nothing and returns \c false.
270
271 \sa commitTransaction(), rollbackTransaction()
272*/
273
274bool QSqlDriver::beginTransaction()
275{
276 return false;
277}
278
279/*!
280 This function is called to commit a transaction. If successful,
281 return true, otherwise return false. The default implementation
282 does nothing and returns \c false.
283
284 \sa beginTransaction(), rollbackTransaction()
285*/
286
287bool QSqlDriver::commitTransaction()
288{
289 return false;
290}
291
292/*!
293 This function is called to rollback a transaction. If successful,
294 return true, otherwise return false. The default implementation
295 does nothing and returns \c false.
296
297 \sa beginTransaction(), commitTransaction()
298*/
299
300bool QSqlDriver::rollbackTransaction()
301{
302 return false;
303}
304
305/*!
306 This function is used to set the value of the last error, \a error,
307 that occurred on the database.
308
309 \sa lastError()
310*/
311
312void QSqlDriver::setLastError(const QSqlError &error)
313{
314 Q_D(QSqlDriver);
315 d->error = error;
316}
317
318/*!
319 Returns a QSqlError object which contains information about the
320 last error that occurred on the database.
321*/
322
323QSqlError QSqlDriver::lastError() const
324{
325 Q_D(const QSqlDriver);
326 return d->error;
327}
328
329/*!
330 Returns a list of the names of the tables in the database. The
331 default implementation returns an empty list.
332
333 The \a tableType argument describes what types of tables
334 should be returned. Due to binary compatibility, the string
335 contains the value of the enum QSql::TableTypes as text.
336 An empty string should be treated as QSql::Tables for
337 backward compatibility.
338*/
339
340QStringList QSqlDriver::tables(QSql::TableType) const
341{
342 return QStringList();
343}
344
345/*!
346 Returns the primary index for table \a tableName. Returns an empty
347 QSqlIndex if the table doesn't have a primary index. The default
348 implementation returns an empty index.
349*/
350
351QSqlIndex QSqlDriver::primaryIndex(const QString&) const
352{
353 return QSqlIndex();
354}
355
356
357/*!
358 Returns a QSqlRecord populated with the names of the fields in
359 table \a tableName. If no such table exists, an empty record is
360 returned. The default implementation returns an empty record.
361*/
362
363QSqlRecord QSqlDriver::record(const QString & /* tableName */) const
364{
365 return QSqlRecord();
366}
367
368/*!
369 Returns the \a identifier escaped according to the database rules.
370 \a identifier can either be a table name or field name, dependent
371 on \a type.
372
373 The default implementation does nothing.
374 \sa isIdentifierEscaped()
375 */
376QString QSqlDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
377{
378 return identifier;
379}
380
381/*!
382 Returns whether \a identifier is escaped according to the database rules.
383 \a identifier can either be a table name or field name, dependent
384 on \a type.
385
386 Reimplement this function if you want to provide your own implementation in your
387 QSqlDriver subclass,
388
389 \sa stripDelimiters(), escapeIdentifier()
390 */
391bool QSqlDriver::isIdentifierEscaped(const QString &identifier, IdentifierType type) const
392{
393 Q_UNUSED(type);
394 return identifier.size() > 2
395 && identifier.startsWith(c: u'"') //left delimited
396 && identifier.endsWith(c: u'"'); //right delimited
397}
398
399/*!
400 Returns the \a identifier with the leading and trailing delimiters removed,
401 \a identifier can either be a table name or field name,
402 dependent on \a type. If \a identifier does not have leading
403 and trailing delimiter characters, \a identifier is returned without
404 modification.
405
406 Reimplement this function if you want to provide your own implementation in your
407 QSqlDriver subclass,
408
409 \sa isIdentifierEscaped()
410 */
411QString QSqlDriver::stripDelimiters(const QString &identifier, IdentifierType type) const
412{
413 QString ret;
414 if (isIdentifierEscaped(identifier, type)) {
415 ret = identifier.mid(position: 1);
416 ret.chop(n: 1);
417 } else {
418 ret = identifier;
419 }
420 return ret;
421}
422
423/*!
424 Returns a SQL statement of type \a type for the table \a tableName
425 with the values from \a rec. If \a preparedStatement is true, the
426 string will contain placeholders instead of values.
427
428 The generated flag in each field of \a rec determines whether the
429 field is included in the generated statement.
430
431 This method can be used to manipulate tables without having to worry
432 about database-dependent SQL dialects. For non-prepared statements,
433 the values will be properly escaped.
434
435 In the WHERE statement, each non-null field of \a rec specifies a
436 filter condition of equality to the field value, or if prepared, a
437 placeholder. However, prepared or not, a null field specifies the
438 condition IS NULL and never introduces a placeholder. The
439 application must not attempt to bind data for the null field during
440 execution. The field must be set to some non-null value if a
441 placeholder is desired. Furthermore, since non-null fields specify
442 equality conditions and SQL NULL is not equal to anything, even
443 itself, it is generally not useful to bind a null to a placeholder.
444
445*/
446QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
447 const QSqlRecord &rec, bool preparedStatement) const
448{
449 const auto tableNameString = tableName.isEmpty() ? QString()
450 : prepareIdentifier(identifier: tableName, type: QSqlDriver::TableName, driver: this);
451 QString s;
452 s.reserve(asize: 128);
453 switch (type) {
454 case SelectStatement:
455 for (qsizetype i = 0; i < rec.count(); ++i) {
456 if (rec.isGenerated(i))
457 s.append(s: prepareIdentifier(identifier: rec.fieldName(i), type: QSqlDriver::FieldName, driver: this)).append(s: ", "_L1);
458 }
459 if (s.isEmpty())
460 return s;
461 s.chop(n: 2);
462 s = "SELECT "_L1 + s + " FROM "_L1 + tableNameString;
463 break;
464 case WhereStatement:
465 {
466 const QString tableNamePrefix = tableNameString.isEmpty()
467 ? QString() : tableNameString + u'.';
468 for (qsizetype i = 0; i < rec.count(); ++i) {
469 if (!rec.isGenerated(i))
470 continue;
471 s.append(s: s.isEmpty() ? "WHERE "_L1 : " AND "_L1);
472 s.append(s: tableNamePrefix);
473 s.append(s: prepareIdentifier(identifier: rec.fieldName(i), type: QSqlDriver::FieldName, driver: this));
474 if (rec.isNull(i))
475 s.append(s: " IS NULL"_L1);
476 else if (preparedStatement)
477 s.append(s: " = ?"_L1);
478 else
479 s.append(s: " = "_L1).append(s: formatValue(field: rec.field(i)));
480 }
481 break;
482 }
483 case UpdateStatement:
484 s = s + "UPDATE "_L1 + tableNameString + " SET "_L1;
485 for (qsizetype i = 0; i < rec.count(); ++i) {
486 if (!rec.isGenerated(i))
487 continue;
488 s.append(s: prepareIdentifier(identifier: rec.fieldName(i), type: QSqlDriver::FieldName, driver: this)).append(c: u'=');
489 if (preparedStatement)
490 s.append(c: u'?');
491 else
492 s.append(s: formatValue(field: rec.field(i)));
493 s.append(s: ", "_L1);
494 }
495 if (s.endsWith(s: ", "_L1))
496 s.chop(n: 2);
497 else
498 s.clear();
499 break;
500 case DeleteStatement:
501 s = s + "DELETE FROM "_L1 + tableNameString;
502 break;
503 case InsertStatement: {
504 s = s + "INSERT INTO "_L1 + tableNameString + " ("_L1;
505 QString vals;
506 for (qsizetype i = 0; i < rec.count(); ++i) {
507 if (!rec.isGenerated(i))
508 continue;
509 s.append(s: prepareIdentifier(identifier: rec.fieldName(i), type: QSqlDriver::FieldName, driver: this)).append(s: ", "_L1);
510 if (preparedStatement)
511 vals.append(c: u'?');
512 else
513 vals.append(s: formatValue(field: rec.field(i)));
514 vals.append(s: ", "_L1);
515 }
516 if (vals.isEmpty()) {
517 s.clear();
518 } else {
519 vals.chop(n: 2); // remove trailing comma
520 s[s.size() - 2] = u')';
521 s.append(s: "VALUES ("_L1).append(s: vals).append(c: u')');
522 }
523 break; }
524 }
525 return s;
526}
527
528/*!
529 Returns a string representation of the \a field value for the
530 database. This is used, for example, when constructing INSERT and
531 UPDATE statements.
532
533 The default implementation returns the value formatted as a string
534 according to the following rules:
535
536 \list
537
538 \li If \a field is character data, the value is returned enclosed
539 in single quotation marks, which is appropriate for many SQL
540 databases. Any embedded single-quote characters are escaped
541 (replaced with two single-quote characters). If \a trimStrings is
542 true (the default is false), all trailing whitespace is trimmed
543 from the field.
544
545 \li If \a field is date/time data, the value is formatted in ISO
546 format and enclosed in single quotation marks. If the date/time
547 data is invalid, "NULL" is returned.
548
549 \li If \a field is \l{QByteArray}{bytearray} data, and the
550 driver can edit binary fields, the value is formatted as a
551 hexadecimal string.
552
553 \li For any other field type, toString() is called on its value
554 and the result of this is returned.
555
556 \endlist
557
558 \sa QVariant::toString()
559
560*/
561QString QSqlDriver::formatValue(const QSqlField &field, bool trimStrings) const
562{
563 const auto nullTxt = "NULL"_L1;
564
565 QString r;
566 if (field.isNull())
567 r = nullTxt;
568 else {
569 switch (field.metaType().id()) {
570 case QMetaType::Int:
571 case QMetaType::UInt:
572 if (field.value().userType() == QMetaType::Bool)
573 r = field.value().toBool() ? "1"_L1 : "0"_L1;
574 else
575 r = field.value().toString();
576 break;
577#if QT_CONFIG(datestring)
578 case QMetaType::QDate:
579 if (field.value().toDate().isValid())
580 r = u'\'' + field.value().toDate().toString(format: Qt::ISODate) + u'\'';
581 else
582 r = nullTxt;
583 break;
584 case QMetaType::QTime:
585 if (field.value().toTime().isValid())
586 r = u'\'' + field.value().toTime().toString(f: Qt::ISODate) + u'\'';
587 else
588 r = nullTxt;
589 break;
590 case QMetaType::QDateTime:
591 if (field.value().toDateTime().isValid())
592 r = u'\'' + field.value().toDateTime().toString(format: Qt::ISODate) + u'\'';
593 else
594 r = nullTxt;
595 break;
596#endif
597 case QMetaType::QString:
598 case QMetaType::QChar:
599 {
600 QString result = field.value().toString();
601 if (trimStrings) {
602 int end = result.size();
603 while (end && result.at(i: end-1).isSpace()) /* skip white space from end */
604 end--;
605 result.truncate(pos: end);
606 }
607 /* escape the "'" character */
608 result.replace(c: u'\'', after: "''"_L1);
609 r = u'\'' + result + u'\'';
610 break;
611 }
612 case QMetaType::Bool:
613 r = QString::number(field.value().toBool());
614 break;
615 case QMetaType::QByteArray : {
616 if (hasFeature(f: BLOB)) {
617 const QByteArray ba = field.value().toByteArray();
618 r.reserve(asize: (ba.size() + 1) * 2);
619 r += u'\'';
620 for (const char c : ba) {
621 const uchar s = uchar(c);
622 r += QLatin1Char(QtMiscUtils::toHexLower(value: s >> 4));
623 r += QLatin1Char(QtMiscUtils::toHexLower(value: s & 0x0f));
624 }
625 r += u'\'';
626 break;
627 }
628 }
629 Q_FALLTHROUGH();
630 default:
631 r = field.value().toString();
632 break;
633 }
634 }
635 return r;
636}
637
638/*!
639 Returns the low-level database handle wrapped in a QVariant or an
640 invalid variant if there is no handle.
641
642 \warning Use this with uttermost care and only if you know what you're doing.
643
644 \warning The handle returned here can become a stale pointer if the connection
645 is modified (for example, if you close the connection).
646
647 \warning The handle can be NULL if the connection is not open yet.
648
649 The handle returned here is database-dependent, you should query the type
650 name of the variant before accessing it.
651
652 This example retrieves the handle for a connection to sqlite:
653
654 \snippet code/src_sql_kernel_qsqldriver.cpp 0
655
656 This snippet returns the handle for PostgreSQL or MySQL:
657
658 \snippet code/src_sql_kernel_qsqldriver.cpp 1
659
660 \sa QSqlResult::handle()
661*/
662QVariant QSqlDriver::handle() const
663{
664 return QVariant();
665}
666
667/*!
668 This function is called to subscribe to event notifications from the database.
669 \a name identifies the event notification.
670
671 If successful, return true, otherwise return false.
672
673 The database must be open when this function is called. When the database is closed
674 by calling close() all subscribed event notifications are automatically unsubscribed.
675 Note that calling open() on an already open database may implicitly cause close() to
676 be called, which will cause the driver to unsubscribe from all event notifications.
677
678 When an event notification identified by \a name is posted by the database the
679 notification() signal is emitted.
680
681 Reimplement this function if you want to provide event notification support in your
682 own QSqlDriver subclass,
683
684 \sa unsubscribeFromNotification(), subscribedToNotifications(), QSqlDriver::hasFeature()
685*/
686bool QSqlDriver::subscribeToNotification(const QString &name)
687{
688 Q_UNUSED(name);
689 return false;
690}
691
692/*!
693 This function is called to unsubscribe from event notifications from the database.
694 \a name identifies the event notification.
695
696 If successful, return true, otherwise return false.
697
698 The database must be open when this function is called. All subscribed event
699 notifications are automatically unsubscribed from when the close() function is called.
700
701 After calling \e this function the notification() signal will no longer be emitted
702 when an event notification identified by \a name is posted by the database.
703
704 Reimplement this function if you want to provide event notification support in your
705 own QSqlDriver subclass,
706
707 \sa subscribeToNotification(), subscribedToNotifications()
708*/
709bool QSqlDriver::unsubscribeFromNotification(const QString &name)
710{
711 Q_UNUSED(name);
712 return false;
713}
714
715/*!
716 Returns a list of the names of the event notifications that are currently subscribed to.
717
718 Reimplement this function if you want to provide event notification support in your
719 own QSqlDriver subclass,
720
721 \sa subscribeToNotification(), unsubscribeFromNotification()
722*/
723QStringList QSqlDriver::subscribedToNotifications() const
724{
725 return QStringList();
726}
727
728/*!
729 Sets \l numericalPrecisionPolicy to \a precisionPolicy.
730*/
731void QSqlDriver::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy)
732{
733 Q_D(QSqlDriver);
734 d->precisionPolicy = precisionPolicy;
735}
736
737/*!
738 \property QSqlDriver::numericalPrecisionPolicy
739 \since 6.8
740
741 This property holds the precision policy for the database connection.
742 \note Setting the precision policy doesn't affect any currently active queries.
743
744 \sa QSql::NumericalPrecisionPolicy, QSqlQuery::numericalPrecisionPolicy,
745 QSqlDatabase::numericalPrecisionPolicy
746*/
747/*!
748 Returns the \l numericalPrecisionPolicy.
749*/
750QSql::NumericalPrecisionPolicy QSqlDriver::numericalPrecisionPolicy() const
751{
752 Q_D(const QSqlDriver);
753 return d->precisionPolicy;
754}
755
756/*!
757 \since 5.4
758 \internal
759
760 Returns the current DBMS type for the database connection.
761*/
762QSqlDriver::DbmsType QSqlDriver::dbmsType() const
763{
764 Q_D(const QSqlDriver);
765 return d->dbmsType;
766}
767
768/*!
769 \since 5.0
770 \internal
771
772 Tries to cancel the running query, if the underlying driver has the
773 capability to cancel queries. Returns \c true on success, otherwise false.
774
775 This function can be called from a different thread.
776
777 If you use this function as a slot, you need to use a Qt::DirectConnection
778 from a different thread.
779
780 Reimplement this function to support canceling running queries in
781 your own QSqlDriver subclass. It must be implemented in a thread-safe
782 manner.
783
784 \sa QSqlDriver::hasFeature()
785*/
786bool QSqlDriver::cancelQuery()
787{
788 return false;
789}
790
791/*!
792 \since 6.0
793
794 Returns the maximum length for the identifier \a type according to the database settings. Returns
795 INT_MAX by default if the is no maximum for the database.
796*/
797
798int QSqlDriver::maximumIdentifierLength(QSqlDriver::IdentifierType type) const
799{
800 Q_UNUSED(type);
801 return INT_MAX;
802}
803
804QT_END_NAMESPACE
805
806#include "moc_qsqldriver.cpp"
807

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/sql/kernel/qsqldriver.cpp