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