Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsql_sqlite.cpp
Go to the documentation of this file.
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 "qsql_sqlite_p.h"
5
6#include <qcoreapplication.h>
7#include <qdatetime.h>
8#include <qdebug.h>
9#include <qlist.h>
10#include <qsqlerror.h>
11#include <qsqlfield.h>
12#include <qsqlindex.h>
13#include <qsqlquery.h>
14#include <QtSql/private/qsqlcachedresult_p.h>
15#include <QtSql/private/qsqldriver_p.h>
16#include <qstringlist.h>
17#include <qvariant.h>
18#if QT_CONFIG(regularexpression)
19#include <qcache.h>
20#include <qregularexpression.h>
21#endif
22#include <QScopedValueRollback>
23
24#if defined Q_OS_WIN
25# include <qt_windows.h>
26#else
27# include <unistd.h>
28#endif
29
30#include <sqlite3.h>
31#include <functional>
32
34Q_DECLARE_METATYPE(sqlite3*)
35
38
40
41using namespace Qt::StringLiterals;
42
44{
45 QString res = identifier;
46 // If it contains [ and ] then we assume it to be escaped properly already as this indicates
47 // the syntax is exactly how it should be
48 if (identifier.contains(u'[') && identifier.contains(u']'))
49 return res;
50 if (!identifier.isEmpty() && !identifier.startsWith(u'"') && !identifier.endsWith(u'"')) {
51 res.replace(u'"', "\"\""_L1);
53 res.replace(u'.', "\".\""_L1);
54 res = u'"' + res + u'"';
55 }
56 return res;
57}
58
59static int qGetColumnType(const QString &tpName)
60{
61 const QString typeName = tpName.toLower();
62
63 if (typeName == "integer"_L1 || typeName == "int"_L1)
64 return QMetaType::Int;
65 if (typeName == "double"_L1
66 || typeName == "float"_L1
67 || typeName == "real"_L1
68 || typeName.startsWith("numeric"_L1))
69 return QMetaType::Double;
70 if (typeName == "blob"_L1)
71 return QMetaType::QByteArray;
72 if (typeName == "boolean"_L1 || typeName == "bool"_L1)
73 return QMetaType::Bool;
74 return QMetaType::QString;
75}
76
78 int errorCode)
79{
80 return QSqlError(descr,
81 QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))),
82 type, QString::number(errorCode));
83}
84
86
88{
89 Q_DECLARE_PRIVATE(QSQLiteResult)
90 friend class QSQLiteDriver;
91
92public:
95 QVariant handle() const override;
96
97protected:
98 bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) override;
99 bool reset(const QString &query) override;
100 bool prepare(const QString &query) override;
101 bool execBatch(bool arrayBind) override;
102 bool exec() override;
103 int size() override;
104 int numRowsAffected() override;
105 QVariant lastInsertId() const override;
106 QSqlRecord record() const override;
107 void detachFromResultSet() override;
108 void virtual_hook(int id, void *data) override;
109};
110
112{
113 Q_DECLARE_PUBLIC(QSQLiteDriver)
114
115public:
117 sqlite3 *access = nullptr;
120};
121
122
124{
125 Q_DECLARE_PUBLIC(QSQLiteResult)
126
127public:
129 using QSqlCachedResultPrivate::QSqlCachedResultPrivate;
130 void cleanup();
131 bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch);
132 // initializes the recordInfo and the cache
133 void initColumns(bool emptyResultset);
134 void finalize();
135
136 sqlite3_stmt *stmt = nullptr;
139 bool skippedStatus = false; // the status of the fetchNext() that's skipped
140 bool skipRow = false; // skip the next fetchNext()?
141};
142
144{
145 Q_Q(QSQLiteResult);
146 finalize();
147 rInf.clear();
148 skippedStatus = false;
149 skipRow = false;
150 q->setAt(QSql::BeforeFirstRow);
151 q->setActive(false);
152 q->cleanup();
153}
154
156{
157 if (!stmt)
158 return;
159
160 sqlite3_finalize(stmt);
161 stmt = nullptr;
162}
163
164void QSQLiteResultPrivate::initColumns(bool emptyResultset)
165{
166 Q_Q(QSQLiteResult);
167 int nCols = sqlite3_column_count(stmt);
168 if (nCols <= 0)
169 return;
170
171 q->init(nCols);
172
173 for (int i = 0; i < nCols; ++i) {
174 QString colName = QString(reinterpret_cast<const QChar *>(
175 sqlite3_column_name16(stmt, i))
176 ).remove(u'"');
177 const QString tableName = QString(reinterpret_cast<const QChar *>(
178 sqlite3_column_table_name16(stmt, i))
179 ).remove(u'"');
180 // must use typeName for resolving the type to match QSqliteDriver::record
181 QString typeName = QString(reinterpret_cast<const QChar *>(
182 sqlite3_column_decltype16(stmt, i)));
183 // sqlite3_column_type is documented to have undefined behavior if the result set is empty
184 int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i);
185
186 int fieldType;
187
188 if (!typeName.isEmpty()) {
189 fieldType = qGetColumnType(typeName);
190 } else {
191 // Get the proper type for the field based on stp value
192 switch (stp) {
193 case SQLITE_INTEGER:
194 fieldType = QMetaType::Int;
195 break;
196 case SQLITE_FLOAT:
197 fieldType = QMetaType::Double;
198 break;
199 case SQLITE_BLOB:
200 fieldType = QMetaType::QByteArray;
201 break;
202 case SQLITE_TEXT:
203 fieldType = QMetaType::QString;
204 break;
205 case SQLITE_NULL:
206 default:
207 fieldType = QMetaType::UnknownType;
208 break;
209 }
210 }
211
212 QSqlField fld(colName, QMetaType(fieldType), tableName);
213 fld.setSqlType(stp);
214 rInf.append(fld);
215 }
216}
217
219{
220 Q_Q(QSQLiteResult);
221
222 if (skipRow) {
223 // already fetched
224 Q_ASSERT(!initialFetch);
225 skipRow = false;
226 for(int i=0;i<firstRow.size();i++)
227 values[i]=firstRow[i];
228 return skippedStatus;
229 }
230 skipRow = initialFetch;
231
232 if (initialFetch) {
233 firstRow.clear();
234 firstRow.resize(sqlite3_column_count(stmt));
235 }
236
237 if (!stmt) {
238 q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"),
239 QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
240 q->setAt(QSql::AfterLastRow);
241 return false;
242 }
243 int res = sqlite3_step(stmt);
244 switch(res) {
245 case SQLITE_ROW:
246 // check to see if should fill out columns
247 if (rInf.isEmpty())
248 // must be first call.
249 initColumns(false);
250 if (idx < 0 && !initialFetch)
251 return true;
252 for (int i = 0; i < rInf.count(); ++i) {
253 switch (sqlite3_column_type(stmt, i)) {
254 case SQLITE_BLOB:
255 values[i + idx] = QByteArray(static_cast<const char *>(
256 sqlite3_column_blob(stmt, i)),
257 sqlite3_column_bytes(stmt, i));
258 break;
259 case SQLITE_INTEGER:
260 values[i + idx] = sqlite3_column_int64(stmt, i);
261 break;
262 case SQLITE_FLOAT:
263 switch(q->numericalPrecisionPolicy()) {
265 values[i + idx] = sqlite3_column_int(stmt, i);
266 break;
268 values[i + idx] = sqlite3_column_int64(stmt, i);
269 break;
272 default:
273 values[i + idx] = sqlite3_column_double(stmt, i);
274 break;
275 };
276 break;
277 case SQLITE_NULL:
278 values[i + idx] = QVariant(QMetaType::fromType<QString>());
279 break;
280 default:
281 values[i + idx] = QString(reinterpret_cast<const QChar *>(
282 sqlite3_column_text16(stmt, i)),
283 sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
284 break;
285 }
286 }
287 return true;
288 case SQLITE_DONE:
289 if (rInf.isEmpty())
290 // must be first call.
291 initColumns(true);
292 q->setAt(QSql::AfterLastRow);
293 sqlite3_reset(stmt);
294 return false;
295 case SQLITE_CONSTRAINT:
296 case SQLITE_ERROR:
297 // SQLITE_ERROR is a generic error code and we must call sqlite3_reset()
298 // to get the specific error message.
299 res = sqlite3_reset(stmt);
300 q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
301 "Unable to fetch row"), QSqlError::ConnectionError, res));
302 q->setAt(QSql::AfterLastRow);
303 return false;
304 case SQLITE_MISUSE:
305 case SQLITE_BUSY:
306 default:
307 // something wrong, don't get col info, but still return false
308 q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
309 "Unable to fetch row"), QSqlError::ConnectionError, res));
310 sqlite3_reset(stmt);
311 q->setAt(QSql::AfterLastRow);
312 return false;
313 }
314 return false;
315}
316
319{
320 Q_D(QSQLiteResult);
321 const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.append(this);
322}
323
325{
326 Q_D(QSQLiteResult);
327 if (d->drv_d_func())
328 const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.removeOne(this);
329 d->cleanup();
330}
331
333{
335}
336
338{
339 if (!prepare(query))
340 return false;
341 return exec();
342}
343
345{
346 Q_D(QSQLiteResult);
347 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
348 return false;
349
350 d->cleanup();
351
352 setSelect(false);
353
354 const void *pzTail = nullptr;
355 const auto size = int((query.size() + 1) * sizeof(QChar));
356
357#if (SQLITE_VERSION_NUMBER >= 3003011)
358 int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), size,
359 &d->stmt, &pzTail);
360#else
361 int res = sqlite3_prepare16(d->access, query.constData(), size,
362 &d->stmt, &pzTail);
363#endif
364
365 if (res != SQLITE_OK) {
366 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
367 "Unable to execute statement"), QSqlError::StatementError, res));
368 d->finalize();
369 return false;
370 } else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) {
371 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
372 "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE));
373 d->finalize();
374 return false;
375 }
376 return true;
377}
378
379bool QSQLiteResult::execBatch(bool arrayBind)
380{
381 Q_UNUSED(arrayBind);
382 Q_D(QSqlResult);
383 QScopedValueRollback<QList<QVariant>> valuesScope(d->values);
384 QList<QVariant> values = d->values;
385 if (values.size() == 0)
386 return false;
387
388 for (int i = 0; i < values.at(0).toList().size(); ++i) {
389 d->values.clear();
390 QScopedValueRollback<QHash<QString, QList<int>>> indexesScope(d->indexes);
391 auto it = d->indexes.constBegin();
392 while (it != d->indexes.constEnd()) {
393 bindValue(it.key(), values.at(it.value().first()).toList().at(i), QSql::In);
394 ++it;
395 }
396 if (!exec())
397 return false;
398 }
399 return true;
400}
401
403{
404 Q_D(QSQLiteResult);
406
407 d->skippedStatus = false;
408 d->skipRow = false;
409 d->rInf.clear();
410 clearValues();
412
413 int res = sqlite3_reset(d->stmt);
414 if (res != SQLITE_OK) {
415 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
416 "Unable to reset statement"), QSqlError::StatementError, res));
417 d->finalize();
418 return false;
419 }
420
421 int paramCount = sqlite3_bind_parameter_count(d->stmt);
422 bool paramCountIsValid = paramCount == values.size();
423
424#if (SQLITE_VERSION_NUMBER >= 3003011)
425 // In the case of the reuse of a named placeholder
426 // We need to check explicitly that paramCount is greater than or equal to 1, as sqlite
427 // can end up in a case where for virtual tables it returns 0 even though it
428 // has parameters
429 if (paramCount >= 1 && paramCount < values.size()) {
430 const auto countIndexes = [](int counter, const QList<int> &indexList) {
431 return counter + indexList.size();
432 };
433
434 const int bindParamCount = std::accumulate(d->indexes.cbegin(),
435 d->indexes.cend(),
436 0,
437 countIndexes);
438
439 paramCountIsValid = bindParamCount == values.size();
440 // When using named placeholders, it will reuse the index for duplicated
441 // placeholders. So we need to ensure the QList has only one instance of
442 // each value as SQLite will do the rest for us.
443 QList<QVariant> prunedValues;
444 QList<int> handledIndexes;
445 for (int i = 0, currentIndex = 0; i < values.size(); ++i) {
446 if (handledIndexes.contains(i))
447 continue;
448 const char *parameterName = sqlite3_bind_parameter_name(d->stmt, currentIndex + 1);
449 if (!parameterName) {
450 paramCountIsValid = false;
451 continue;
452 }
453 const auto placeHolder = QString::fromUtf8(parameterName);
454 const auto &indexes = d->indexes.value(placeHolder);
455 handledIndexes << indexes;
456 prunedValues << values.at(indexes.first());
457 ++currentIndex;
458 }
459 values = prunedValues;
460 }
461#endif
462
463 if (paramCountIsValid) {
464 for (int i = 0; i < paramCount; ++i) {
465 res = SQLITE_OK;
466 const QVariant &value = values.at(i);
467
469 res = sqlite3_bind_null(d->stmt, i + 1);
470 } else {
471 switch (value.userType()) {
472 case QMetaType::QByteArray: {
473 const QByteArray *ba = static_cast<const QByteArray*>(value.constData());
474 res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(),
475 ba->size(), SQLITE_STATIC);
476 break; }
477 case QMetaType::Int:
478 case QMetaType::Bool:
479 res = sqlite3_bind_int(d->stmt, i + 1, value.toInt());
480 break;
481 case QMetaType::Double:
482 res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble());
483 break;
484 case QMetaType::UInt:
485 case QMetaType::LongLong:
486 res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong());
487 break;
488 case QMetaType::QDateTime: {
489 const QDateTime dateTime = value.toDateTime();
490 const QString str = dateTime.toString(Qt::ISODateWithMs);
491 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
492 int(str.size() * sizeof(ushort)),
493 SQLITE_TRANSIENT);
494 break;
495 }
496 case QMetaType::QTime: {
497 const QTime time = value.toTime();
498 const QString str = time.toString(u"hh:mm:ss.zzz");
499 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
500 int(str.size() * sizeof(ushort)),
501 SQLITE_TRANSIENT);
502 break;
503 }
504 case QMetaType::QString: {
505 // lifetime of string == lifetime of its qvariant
506 const QString *str = static_cast<const QString*>(value.constData());
507 res = sqlite3_bind_text16(d->stmt, i + 1, str->unicode(),
508 int(str->size()) * sizeof(QChar),
509 SQLITE_STATIC);
510 break; }
511 default: {
512 const QString str = value.toString();
513 // SQLITE_TRANSIENT makes sure that sqlite buffers the data
514 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
515 int(str.size()) * sizeof(QChar),
516 SQLITE_TRANSIENT);
517 break; }
518 }
519 }
520 if (res != SQLITE_OK) {
521 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
522 "Unable to bind parameters"), QSqlError::StatementError, res));
523 d->finalize();
524 return false;
525 }
526 }
527 } else {
529 "Parameter count mismatch"), QString(), QSqlError::StatementError));
530 return false;
531 }
532 d->skippedStatus = d->fetchNext(d->firstRow, 0, true);
533 if (lastError().isValid()) {
534 setSelect(false);
535 setActive(false);
536 return false;
537 }
538 setSelect(!d->rInf.isEmpty());
539 setActive(true);
540 return true;
541}
542
544{
545 Q_D(QSQLiteResult);
546 return d->fetchNext(row, idx, false);
547}
548
550{
551 return -1;
552}
553
555{
556 Q_D(const QSQLiteResult);
557 return sqlite3_changes(d->drv_d_func()->access);
558}
559
561{
562 Q_D(const QSQLiteResult);
563 if (isActive()) {
564 qint64 id = sqlite3_last_insert_rowid(d->drv_d_func()->access);
565 if (id)
566 return id;
567 }
568 return QVariant();
569}
570
572{
573 Q_D(const QSQLiteResult);
574 if (!isActive() || !isSelect())
575 return QSqlRecord();
576 return d->rInf;
577}
578
580{
581 Q_D(QSQLiteResult);
582 if (d->stmt)
583 sqlite3_reset(d->stmt);
584}
585
587{
588 Q_D(const QSQLiteResult);
589 return QVariant::fromValue(d->stmt);
590}
591
593
594#if QT_CONFIG(regularexpression)
595static void _q_regexp(sqlite3_context* context, int argc, sqlite3_value** argv)
596{
597 if (Q_UNLIKELY(argc != 2)) {
598 sqlite3_result_int(context, 0);
599 return;
600 }
601
603 reinterpret_cast<const char*>(sqlite3_value_text(argv[0])));
604 const QString subject = QString::fromUtf8(
605 reinterpret_cast<const char*>(sqlite3_value_text(argv[1])));
606
607 auto cache = static_cast<QCache<QString, QRegularExpression>*>(sqlite3_user_data(context));
608 auto regexp = cache->object(pattern);
609 const bool wasCached = regexp;
610
611 if (!wasCached)
613
614 const bool found = subject.contains(*regexp);
615
616 if (!wasCached)
617 cache->insert(pattern, regexp);
618
619 sqlite3_result_int(context, int(found));
620}
621
622static void _q_regexp_cleanup(void *cache)
623{
624 delete static_cast<QCache<QString, QRegularExpression>*>(cache);
625}
626#endif
627
628static void _q_lower(sqlite3_context* context, int argc, sqlite3_value** argv)
629{
630 if (Q_UNLIKELY(argc != 1)) {
631 sqlite3_result_text(context, nullptr, 0, nullptr);
632 return;
633 }
634 const QString lower = QString::fromUtf8(
635 reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toLower();
636 const QByteArray ba = lower.toUtf8();
637 sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
638}
639
640static void _q_upper(sqlite3_context* context, int argc, sqlite3_value** argv)
641{
642 if (Q_UNLIKELY(argc != 1)) {
643 sqlite3_result_text(context, nullptr, 0, nullptr);
644 return;
645 }
646 const QString upper = QString::fromUtf8(
647 reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toUpper();
648 const QByteArray ba = upper.toUtf8();
649 sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
650}
651
654{
655}
656
659{
660 Q_D(QSQLiteDriver);
661 d->access = connection;
662 setOpen(true);
663 setOpenError(false);
664}
665
666
668{
669 close();
670}
671
673{
674 switch (f) {
675 case BLOB:
676 case Transactions:
677 case Unicode:
678 case LastInsertId:
679 case PreparedQueries:
681 case SimpleLocking:
682 case FinishQuery:
685 return true;
686 case QuerySize:
687 case BatchOperations:
689 case CancelQuery:
690 return false;
692#if (SQLITE_VERSION_NUMBER < 3003011)
693 return false;
694#else
695 return true;
696#endif
697
698 }
699 return false;
700}
701
702/*
703 SQLite dbs have no user name, passwords, hosts or ports.
704 just file names.
705*/
706bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts)
707{
708 Q_D(QSQLiteDriver);
709 if (isOpen())
710 close();
711
712
713 int timeOut = 5000;
714 bool sharedCache = false;
715 bool openReadOnlyOption = false;
716 bool openUriOption = false;
717 bool useExtendedResultCodes = true;
718 bool useQtVfs = false;
719 bool useQtCaseFolding = false;
720#if QT_CONFIG(regularexpression)
721 static const auto regexpConnectOption = "QSQLITE_ENABLE_REGEXP"_L1;
722 bool defineRegexp = false;
723 int regexpCacheSize = 25;
724#endif
725
726 const auto opts = QStringView{conOpts}.split(u';');
727 for (auto option : opts) {
728 option = option.trimmed();
729 if (option.startsWith("QSQLITE_BUSY_TIMEOUT"_L1)) {
730 option = option.mid(20).trimmed();
731 if (option.startsWith(u'=')) {
732 bool ok;
733 const int nt = option.mid(1).trimmed().toInt(&ok);
734 if (ok)
735 timeOut = nt;
736 }
737 } else if (option == "QSQLITE_USE_QT_VFS"_L1) {
738 useQtVfs = true;
739 } else if (option == "QSQLITE_OPEN_READONLY"_L1) {
740 openReadOnlyOption = true;
741 } else if (option == "QSQLITE_OPEN_URI"_L1) {
742 openUriOption = true;
743 } else if (option == "QSQLITE_ENABLE_SHARED_CACHE"_L1) {
744 sharedCache = true;
745 } else if (option == "QSQLITE_NO_USE_EXTENDED_RESULT_CODES"_L1) {
746 useExtendedResultCodes = false;
747 } else if (option == "QSQLITE_ENABLE_NON_ASCII_CASE_FOLDING"_L1) {
748 useQtCaseFolding = true;
749 }
750#if QT_CONFIG(regularexpression)
751 else if (option.startsWith(regexpConnectOption)) {
752 option = option.mid(regexpConnectOption.size()).trimmed();
753 if (option.isEmpty()) {
754 defineRegexp = true;
755 } else if (option.startsWith(u'=')) {
756 bool ok = false;
757 const int cacheSize = option.mid(1).trimmed().toInt(&ok);
758 if (ok) {
759 defineRegexp = true;
760 if (cacheSize > 0)
761 regexpCacheSize = cacheSize;
762 }
763 }
764 }
765#endif
766 }
767
768 int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
769 openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
770 if (openUriOption)
771 openMode |= SQLITE_OPEN_URI;
772
773 openMode |= SQLITE_OPEN_NOMUTEX;
774
775 const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, useQtVfs ? "QtVFS" : nullptr);
776
777 if (res == SQLITE_OK) {
778 sqlite3_busy_timeout(d->access, timeOut);
779 sqlite3_extended_result_codes(d->access, useExtendedResultCodes);
780 setOpen(true);
781 setOpenError(false);
782#if QT_CONFIG(regularexpression)
783 if (defineRegexp) {
784 auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize);
785 sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache,
786 &_q_regexp, nullptr,
787 nullptr, &_q_regexp_cleanup);
788 }
789#endif
790 if (useQtCaseFolding) {
791 sqlite3_create_function_v2(d->access, "lower", 1, SQLITE_UTF8, nullptr,
792 &_q_lower, nullptr, nullptr, nullptr);
793 sqlite3_create_function_v2(d->access, "upper", 1, SQLITE_UTF8, nullptr,
794 &_q_upper, nullptr, nullptr, nullptr);
795 }
796 return true;
797 } else {
798 setLastError(qMakeError(d->access, tr("Error opening database"),
800 setOpenError(true);
801
802 if (d->access) {
803 sqlite3_close(d->access);
804 d->access = nullptr;
805 }
806
807 return false;
808 }
809}
810
812{
813 Q_D(QSQLiteDriver);
814 if (isOpen()) {
815 for (QSQLiteResult *result : std::as_const(d->results))
816 result->d_func()->finalize();
817
818 if (d->access && (d->notificationid.size() > 0)) {
819 d->notificationid.clear();
820 sqlite3_update_hook(d->access, nullptr, nullptr);
821 }
822
823 const int res = sqlite3_close(d->access);
824
825 if (res != SQLITE_OK)
826 setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError, res));
827 d->access = nullptr;
828 setOpen(false);
829 setOpenError(false);
830 }
831}
832
834{
835 return new QSQLiteResult(this);
836}
837
839{
840 if (!isOpen() || isOpenError())
841 return false;
842
844 if (!q.exec("BEGIN"_L1)) {
845 setLastError(QSqlError(tr("Unable to begin transaction"),
846 q.lastError().databaseText(), QSqlError::TransactionError));
847 return false;
848 }
849
850 return true;
851}
852
854{
855 if (!isOpen() || isOpenError())
856 return false;
857
859 if (!q.exec("COMMIT"_L1)) {
860 setLastError(QSqlError(tr("Unable to commit transaction"),
861 q.lastError().databaseText(), QSqlError::TransactionError));
862 return false;
863 }
864
865 return true;
866}
867
869{
870 if (!isOpen() || isOpenError())
871 return false;
872
874 if (!q.exec("ROLLBACK"_L1)) {
875 setLastError(QSqlError(tr("Unable to rollback transaction"),
876 q.lastError().databaseText(), QSqlError::TransactionError));
877 return false;
878 }
879
880 return true;
881}
882
884{
886 if (!isOpen())
887 return res;
888
890 q.setForwardOnly(true);
891
892 QString sql = "SELECT name FROM sqlite_master WHERE %1 "
893 "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"_L1;
894 if ((type & QSql::Tables) && (type & QSql::Views))
895 sql = sql.arg("type='table' OR type='view'"_L1);
896 else if (type & QSql::Tables)
897 sql = sql.arg("type='table'"_L1);
898 else if (type & QSql::Views)
899 sql = sql.arg("type='view'"_L1);
900 else
901 sql.clear();
902
903 if (!sql.isEmpty() && q.exec(sql)) {
904 while(q.next())
905 res.append(q.value(0).toString());
906 }
907
908 if (type & QSql::SystemTables) {
909 // there are no internal tables beside this one:
910 res.append("sqlite_master"_L1);
911 }
912
913 return res;
914}
915
916static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false)
917{
918 QString schema;
919 QString table(tableName);
920 const qsizetype indexOfSeparator = tableName.indexOf(u'.');
921 if (indexOfSeparator > -1) {
922 const qsizetype indexOfCloseBracket = tableName.indexOf(u']');
923 if (indexOfCloseBracket != tableName.size() - 1) {
924 // Handles a case like databaseName.tableName
925 schema = tableName.left(indexOfSeparator + 1);
926 table = tableName.mid(indexOfSeparator + 1);
927 } else {
928 const qsizetype indexOfOpenBracket = tableName.lastIndexOf(u'[', indexOfCloseBracket);
929 if (indexOfOpenBracket > 0) {
930 // Handles a case like databaseName.[tableName]
931 schema = tableName.left(indexOfOpenBracket);
932 table = tableName.mid(indexOfOpenBracket);
933 }
934 }
935 }
936 q.exec("PRAGMA "_L1 + schema + "table_info ("_L1 +
938 QSqlIndex ind;
939 while (q.next()) {
940 bool isPk = q.value(5).toInt();
941 if (onlyPIndex && !isPk)
942 continue;
943 QString typeName = q.value(2).toString().toLower();
944 QString defVal = q.value(4).toString();
945 if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
946 const int end = defVal.lastIndexOf(u'\'');
947 if (end > 0)
948 defVal = defVal.mid(1, end - 1);
949 }
950
951 QSqlField fld(q.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
952 if (isPk && (typeName == "integer"_L1))
953 // INTEGER PRIMARY KEY fields are auto-generated in sqlite
954 // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
955 fld.setAutoValue(true);
956 fld.setRequired(q.value(3).toInt() != 0);
957 fld.setDefaultValue(defVal);
958 ind.append(fld);
959 }
960 return ind;
961}
962
964{
965 if (!isOpen())
966 return QSqlIndex();
967
968 QString table = tblname;
971
973 q.setForwardOnly(true);
974 return qGetTableInfo(q, table, true);
975}
976
978{
979 if (!isOpen())
980 return QSqlRecord();
981
982 QString table = tbl;
985
987 q.setForwardOnly(true);
988 return qGetTableInfo(q, table);
989}
990
992{
993 Q_D(const QSQLiteDriver);
994 return QVariant::fromValue(d->access);
995}
996
998{
999 return _q_escapeIdentifier(identifier, type);
1000}
1001
1002static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,
1003 sqlite3_int64 arowid)
1004{
1005 Q_UNUSED(aoperation);
1006 Q_UNUSED(adbname);
1007 QSQLiteDriver *driver = static_cast<QSQLiteDriver *>(qobj);
1008 if (driver) {
1009 QMetaObject::invokeMethod(driver, "handleNotification", Qt::QueuedConnection,
1010 Q_ARG(QString, QString::fromUtf8(atablename)), Q_ARG(qint64, arowid));
1011 }
1012}
1013
1015{
1016 Q_D(QSQLiteDriver);
1017 if (!isOpen()) {
1018 qWarning("Database not open.");
1019 return false;
1020 }
1021
1022 if (d->notificationid.contains(name)) {
1023 qWarning("Already subscribing to '%ls'.", qUtf16Printable(name));
1024 return false;
1025 }
1026
1027 //sqlite supports only one notification callback, so only the first is registered
1028 d->notificationid << name;
1029 if (d->notificationid.size() == 1)
1030 sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this));
1031
1032 return true;
1033}
1034
1036{
1037 Q_D(QSQLiteDriver);
1038 if (!isOpen()) {
1039 qWarning("Database not open.");
1040 return false;
1041 }
1042
1043 if (!d->notificationid.contains(name)) {
1044 qWarning("Not subscribed to '%ls'.", qUtf16Printable(name));
1045 return false;
1046 }
1047
1048 d->notificationid.removeAll(name);
1049 if (d->notificationid.isEmpty())
1050 sqlite3_update_hook(d->access, nullptr, nullptr);
1051
1052 return true;
1053}
1054
1056{
1057 Q_D(const QSQLiteDriver);
1058 return d->notificationid;
1059}
1060
1061void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid)
1062{
1063 Q_D(const QSQLiteDriver);
1064 if (d->notificationid.contains(tableName))
1066}
1067
1069
1070#include "moc_qsql_sqlite_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
\inmodule QtCore
Definition qchar.h:48
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtCore\reentrant
Definition qdatetime.h:257
bool removeOne(const AT &t)
Definition qlist.h:581
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmetatype.h:320
\inmodule QtCore
Definition qobject.h:90
\inmodule QtCore \reentrant
QList< QSQLiteResult * > results
QStringList notificationid
QSqlIndex primaryIndex(const QString &table) const override
Returns the primary index for table tableName.
QSQLiteDriver(QObject *parent=nullptr)
QStringList subscribedToNotifications() const override
Returns a list of the names of the event notifications that are currently subscribed to.
QStringList tables(QSql::TableType) const override
Returns a list of the names of the tables in the database.
void close() override
Derived classes must reimplement this pure virtual function in order to close the database connection...
bool commitTransaction() override
This function is called to commit a transaction.
bool beginTransaction() override
This function is called to begin a transaction.
QString escapeIdentifier(const QString &identifier, IdentifierType) const override
Returns the identifier escaped according to the database rules.
QVariant handle() const override
Returns the low-level database handle wrapped in a QVariant or an invalid variant if there is no hand...
QSqlResult * createResult() const override
Creates an empty SQL result on the database.
bool unsubscribeFromNotification(const QString &name) override
This function is called to unsubscribe from event notifications from the database.
bool subscribeToNotification(const QString &name) override
This function is called to subscribe to event notifications from the database.
bool rollbackTransaction() override
This function is called to rollback a transaction.
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...
bool hasFeature(DriverFeature f) const override
Returns true if the driver supports feature feature; otherwise returns false.
QSqlRecord record(const QString &tablename) const override
Returns a QSqlRecord populated with the names of the fields in table tableName.
QList< QVariant > firstRow
bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
void initColumns(bool emptyResultset)
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 execBatch(bool arrayBind) override
bool reset(const QString &query) override
Sets the result to use the SQL statement query for subsequent data retrieval.
QSqlRecord record() const override
Returns the current record if the query is active; otherwise returns an empty QSqlRecord.
void virtual_hook(int id, void *data) override
QSQLiteResult(const QSQLiteDriver *db)
bool exec() override
Executes the query, returning true if successful; otherwise returns false.
bool prepare(const QString &query) override
Prepares the given query for execution; the query will normally use placeholders so that it can be ex...
QVariant lastInsertId() const override
Returns the object ID of the most recent inserted row if the database supports it.
QVariant handle() const override
Returns the low-level database handle for this result set wrapped in a QVariant or an invalid QVarian...
bool gotoNext(QSqlCachedResult::ValueCache &row, int idx) override
void detachFromResultSet() override
int numRowsAffected() override
Returns the number of rows affected by the last query executed, or -1 if it cannot be determined or i...
void virtual_hook(int id, void *data) override
The QSqlDriver class is an abstract base class for accessing specific SQL databases.
Definition qsqldriver.h:25
IdentifierType
This enum contains a list of SQL identifier types.
Definition qsqldriver.h:40
virtual QString stripDelimiters(const QString &identifier, IdentifierType type) const
Returns the identifier with the leading and trailing delimiters removed, identifier can either be a t...
DriverFeature
This enum contains a list of features a driver might support.
Definition qsqldriver.h:32
@ PositionalPlaceholders
Definition qsqldriver.h:33
@ LowPrecisionNumbers
Definition qsqldriver.h:34
@ EventNotifications
Definition qsqldriver.h:35
@ PreparedQueries
Definition qsqldriver.h:32
@ NamedPlaceholders
Definition qsqldriver.h:33
@ BatchOperations
Definition qsqldriver.h:34
@ MultipleResultSets
Definition qsqldriver.h:35
bool isOpenError() const
Returns true if the there was an error opening the database connection; otherwise returns false.
virtual void setLastError(const QSqlError &e)
This function is used to set the value of the last error, error, that occurred on the database.
void notification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
virtual bool isOpen() const
Returns true if the database connection is open; otherwise returns false.
virtual void setOpenError(bool e)
This function sets the open error state of the database to error.
virtual bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const
Returns whether identifier is escaped according to the database rules.
virtual void setOpen(bool o)
This function sets the open state of the database to open.
The QSqlError class provides SQL database error information.
Definition qsqlerror.h:17
ErrorType
This enum type describes the context in which the error occurred, e.g., a connection error,...
Definition qsqlerror.h:19
@ StatementError
Definition qsqlerror.h:22
@ TransactionError
Definition qsqlerror.h:23
@ ConnectionError
Definition qsqlerror.h:21
The QSqlField class manipulates the fields in SQL database tables and views.
Definition qsqlfield.h:19
void setAutoValue(bool autoVal)
Marks the field as an auto-generated value if autoVal is true.
void setDefaultValue(const QVariant &value)
Sets the default value used for this field to value.
void setRequired(bool required)
Sets the required status of this field to \l Required if required is true; otherwise sets it to \l Op...
Definition qsqlfield.h:67
void setSqlType(int type)
The QSqlIndex class provides functions to manipulate and describe database indexes.
Definition qsqlindex.h:16
void append(const QSqlField &field)
Appends the field field to the list of indexed fields.
The QSqlQuery class provides a means of executing and manipulating SQL statements.
Definition qsqlquery.h:23
The QSqlRecord class encapsulates a database record.
Definition qsqlrecord.h:20
QVariant value(int i) const
Returns the value of the field located at position index in the record.
static bool isVariantNull(const QVariant &variant)
The QSqlResult class provides an abstract interface for accessing data from specific SQL databases.
Definition qsqlresult.h:25
bool isSelect() const
Returns true if the current result is from a SELECT statement; otherwise returns false.
virtual void setSelect(bool s)
This function is provided for derived classes to indicate whether or not the current statement is a S...
virtual void setActive(bool a)
This function is provided for derived classes to set the internal active state to active.
QVariantList & boundValues(QT6_DECL_NEW_OVERLOAD)
QSqlError lastError() const
Returns the last error associated with the result.
virtual void setLastError(const QSqlError &e)
This function is provided for derived classes to set the last error to error.
const QSqlDriver * driver() const
Returns the driver associated with the result.
bool isValid() const
Returns true if the result is positioned on a valid record (that is, the result is not positioned bef...
virtual void bindValue(int pos, const QVariant &val, QSql::ParamType type)
Binds the value val of parameter type paramType to position index in the current record (row).
bool isActive() const
Returns true if the result has records to be retrieved; otherwise returns false.
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString toLower() const &
Definition qstring.h:368
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1095
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
QByteArray toUtf8() const &
Definition qstring.h:563
QString toUpper() const &
Definition qstring.h:372
const QChar * unicode() const
Returns a Unicode representation of the string.
Definition qstring.h:1085
\inmodule QtCore \reentrant
Definition qdatetime.h:189
\inmodule QtCore
Definition qvariant.h:64
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
#define this
Definition dialogs.cpp:9
QString str
[2]
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
@ AfterLastRow
Definition qtsqlglobal.h:23
@ BeforeFirstRow
Definition qtsqlglobal.h:22
@ SystemTables
Definition qtsqlglobal.h:38
@ Views
Definition qtsqlglobal.h:39
@ Tables
Definition qtsqlglobal.h:37
@ LowPrecisionInt32
Definition qtsqlglobal.h:45
@ LowPrecisionDouble
Definition qtsqlglobal.h:47
@ LowPrecisionInt64
Definition qtsqlglobal.h:46
@ HighPrecision
Definition qtsqlglobal.h:49
Combined button and popup list for selecting options.
@ ISODateWithMs
@ QueuedConnection
static void * context
#define Q_UNLIKELY(x)
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
const char * typeName
#define Q_DECLARE_OPAQUE_POINTER(POINTER)
Definition qmetatype.h:1496
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1504
#define Q_ARG(Type, data)
Definition qobjectdefs.h:62
GLenum GLsizei GLsizei GLint * values
[15]
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint id
[7]
GLfloat GLfloat f
GLenum type
GLenum access
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLuint counter
GLboolean reset
GLenum query
GLuint res
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLuint GLenum option
GLubyte * pattern
GLenum GLenum GLsizei void * table
static quint64 cacheSize()
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QDB2DriverPrivate *p)
Definition qsql_db2.cpp:203
static QList< QVariant > toList(char **buf, int count)
static void _q_lower(sqlite3_context *context, int argc, sqlite3_value **argv)
static void _q_upper(sqlite3_context *context, int argc, sqlite3_value **argv)
static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex=false)
static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, int errorCode)
static int qGetColumnType(const QString &tpName)
static void handle_sqlite_callback(void *qobj, int aoperation, char const *adbname, char const *atablename, sqlite3_int64 arowid)
static QString _q_escapeIdentifier(const QString &identifier, QSqlDriver::IdentifierType type)
#define Q_DECLARE_SQLDRIVER_PRIVATE(Class)
#define qUtf16Printable(string)
Definition qstring.h:1403
#define tr(X)
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
unsigned short ushort
Definition qtypes.h:28
#define explicit
QByteArray ba
[0]
QMimeDatabase db
[0]
QDateTime dateTime
[12]
void * sqlite3_stmt
bool contains(const AT &t) const noexcept
Definition qlist.h:44
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent