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