Changeset View
Changeset View
Standalone View
Standalone View
kmymoney/plugins/sqlcipher/QSQLite/5.9/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(timezone) | ||||
56 | #include <QTimeZone> | ||||
57 | #endif | ||||
58 | | ||||
59 | #if defined Q_OS_WIN | ||||
60 | # include <qt_windows.h> | ||||
61 | #else | ||||
62 | # include <unistd.h> | ||||
63 | #endif | ||||
64 | | ||||
65 | #include <sqlite3.h> | ||||
66 | #include <functional> | ||||
67 | | ||||
68 | Q_DECLARE_OPAQUE_POINTER(sqlite3*) | ||||
69 | Q_DECLARE_METATYPE(sqlite3*) | ||||
70 | | ||||
71 | Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) | ||||
72 | Q_DECLARE_METATYPE(sqlite3_stmt*) | ||||
73 | | ||||
74 | QT_BEGIN_NAMESPACE | ||||
75 | | ||||
76 | static QString _q_escapeIdentifier(const QString &identifier) | ||||
77 | { | ||||
78 | QString res = identifier; | ||||
79 | if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"'))) { | ||||
80 | res.replace(QLatin1Char('"'), QLatin1String("\"\"")); | ||||
81 | res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); | ||||
82 | res.replace(QLatin1Char('.'), QLatin1String("\".\"")); | ||||
83 | } | ||||
84 | return res; | ||||
85 | } | ||||
86 | | ||||
87 | static QVariant::Type qGetColumnType(const QString &tpName) | ||||
88 | { | ||||
89 | const QString typeName = tpName.toLower(); | ||||
90 | | ||||
91 | if (typeName == QLatin1String("integer") | ||||
92 | || typeName == QLatin1String("int")) | ||||
93 | return QVariant::Int; | ||||
94 | if (typeName == QLatin1String("double") | ||||
95 | || typeName == QLatin1String("float") | ||||
96 | || typeName == QLatin1String("real") | ||||
97 | || typeName.startsWith(QLatin1String("numeric"))) | ||||
98 | return QVariant::Double; | ||||
99 | if (typeName == QLatin1String("blob")) | ||||
100 | return QVariant::ByteArray; | ||||
101 | if (typeName == QLatin1String("boolean") | ||||
102 | || typeName == QLatin1String("bool")) | ||||
103 | return QVariant::Bool; | ||||
104 | return QVariant::String; | ||||
105 | } | ||||
106 | | ||||
107 | static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, | ||||
108 | int errorCode = -1) | ||||
109 | { | ||||
110 | return QSqlError(descr, | ||||
111 | QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))), | ||||
112 | type, QString::number(errorCode)); | ||||
113 | } | ||||
114 | | ||||
115 | class QSQLiteResultPrivate; | ||||
116 | | ||||
117 | class QSQLiteResult : public QSqlCachedResult | ||||
118 | { | ||||
119 | Q_DECLARE_PRIVATE(QSQLiteResult) | ||||
120 | friend class QSQLiteDriver; | ||||
121 | | ||||
122 | public: | ||||
123 | explicit QSQLiteResult(const QSQLiteDriver* db); | ||||
124 | ~QSQLiteResult(); | ||||
125 | QVariant handle() const Q_DECL_OVERRIDE; | ||||
126 | | ||||
127 | protected: | ||||
128 | bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) Q_DECL_OVERRIDE; | ||||
129 | bool reset(const QString &query) Q_DECL_OVERRIDE; | ||||
130 | bool prepare(const QString &query) Q_DECL_OVERRIDE; | ||||
131 | bool exec() Q_DECL_OVERRIDE; | ||||
132 | int size() Q_DECL_OVERRIDE; | ||||
133 | int numRowsAffected() Q_DECL_OVERRIDE; | ||||
134 | QVariant lastInsertId() const Q_DECL_OVERRIDE; | ||||
135 | QSqlRecord record() const Q_DECL_OVERRIDE; | ||||
136 | void detachFromResultSet() Q_DECL_OVERRIDE; | ||||
137 | void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; | ||||
138 | }; | ||||
139 | | ||||
140 | class QSQLiteDriverPrivate : public QSqlDriverPrivate | ||||
141 | { | ||||
142 | Q_DECLARE_PUBLIC(QSQLiteDriver) | ||||
143 | | ||||
144 | public: | ||||
145 | inline QSQLiteDriverPrivate() : QSqlDriverPrivate(), access(0) { dbmsType = QSqlDriver::SQLite; } | ||||
146 | sqlite3 *access; | ||||
147 | QList <QSQLiteResult *> results; | ||||
148 | QStringList notificationid; | ||||
149 | }; | ||||
150 | | ||||
151 | | ||||
152 | class QSQLiteResultPrivate: public QSqlCachedResultPrivate | ||||
153 | { | ||||
154 | Q_DECLARE_PUBLIC(QSQLiteResult) | ||||
155 | | ||||
156 | public: | ||||
157 | Q_DECLARE_SQLDRIVER_PRIVATE(QSQLiteDriver) | ||||
158 | QSQLiteResultPrivate(QSQLiteResult *q, const QSQLiteDriver *drv); | ||||
159 | void cleanup(); | ||||
160 | bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); | ||||
161 | // initializes the recordInfo and the cache | ||||
162 | void initColumns(bool emptyResultset); | ||||
163 | void finalize(); | ||||
164 | | ||||
165 | sqlite3_stmt *stmt; | ||||
166 | | ||||
167 | bool skippedStatus; // the status of the fetchNext() that's skipped | ||||
168 | bool skipRow; // skip the next fetchNext()? | ||||
169 | QSqlRecord rInf; | ||||
170 | QVector<QVariant> firstRow; | ||||
171 | }; | ||||
172 | | ||||
173 | QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult *q, const QSQLiteDriver *drv) | ||||
174 | : QSqlCachedResultPrivate(q, drv), | ||||
175 | stmt(0), | ||||
176 | skippedStatus(false), | ||||
177 | skipRow(false) | ||||
178 | { | ||||
179 | } | ||||
180 | | ||||
181 | void QSQLiteResultPrivate::cleanup() | ||||
182 | { | ||||
183 | Q_Q(QSQLiteResult); | ||||
184 | finalize(); | ||||
185 | rInf.clear(); | ||||
186 | skippedStatus = false; | ||||
187 | skipRow = false; | ||||
188 | q->setAt(QSql::BeforeFirstRow); | ||||
189 | q->setActive(false); | ||||
190 | q->cleanup(); | ||||
191 | } | ||||
192 | | ||||
193 | void QSQLiteResultPrivate::finalize() | ||||
194 | { | ||||
195 | if (!stmt) | ||||
196 | return; | ||||
197 | | ||||
198 | sqlite3_finalize(stmt); | ||||
199 | stmt = 0; | ||||
200 | } | ||||
201 | | ||||
202 | void QSQLiteResultPrivate::initColumns(bool emptyResultset) | ||||
203 | { | ||||
204 | Q_Q(QSQLiteResult); | ||||
205 | int nCols = sqlite3_column_count(stmt); | ||||
206 | if (nCols <= 0) | ||||
207 | return; | ||||
208 | | ||||
209 | q->init(nCols); | ||||
210 | | ||||
211 | for (int i = 0; i < nCols; ++i) { | ||||
212 | QString colName = QString(reinterpret_cast<const QChar *>( | ||||
213 | sqlite3_column_name16(stmt, i)) | ||||
214 | ).remove(QLatin1Char('"')); | ||||
215 | | ||||
216 | // must use typeName for resolving the type to match QSqliteDriver::record | ||||
217 | QString typeName = QString(reinterpret_cast<const QChar *>( | ||||
218 | sqlite3_column_decltype16(stmt, i))); | ||||
219 | // sqlite3_column_type is documented to have undefined behavior if the result set is empty | ||||
220 | int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); | ||||
221 | | ||||
222 | QVariant::Type fieldType; | ||||
223 | | ||||
224 | if (!typeName.isEmpty()) { | ||||
225 | fieldType = qGetColumnType(typeName); | ||||
226 | } else { | ||||
227 | // Get the proper type for the field based on stp value | ||||
228 | switch (stp) { | ||||
229 | case SQLITE_INTEGER: | ||||
230 | fieldType = QVariant::Int; | ||||
231 | break; | ||||
232 | case SQLITE_FLOAT: | ||||
233 | fieldType = QVariant::Double; | ||||
234 | break; | ||||
235 | case SQLITE_BLOB: | ||||
236 | fieldType = QVariant::ByteArray; | ||||
237 | break; | ||||
238 | case SQLITE_TEXT: | ||||
239 | fieldType = QVariant::String; | ||||
240 | break; | ||||
241 | case SQLITE_NULL: | ||||
242 | default: | ||||
243 | fieldType = QVariant::Invalid; | ||||
244 | break; | ||||
245 | } | ||||
246 | } | ||||
247 | | ||||
248 | QSqlField fld(colName, fieldType); | ||||
249 | fld.setSqlType(stp); | ||||
250 | rInf.append(fld); | ||||
251 | } | ||||
252 | } | ||||
253 | | ||||
254 | bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) | ||||
255 | { | ||||
256 | Q_Q(QSQLiteResult); | ||||
257 | int res; | ||||
258 | int i; | ||||
259 | | ||||
260 | if (skipRow) { | ||||
261 | // already fetched | ||||
262 | Q_ASSERT(!initialFetch); | ||||
263 | skipRow = false; | ||||
264 | for(int i=0;i<firstRow.count();i++) | ||||
265 | values[i]=firstRow[i]; | ||||
266 | return skippedStatus; | ||||
267 | } | ||||
268 | skipRow = initialFetch; | ||||
269 | | ||||
270 | if(initialFetch) { | ||||
271 | firstRow.clear(); | ||||
272 | firstRow.resize(sqlite3_column_count(stmt)); | ||||
273 | } | ||||
274 | | ||||
275 | if (!stmt) { | ||||
276 | q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), | ||||
277 | QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError)); | ||||
278 | q->setAt(QSql::AfterLastRow); | ||||
279 | return false; | ||||
280 | } | ||||
281 | res = sqlite3_step(stmt); | ||||
282 | | ||||
283 | switch(res) { | ||||
284 | case SQLITE_ROW: | ||||
285 | // check to see if should fill out columns | ||||
286 | if (rInf.isEmpty()) | ||||
287 | // must be first call. | ||||
288 | initColumns(false); | ||||
289 | if (idx < 0 && !initialFetch) | ||||
290 | return true; | ||||
291 | for (i = 0; i < rInf.count(); ++i) { | ||||
292 | switch (sqlite3_column_type(stmt, i)) { | ||||
293 | case SQLITE_BLOB: | ||||
294 | values[i + idx] = QByteArray(static_cast<const char *>( | ||||
295 | sqlite3_column_blob(stmt, i)), | ||||
296 | sqlite3_column_bytes(stmt, i)); | ||||
297 | break; | ||||
298 | case SQLITE_INTEGER: | ||||
299 | values[i + idx] = sqlite3_column_int64(stmt, i); | ||||
300 | break; | ||||
301 | case SQLITE_FLOAT: | ||||
302 | switch(q->numericalPrecisionPolicy()) { | ||||
303 | case QSql::LowPrecisionInt32: | ||||
304 | values[i + idx] = sqlite3_column_int(stmt, i); | ||||
305 | break; | ||||
306 | case QSql::LowPrecisionInt64: | ||||
307 | values[i + idx] = sqlite3_column_int64(stmt, i); | ||||
308 | break; | ||||
309 | case QSql::LowPrecisionDouble: | ||||
310 | case QSql::HighPrecision: | ||||
311 | default: | ||||
312 | values[i + idx] = sqlite3_column_double(stmt, i); | ||||
313 | break; | ||||
314 | }; | ||||
315 | break; | ||||
316 | case SQLITE_NULL: | ||||
317 | values[i + idx] = QVariant(QVariant::String); | ||||
318 | break; | ||||
319 | default: | ||||
320 | values[i + idx] = QString(reinterpret_cast<const QChar *>( | ||||
321 | sqlite3_column_text16(stmt, i)), | ||||
322 | sqlite3_column_bytes16(stmt, i) / sizeof(QChar)); | ||||
323 | break; | ||||
324 | } | ||||
325 | } | ||||
326 | return true; | ||||
327 | case SQLITE_DONE: | ||||
328 | if (rInf.isEmpty()) | ||||
329 | // must be first call. | ||||
330 | initColumns(true); | ||||
331 | q->setAt(QSql::AfterLastRow); | ||||
332 | sqlite3_reset(stmt); | ||||
333 | return false; | ||||
334 | case SQLITE_CONSTRAINT: | ||||
335 | case SQLITE_ERROR: | ||||
336 | // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() | ||||
337 | // to get the specific error message. | ||||
338 | res = sqlite3_reset(stmt); | ||||
339 | q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
340 | "Unable to fetch row"), QSqlError::ConnectionError, res)); | ||||
341 | q->setAt(QSql::AfterLastRow); | ||||
342 | return false; | ||||
343 | case SQLITE_MISUSE: | ||||
344 | case SQLITE_BUSY: | ||||
345 | default: | ||||
346 | // something wrong, don't get col info, but still return false | ||||
347 | q->setLastError(qMakeError(drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
348 | "Unable to fetch row"), QSqlError::ConnectionError, res)); | ||||
349 | sqlite3_reset(stmt); | ||||
350 | q->setAt(QSql::AfterLastRow); | ||||
351 | return false; | ||||
352 | } | ||||
353 | return false; | ||||
354 | } | ||||
355 | | ||||
356 | QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) | ||||
357 | : QSqlCachedResult(*new QSQLiteResultPrivate(this, db)) | ||||
358 | { | ||||
359 | Q_D(QSQLiteResult); | ||||
360 | const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.append(this); | ||||
361 | } | ||||
362 | | ||||
363 | QSQLiteResult::~QSQLiteResult() | ||||
364 | { | ||||
365 | Q_D(QSQLiteResult); | ||||
366 | if (d->drv_d_func()) | ||||
367 | const_cast<QSQLiteDriverPrivate*>(d->drv_d_func())->results.removeOne(this); | ||||
368 | d->cleanup(); | ||||
369 | } | ||||
370 | | ||||
371 | void QSQLiteResult::virtual_hook(int id, void *data) | ||||
372 | { | ||||
373 | QSqlCachedResult::virtual_hook(id, data); | ||||
374 | } | ||||
375 | | ||||
376 | bool QSQLiteResult::reset(const QString &query) | ||||
377 | { | ||||
378 | if (!prepare(query)) | ||||
379 | return false; | ||||
380 | return exec(); | ||||
381 | } | ||||
382 | | ||||
383 | bool QSQLiteResult::prepare(const QString &query) | ||||
384 | { | ||||
385 | Q_D(QSQLiteResult); | ||||
386 | if (!driver() || !driver()->isOpen() || driver()->isOpenError()) | ||||
387 | return false; | ||||
388 | | ||||
389 | d->cleanup(); | ||||
390 | | ||||
391 | setSelect(false); | ||||
392 | | ||||
393 | const void *pzTail = NULL; | ||||
394 | | ||||
395 | #if (SQLITE_VERSION_NUMBER >= 3003011) | ||||
396 | int res = sqlite3_prepare16_v2(d->drv_d_func()->access, query.constData(), (query.size() + 1) * sizeof(QChar), | ||||
397 | &d->stmt, &pzTail); | ||||
398 | #else | ||||
399 | int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), | ||||
400 | &d->stmt, &pzTail); | ||||
401 | #endif | ||||
402 | | ||||
403 | if (res != SQLITE_OK) { | ||||
404 | setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
405 | "Unable to execute statement"), QSqlError::StatementError, res)); | ||||
406 | d->finalize(); | ||||
407 | return false; | ||||
408 | } else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) { | ||||
409 | setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
410 | "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); | ||||
411 | d->finalize(); | ||||
412 | return false; | ||||
413 | } | ||||
414 | return true; | ||||
415 | } | ||||
416 | | ||||
417 | static QString secondsToOffset(int seconds) | ||||
418 | { | ||||
419 | const QChar sign = ushort(seconds < 0 ? '-' : '+'); | ||||
420 | seconds = qAbs(seconds); | ||||
421 | const int hours = seconds / 3600; | ||||
422 | const int minutes = (seconds % 3600) / 60; | ||||
423 | | ||||
424 | return QString(QStringLiteral("%1%2:%3")).arg(sign).arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')); | ||||
425 | } | ||||
426 | | ||||
427 | static QString timespecToString(const QDateTime &dateTime) | ||||
428 | { | ||||
429 | switch (dateTime.timeSpec()) { | ||||
430 | case Qt::LocalTime: | ||||
431 | return QString(); | ||||
432 | case Qt::UTC: | ||||
433 | return QStringLiteral("Z"); | ||||
434 | case Qt::OffsetFromUTC: | ||||
435 | return secondsToOffset(dateTime.offsetFromUtc()); | ||||
436 | #if QT_CONFIG(timezone) | ||||
437 | case Qt::TimeZone: | ||||
438 | return secondsToOffset(dateTime.timeZone().offsetFromUtc(dateTime)); | ||||
439 | #endif | ||||
440 | default: | ||||
441 | return QString(); | ||||
442 | } | ||||
443 | } | ||||
444 | | ||||
445 | bool QSQLiteResult::exec() | ||||
446 | { | ||||
447 | Q_D(QSQLiteResult); | ||||
448 | const QVector<QVariant> values = boundValues(); | ||||
449 | | ||||
450 | d->skippedStatus = false; | ||||
451 | d->skipRow = false; | ||||
452 | d->rInf.clear(); | ||||
453 | clearValues(); | ||||
454 | setLastError(QSqlError()); | ||||
455 | | ||||
456 | int res = sqlite3_reset(d->stmt); | ||||
457 | if (res != SQLITE_OK) { | ||||
458 | setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
459 | "Unable to reset statement"), QSqlError::StatementError, res)); | ||||
460 | d->finalize(); | ||||
461 | return false; | ||||
462 | } | ||||
463 | int paramCount = sqlite3_bind_parameter_count(d->stmt); | ||||
464 | if (paramCount == values.count()) { | ||||
465 | for (int i = 0; i < paramCount; ++i) { | ||||
466 | res = SQLITE_OK; | ||||
467 | const QVariant value = values.at(i); | ||||
468 | | ||||
469 | if (value.isNull()) { | ||||
470 | res = sqlite3_bind_null(d->stmt, i + 1); | ||||
471 | } else { | ||||
472 | switch (value.type()) { | ||||
473 | case QVariant::ByteArray: { | ||||
474 | const QByteArray *ba = static_cast<const QByteArray*>(value.constData()); | ||||
475 | res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(), | ||||
476 | ba->size(), SQLITE_STATIC); | ||||
477 | break; } | ||||
478 | case QVariant::Int: | ||||
479 | case QVariant::Bool: | ||||
480 | res = sqlite3_bind_int(d->stmt, i + 1, value.toInt()); | ||||
481 | break; | ||||
482 | case QVariant::Double: | ||||
483 | res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble()); | ||||
484 | break; | ||||
485 | case QVariant::UInt: | ||||
486 | case QVariant::LongLong: | ||||
487 | res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong()); | ||||
488 | break; | ||||
489 | case QVariant::DateTime: { | ||||
490 | const QDateTime dateTime = value.toDateTime(); | ||||
491 | const QString str = dateTime.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz") + timespecToString(dateTime)); | ||||
492 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), | ||||
493 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); | ||||
494 | break; | ||||
495 | } | ||||
496 | case QVariant::Time: { | ||||
497 | const QTime time = value.toTime(); | ||||
498 | const QString str = time.toString(QStringLiteral("hh:mm:ss.zzz")); | ||||
499 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), | ||||
500 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); | ||||
501 | break; | ||||
502 | } | ||||
503 | case QVariant::String: { | ||||
504 | // lifetime of string == lifetime of its qvariant | ||||
505 | const QString *str = static_cast<const QString*>(value.constData()); | ||||
506 | res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(), | ||||
507 | (str->size()) * sizeof(QChar), SQLITE_STATIC); | ||||
508 | break; } | ||||
509 | default: { | ||||
510 | QString str = value.toString(); | ||||
511 | // SQLITE_TRANSIENT makes sure that sqlite buffers the data | ||||
512 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), | ||||
513 | (str.size()) * sizeof(QChar), SQLITE_TRANSIENT); | ||||
514 | break; } | ||||
515 | } | ||||
516 | } | ||||
517 | if (res != SQLITE_OK) { | ||||
518 | setLastError(qMakeError(d->drv_d_func()->access, QCoreApplication::translate("QSQLiteResult", | ||||
519 | "Unable to bind parameters"), QSqlError::StatementError, res)); | ||||
520 | d->finalize(); | ||||
521 | return false; | ||||
522 | } | ||||
523 | } | ||||
524 | } else { | ||||
525 | setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", | ||||
526 | "Parameter count mismatch"), QString(), QSqlError::StatementError)); | ||||
527 | return false; | ||||
528 | } | ||||
529 | d->skippedStatus = d->fetchNext(d->firstRow, 0, true); | ||||
530 | if (lastError().isValid()) { | ||||
531 | setSelect(false); | ||||
532 | setActive(false); | ||||
533 | return false; | ||||
534 | } | ||||
535 | setSelect(!d->rInf.isEmpty()); | ||||
536 | setActive(true); | ||||
537 | return true; | ||||
538 | } | ||||
539 | | ||||
540 | bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) | ||||
541 | { | ||||
542 | Q_D(QSQLiteResult); | ||||
543 | return d->fetchNext(row, idx, false); | ||||
544 | } | ||||
545 | | ||||
546 | int QSQLiteResult::size() | ||||
547 | { | ||||
548 | return -1; | ||||
549 | } | ||||
550 | | ||||
551 | int QSQLiteResult::numRowsAffected() | ||||
552 | { | ||||
553 | Q_D(const QSQLiteResult); | ||||
554 | return sqlite3_changes(d->drv_d_func()->access); | ||||
555 | } | ||||
556 | | ||||
557 | QVariant QSQLiteResult::lastInsertId() const | ||||
558 | { | ||||
559 | Q_D(const QSQLiteResult); | ||||
560 | if (isActive()) { | ||||
561 | qint64 id = sqlite3_last_insert_rowid(d->drv_d_func()->access); | ||||
562 | if (id) | ||||
563 | return id; | ||||
564 | } | ||||
565 | return QVariant(); | ||||
566 | } | ||||
567 | | ||||
568 | QSqlRecord QSQLiteResult::record() const | ||||
569 | { | ||||
570 | Q_D(const QSQLiteResult); | ||||
571 | if (!isActive() || !isSelect()) | ||||
572 | return QSqlRecord(); | ||||
573 | return d->rInf; | ||||
574 | } | ||||
575 | | ||||
576 | void QSQLiteResult::detachFromResultSet() | ||||
577 | { | ||||
578 | Q_D(QSQLiteResult); | ||||
579 | if (d->stmt) | ||||
580 | sqlite3_reset(d->stmt); | ||||
581 | } | ||||
582 | | ||||
583 | QVariant QSQLiteResult::handle() const | ||||
584 | { | ||||
585 | Q_D(const QSQLiteResult); | ||||
586 | return QVariant::fromValue(d->stmt); | ||||
587 | } | ||||
588 | | ||||
589 | ///////////////////////////////////////////////////////// | ||||
590 | | ||||
591 | QSQLiteDriver::QSQLiteDriver(QObject * parent) | ||||
592 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) | ||||
593 | { | ||||
594 | } | ||||
595 | | ||||
596 | QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) | ||||
597 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) | ||||
598 | { | ||||
599 | Q_D(QSQLiteDriver); | ||||
600 | d->access = connection; | ||||
601 | setOpen(true); | ||||
602 | setOpenError(false); | ||||
603 | } | ||||
604 | | ||||
605 | | ||||
606 | QSQLiteDriver::~QSQLiteDriver() | ||||
607 | { | ||||
608 | close(); | ||||
609 | } | ||||
610 | | ||||
611 | bool QSQLiteDriver::hasFeature(DriverFeature f) const | ||||
612 | { | ||||
613 | switch (f) { | ||||
614 | case BLOB: | ||||
615 | case Transactions: | ||||
616 | case Unicode: | ||||
617 | case LastInsertId: | ||||
618 | case PreparedQueries: | ||||
619 | case PositionalPlaceholders: | ||||
620 | case SimpleLocking: | ||||
621 | case FinishQuery: | ||||
622 | case LowPrecisionNumbers: | ||||
623 | case EventNotifications: | ||||
624 | return true; | ||||
625 | case QuerySize: | ||||
626 | case NamedPlaceholders: | ||||
627 | case BatchOperations: | ||||
628 | case MultipleResultSets: | ||||
629 | case CancelQuery: | ||||
630 | return false; | ||||
631 | } | ||||
632 | return false; | ||||
633 | } | ||||
634 | | ||||
635 | /* | ||||
636 | SQLite dbs have no user name, passwords, hosts or ports. | ||||
637 | just file names. | ||||
638 | */ | ||||
639 | bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) | ||||
640 | { | ||||
641 | Q_D(QSQLiteDriver); | ||||
642 | if (isOpen()) | ||||
643 | close(); | ||||
644 | | ||||
645 | | ||||
646 | int timeOut = 5000; | ||||
647 | bool sharedCache = false; | ||||
648 | bool openReadOnlyOption = false; | ||||
649 | bool openUriOption = false; | ||||
650 | | ||||
651 | const auto opts = conOpts.splitRef(QLatin1Char(';')); | ||||
652 | for (auto option : opts) { | ||||
653 | option = option.trimmed(); | ||||
654 | if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT"))) { | ||||
655 | option = option.mid(20).trimmed(); | ||||
656 | if (option.startsWith(QLatin1Char('='))) { | ||||
657 | bool ok; | ||||
658 | const int nt = option.mid(1).trimmed().toInt(&ok); | ||||
659 | if (ok) | ||||
660 | timeOut = nt; | ||||
661 | } | ||||
662 | } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) { | ||||
663 | openReadOnlyOption = true; | ||||
664 | } else if (option == QLatin1String("QSQLITE_OPEN_URI")) { | ||||
665 | openUriOption = true; | ||||
666 | } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) { | ||||
667 | sharedCache = true; | ||||
668 | } | ||||
669 | } | ||||
670 | | ||||
671 | int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); | ||||
672 | if (openUriOption) | ||||
673 | openMode |= SQLITE_OPEN_URI; | ||||
674 | | ||||
675 | sqlite3_enable_shared_cache(sharedCache); | ||||
676 | | ||||
677 | if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) { | ||||
678 | sqlite3_busy_timeout(d->access, timeOut); | ||||
679 | setOpen(true); | ||||
680 | setOpenError(false); | ||||
681 | return true; | ||||
682 | } else { | ||||
683 | if (d->access) { | ||||
684 | sqlite3_close(d->access); | ||||
685 | d->access = 0; | ||||
686 | } | ||||
687 | | ||||
688 | setLastError(qMakeError(d->access, tr("Error opening database"), | ||||
689 | QSqlError::ConnectionError)); | ||||
690 | setOpenError(true); | ||||
691 | return false; | ||||
692 | } | ||||
693 | } | ||||
694 | | ||||
695 | void QSQLiteDriver::close() | ||||
696 | { | ||||
697 | Q_D(QSQLiteDriver); | ||||
698 | if (isOpen()) { | ||||
699 | for (QSQLiteResult *result : qAsConst(d->results)) | ||||
700 | result->d_func()->finalize(); | ||||
701 | | ||||
702 | if (d->access && (d->notificationid.count() > 0)) { | ||||
703 | d->notificationid.clear(); | ||||
704 | sqlite3_update_hook(d->access, NULL, NULL); | ||||
705 | } | ||||
706 | | ||||
707 | if (sqlite3_close(d->access) != SQLITE_OK) | ||||
708 | setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError)); | ||||
709 | d->access = 0; | ||||
710 | setOpen(false); | ||||
711 | setOpenError(false); | ||||
712 | } | ||||
713 | } | ||||
714 | | ||||
715 | QSqlResult *QSQLiteDriver::createResult() const | ||||
716 | { | ||||
717 | return new QSQLiteResult(this); | ||||
718 | } | ||||
719 | | ||||
720 | bool QSQLiteDriver::beginTransaction() | ||||
721 | { | ||||
722 | if (!isOpen() || isOpenError()) | ||||
723 | return false; | ||||
724 | | ||||
725 | QSqlQuery q(createResult()); | ||||
726 | if (!q.exec(QLatin1String("BEGIN"))) { | ||||
727 | setLastError(QSqlError(tr("Unable to begin transaction"), | ||||
728 | q.lastError().databaseText(), QSqlError::TransactionError)); | ||||
729 | return false; | ||||
730 | } | ||||
731 | | ||||
732 | return true; | ||||
733 | } | ||||
734 | | ||||
735 | bool QSQLiteDriver::commitTransaction() | ||||
736 | { | ||||
737 | if (!isOpen() || isOpenError()) | ||||
738 | return false; | ||||
739 | | ||||
740 | QSqlQuery q(createResult()); | ||||
741 | if (!q.exec(QLatin1String("COMMIT"))) { | ||||
742 | setLastError(QSqlError(tr("Unable to commit transaction"), | ||||
743 | q.lastError().databaseText(), QSqlError::TransactionError)); | ||||
744 | return false; | ||||
745 | } | ||||
746 | | ||||
747 | return true; | ||||
748 | } | ||||
749 | | ||||
750 | bool QSQLiteDriver::rollbackTransaction() | ||||
751 | { | ||||
752 | if (!isOpen() || isOpenError()) | ||||
753 | return false; | ||||
754 | | ||||
755 | QSqlQuery q(createResult()); | ||||
756 | if (!q.exec(QLatin1String("ROLLBACK"))) { | ||||
757 | setLastError(QSqlError(tr("Unable to rollback transaction"), | ||||
758 | q.lastError().databaseText(), QSqlError::TransactionError)); | ||||
759 | return false; | ||||
760 | } | ||||
761 | | ||||
762 | return true; | ||||
763 | } | ||||
764 | | ||||
765 | QStringList QSQLiteDriver::tables(QSql::TableType type) const | ||||
766 | { | ||||
767 | QStringList res; | ||||
768 | if (!isOpen()) | ||||
769 | return res; | ||||
770 | | ||||
771 | QSqlQuery q(createResult()); | ||||
772 | q.setForwardOnly(true); | ||||
773 | | ||||
774 | QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " | ||||
775 | "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); | ||||
776 | if ((type & QSql::Tables) && (type & QSql::Views)) | ||||
777 | sql = sql.arg(QLatin1String("type='table' OR type='view'")); | ||||
778 | else if (type & QSql::Tables) | ||||
779 | sql = sql.arg(QLatin1String("type='table'")); | ||||
780 | else if (type & QSql::Views) | ||||
781 | sql = sql.arg(QLatin1String("type='view'")); | ||||
782 | else | ||||
783 | sql.clear(); | ||||
784 | | ||||
785 | if (!sql.isEmpty() && q.exec(sql)) { | ||||
786 | while(q.next()) | ||||
787 | res.append(q.value(0).toString()); | ||||
788 | } | ||||
789 | | ||||
790 | if (type & QSql::SystemTables) { | ||||
791 | // there are no internal tables beside this one: | ||||
792 | res.append(QLatin1String("sqlite_master")); | ||||
793 | } | ||||
794 | | ||||
795 | return res; | ||||
796 | } | ||||
797 | | ||||
798 | static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) | ||||
799 | { | ||||
800 | QString schema; | ||||
801 | QString table(tableName); | ||||
802 | int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); | ||||
803 | if (indexOfSeparator > -1) { | ||||
804 | schema = tableName.left(indexOfSeparator).append(QLatin1Char('.')); | ||||
805 | table = tableName.mid(indexOfSeparator + 1); | ||||
806 | } | ||||
807 | q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1Char(')')); | ||||
808 | | ||||
809 | QSqlIndex ind; | ||||
810 | while (q.next()) { | ||||
811 | bool isPk = q.value(5).toInt(); | ||||
812 | if (onlyPIndex && !isPk) | ||||
813 | continue; | ||||
814 | QString typeName = q.value(2).toString().toLower(); | ||||
815 | QSqlField fld(q.value(1).toString(), qGetColumnType(typeName)); | ||||
816 | if (isPk && (typeName == QLatin1String("integer"))) | ||||
817 | // INTEGER PRIMARY KEY fields are auto-generated in sqlite | ||||
818 | // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! | ||||
819 | fld.setAutoValue(true); | ||||
820 | fld.setRequired(q.value(3).toInt() != 0); | ||||
821 | fld.setDefaultValue(q.value(4)); | ||||
822 | ind.append(fld); | ||||
823 | } | ||||
824 | return ind; | ||||
825 | } | ||||
826 | | ||||
827 | QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const | ||||
828 | { | ||||
829 | if (!isOpen()) | ||||
830 | return QSqlIndex(); | ||||
831 | | ||||
832 | QString table = tblname; | ||||
833 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) | ||||
834 | table = stripDelimiters(table, QSqlDriver::TableName); | ||||
835 | | ||||
836 | QSqlQuery q(createResult()); | ||||
837 | q.setForwardOnly(true); | ||||
838 | return qGetTableInfo(q, table, true); | ||||
839 | } | ||||
840 | | ||||
841 | QSqlRecord QSQLiteDriver::record(const QString &tbl) const | ||||
842 | { | ||||
843 | if (!isOpen()) | ||||
844 | return QSqlRecord(); | ||||
845 | | ||||
846 | QString table = tbl; | ||||
847 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) | ||||
848 | table = stripDelimiters(table, QSqlDriver::TableName); | ||||
849 | | ||||
850 | QSqlQuery q(createResult()); | ||||
851 | q.setForwardOnly(true); | ||||
852 | return qGetTableInfo(q, table); | ||||
853 | } | ||||
854 | | ||||
855 | QVariant QSQLiteDriver::handle() const | ||||
856 | { | ||||
857 | Q_D(const QSQLiteDriver); | ||||
858 | return QVariant::fromValue(d->access); | ||||
859 | } | ||||
860 | | ||||
861 | QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const | ||||
862 | { | ||||
863 | Q_UNUSED(type); | ||||
864 | return _q_escapeIdentifier(identifier); | ||||
865 | } | ||||
866 | | ||||
867 | static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename, | ||||
868 | sqlite3_int64 arowid) | ||||
869 | { | ||||
870 | Q_UNUSED(aoperation); | ||||
871 | Q_UNUSED(adbname); | ||||
872 | QSQLiteDriver *driver = static_cast<QSQLiteDriver *>(qobj); | ||||
873 | if (driver) { | ||||
874 | QMetaObject::invokeMethod(driver, "handleNotification", Qt::QueuedConnection, | ||||
875 | Q_ARG(QString, QString::fromUtf8(atablename)), Q_ARG(qint64, arowid)); | ||||
876 | } | ||||
877 | } | ||||
878 | | ||||
879 | bool QSQLiteDriver::subscribeToNotification(const QString &name) | ||||
880 | { | ||||
881 | Q_D(QSQLiteDriver); | ||||
882 | if (!isOpen()) { | ||||
883 | qWarning("Database not open."); | ||||
884 | return false; | ||||
885 | } | ||||
886 | | ||||
887 | if (d->notificationid.contains(name)) { | ||||
888 | qWarning("Already subscribing to '%s'.", qPrintable(name)); | ||||
889 | return false; | ||||
890 | } | ||||
891 | | ||||
892 | //sqlite supports only one notification callback, so only the first is registered | ||||
893 | d->notificationid << name; | ||||
894 | if (d->notificationid.count() == 1) | ||||
895 | sqlite3_update_hook(d->access, &handle_sqlite_callback, reinterpret_cast<void *> (this)); | ||||
896 | | ||||
897 | return true; | ||||
898 | } | ||||
899 | | ||||
900 | bool QSQLiteDriver::unsubscribeFromNotification(const QString &name) | ||||
901 | { | ||||
902 | Q_D(QSQLiteDriver); | ||||
903 | if (!isOpen()) { | ||||
904 | qWarning("Database not open."); | ||||
905 | return false; | ||||
906 | } | ||||
907 | | ||||
908 | if (!d->notificationid.contains(name)) { | ||||
909 | qWarning("Not subscribed to '%s'.", qPrintable(name)); | ||||
910 | return false; | ||||
911 | } | ||||
912 | | ||||
913 | d->notificationid.removeAll(name); | ||||
914 | if (d->notificationid.isEmpty()) | ||||
915 | sqlite3_update_hook(d->access, NULL, NULL); | ||||
916 | | ||||
917 | return true; | ||||
918 | } | ||||
919 | | ||||
920 | QStringList QSQLiteDriver::subscribedToNotifications() const | ||||
921 | { | ||||
922 | Q_D(const QSQLiteDriver); | ||||
923 | return d->notificationid; | ||||
924 | } | ||||
925 | | ||||
926 | void QSQLiteDriver::handleNotification(const QString &tableName, qint64 rowid) | ||||
927 | { | ||||
928 | Q_D(const QSQLiteDriver); | ||||
929 | if (d->notificationid.contains(tableName)) { | ||||
930 | emit notification(tableName); | ||||
931 | emit notification(tableName, QSqlDriver::UnknownSource, QVariant(rowid)); | ||||
932 | } | ||||
933 | } | ||||
934 | | ||||
935 | QT_END_NAMESPACE |