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 | |
33 | Q_DECLARE_OPAQUE_POINTER(sqlite3*) |
34 | Q_DECLARE_METATYPE(sqlite3*) |
35 | |
36 | Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) |
37 | Q_DECLARE_METATYPE(sqlite3_stmt*) |
38 | |
39 | QT_BEGIN_NAMESPACE |
40 | |
41 | using namespace Qt::StringLiterals; |
42 | |
43 | static QString _q_escapeIdentifier(const QString &identifier, QSqlDriver::IdentifierType type) |
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(c: u'[') && identifier.contains(c: u']')) |
49 | return res; |
50 | if (!identifier.isEmpty() && !identifier.startsWith(c: u'"') && !identifier.endsWith(c: u'"')) { |
51 | res.replace(c: u'"', after: "\"\""_L1 ); |
52 | if (type == QSqlDriver::TableName) |
53 | res.replace(c: u'.', after: "\".\""_L1 ); |
54 | res = u'"' + res + u'"'; |
55 | } |
56 | return res; |
57 | } |
58 | |
59 | static 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(s: "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 | |
77 | static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, |
78 | int errorCode) |
79 | { |
80 | return QSqlError(descr, |
81 | QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))), |
82 | type, QString::number(errorCode)); |
83 | } |
84 | |
85 | class QSQLiteResultPrivate; |
86 | |
87 | class QSQLiteResult : public QSqlCachedResult |
88 | { |
89 | Q_DECLARE_PRIVATE(QSQLiteResult) |
90 | friend class QSQLiteDriver; |
91 | |
92 | public: |
93 | explicit QSQLiteResult(const QSQLiteDriver* db); |
94 | ~QSQLiteResult(); |
95 | QVariant handle() const override; |
96 | |
97 | protected: |
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 | |
111 | class QSQLiteDriverPrivate : public QSqlDriverPrivate |
112 | { |
113 | Q_DECLARE_PUBLIC(QSQLiteDriver) |
114 | |
115 | public: |
116 | inline QSQLiteDriverPrivate() : QSqlDriverPrivate(QSqlDriver::SQLite) {} |
117 | sqlite3 *access = nullptr; |
118 | QList<QSQLiteResult *> results; |
119 | QStringList notificationid; |
120 | }; |
121 | |
122 | |
123 | class QSQLiteResultPrivate : public QSqlCachedResultPrivate |
124 | { |
125 | Q_DECLARE_PUBLIC(QSQLiteResult) |
126 | |
127 | public: |
128 | Q_DECLARE_SQLDRIVER_PRIVATE(QSQLiteDriver) |
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; |
137 | QSqlRecord rInf; |
138 | QList<QVariant> firstRow; |
139 | bool skippedStatus = false; // the status of the fetchNext() that's skipped |
140 | bool skipRow = false; // skip the next fetchNext()? |
141 | }; |
142 | |
143 | void QSQLiteResultPrivate::cleanup() |
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 | |
155 | void QSQLiteResultPrivate::finalize() |
156 | { |
157 | if (!stmt) |
158 | return; |
159 | |
160 | sqlite3_finalize(pStmt: stmt); |
161 | stmt = nullptr; |
162 | } |
163 | |
164 | void QSQLiteResultPrivate::initColumns(bool emptyResultset) |
165 | { |
166 | Q_Q(QSQLiteResult); |
167 | int nCols = sqlite3_column_count(pStmt: stmt); |
168 | if (nCols <= 0) |
169 | return; |
170 | |
171 | q->init(colCount: nCols); |
172 | |
173 | for (int i = 0; i < nCols; ++i) { |
174 | QString colName = QString(reinterpret_cast<const QChar *>( |
175 | sqlite3_column_name16(stmt, N: i)) |
176 | ).remove(c: u'"'); |
177 | const QString tableName = QString(reinterpret_cast<const QChar *>( |
178 | sqlite3_column_table_name16(stmt, i)) |
179 | ).remove(c: 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, iCol: i); |
185 | |
186 | int fieldType; |
187 | |
188 | if (!typeName.isEmpty()) { |
189 | fieldType = qGetColumnType(tpName: 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(field: fld); |
215 | } |
216 | } |
217 | |
218 | bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) |
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(size: sqlite3_column_count(pStmt: stmt)); |
235 | } |
236 | |
237 | if (!stmt) { |
238 | q->setLastError(QSqlError(QCoreApplication::translate(context: "QSQLiteResult" , key: "Unable to fetch row" ), |
239 | QCoreApplication::translate(context: "QSQLiteResult" , key: "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(emptyResultset: false); |
250 | if (idx < 0 && !initialFetch) |
251 | return true; |
252 | for (int i = 0; i < rInf.count(); ++i) { |
253 | switch (sqlite3_column_type(stmt, iCol: i)) { |
254 | case SQLITE_BLOB: |
255 | values[i + idx] = QByteArray(static_cast<const char *>( |
256 | sqlite3_column_blob(stmt, iCol: i)), |
257 | sqlite3_column_bytes(stmt, iCol: i)); |
258 | break; |
259 | case SQLITE_INTEGER: |
260 | values[i + idx] = sqlite3_column_int64(stmt, iCol: i); |
261 | break; |
262 | case SQLITE_FLOAT: |
263 | switch(q->numericalPrecisionPolicy()) { |
264 | case QSql::LowPrecisionInt32: |
265 | values[i + idx] = sqlite3_column_int(stmt, iCol: i); |
266 | break; |
267 | case QSql::LowPrecisionInt64: |
268 | values[i + idx] = sqlite3_column_int64(stmt, iCol: i); |
269 | break; |
270 | case QSql::LowPrecisionDouble: |
271 | case QSql::HighPrecision: |
272 | default: |
273 | values[i + idx] = sqlite3_column_double(stmt, iCol: 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, iCol: i)), |
283 | sqlite3_column_bytes16(stmt, iCol: 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(emptyResultset: true); |
292 | q->setAt(QSql::AfterLastRow); |
293 | sqlite3_reset(pStmt: 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(pStmt: stmt); |
300 | q->setLastError(qMakeError(access: drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
301 | key: "Unable to fetch row" ), type: QSqlError::ConnectionError, errorCode: 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(access: drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
309 | key: "Unable to fetch row" ), type: QSqlError::ConnectionError, errorCode: res)); |
310 | sqlite3_reset(pStmt: stmt); |
311 | q->setAt(QSql::AfterLastRow); |
312 | return false; |
313 | } |
314 | return false; |
315 | } |
316 | |
317 | QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) |
318 | : QSqlCachedResult(*new QSQLiteResultPrivate(this, db)) |
319 | { |
320 | Q_D(QSQLiteResult); |
321 | const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.append(t: this); |
322 | } |
323 | |
324 | QSQLiteResult::~QSQLiteResult() |
325 | { |
326 | Q_D(QSQLiteResult); |
327 | if (d->drv_d_func()) |
328 | const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.removeOne(t: this); |
329 | d->cleanup(); |
330 | } |
331 | |
332 | void QSQLiteResult::virtual_hook(int id, void *data) |
333 | { |
334 | QSqlCachedResult::virtual_hook(id, data); |
335 | } |
336 | |
337 | bool QSQLiteResult::reset(const QString &query) |
338 | { |
339 | if (!prepare(query)) |
340 | return false; |
341 | return exec(); |
342 | } |
343 | |
344 | bool QSQLiteResult::prepare(const QString &query) |
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(db: d->drv_d_func()->access, zSql: query.constData(), nByte: size, |
359 | ppStmt: &d->stmt, pzTail: &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(access: d->drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
367 | key: "Unable to execute statement" ), type: QSqlError::StatementError, errorCode: res)); |
368 | d->finalize(); |
369 | return false; |
370 | } else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) { |
371 | setLastError(qMakeError(access: d->drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
372 | key: "Unable to execute multiple statements at a time" ), type: QSqlError::StatementError, SQLITE_MISUSE)); |
373 | d->finalize(); |
374 | return false; |
375 | } |
376 | return true; |
377 | } |
378 | |
379 | bool 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(i: 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(placeholder: it.key(), val: values.at(i: it.value().first()).toList().at(i), type: QSql::In); |
394 | ++it; |
395 | } |
396 | if (!exec()) |
397 | return false; |
398 | } |
399 | return true; |
400 | } |
401 | |
402 | bool QSQLiteResult::exec() |
403 | { |
404 | Q_D(QSQLiteResult); |
405 | QList<QVariant> values = boundValues(); |
406 | |
407 | d->skippedStatus = false; |
408 | d->skipRow = false; |
409 | d->rInf.clear(); |
410 | clearValues(); |
411 | setLastError(QSqlError()); |
412 | |
413 | int res = sqlite3_reset(pStmt: d->stmt); |
414 | if (res != SQLITE_OK) { |
415 | setLastError(qMakeError(access: d->drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
416 | key: "Unable to reset statement" ), type: QSqlError::StatementError, errorCode: 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(first: d->indexes.cbegin(), |
435 | last: d->indexes.cend(), |
436 | init: 0, |
437 | binary_op: 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(t: 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(utf8: parameterName); |
454 | const auto &indexes = d->indexes.value(key: placeHolder); |
455 | handledIndexes << indexes; |
456 | prunedValues << values.at(i: 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 | |
468 | if (QSqlResultPrivate::isVariantNull(variant: value)) { |
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 | n: 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(format: 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(format: 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(access: d->drv_d_func()->access, descr: QCoreApplication::translate(context: "QSQLiteResult" , |
522 | key: "Unable to bind parameters" ), type: QSqlError::StatementError, errorCode: res)); |
523 | d->finalize(); |
524 | return false; |
525 | } |
526 | } |
527 | } else { |
528 | setLastError(QSqlError(QCoreApplication::translate(context: "QSQLiteResult" , |
529 | key: "Parameter count mismatch" ), QString(), QSqlError::StatementError)); |
530 | return false; |
531 | } |
532 | d->skippedStatus = d->fetchNext(values&: d->firstRow, idx: 0, initialFetch: 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 | |
543 | bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) |
544 | { |
545 | Q_D(QSQLiteResult); |
546 | return d->fetchNext(values&: row, idx, initialFetch: false); |
547 | } |
548 | |
549 | int QSQLiteResult::size() |
550 | { |
551 | return -1; |
552 | } |
553 | |
554 | int QSQLiteResult::numRowsAffected() |
555 | { |
556 | Q_D(const QSQLiteResult); |
557 | return sqlite3_changes(d->drv_d_func()->access); |
558 | } |
559 | |
560 | QVariant QSQLiteResult::lastInsertId() const |
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 | |
571 | QSqlRecord QSQLiteResult::record() const |
572 | { |
573 | Q_D(const QSQLiteResult); |
574 | if (!isActive() || !isSelect()) |
575 | return QSqlRecord(); |
576 | return d->rInf; |
577 | } |
578 | |
579 | void QSQLiteResult::detachFromResultSet() |
580 | { |
581 | Q_D(QSQLiteResult); |
582 | if (d->stmt) |
583 | sqlite3_reset(pStmt: d->stmt); |
584 | } |
585 | |
586 | QVariant QSQLiteResult::handle() const |
587 | { |
588 | Q_D(const QSQLiteResult); |
589 | return QVariant::fromValue(value: d->stmt); |
590 | } |
591 | |
592 | ///////////////////////////////////////////////////////// |
593 | |
594 | #if QT_CONFIG(regularexpression) |
595 | static 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 | |
602 | const QString pattern = QString::fromUtf8( |
603 | utf8: reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))); |
604 | const QString subject = QString::fromUtf8( |
605 | utf8: 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(key: pattern); |
609 | const bool wasCached = regexp; |
610 | |
611 | if (!wasCached) |
612 | regexp = new QRegularExpression(pattern, QRegularExpression::DontCaptureOption); |
613 | |
614 | const bool found = subject.contains(re: *regexp); |
615 | |
616 | if (!wasCached) |
617 | cache->insert(key: pattern, object: regexp); |
618 | |
619 | sqlite3_result_int(context, int(found)); |
620 | } |
621 | |
622 | static void _q_regexp_cleanup(void *cache) |
623 | { |
624 | delete static_cast<QCache<QString, QRegularExpression>*>(cache); |
625 | } |
626 | #endif |
627 | |
628 | QSQLiteDriver::QSQLiteDriver(QObject * parent) |
629 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) |
630 | { |
631 | } |
632 | |
633 | QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) |
634 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) |
635 | { |
636 | Q_D(QSQLiteDriver); |
637 | d->access = connection; |
638 | setOpen(true); |
639 | setOpenError(false); |
640 | } |
641 | |
642 | |
643 | QSQLiteDriver::~QSQLiteDriver() |
644 | { |
645 | close(); |
646 | } |
647 | |
648 | bool QSQLiteDriver::hasFeature(DriverFeature f) const |
649 | { |
650 | switch (f) { |
651 | case BLOB: |
652 | case Transactions: |
653 | case Unicode: |
654 | case LastInsertId: |
655 | case PreparedQueries: |
656 | case PositionalPlaceholders: |
657 | case SimpleLocking: |
658 | case FinishQuery: |
659 | case LowPrecisionNumbers: |
660 | case EventNotifications: |
661 | return true; |
662 | case QuerySize: |
663 | case BatchOperations: |
664 | case MultipleResultSets: |
665 | case CancelQuery: |
666 | return false; |
667 | case NamedPlaceholders: |
668 | #if (SQLITE_VERSION_NUMBER < 3003011) |
669 | return false; |
670 | #else |
671 | return true; |
672 | #endif |
673 | |
674 | } |
675 | return false; |
676 | } |
677 | |
678 | /* |
679 | SQLite dbs have no user name, passwords, hosts or ports. |
680 | just file names. |
681 | */ |
682 | bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) |
683 | { |
684 | Q_D(QSQLiteDriver); |
685 | if (isOpen()) |
686 | close(); |
687 | |
688 | |
689 | int timeOut = 5000; |
690 | bool sharedCache = false; |
691 | bool openReadOnlyOption = false; |
692 | bool openUriOption = false; |
693 | bool useExtendedResultCodes = true; |
694 | #if QT_CONFIG(regularexpression) |
695 | static const auto regexpConnectOption = "QSQLITE_ENABLE_REGEXP"_L1 ; |
696 | bool defineRegexp = false; |
697 | int regexpCacheSize = 25; |
698 | #endif |
699 | |
700 | const auto opts = QStringView{conOpts}.split(sep: u';'); |
701 | for (auto option : opts) { |
702 | option = option.trimmed(); |
703 | if (option.startsWith(s: "QSQLITE_BUSY_TIMEOUT"_L1 )) { |
704 | option = option.mid(pos: 20).trimmed(); |
705 | if (option.startsWith(c: u'=')) { |
706 | bool ok; |
707 | const int nt = option.mid(pos: 1).trimmed().toInt(ok: &ok); |
708 | if (ok) |
709 | timeOut = nt; |
710 | } |
711 | } else if (option == "QSQLITE_OPEN_READONLY"_L1 ) { |
712 | openReadOnlyOption = true; |
713 | } else if (option == "QSQLITE_OPEN_URI"_L1 ) { |
714 | openUriOption = true; |
715 | } else if (option == "QSQLITE_ENABLE_SHARED_CACHE"_L1 ) { |
716 | sharedCache = true; |
717 | } else if (option == "QSQLITE_NO_USE_EXTENDED_RESULT_CODES"_L1 ) { |
718 | useExtendedResultCodes = false; |
719 | } |
720 | #if QT_CONFIG(regularexpression) |
721 | else if (option.startsWith(s: regexpConnectOption)) { |
722 | option = option.mid(pos: regexpConnectOption.size()).trimmed(); |
723 | if (option.isEmpty()) { |
724 | defineRegexp = true; |
725 | } else if (option.startsWith(c: u'=')) { |
726 | bool ok = false; |
727 | const int cacheSize = option.mid(pos: 1).trimmed().toInt(ok: &ok); |
728 | if (ok) { |
729 | defineRegexp = true; |
730 | if (cacheSize > 0) |
731 | regexpCacheSize = cacheSize; |
732 | } |
733 | } |
734 | } |
735 | #endif |
736 | } |
737 | |
738 | int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); |
739 | openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE); |
740 | if (openUriOption) |
741 | openMode |= SQLITE_OPEN_URI; |
742 | |
743 | openMode |= SQLITE_OPEN_NOMUTEX; |
744 | |
745 | const int res = sqlite3_open_v2(filename: db.toUtf8().constData(), ppDb: &d->access, flags: openMode, zVfs: nullptr); |
746 | |
747 | if (res == SQLITE_OK) { |
748 | sqlite3_busy_timeout(d->access, ms: timeOut); |
749 | sqlite3_extended_result_codes(d->access, onoff: useExtendedResultCodes); |
750 | setOpen(true); |
751 | setOpenError(false); |
752 | #if QT_CONFIG(regularexpression) |
753 | if (defineRegexp) { |
754 | auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize); |
755 | sqlite3_create_function_v2(db: d->access, zFunctionName: "regexp" , nArg: 2, SQLITE_UTF8, pApp: cache, |
756 | xFunc: &_q_regexp, xStep: nullptr, |
757 | xFinal: nullptr, xDestroy: &_q_regexp_cleanup); |
758 | } |
759 | #endif |
760 | return true; |
761 | } else { |
762 | setLastError(qMakeError(access: d->access, descr: tr(s: "Error opening database" ), |
763 | type: QSqlError::ConnectionError, errorCode: res)); |
764 | setOpenError(true); |
765 | |
766 | if (d->access) { |
767 | sqlite3_close(d->access); |
768 | d->access = nullptr; |
769 | } |
770 | |
771 | return false; |
772 | } |
773 | } |
774 | |
775 | void QSQLiteDriver::close() |
776 | { |
777 | Q_D(QSQLiteDriver); |
778 | if (isOpen()) { |
779 | for (QSQLiteResult *result : std::as_const(t&: d->results)) |
780 | result->d_func()->finalize(); |
781 | |
782 | if (d->access && (d->notificationid.size() > 0)) { |
783 | d->notificationid.clear(); |
784 | sqlite3_update_hook(d->access, nullptr, nullptr); |
785 | } |
786 | |
787 | const int res = sqlite3_close(d->access); |
788 | |
789 | if (res != SQLITE_OK) |
790 | setLastError(qMakeError(access: d->access, descr: tr(s: "Error closing database" ), type: QSqlError::ConnectionError, errorCode: res)); |
791 | d->access = nullptr; |
792 | setOpen(false); |
793 | setOpenError(false); |
794 | } |
795 | } |
796 | |
797 | QSqlResult *QSQLiteDriver::createResult() const |
798 | { |
799 | return new QSQLiteResult(this); |
800 | } |
801 | |
802 | bool QSQLiteDriver::beginTransaction() |
803 | { |
804 | if (!isOpen() || isOpenError()) |
805 | return false; |
806 | |
807 | QSqlQuery q(createResult()); |
808 | if (!q.exec(query: "BEGIN"_L1 )) { |
809 | setLastError(QSqlError(tr(s: "Unable to begin transaction" ), |
810 | q.lastError().databaseText(), QSqlError::TransactionError)); |
811 | return false; |
812 | } |
813 | |
814 | return true; |
815 | } |
816 | |
817 | bool QSQLiteDriver::commitTransaction() |
818 | { |
819 | if (!isOpen() || isOpenError()) |
820 | return false; |
821 | |
822 | QSqlQuery q(createResult()); |
823 | if (!q.exec(query: "COMMIT"_L1 )) { |
824 | setLastError(QSqlError(tr(s: "Unable to commit transaction" ), |
825 | q.lastError().databaseText(), QSqlError::TransactionError)); |
826 | return false; |
827 | } |
828 | |
829 | return true; |
830 | } |
831 | |
832 | bool QSQLiteDriver::rollbackTransaction() |
833 | { |
834 | if (!isOpen() || isOpenError()) |
835 | return false; |
836 | |
837 | QSqlQuery q(createResult()); |
838 | if (!q.exec(query: "ROLLBACK"_L1 )) { |
839 | setLastError(QSqlError(tr(s: "Unable to rollback transaction" ), |
840 | q.lastError().databaseText(), QSqlError::TransactionError)); |
841 | return false; |
842 | } |
843 | |
844 | return true; |
845 | } |
846 | |
847 | QStringList QSQLiteDriver::tables(QSql::TableType type) const |
848 | { |
849 | QStringList res; |
850 | if (!isOpen()) |
851 | return res; |
852 | |
853 | QSqlQuery q(createResult()); |
854 | q.setForwardOnly(true); |
855 | |
856 | QString sql = "SELECT name FROM sqlite_master WHERE %1 " |
857 | "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"_L1 ; |
858 | if ((type & QSql::Tables) && (type & QSql::Views)) |
859 | sql = sql.arg(a: "type='table' OR type='view'"_L1 ); |
860 | else if (type & QSql::Tables) |
861 | sql = sql.arg(a: "type='table'"_L1 ); |
862 | else if (type & QSql::Views) |
863 | sql = sql.arg(a: "type='view'"_L1 ); |
864 | else |
865 | sql.clear(); |
866 | |
867 | if (!sql.isEmpty() && q.exec(query: sql)) { |
868 | while(q.next()) |
869 | res.append(t: q.value(i: 0).toString()); |
870 | } |
871 | |
872 | if (type & QSql::SystemTables) { |
873 | // there are no internal tables beside this one: |
874 | res.append(t: "sqlite_master"_L1 ); |
875 | } |
876 | |
877 | return res; |
878 | } |
879 | |
880 | static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) |
881 | { |
882 | QString schema; |
883 | QString table(tableName); |
884 | const qsizetype indexOfSeparator = tableName.indexOf(c: u'.'); |
885 | if (indexOfSeparator > -1) { |
886 | const qsizetype indexOfCloseBracket = tableName.indexOf(c: u']'); |
887 | if (indexOfCloseBracket != tableName.size() - 1) { |
888 | // Handles a case like databaseName.tableName |
889 | schema = tableName.left(n: indexOfSeparator + 1); |
890 | table = tableName.mid(position: indexOfSeparator + 1); |
891 | } else { |
892 | const qsizetype indexOfOpenBracket = tableName.lastIndexOf(c: u'[', from: indexOfCloseBracket); |
893 | if (indexOfOpenBracket > 0) { |
894 | // Handles a case like databaseName.[tableName] |
895 | schema = tableName.left(n: indexOfOpenBracket); |
896 | table = tableName.mid(position: indexOfOpenBracket); |
897 | } |
898 | } |
899 | } |
900 | q.exec(query: "PRAGMA "_L1 + schema + "table_info ("_L1 + |
901 | _q_escapeIdentifier(identifier: table, type: QSqlDriver::TableName) + u')'); |
902 | QSqlIndex ind; |
903 | while (q.next()) { |
904 | bool isPk = q.value(i: 5).toInt(); |
905 | if (onlyPIndex && !isPk) |
906 | continue; |
907 | QString typeName = q.value(i: 2).toString().toLower(); |
908 | QString defVal = q.value(i: 4).toString(); |
909 | if (!defVal.isEmpty() && defVal.at(i: 0) == u'\'') { |
910 | const int end = defVal.lastIndexOf(c: u'\''); |
911 | if (end > 0) |
912 | defVal = defVal.mid(position: 1, n: end - 1); |
913 | } |
914 | |
915 | QSqlField fld(q.value(i: 1).toString(), QMetaType(qGetColumnType(tpName: typeName)), tableName); |
916 | if (isPk && (typeName == "integer"_L1 )) |
917 | // INTEGER PRIMARY KEY fields are auto-generated in sqlite |
918 | // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! |
919 | fld.setAutoValue(true); |
920 | fld.setRequired(q.value(i: 3).toInt() != 0); |
921 | fld.setDefaultValue(defVal); |
922 | ind.append(field: fld); |
923 | } |
924 | return ind; |
925 | } |
926 | |
927 | QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const |
928 | { |
929 | if (!isOpen()) |
930 | return QSqlIndex(); |
931 | |
932 | QString table = tblname; |
933 | if (isIdentifierEscaped(identifier: table, type: QSqlDriver::TableName)) |
934 | table = stripDelimiters(identifier: table, type: QSqlDriver::TableName); |
935 | |
936 | QSqlQuery q(createResult()); |
937 | q.setForwardOnly(true); |
938 | return qGetTableInfo(q, tableName: table, onlyPIndex: true); |
939 | } |
940 | |
941 | QSqlRecord QSQLiteDriver::record(const QString &tbl) const |
942 | { |
943 | if (!isOpen()) |
944 | return QSqlRecord(); |
945 | |
946 | QString table = tbl; |
947 | if (isIdentifierEscaped(identifier: table, type: QSqlDriver::TableName)) |
948 | table = stripDelimiters(identifier: table, type: QSqlDriver::TableName); |
949 | |
950 | QSqlQuery q(createResult()); |
951 | q.setForwardOnly(true); |
952 | return qGetTableInfo(q, tableName: table); |
953 | } |
954 | |
955 | QVariant QSQLiteDriver::handle() const |
956 | { |
957 | Q_D(const QSQLiteDriver); |
958 | return QVariant::fromValue(value: d->access); |
959 | } |
960 | |
961 | QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const |
962 | { |
963 | return _q_escapeIdentifier(identifier, type); |
964 | } |
965 | |
966 | static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename, |
967 | sqlite3_int64 arowid) |
968 | { |
969 | Q_UNUSED(aoperation); |
970 | Q_UNUSED(adbname); |
971 | QSQLiteDriver *driver = static_cast<QSQLiteDriver *>(qobj); |
972 | if (driver) { |
973 | QMetaObject::invokeMethod(obj: driver, member: "handleNotification" , c: Qt::QueuedConnection, |
974 | Q_ARG(QString, QString::fromUtf8(atablename)), Q_ARG(qint64, arowid)); |
975 | } |
976 | } |
977 | |
978 | bool QSQLiteDriver::subscribeToNotification(const QString &name) |
979 | { |
980 | Q_D(QSQLiteDriver); |
981 | if (!isOpen()) { |
982 | qWarning(msg: "Database not open." ); |
983 | return false; |
984 | } |
985 | |
986 | if (d->notificationid.contains(str: name)) { |
987 | qWarning(msg: "Already subscribing to '%ls'." , qUtf16Printable(name)); |
988 | return false; |
989 | } |
990 | |
991 | //sqlite supports only one notification callback, so only the first is registered |
992 | d->notificationid << name; |
993 | if (d->notificationid.size() == 1) |
994 | sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this)); |
995 | |
996 | return true; |
997 | } |
998 | |
999 | bool QSQLiteDriver::unsubscribeFromNotification(const QString &name) |
1000 | { |
1001 | Q_D(QSQLiteDriver); |
1002 | if (!isOpen()) { |
1003 | qWarning(msg: "Database not open." ); |
1004 | return false; |
1005 | } |
1006 | |
1007 | if (!d->notificationid.contains(str: name)) { |
1008 | qWarning(msg: "Not subscribed to '%ls'." , qUtf16Printable(name)); |
1009 | return false; |
1010 | } |
1011 | |
1012 | d->notificationid.removeAll(t: name); |
1013 | if (d->notificationid.isEmpty()) |
1014 | sqlite3_update_hook(d->access, nullptr, nullptr); |
1015 | |
1016 | return true; |
1017 | } |
1018 | |
1019 | QStringList QSQLiteDriver::subscribedToNotifications() const |
1020 | { |
1021 | Q_D(const QSQLiteDriver); |
1022 | return d->notificationid; |
1023 | } |
1024 | |
1025 | void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid) |
1026 | { |
1027 | Q_D(const QSQLiteDriver); |
1028 | if (d->notificationid.contains(str: tableName)) |
1029 | emit notification(name: tableName, source: QSqlDriver::UnknownSource, payload: QVariant(rowid)); |
1030 | } |
1031 | |
1032 | QT_END_NAMESPACE |
1033 | |
1034 | #include "moc_qsql_sqlite_p.cpp" |
1035 | |