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_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// Qt-Security score:critical reason:data-parser
4
6
7#include <qcoreapplication.h>
8#include <qdatetime.h>
9#include <qdebug.h>
10#include <qlist.h>
11#include <qloggingcategory.h>
12#include <qsqlerror.h>
13#include <qsqlfield.h>
14#include <qsqlindex.h>
15#include <qsqlquery.h>
16#include <QtSql/private/qsqlcachedresult_p.h>
17#include <QtSql/private/qsqldriver_p.h>
18#include <qstringlist.h>
19#include <qvariant.h>
20#if QT_CONFIG(regularexpression)
21#include <qcache.h>
22#include <qregularexpression.h>
23#endif
24#include <QScopedValueRollback>
25
26#if defined Q_OS_WIN
27# include <qt_windows.h>
28#else
29# include <unistd.h>
30#endif
31
32#include <sqlite3.h>
33#include <functional>
34
35Q_DECLARE_OPAQUE_POINTER(sqlite3*)
36Q_DECLARE_METATYPE(sqlite3*)
37
38Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*)
39Q_DECLARE_METATYPE(sqlite3_stmt*)
40
41QT_BEGIN_NAMESPACE
42
43Q_STATIC_LOGGING_CATEGORY(lcSqlite, "qt.sql.sqlite")
44
45using namespace Qt::StringLiterals;
46
47static int qGetColumnType(const QString &tpName)
48{
49 if (tpName.compare("integer"_L1, Qt::CaseInsensitive) == 0 || tpName.compare("int"_L1, Qt::CaseInsensitive) == 0)
50 return QMetaType::Int;
51 if (tpName.compare("double"_L1, Qt::CaseInsensitive) == 0
52 || tpName.compare("float"_L1, Qt::CaseInsensitive) == 0
53 || tpName.compare("real"_L1, Qt::CaseInsensitive) == 0
54 || tpName.startsWith("numeric"_L1, Qt::CaseInsensitive))
55 return QMetaType::Double;
56 if (tpName.compare("blob"_L1, Qt::CaseInsensitive) == 0)
57 return QMetaType::QByteArray;
58 if (tpName.compare("boolean"_L1, Qt::CaseInsensitive) == 0 || tpName.compare("bool"_L1, Qt::CaseInsensitive) == 0)
59 return QMetaType::Bool;
60 return QMetaType::QString;
61}
62
63static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type,
64 int errorCode)
65{
66 return QSqlError(descr,
67 QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))),
68 type, QString::number(errorCode));
69}
70
72
74{
76 friend class QSQLiteDriver;
77
78public:
79 explicit QSQLiteResult(const QSQLiteDriver* db);
81 QVariant handle() const override;
82
83protected:
84 bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) override;
85 bool reset(const QString &query) override;
86 bool prepare(const QString &query) override;
87 bool execBatch(bool arrayBind) override;
88 bool exec() override;
91 QVariant lastInsertId() const override;
92 QSqlRecord record() const override;
94 void virtual_hook(int id, void *data) override;
95};
96
98{
99 Q_DECLARE_PUBLIC(QSQLiteDriver)
100
101public:
104 QSqlIndex getTableInfo(QSqlQuery &query, const QString &tableName,
105 bool onlyPIndex = false) const;
106
107 sqlite3 *access = nullptr;
110};
111
112bool QSQLiteDriverPrivate::isIdentifierEscaped(QStringView identifier) const
113{
114 return identifier.size() > 2
115 && ((identifier.startsWith(u'"') && identifier.endsWith(u'"'))
116 || (identifier.startsWith(u'`') && identifier.endsWith(u'`'))
117 || (identifier.startsWith(u'[') && identifier.endsWith(u']')));
118}
119
120QSqlIndex QSQLiteDriverPrivate::getTableInfo(QSqlQuery &query, const QString &tableName,
121 bool onlyPIndex) const
122{
123 Q_Q(const QSQLiteDriver);
124 QString schema;
125 QString table = q->escapeIdentifier(tableName, QSqlDriver::TableName);
126 const auto indexOfSeparator = table.indexOf(u'.');
127 if (indexOfSeparator > -1) {
128 auto leftName = QStringView{table}.first(indexOfSeparator);
129 auto rightName = QStringView{table}.sliced(indexOfSeparator + 1);
130 if (isIdentifierEscaped(leftName) && isIdentifierEscaped(rightName)) {
131 schema = leftName.toString() + u'.';
132 table = rightName.toString();
133 }
134 }
135
136 query.exec("PRAGMA "_L1 + schema + "table_xinfo ("_L1 + table + u')');
137 QSqlIndex ind;
138 while (query.next()) {
139 bool isPk = query.value(5).toInt();
140 if (onlyPIndex && !isPk)
141 continue;
142 QString typeName = query.value(2).toString().toLower();
143 QString defVal = query.value(4).toString();
144 if (!defVal.isEmpty() && defVal.at(0) == u'\'') {
145 const int end = defVal.lastIndexOf(u'\'');
146 if (end > 0)
147 defVal = defVal.mid(1, end - 1);
148 }
149
150 QSqlField fld(query.value(1).toString(), QMetaType(qGetColumnType(typeName)), tableName);
151 if (isPk && (typeName == "integer"_L1))
152 // INTEGER PRIMARY KEY fields are auto-generated in sqlite
153 // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
154 fld.setAutoValue(true);
155 fld.setRequired(query.value(3).toInt() != 0);
156 fld.setDefaultValue(defVal);
157 ind.append(fld);
158 }
159 return ind;
160}
161
163{
164 Q_DECLARE_PUBLIC(QSQLiteResult)
165
166public:
169 void cleanup();
170 bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch);
171 // initializes the recordInfo and the cache
172 void initColumns(bool emptyResultset);
173 void finalize();
174
175 sqlite3_stmt *stmt = nullptr;
176 QSqlRecord rInf;
177 QList<QVariant> firstRow;
178 bool skippedStatus = false; // the status of the fetchNext() that's skipped
179 bool skipRow = false; // skip the next fetchNext()?
180};
181
183{
184 Q_Q(QSQLiteResult);
186 rInf.clear();
187 skippedStatus = false;
188 skipRow = false;
189 q->setAt(QSql::BeforeFirstRow);
190 q->setActive(false);
191 q->cleanup();
192}
193
195{
196 if (!stmt)
197 return;
198
199 sqlite3_finalize(stmt);
200 stmt = nullptr;
201}
202
203void QSQLiteResultPrivate::initColumns(bool emptyResultset)
204{
205 Q_Q(QSQLiteResult);
206 int nCols = sqlite3_column_count(stmt);
207 if (nCols <= 0)
208 return;
209
210 q->init(nCols);
211
212 for (int i = 0; i < nCols; ++i) {
213 QString colName = QString(reinterpret_cast<const QChar *>(
214 sqlite3_column_name16(stmt, i))
215 ).remove(u'"');
216 const QString tableName = QString(reinterpret_cast<const QChar *>(
217 sqlite3_column_table_name16(stmt, i))
218 ).remove(u'"');
219 // must use typeName for resolving the type to match QSqliteDriver::record
220 QString typeName = QString(reinterpret_cast<const QChar *>(
221 sqlite3_column_decltype16(stmt, i)));
222 // sqlite3_column_type is documented to have undefined behavior if the result set is empty
223 int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i);
224
225 int fieldType;
226
227 if (!typeName.isEmpty()) {
228 fieldType = qGetColumnType(typeName);
229 } else {
230 // Get the proper type for the field based on stp value
231 switch (stp) {
232 case SQLITE_INTEGER:
233 fieldType = QMetaType::Int;
234 break;
235 case SQLITE_FLOAT:
236 fieldType = QMetaType::Double;
237 break;
238 case SQLITE_BLOB:
239 fieldType = QMetaType::QByteArray;
240 break;
241 case SQLITE_TEXT:
242 fieldType = QMetaType::QString;
243 break;
244 case SQLITE_NULL:
245 default:
246 fieldType = QMetaType::UnknownType;
247 break;
248 }
249 }
250
251 QSqlField fld(colName, QMetaType(fieldType), tableName);
252 rInf.append(fld);
253 }
254}
255
256bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
257{
258 Q_Q(QSQLiteResult);
259
260 if (skipRow) {
261 // already fetched
262 Q_ASSERT(!initialFetch);
263 skipRow = false;
264 for(int i=0;i<firstRow.size();i++)
265 values[i]=firstRow[i];
266 return skippedStatus;
267 }
268 skipRow = initialFetch;
269
270 if (initialFetch) {
271 firstRow.clear();
272 firstRow.resize(sqlite3_column_count(stmt));
273 }
274
275 if (!stmt) {
276 q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"),
277 QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
278 q->setAt(QSql::AfterLastRow);
279 return false;
280 }
281 int res = sqlite3_step(stmt);
282 switch(res) {
283 case SQLITE_ROW:
284 // check to see if should fill out columns
285 if (rInf.isEmpty())
286 // must be first call.
287 initColumns(false);
288 if (idx < 0 && !initialFetch)
289 return true;
290 for (int i = 0; i < rInf.count(); ++i) {
291 switch (sqlite3_column_type(stmt, i)) {
292 case SQLITE_BLOB:
293 values[i + idx] = QByteArray(static_cast<const char *>(
294 sqlite3_column_blob(stmt, i)),
295 sqlite3_column_bytes(stmt, i));
296 break;
297 case SQLITE_INTEGER:
298 values[i + idx] = sqlite3_column_int64(stmt, i);
299 break;
300 case SQLITE_FLOAT:
301 switch(q->numericalPrecisionPolicy()) {
302 case QSql::LowPrecisionInt32:
303 values[i + idx] = sqlite3_column_int(stmt, i);
304 break;
305 case QSql::LowPrecisionInt64:
306 values[i + idx] = sqlite3_column_int64(stmt, i);
307 break;
308 case QSql::LowPrecisionDouble:
309 case QSql::HighPrecision:
310 default:
311 values[i + idx] = sqlite3_column_double(stmt, i);
312 break;
313 };
314 break;
315 case SQLITE_NULL:
316 values[i + idx] = QVariant(QMetaType::fromType<QString>());
317 break;
318 default:
319 values[i + idx] = QString(reinterpret_cast<const QChar *>(
320 sqlite3_column_text16(stmt, i)),
321 sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
322 break;
323 }
324 }
325 return true;
326 case SQLITE_DONE:
327 if (rInf.isEmpty())
328 // must be first call.
329 initColumns(true);
330 q->setAt(QSql::AfterLastRow);
331 sqlite3_reset(stmt);
332 return false;
333 case SQLITE_CONSTRAINT:
334 case SQLITE_ERROR:
335 // SQLITE_ERROR is a generic error code and we must call sqlite3_reset()
336 // to get the specific error message.
337 res = sqlite3_reset(stmt);
338 q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
339 "Unable to fetch row"), QSqlError::ConnectionError, res));
340 q->setAt(QSql::AfterLastRow);
341 return false;
342 case SQLITE_MISUSE:
343 case SQLITE_BUSY:
344 default:
345 // something wrong, don't get col info, but still return false
346 q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
347 "Unable to fetch row"), QSqlError::ConnectionError, res));
348 sqlite3_reset(stmt);
349 q->setAt(QSql::AfterLastRow);
350 return false;
351 }
352 return false;
353}
354
355QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
357{
358 Q_D(QSQLiteResult);
359 const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.append(this);
360}
361
363{
364 Q_D(QSQLiteResult);
365 if (d->drv_d_func())
366 const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.removeOne(this);
367 d->cleanup();
368}
369
370void QSQLiteResult::virtual_hook(int id, void *data)
371{
372 QSqlCachedResult::virtual_hook(id, data);
373}
374
375bool QSQLiteResult::reset(const QString &query)
376{
377 if (!prepare(query))
378 return false;
379 return exec();
380}
381
382bool QSQLiteResult::prepare(const QString &query)
383{
384 Q_D(QSQLiteResult);
385 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
386 return false;
387
388 d->cleanup();
389
390 setSelect(false);
391
392 const void *pzTail = nullptr;
393 const auto size = int((query.size() + 1) * sizeof(QChar));
394
395#if (SQLITE_VERSION_NUMBER >= 3003011)
396 int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), size,
397 &d->stmt, &pzTail);
398#else
399 int res = sqlite3_prepare16(d->access, query.constData(), size,
400 &d->stmt, &pzTail);
401#endif
402
403 if (res != SQLITE_OK) {
404 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
405 "Unable to execute statement"), QSqlError::StatementError, res));
406 d->finalize();
407 return false;
408 } else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) {
409 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
410 "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE));
411 d->finalize();
412 return false;
413 }
414 return true;
415}
416
417bool QSQLiteResult::execBatch(bool arrayBind)
418{
419 Q_UNUSED(arrayBind);
420 Q_D(QSqlResult);
421 QScopedValueRollback<QList<QVariant>> valuesScope(d->values);
422 QList<QVariant> values = d->values;
423 if (values.size() == 0)
424 return false;
425
426 for (int i = 0; i < values.at(0).toList().size(); ++i) {
427 d->values.clear();
428 QScopedValueRollback<QHash<QString, QList<int>>> indexesScope(d->indexes);
429 auto it = d->indexes.constBegin();
430 while (it != d->indexes.constEnd()) {
431 bindValue(it.key(), values.at(it.value().first()).toList().at(i), QSql::In);
432 ++it;
433 }
434 if (!exec())
435 return false;
436 }
437 return true;
438}
439
441{
442 Q_D(QSQLiteResult);
443 QList<QVariant> values = boundValues();
444
445 d->skippedStatus = false;
446 d->skipRow = false;
447 d->rInf.clear();
448 clearValues();
449 setLastError(QSqlError());
450
451 int res = sqlite3_reset(d->stmt);
452 if (res != SQLITE_OK) {
453 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
454 "Unable to reset statement"), QSqlError::StatementError, res));
455 d->finalize();
456 return false;
457 }
458
459 int paramCount = sqlite3_bind_parameter_count(d->stmt);
460 bool paramCountIsValid = paramCount == values.size();
461
462#if (SQLITE_VERSION_NUMBER >= 3003011)
463 // In the case of the reuse of a named placeholder
464 // We need to check explicitly that paramCount is greater than or equal to 1, as sqlite
465 // can end up in a case where for virtual tables it returns 0 even though it
466 // has parameters
467 if (paramCount >= 1 && paramCount < values.size()) {
468 const auto countIndexes = [](int counter, const QList<int> &indexList) {
469 return counter + indexList.size();
470 };
471
472 const int bindParamCount = std::accumulate(d->indexes.cbegin(),
473 d->indexes.cend(),
474 0,
475 countIndexes);
476
477 paramCountIsValid = bindParamCount == values.size();
478 // When using named placeholders, it will reuse the index for duplicated
479 // placeholders. So we need to ensure the QList has only one instance of
480 // each value as SQLite will do the rest for us.
481 QList<QVariant> prunedValues;
482 QList<int> handledIndexes;
483 for (int i = 0, currentIndex = 0; i < values.size(); ++i) {
484 if (handledIndexes.contains(i))
485 continue;
486 const char *parameterName = sqlite3_bind_parameter_name(d->stmt, currentIndex + 1);
487 if (!parameterName) {
488 paramCountIsValid = false;
489 continue;
490 }
491 const auto placeHolder = QString::fromUtf8(parameterName);
492 const auto &indexes = d->indexes.value(placeHolder);
493 handledIndexes << indexes;
494 prunedValues << values.at(indexes.first());
495 ++currentIndex;
496 }
497 values = prunedValues;
498 }
499#endif
500
501 if (paramCountIsValid) {
502 for (int i = 0; i < paramCount; ++i) {
503 res = SQLITE_OK;
504 const QVariant &value = values.at(i);
505
506 if (QSqlResultPrivate::isVariantNull(value)) {
507 res = sqlite3_bind_null(d->stmt, i + 1);
508 } else {
509 switch (value.userType()) {
510 case QMetaType::QByteArray: {
511 const QByteArray *ba = static_cast<const QByteArray*>(value.constData());
512 res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(),
513 ba->size(), SQLITE_STATIC);
514 break; }
515 case QMetaType::Int:
516 case QMetaType::Bool:
517 res = sqlite3_bind_int(d->stmt, i + 1, value.toInt());
518 break;
519 case QMetaType::Double:
520 res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble());
521 break;
522 case QMetaType::UInt:
523 case QMetaType::LongLong:
524 res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong());
525 break;
526 case QMetaType::QDateTime: {
527 const QDateTime dateTime = value.toDateTime();
528 const QString str = dateTime.toString(Qt::ISODateWithMs);
529 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
530 int(str.size() * sizeof(ushort)),
531 SQLITE_TRANSIENT);
532 break;
533 }
534 case QMetaType::QTime: {
535 const QTime time = value.toTime();
536 const QString str = time.toString(u"hh:mm:ss.zzz");
537 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
538 int(str.size() * sizeof(ushort)),
539 SQLITE_TRANSIENT);
540 break;
541 }
542 case QMetaType::QString: {
543 // lifetime of string == lifetime of its qvariant
544 const QString *str = static_cast<const QString*>(value.constData());
545 res = sqlite3_bind_text16(d->stmt, i + 1, str->unicode(),
546 int(str->size()) * sizeof(QChar),
547 SQLITE_STATIC);
548 break; }
549 default: {
550 const QString str = value.toString();
551 // SQLITE_TRANSIENT makes sure that sqlite buffers the data
552 res = sqlite3_bind_text16(d->stmt, i + 1, str.data(),
553 int(str.size()) * sizeof(QChar),
554 SQLITE_TRANSIENT);
555 break; }
556 }
557 }
558 if (res != SQLITE_OK) {
559 setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult",
560 "Unable to bind parameters"), QSqlError::StatementError, res));
561 d->finalize();
562 return false;
563 }
564 }
565 } else {
566 setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult",
567 "Parameter count mismatch"), QString(), QSqlError::StatementError));
568 return false;
569 }
570 d->skippedStatus = d->fetchNext(d->firstRow, 0, true);
571 if (lastError().isValid()) {
572 setSelect(false);
573 setActive(false);
574 return false;
575 }
576 setSelect(!d->rInf.isEmpty());
577 setActive(true);
578 return true;
579}
580
581bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx)
582{
583 Q_D(QSQLiteResult);
584 return d->fetchNext(row, idx, false);
585}
586
588{
589 return -1;
590}
591
593{
594 Q_D(const QSQLiteResult);
595 return sqlite3_changes(d->drv_d_func()->access);
596}
597
599{
600 Q_D(const QSQLiteResult);
601 if (isActive()) {
602 qint64 id = sqlite3_last_insert_rowid(d->drv_d_func()->access);
603 if (id)
604 return id;
605 }
606 return QVariant();
607}
608
609QSqlRecord QSQLiteResult::record() const
610{
611 Q_D(const QSQLiteResult);
612 if (!isActive() || !isSelect())
613 return QSqlRecord();
614 return d->rInf;
615}
616
618{
619 Q_D(QSQLiteResult);
620 if (d->stmt)
621 sqlite3_reset(d->stmt);
622}
623
624QVariant QSQLiteResult::handle() const
625{
626 Q_D(const QSQLiteResult);
627 return QVariant::fromValue(d->stmt);
628}
629
630/////////////////////////////////////////////////////////
631
632#if QT_CONFIG(regularexpression)
633static void _q_regexp(sqlite3_context* context, int argc, sqlite3_value** argv)
634{
635 if (Q_UNLIKELY(argc != 2)) {
636 sqlite3_result_int(context, 0);
637 return;
638 }
639
640 const QString pattern = QString::fromUtf8(
641 reinterpret_cast<const char*>(sqlite3_value_text(argv[0])));
642 const QString subject = QString::fromUtf8(
643 reinterpret_cast<const char*>(sqlite3_value_text(argv[1])));
644
645 auto cache = static_cast<QCache<QString, QRegularExpression>*>(sqlite3_user_data(context));
646 auto regexp = cache->object(pattern);
647 const bool wasCached = regexp;
648
649 if (!wasCached)
650 regexp = new QRegularExpression(pattern, QRegularExpression::DontCaptureOption);
651
652 const bool found = subject.contains(*regexp);
653
654 if (!wasCached)
655 cache->insert(pattern, regexp);
656
657 sqlite3_result_int(context, int(found));
658}
659
660static void _q_regexp_cleanup(void *cache)
661{
662 delete static_cast<QCache<QString, QRegularExpression>*>(cache);
663}
664#endif
665
666static void _q_lower(sqlite3_context* context, int argc, sqlite3_value** argv)
667{
668 if (Q_UNLIKELY(argc != 1)) {
669 sqlite3_result_text(context, nullptr, 0, nullptr);
670 return;
671 }
672 const QString lower = QString::fromUtf8(
673 reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toLower();
674 const QByteArray ba = lower.toUtf8();
675 sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
676}
677
678static void _q_upper(sqlite3_context* context, int argc, sqlite3_value** argv)
679{
680 if (Q_UNLIKELY(argc != 1)) {
681 sqlite3_result_text(context, nullptr, 0, nullptr);
682 return;
683 }
684 const QString upper = QString::fromUtf8(
685 reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))).toUpper();
686 const QByteArray ba = upper.toUtf8();
687 sqlite3_result_text(context, ba.data(), ba.size(), SQLITE_TRANSIENT);
688}
689
690QSQLiteDriver::QSQLiteDriver(QObject * parent)
691 : QSqlDriver(*new QSQLiteDriverPrivate, parent)
692{
693}
694
695QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent)
696 : QSqlDriver(*new QSQLiteDriverPrivate, parent)
697{
698 Q_D(QSQLiteDriver);
699 d->access = connection;
700 setOpen(true);
701 setOpenError(false);
702}
703
704
705QSQLiteDriver::~QSQLiteDriver()
706{
707 close();
708}
709
710bool QSQLiteDriver::hasFeature(DriverFeature f) const
711{
712 switch (f) {
713 case BLOB:
714 case Transactions:
715 case Unicode:
716 case LastInsertId:
717 case PreparedQueries:
718 case PositionalPlaceholders:
719 case SimpleLocking:
720 case FinishQuery:
721 case LowPrecisionNumbers:
722 case EventNotifications:
723 return true;
724 case QuerySize:
725 case BatchOperations:
726 case MultipleResultSets:
727 case CancelQuery:
728 return false;
729 case NamedPlaceholders:
730#if (SQLITE_VERSION_NUMBER < 3003011)
731 return false;
732#else
733 return true;
734#endif
735
736 }
737 return false;
738}
739
740/*
741 SQLite dbs have no user name, passwords, hosts or ports.
742 just file names.
743*/
744bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts)
745{
746 Q_D(QSQLiteDriver);
747 if (isOpen())
748 close();
749
750
751 int timeOut = 5000;
752 bool sharedCache = false;
753 bool openReadOnlyOption = false;
754 bool openUriOption = false;
755 bool useExtendedResultCodes = true;
756 bool useQtVfs = false;
757 bool useQtCaseFolding = false;
758 bool openNoFollow = false;
759#if QT_CONFIG(regularexpression)
760 static const auto regexpConnectOption = "QSQLITE_ENABLE_REGEXP"_L1;
761 bool defineRegexp = false;
762 int regexpCacheSize = 25;
763#endif
764
765 const auto opts = QStringView{conOpts}.split(u';', Qt::SkipEmptyParts);
766 for (auto option : opts) {
767 option = option.trimmed();
768 if (option.startsWith("QSQLITE_BUSY_TIMEOUT"_L1)) {
769 option = option.mid(20).trimmed();
770 if (option.startsWith(u'=')) {
771 bool ok;
772 const int nt = option.mid(1).trimmed().toInt(&ok);
773 if (ok)
774 timeOut = nt;
775 }
776 } else if (option == "QSQLITE_USE_QT_VFS"_L1) {
777 useQtVfs = true;
778 } else if (option == "QSQLITE_OPEN_READONLY"_L1) {
779 openReadOnlyOption = true;
780 } else if (option == "QSQLITE_OPEN_URI"_L1) {
781 openUriOption = true;
782 } else if (option == "QSQLITE_ENABLE_SHARED_CACHE"_L1) {
783 sharedCache = true;
784 } else if (option == "QSQLITE_NO_USE_EXTENDED_RESULT_CODES"_L1) {
785 useExtendedResultCodes = false;
786 } else if (option == "QSQLITE_ENABLE_NON_ASCII_CASE_FOLDING"_L1) {
787 useQtCaseFolding = true;
788 } else if (option == "QSQLITE_OPEN_NOFOLLOW"_L1) {
789 openNoFollow = true;
790 }
791#if QT_CONFIG(regularexpression)
792 else if (option.startsWith(regexpConnectOption)) {
793 option = option.mid(regexpConnectOption.size()).trimmed();
794 if (option.isEmpty()) {
795 defineRegexp = true;
796 } else if (option.startsWith(u'=')) {
797 bool ok = false;
798 const int cacheSize = option.mid(1).trimmed().toInt(&ok);
799 if (ok) {
800 defineRegexp = true;
801 if (cacheSize > 0)
802 regexpCacheSize = cacheSize;
803 }
804 }
805 }
806#endif
807 else
808 qCWarning(lcSqlite, "Unsupported option '%ls'", qUtf16Printable(option.toString()));
809 }
810
811 int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
812 openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
813 if (openUriOption)
814 openMode |= SQLITE_OPEN_URI;
815 if (openNoFollow) {
816#if defined(SQLITE_OPEN_NOFOLLOW)
817 openMode |= SQLITE_OPEN_NOFOLLOW;
818#else
819 qCWarning(lcSqlite, "SQLITE_OPEN_NOFOLLOW not supported with the SQLite version %s", sqlite3_libversion());
820#endif
821 }
822
823 openMode |= SQLITE_OPEN_NOMUTEX;
824
825 const int res = sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, useQtVfs ? "QtVFS" : nullptr);
826
827 if (res == SQLITE_OK) {
828 sqlite3_busy_timeout(d->access, timeOut);
829 sqlite3_extended_result_codes(d->access, useExtendedResultCodes);
830 setOpen(true);
831 setOpenError(false);
832#if QT_CONFIG(regularexpression)
833 if (defineRegexp) {
834 auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize);
835 sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache,
836 &_q_regexp, nullptr,
837 nullptr, &_q_regexp_cleanup);
838 }
839#endif
840 if (useQtCaseFolding) {
841 sqlite3_create_function_v2(d->access, "lower", 1, SQLITE_UTF8, nullptr,
842 &_q_lower, nullptr, nullptr, nullptr);
843 sqlite3_create_function_v2(d->access, "upper", 1, SQLITE_UTF8, nullptr,
844 &_q_upper, nullptr, nullptr, nullptr);
845 }
846 return true;
847 } else {
848 setLastError(qMakeError(d->access, tr("Error opening database"),
849 QSqlError::ConnectionError, res));
850 setOpenError(true);
851
852 if (d->access) {
853 sqlite3_close(d->access);
854 d->access = nullptr;
855 }
856
857 return false;
858 }
859}
860
861void QSQLiteDriver::close()
862{
863 Q_D(QSQLiteDriver);
864 if (isOpen()) {
865 for (QSQLiteResult *result : std::as_const(d->results))
866 result->d_func()->finalize();
867
868 if (d->access && (d->notificationid.size() > 0)) {
869 d->notificationid.clear();
870 sqlite3_update_hook(d->access, nullptr, nullptr);
871 }
872
873 const int res = sqlite3_close(d->access);
874
875 if (res != SQLITE_OK)
876 setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError, res));
877 d->access = nullptr;
878 setOpen(false);
879 setOpenError(false);
880 }
881}
882
883QSqlResult *QSQLiteDriver::createResult() const
884{
885 return new QSQLiteResult(this);
886}
887
888bool QSQLiteDriver::beginTransaction()
889{
890 if (!isOpen() || isOpenError())
891 return false;
892
893 QSqlQuery q(createResult());
894 if (!q.exec("BEGIN"_L1)) {
895 setLastError(QSqlError(tr("Unable to begin transaction"),
896 q.lastError().databaseText(), QSqlError::TransactionError));
897 return false;
898 }
899
900 return true;
901}
902
903bool QSQLiteDriver::commitTransaction()
904{
905 if (!isOpen() || isOpenError())
906 return false;
907
908 QSqlQuery q(createResult());
909 if (!q.exec("COMMIT"_L1)) {
910 setLastError(QSqlError(tr("Unable to commit transaction"),
911 q.lastError().databaseText(), QSqlError::TransactionError));
912 return false;
913 }
914
915 return true;
916}
917
918bool QSQLiteDriver::rollbackTransaction()
919{
920 if (!isOpen() || isOpenError())
921 return false;
922
923 QSqlQuery q(createResult());
924 if (!q.exec("ROLLBACK"_L1)) {
925 setLastError(QSqlError(tr("Unable to rollback transaction"),
926 q.lastError().databaseText(), QSqlError::TransactionError));
927 return false;
928 }
929
930 return true;
931}
932
933QStringList QSQLiteDriver::tables(QSql::TableType type) const
934{
935 QStringList res;
936 if (!isOpen())
937 return res;
938
939 QSqlQuery q(createResult());
940 q.setForwardOnly(true);
941
942 QString sql = "SELECT name FROM sqlite_master WHERE %1 "
943 "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"_L1;
944 if ((type & QSql::Tables) && (type & QSql::Views))
945 sql = sql.arg("type='table' OR type='view'"_L1);
946 else if (type & QSql::Tables)
947 sql = sql.arg("type='table'"_L1);
948 else if (type & QSql::Views)
949 sql = sql.arg("type='view'"_L1);
950 else
951 sql.clear();
952
953 if (!sql.isEmpty() && q.exec(sql)) {
954 while(q.next())
955 res.append(q.value(0).toString());
956 }
957
958 if (type & QSql::SystemTables) {
959 // there are no internal tables beside this one:
960 res.append("sqlite_master"_L1);
961 }
962
963 return res;
964}
965
966QSqlIndex QSQLiteDriver::primaryIndex(const QString &tablename) const
967{
968 Q_D(const QSQLiteDriver);
969 if (!isOpen())
970 return QSqlIndex();
971
972 QSqlQuery q(createResult());
973 q.setForwardOnly(true);
974 return d->getTableInfo(q, tablename, true);
975}
976
977QSqlRecord QSQLiteDriver::record(const QString &tablename) const
978{
979 Q_D(const QSQLiteDriver);
980 if (!isOpen())
981 return QSqlRecord();
982
983 QSqlQuery q(createResult());
984 q.setForwardOnly(true);
985 return d->getTableInfo(q, tablename);
986}
987
988QVariant QSQLiteDriver::handle() const
989{
990 Q_D(const QSQLiteDriver);
991 return QVariant::fromValue(d->access);
992}
993
994QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
995{
996 Q_D(const QSQLiteDriver);
997 if (identifier.isEmpty() || isIdentifierEscaped(identifier, type))
998 return identifier;
999
1000 const auto indexOfSeparator = identifier.indexOf(u'.');
1001 if (indexOfSeparator > -1) {
1002 auto leftName = QStringView{identifier}.first(indexOfSeparator);
1003 auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
1004 const QStringView leftEnclose = d->isIdentifierEscaped(leftName) ? u"" : u"\"";
1005 const QStringView rightEnclose = d->isIdentifierEscaped(rightName) ? u"" : u"\"";
1006 if (leftEnclose.isEmpty() || rightEnclose.isEmpty())
1007 return (leftEnclose + leftName + leftEnclose + u'.' + rightEnclose + rightName
1008 + rightEnclose);
1009 }
1010 return u'"' + identifier + u'"';
1011}
1012
1013bool QSQLiteDriver::isIdentifierEscaped(const QString &identifier, IdentifierType type) const
1014{
1015 Q_D(const QSQLiteDriver);
1016 Q_UNUSED(type);
1017 return d->isIdentifierEscaped(QStringView{identifier});
1018}
1019
1020QString QSQLiteDriver::stripDelimiters(const QString &identifier, IdentifierType type) const
1021{
1022 Q_D(const QSQLiteDriver);
1023 const auto indexOfSeparator = identifier.indexOf(u'.');
1024 if (indexOfSeparator > -1) {
1025 auto leftName = QStringView{identifier}.first(indexOfSeparator);
1026 auto rightName = QStringView{identifier}.sliced(indexOfSeparator + 1);
1027 const auto leftEscaped = d->isIdentifierEscaped(leftName);
1028 const auto rightEscaped = d->isIdentifierEscaped(rightName);
1029 if (leftEscaped || rightEscaped) {
1030 if (leftEscaped)
1031 leftName = leftName.sliced(1).chopped(1);
1032 if (rightEscaped)
1033 rightName = rightName.sliced(1).chopped(1);
1034 return leftName + u'.' + rightName;
1035 }
1036 }
1037
1038 if (isIdentifierEscaped(identifier, type))
1039 return identifier.mid(1, identifier.size() - 2);
1040
1041 return identifier;
1042}
1043
1044static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,
1045 sqlite3_int64 arowid)
1046{
1047 Q_UNUSED(aoperation);
1048 Q_UNUSED(adbname);
1049 QSQLiteDriver *driver = static_cast<QSQLiteDriver *>(qobj);
1050 if (driver) {
1051 QMetaObject::invokeMethod(driver, "handleNotification", Qt::QueuedConnection,
1052 Q_ARG(QString, QString::fromUtf8(atablename)), Q_ARG(qint64, arowid));
1053 }
1054}
1055
1056bool QSQLiteDriver::subscribeToNotification(const QString &name)
1057{
1058 Q_D(QSQLiteDriver);
1059 if (!isOpen()) {
1060 qCWarning(lcSqlite, "QSQLiteDriver::subscribeToNotification: Database not open.");
1061 return false;
1062 }
1063
1064 if (d->notificationid.contains(name)) {
1065 qCWarning(lcSqlite, "QSQLiteDriver::subscribeToNotification: Already subscribing to '%ls'.",
1066 qUtf16Printable(name));
1067 return false;
1068 }
1069
1070 //sqlite supports only one notification callback, so only the first is registered
1071 d->notificationid << name;
1072 if (d->notificationid.size() == 1)
1073 sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this));
1074
1075 return true;
1076}
1077
1078bool QSQLiteDriver::unsubscribeFromNotification(const QString &name)
1079{
1080 Q_D(QSQLiteDriver);
1081 if (!isOpen()) {
1082 qCWarning(lcSqlite, "QSQLiteDriver::unsubscribeFromNotification: Database not open.");
1083 return false;
1084 }
1085
1086 if (!d->notificationid.contains(name)) {
1087 qCWarning(lcSqlite, "QSQLiteDriver::unsubscribeFromNotification: Not subscribed to '%ls'.",
1088 qUtf16Printable(name));
1089 return false;
1090 }
1091
1092 d->notificationid.removeAll(name);
1093 if (d->notificationid.isEmpty())
1094 sqlite3_update_hook(d->access, nullptr, nullptr);
1095
1096 return true;
1097}
1098
1099QStringList QSQLiteDriver::subscribedToNotifications() const
1100{
1101 Q_D(const QSQLiteDriver);
1102 return d->notificationid;
1103}
1104
1105void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid)
1106{
1107 Q_D(const QSQLiteDriver);
1108 if (d->notificationid.contains(tableName))
1109 emit notification(tableName, QSqlDriver::UnknownSource, QVariant(rowid));
1110}
1111
1112QT_END_NAMESPACE
1113
1114#include "moc_qsql_sqlite_p.cpp"
Definition qlist.h:81
QList< QSQLiteResult * > results
QSqlIndex getTableInfo(QSqlQuery &query, const QString &tableName, bool onlyPIndex=false) const
QStringList notificationid
sqlite3_stmt * stmt
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...
The QSqlField class manipulates the fields in SQL database tables and views.
Definition qsqlfield.h:20
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:177
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
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 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)