1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
4 | ** Contact: Nokia Corporation (qt-info@nokia.com) |
5 | ** |
6 | ** This file is part of the QtSql module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial Usage |
10 | ** Licensees holding valid Qt Commercial licenses may use this file in |
11 | ** accordance with the Qt Commercial License Agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Nokia. |
14 | ** |
15 | ** GNU Lesser General Public License Usage |
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
17 | ** General Public License version 2.1 as published by the Free Software |
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
19 | ** packaging of this file. Please review the following information to |
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
22 | ** |
23 | ** In addition, as a special exception, Nokia gives you certain additional |
24 | ** rights. These rights are described in the Nokia Qt LGPL Exception |
25 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
26 | ** |
27 | ** GNU General Public License Usage |
28 | ** Alternatively, this file may be used under the terms of the GNU |
29 | ** General Public License version 3.0 as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL included in the |
31 | ** packaging of this file. Please review the following information to |
32 | ** ensure the GNU General Public License version 3.0 requirements will be |
33 | ** met: http://www.gnu.org/copyleft/gpl.html. |
34 | ** |
35 | ** If you have questions regarding the use of this file, please contact |
36 | ** Nokia at qt-info@nokia.com. |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "qsql_sqlite.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 <qstringlist.h> |
51 | #include <qvector.h> |
52 | #include <qdebug.h> |
53 | |
54 | #ifdef QT5_BUILD |
55 | #include "QtSql/private/qsqldriver_p.h" |
56 | #include "QtSql/private/qsqlcachedresult_p.h" |
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 | |
67 | #include <qthread.h> |
68 | #include "sqlite_blocking.h" |
69 | |
70 | #ifdef QT5_BUILD |
71 | Q_DECLARE_OPAQUE_POINTER(sqlite3*) |
72 | Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) |
73 | #endif |
74 | |
75 | Q_DECLARE_METATYPE(sqlite3*) |
76 | Q_DECLARE_METATYPE(sqlite3_stmt*) |
77 | |
78 | QT_BEGIN_NAMESPACE |
79 | |
80 | static QString _q_escapeIdentifier(const QString &identifier) |
81 | { |
82 | QString res = identifier; |
83 | if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) { |
84 | res.replace(QLatin1Char('"'), QLatin1String("\"\"" )); |
85 | res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); |
86 | res.replace(QLatin1Char('.'), QLatin1String("\".\"" )); |
87 | } |
88 | return res; |
89 | } |
90 | |
91 | static QVariant::Type qGetColumnType(const QString &tpName) |
92 | { |
93 | const QString typeName = tpName.toLower(); |
94 | |
95 | if (typeName == QLatin1String("integer" ) |
96 | || typeName == QLatin1String("int" )) |
97 | return QVariant::Int; |
98 | if (typeName == QLatin1String("double" ) |
99 | || typeName == QLatin1String("float" ) |
100 | || typeName == QLatin1String("real" ) |
101 | || typeName.startsWith(QLatin1String("numeric" ))) |
102 | return QVariant::Double; |
103 | if (typeName == QLatin1String("blob" )) |
104 | return QVariant::ByteArray; |
105 | if (typeName == QLatin1String("boolean" ) |
106 | || typeName == QLatin1String("bool" )) |
107 | return QVariant::Bool; |
108 | return QVariant::String; |
109 | } |
110 | |
111 | static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, |
112 | int errorCode = -1) |
113 | { |
114 | return QSqlError(descr, |
115 | QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))), |
116 | type, errorCode); |
117 | } |
118 | |
119 | class QSQLiteResultPrivate; |
120 | |
121 | class QSQLiteResult : public QSqlCachedResult |
122 | { |
123 | friend class QSQLiteDriver; |
124 | friend class QSQLiteResultPrivate; |
125 | public: |
126 | explicit QSQLiteResult(const QSQLiteDriver* db); |
127 | ~QSQLiteResult(); |
128 | QVariant handle() const; |
129 | |
130 | protected: |
131 | bool gotoNext(QSqlCachedResult::ValueCache& row, int idx); |
132 | bool reset(const QString &query); |
133 | bool prepare(const QString &query); |
134 | bool exec(); |
135 | int size(); |
136 | int numRowsAffected(); |
137 | QVariant lastInsertId() const; |
138 | QSqlRecord record() const; |
139 | #ifdef QT5_BUILD |
140 | void detachFromResultSet(); |
141 | #endif |
142 | void virtual_hook(int id, void *data); |
143 | |
144 | private: |
145 | QSQLiteResultPrivate* d; |
146 | }; |
147 | |
148 | |
149 | #ifdef QT5_BUILD |
150 | class QSQLiteDriverPrivate : public QSqlDriverPrivate |
151 | #else |
152 | class QSQLiteDriverPrivate |
153 | #endif |
154 | { |
155 | public: |
156 | inline QSQLiteDriverPrivate() : access(0) { |
157 | #ifdef QT5_BUILD |
158 | dbmsType = SQLite; |
159 | #endif |
160 | } |
161 | sqlite3 *access; |
162 | QList<QSQLiteResult *> results; |
163 | }; |
164 | |
165 | |
166 | class QSQLiteResultPrivate |
167 | { |
168 | public: |
169 | QSQLiteResultPrivate(QSQLiteResult *res); |
170 | void cleanup(); |
171 | bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); |
172 | // initializes the recordInfo and the cache |
173 | void initColumns(bool emptyResultset); |
174 | void finalize(); |
175 | |
176 | QSQLiteResult* q; |
177 | sqlite3 *access; |
178 | |
179 | sqlite3_stmt *stmt; |
180 | |
181 | bool skippedStatus; // the status of the fetchNext() that's skipped |
182 | bool skipRow; // skip the next fetchNext()? |
183 | QSqlRecord rInf; |
184 | QVector<QVariant> firstRow; |
185 | }; |
186 | |
187 | QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0), |
188 | stmt(0), skippedStatus(false), skipRow(false) |
189 | { |
190 | } |
191 | |
192 | void QSQLiteResultPrivate::cleanup() |
193 | { |
194 | finalize(); |
195 | rInf.clear(); |
196 | skippedStatus = false; |
197 | skipRow = false; |
198 | q->setAt(QSql::BeforeFirstRow); |
199 | q->setActive(false); |
200 | q->cleanup(); |
201 | } |
202 | |
203 | void QSQLiteResultPrivate::finalize() |
204 | { |
205 | if (!stmt) |
206 | return; |
207 | |
208 | sqlite3_finalize(stmt); |
209 | stmt = 0; |
210 | } |
211 | |
212 | void QSQLiteResultPrivate::initColumns(bool emptyResultset) |
213 | { |
214 | int nCols = sqlite3_column_count(stmt); |
215 | if (nCols <= 0) |
216 | return; |
217 | |
218 | q->init(nCols); |
219 | |
220 | for (int i = 0; i < nCols; ++i) { |
221 | QString colName = QString::fromUtf16( |
222 | static_cast<const ushort *>(sqlite3_column_name16(stmt, i)) |
223 | ).remove(QLatin1Char('"')); |
224 | |
225 | // must use typeName for resolving the type to match QSqliteDriver::record |
226 | QString typeName = QString::fromUtf16( |
227 | static_cast<const ushort *>(sqlite3_column_decltype16(stmt, i))); |
228 | |
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 | |
258 | QSqlField fld(colName, fieldType); |
259 | fld.setSqlType(stp); |
260 | rInf.append(fld); |
261 | } |
262 | } |
263 | |
264 | bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) |
265 | { |
266 | int res; |
267 | int i; |
268 | |
269 | if (skipRow) { |
270 | // already fetched |
271 | Q_ASSERT(!initialFetch); |
272 | skipRow = false; |
273 | for(int i=0;i<firstRow.count();i++) |
274 | values[i]=firstRow[i]; |
275 | return skippedStatus; |
276 | } |
277 | skipRow = initialFetch; |
278 | |
279 | if(initialFetch) { |
280 | firstRow.clear(); |
281 | firstRow.resize(sqlite3_column_count(stmt)); |
282 | } |
283 | |
284 | if (!stmt) { |
285 | q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult" , "Unable to fetch row" ), |
286 | QCoreApplication::translate("QSQLiteResult" , "No query" ), QSqlError::ConnectionError)); |
287 | q->setAt(QSql::AfterLastRow); |
288 | return false; |
289 | } |
290 | res = sqlite3_blocking_step(stmt); |
291 | |
292 | switch(res) { |
293 | case SQLITE_ROW: |
294 | // check to see if should fill out columns |
295 | if (rInf.isEmpty()) |
296 | // must be first call. |
297 | initColumns(false); |
298 | if (idx < 0 && !initialFetch) |
299 | return true; |
300 | for (i = 0; i < rInf.count(); ++i) { |
301 | switch (sqlite3_column_type(stmt, i)) { |
302 | case SQLITE_BLOB: |
303 | values[i + idx] = QByteArray(static_cast<const char *>( |
304 | sqlite3_column_blob(stmt, i)), |
305 | sqlite3_column_bytes(stmt, i)); |
306 | break; |
307 | case SQLITE_INTEGER: |
308 | values[i + idx] = sqlite3_column_int64(stmt, i); |
309 | break; |
310 | case SQLITE_FLOAT: |
311 | switch(q->numericalPrecisionPolicy()) { |
312 | case QSql::LowPrecisionInt32: |
313 | values[i + idx] = sqlite3_column_int(stmt, i); |
314 | break; |
315 | case QSql::LowPrecisionInt64: |
316 | values[i + idx] = sqlite3_column_int64(stmt, i); |
317 | break; |
318 | case QSql::LowPrecisionDouble: |
319 | case QSql::HighPrecision: |
320 | default: |
321 | values[i + idx] = sqlite3_column_double(stmt, i); |
322 | break; |
323 | }; |
324 | break; |
325 | case SQLITE_NULL: |
326 | values[i + idx] = QVariant(QVariant::String); |
327 | break; |
328 | default: |
329 | values[i + idx] = QString(reinterpret_cast<const QChar *>( |
330 | sqlite3_column_text16(stmt, i)), |
331 | sqlite3_column_bytes16(stmt, i) / sizeof(QChar)); |
332 | break; |
333 | } |
334 | } |
335 | return true; |
336 | case SQLITE_DONE: |
337 | if (rInf.isEmpty()) |
338 | // must be first call. |
339 | initColumns(true); |
340 | q->setAt(QSql::AfterLastRow); |
341 | sqlite3_reset(stmt); |
342 | return false; |
343 | case SQLITE_CONSTRAINT: |
344 | case SQLITE_ERROR: |
345 | // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() |
346 | // to get the specific error message. |
347 | res = sqlite3_reset(stmt); |
348 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult" , |
349 | "Unable to fetch row" ), QSqlError::ConnectionError, res)); |
350 | q->setAt(QSql::AfterLastRow); |
351 | return false; |
352 | case SQLITE_MISUSE: |
353 | case SQLITE_BUSY: |
354 | default: |
355 | // something wrong, don't get col info, but still return false |
356 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult" , |
357 | "Unable to fetch row" ), QSqlError::ConnectionError, res)); |
358 | sqlite3_reset(stmt); |
359 | q->setAt(QSql::AfterLastRow); |
360 | return false; |
361 | } |
362 | return false; |
363 | } |
364 | |
365 | QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) |
366 | : QSqlCachedResult(db) |
367 | { |
368 | d = new QSQLiteResultPrivate(this); |
369 | d->access = db->d_func()->access; |
370 | const_cast<QSQLiteDriverPrivate*>(db->d_func())->results.append(this); |
371 | } |
372 | |
373 | QSQLiteResult::~QSQLiteResult() |
374 | { |
375 | const QSqlDriver *sqlDriver = driver(); |
376 | if (sqlDriver) |
377 | const_cast<QSQLiteDriverPrivate*>(qobject_cast<const QSQLiteDriver *>(sqlDriver)->d_func())->results.removeOne(this); |
378 | d->cleanup(); |
379 | delete d; |
380 | } |
381 | |
382 | void QSQLiteResult::virtual_hook(int id, void *data) |
383 | { |
384 | #ifdef QT5_BUILD |
385 | QSqlCachedResult::virtual_hook(id, data); |
386 | #else |
387 | switch (id) { |
388 | case QSqlResult::DetachFromResultSet: |
389 | if (d->stmt) |
390 | sqlite3_reset(d->stmt); |
391 | break; |
392 | default: |
393 | QSqlCachedResult::virtual_hook(id, data); |
394 | } |
395 | #endif |
396 | } |
397 | |
398 | bool QSQLiteResult::reset(const QString &query) |
399 | { |
400 | if (!prepare(query)) |
401 | return false; |
402 | return exec(); |
403 | } |
404 | |
405 | bool QSQLiteResult::prepare(const QString &query) |
406 | { |
407 | if (!driver() || !driver()->isOpen() || driver()->isOpenError()) |
408 | return false; |
409 | |
410 | d->cleanup(); |
411 | |
412 | setSelect(false); |
413 | |
414 | const void *pzTail = NULL; |
415 | |
416 | #if (SQLITE_VERSION_NUMBER >= 3003011) |
417 | // int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), |
418 | // &d->stmt, 0); |
419 | int res = sqlite3_blocking_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), |
420 | &d->stmt, &pzTail); |
421 | #else |
422 | int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), |
423 | &d->stmt, &pzTail); |
424 | #endif |
425 | |
426 | if (res != SQLITE_OK) { |
427 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult" , |
428 | "Unable to execute statement" ), QSqlError::StatementError, res)); |
429 | d->finalize(); |
430 | return false; |
431 | } else if (pzTail && !QString(reinterpret_cast<const QChar*>(pzTail)).trimmed().isEmpty()) { |
432 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult" , |
433 | "Unable to execute multiple statements at a time" ), QSqlError::StatementError, SQLITE_MISUSE)); |
434 | d->finalize(); |
435 | return false; |
436 | } |
437 | return true; |
438 | } |
439 | |
440 | bool QSQLiteResult::exec() |
441 | { |
442 | const QVector<QVariant> values = boundValues(); |
443 | |
444 | d->skippedStatus = false; |
445 | d->skipRow = false; |
446 | d->rInf.clear(); |
447 | clearValues(); |
448 | setLastError(QSqlError()); |
449 | |
450 | int res = sqlite3_reset(d->stmt); |
451 | if (res != SQLITE_OK) { |
452 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult" , |
453 | "Unable to reset statement" ), QSqlError::StatementError, res)); |
454 | d->finalize(); |
455 | return false; |
456 | } |
457 | int paramCount = sqlite3_bind_parameter_count(d->stmt); |
458 | if (paramCount == values.count()) { |
459 | for (int i = 0; i < paramCount; ++i) { |
460 | res = SQLITE_OK; |
461 | const QVariant value = values.at(i); |
462 | |
463 | if (value.isNull()) { |
464 | res = sqlite3_bind_null(d->stmt, i + 1); |
465 | } else { |
466 | switch (value.type()) { |
467 | case QVariant::ByteArray: { |
468 | const QByteArray *ba = static_cast<const QByteArray*>(value.constData()); |
469 | res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(), |
470 | ba->size(), SQLITE_STATIC); |
471 | break; } |
472 | case QVariant::Int: |
473 | case QVariant::Bool: |
474 | res = sqlite3_bind_int(d->stmt, i + 1, value.toInt()); |
475 | break; |
476 | case QVariant::Double: |
477 | res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble()); |
478 | break; |
479 | case QVariant::UInt: |
480 | case QVariant::LongLong: |
481 | res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong()); |
482 | break; |
483 | case QVariant::DateTime: { |
484 | const QDateTime dateTime = value.toDateTime(); |
485 | #ifdef QT5_BUILD |
486 | const QString str = dateTime.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz" )); |
487 | #else |
488 | const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz" )); |
489 | #endif |
490 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), |
491 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); |
492 | break; |
493 | } |
494 | case QVariant::Time: { |
495 | const QTime time = value.toTime(); |
496 | #ifdef QT5_BUILD |
497 | const QString str = time.toString(QStringLiteral("hh:mm:ss.zzz" )); |
498 | #else |
499 | const QString str = time.toString(QLatin1String("hh:mm:ss.zzz" )); |
500 | #endif |
501 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), |
502 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); |
503 | break; |
504 | } |
505 | case QVariant::String: { |
506 | // lifetime of string == lifetime of its qvariant |
507 | const QString *str = static_cast<const QString*>(value.constData()); |
508 | res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(), |
509 | (str->size()) * sizeof(QChar), SQLITE_STATIC); |
510 | break; } |
511 | default: { |
512 | QString str = value.toString(); |
513 | // SQLITE_TRANSIENT makes sure that sqlite buffers the data |
514 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), |
515 | (str.size()) * sizeof(QChar), SQLITE_TRANSIENT); |
516 | break; } |
517 | } |
518 | } |
519 | if (res != SQLITE_OK) { |
520 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult" , |
521 | "Unable to bind parameters" ), QSqlError::StatementError, res)); |
522 | d->finalize(); |
523 | return false; |
524 | } |
525 | } |
526 | } else { |
527 | setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult" , |
528 | "Parameter count mismatch" ), QString(), QSqlError::StatementError)); |
529 | return false; |
530 | } |
531 | d->skippedStatus = d->fetchNext(d->firstRow, 0, true); |
532 | if (lastError().isValid()) { |
533 | setSelect(false); |
534 | setActive(false); |
535 | return false; |
536 | } |
537 | setSelect(!d->rInf.isEmpty()); |
538 | setActive(true); |
539 | return true; |
540 | } |
541 | |
542 | bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) |
543 | { |
544 | return d->fetchNext(row, idx, false); |
545 | } |
546 | |
547 | int QSQLiteResult::size() |
548 | { |
549 | return -1; |
550 | } |
551 | |
552 | int QSQLiteResult::numRowsAffected() |
553 | { |
554 | return sqlite3_changes(d->access); |
555 | } |
556 | |
557 | QVariant QSQLiteResult::lastInsertId() const |
558 | { |
559 | if (isActive()) { |
560 | qint64 id = sqlite3_last_insert_rowid(d->access); |
561 | if (id) |
562 | return id; |
563 | } |
564 | return QVariant(); |
565 | } |
566 | |
567 | QSqlRecord QSQLiteResult::record() const |
568 | { |
569 | if (!isActive() || !isSelect()) |
570 | return QSqlRecord(); |
571 | return d->rInf; |
572 | } |
573 | |
574 | #ifdef QT5_BUILD |
575 | void QSQLiteResult::detachFromResultSet() |
576 | { |
577 | if (d->stmt) |
578 | sqlite3_reset(d->stmt); |
579 | } |
580 | #endif |
581 | |
582 | QVariant QSQLiteResult::handle() const |
583 | { |
584 | return qVariantFromValue(d->stmt); |
585 | } |
586 | |
587 | ///////////////////////////////////////////////////////// |
588 | |
589 | QSQLiteDriver::QSQLiteDriver(QObject * parent) |
590 | #ifdef QT5_BUILD |
591 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) |
592 | #else |
593 | : QSqlDriver(parent) |
594 | #endif |
595 | { |
596 | #ifndef QT5_BUILD |
597 | d_ptr = new QSQLiteDriverPrivate(); |
598 | #endif |
599 | } |
600 | |
601 | QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) |
602 | #ifdef QT5_BUILD |
603 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) |
604 | #else |
605 | : QSqlDriver(parent) |
606 | #endif |
607 | { |
608 | Q_D(QSQLiteDriver); |
609 | |
610 | #ifndef QT5_BUILD |
611 | d_ptr = new QSQLiteDriverPrivate(); |
612 | #endif |
613 | d->access = connection; |
614 | setOpen(true); |
615 | setOpenError(false); |
616 | } |
617 | |
618 | |
619 | QSQLiteDriver::~QSQLiteDriver() |
620 | { |
621 | #ifndef QT5_BUILD |
622 | delete d_ptr; |
623 | #endif |
624 | } |
625 | |
626 | bool QSQLiteDriver::hasFeature(DriverFeature f) const |
627 | { |
628 | switch (f) { |
629 | case BLOB: |
630 | case Transactions: |
631 | case Unicode: |
632 | case LastInsertId: |
633 | case PreparedQueries: |
634 | case PositionalPlaceholders: |
635 | case SimpleLocking: |
636 | case FinishQuery: |
637 | case LowPrecisionNumbers: |
638 | return true; |
639 | case QuerySize: |
640 | case NamedPlaceholders: |
641 | case BatchOperations: |
642 | case EventNotifications: |
643 | case MultipleResultSets: |
644 | #ifdef QT5_BUILD |
645 | case CancelQuery: |
646 | #endif |
647 | return false; |
648 | } |
649 | return false; |
650 | } |
651 | |
652 | /* |
653 | SQLite dbs have no user name, passwords, hosts or ports. |
654 | just file names. |
655 | */ |
656 | bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) |
657 | { |
658 | Q_D(QSQLiteDriver); |
659 | |
660 | if (isOpen()) |
661 | close(); |
662 | |
663 | int timeout = 5000; |
664 | bool sharedCache = false; |
665 | bool openReadOnlyOption = false; |
666 | bool openUriOption = false; |
667 | |
668 | const QStringList opts = QString(conOpts).remove(QLatin1Char(' ' )).split(QLatin1Char(';')); |
669 | Q_FOREACH (const QString &option, opts) { |
670 | if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT=" ))) { |
671 | bool ok; |
672 | #ifdef QT5_BUILD |
673 | const int nt = option.midRef(21).toInt(&ok); |
674 | #else |
675 | const int nt = option.mid(21).toInt(&ok); |
676 | #endif |
677 | if (ok) |
678 | timeout = nt; |
679 | } else if (option == QLatin1String("QSQLITE_OPEN_READONLY" )) { |
680 | openReadOnlyOption = true; |
681 | } else if (option == QLatin1String("QSQLITE_OPEN_URI" )) { |
682 | openUriOption = true; |
683 | } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE" )) { |
684 | sharedCache = true; |
685 | } |
686 | } |
687 | |
688 | int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); |
689 | if (openUriOption) |
690 | openMode |= SQLITE_OPEN_URI; |
691 | |
692 | sqlite3_enable_shared_cache(sharedCache); |
693 | |
694 | if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) { |
695 | sqlite3_busy_timeout(d->access, timeout); |
696 | sqlite3_extended_result_codes(d->access, 1); |
697 | setOpen(true); |
698 | setOpenError(false); |
699 | return true; |
700 | } else { |
701 | if (d->access) { |
702 | sqlite3_close(d->access); |
703 | d->access = 0; |
704 | } |
705 | |
706 | setLastError(qMakeError(d->access, tr("Error opening database" ), |
707 | QSqlError::ConnectionError)); |
708 | setOpenError(true); |
709 | return false; |
710 | } |
711 | } |
712 | |
713 | void QSQLiteDriver::close() |
714 | { |
715 | Q_D(QSQLiteDriver); |
716 | |
717 | if (isOpen()) { |
718 | Q_FOREACH (QSQLiteResult *result, d->results) { |
719 | result->d->finalize(); |
720 | } |
721 | |
722 | if (sqlite3_close(d->access) != SQLITE_OK) |
723 | setLastError(qMakeError(d->access, tr("Error closing database" ), |
724 | QSqlError::ConnectionError)); |
725 | d->access = 0; |
726 | setOpen(false); |
727 | setOpenError(false); |
728 | } |
729 | } |
730 | |
731 | QSqlResult *QSQLiteDriver::createResult() const |
732 | { |
733 | return new QSQLiteResult(this); |
734 | } |
735 | |
736 | bool QSQLiteDriver::beginTransaction() |
737 | { |
738 | if (!isOpen() || isOpenError()) |
739 | return false; |
740 | |
741 | QSqlQuery q(createResult()); |
742 | if (!q.exec(QLatin1String("BEGIN" ))) { |
743 | setLastError(QSqlError(tr("Unable to begin transaction" ), |
744 | q.lastError().databaseText(), QSqlError::TransactionError)); |
745 | return false; |
746 | } |
747 | |
748 | return true; |
749 | } |
750 | |
751 | bool QSQLiteDriver::commitTransaction() |
752 | { |
753 | if (!isOpen() || isOpenError()) |
754 | return false; |
755 | |
756 | QSqlQuery q(createResult()); |
757 | if (!q.exec(QLatin1String("COMMIT" ))) { |
758 | setLastError(QSqlError(tr("Unable to commit transaction" ), |
759 | q.lastError().databaseText(), QSqlError::TransactionError)); |
760 | return false; |
761 | } |
762 | |
763 | return true; |
764 | } |
765 | |
766 | bool QSQLiteDriver::rollbackTransaction() |
767 | { |
768 | if (!isOpen() || isOpenError()) |
769 | return false; |
770 | |
771 | QSqlQuery q(createResult()); |
772 | if (!q.exec(QLatin1String("ROLLBACK" ))) { |
773 | setLastError(QSqlError(tr("Unable to rollback transaction" ), |
774 | q.lastError().databaseText(), QSqlError::TransactionError)); |
775 | return false; |
776 | } |
777 | |
778 | return true; |
779 | } |
780 | |
781 | QStringList QSQLiteDriver::tables(QSql::TableType type) const |
782 | { |
783 | QStringList res; |
784 | if (!isOpen()) |
785 | return res; |
786 | |
787 | QSqlQuery q(createResult()); |
788 | q.setForwardOnly(true); |
789 | |
790 | QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " |
791 | "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1" ); |
792 | if ((type & QSql::Tables) && (type & QSql::Views)) |
793 | sql = sql.arg(QLatin1String("type='table' OR type='view'" )); |
794 | else if (type & QSql::Tables) |
795 | sql = sql.arg(QLatin1String("type='table'" )); |
796 | else if (type & QSql::Views) |
797 | sql = sql.arg(QLatin1String("type='view'" )); |
798 | else |
799 | sql.clear(); |
800 | |
801 | if (!sql.isEmpty() && q.exec(sql)) { |
802 | while(q.next()) |
803 | res.append(q.value(0).toString()); |
804 | } |
805 | |
806 | if (type & QSql::SystemTables) { |
807 | // there are no internal tables beside this one: |
808 | res.append(QLatin1String("sqlite_master" )); |
809 | } |
810 | |
811 | return res; |
812 | } |
813 | |
814 | static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) |
815 | { |
816 | QString schema; |
817 | QString table(tableName); |
818 | int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); |
819 | if (indexOfSeparator > -1) { |
820 | schema = tableName.left(indexOfSeparator).append(QLatin1Char('.')); |
821 | table = tableName.mid(indexOfSeparator + 1); |
822 | } |
823 | q.exec(QLatin1String("PRAGMA " ) + schema + QLatin1String("table_info (" ) + _q_escapeIdentifier(table) + QLatin1String(")" )); |
824 | |
825 | QSqlIndex ind; |
826 | while (q.next()) { |
827 | bool isPk = q.value(5).toInt(); |
828 | if (onlyPIndex && !isPk) |
829 | continue; |
830 | QString typeName = q.value(2).toString().toLower(); |
831 | QSqlField fld(q.value(1).toString(), qGetColumnType(typeName)); |
832 | if (isPk && (typeName == QLatin1String("integer" ))) |
833 | // INTEGER PRIMARY KEY fields are auto-generated in sqlite |
834 | // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! |
835 | fld.setAutoValue(true); |
836 | fld.setRequired(q.value(3).toInt() != 0); |
837 | fld.setDefaultValue(q.value(4)); |
838 | ind.append(fld); |
839 | } |
840 | return ind; |
841 | } |
842 | |
843 | QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const |
844 | { |
845 | if (!isOpen()) |
846 | return QSqlIndex(); |
847 | |
848 | QString table = tblname; |
849 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
850 | table = stripDelimiters(table, QSqlDriver::TableName); |
851 | |
852 | QSqlQuery q(createResult()); |
853 | q.setForwardOnly(true); |
854 | return qGetTableInfo(q, table, true); |
855 | } |
856 | |
857 | QSqlRecord QSQLiteDriver::record(const QString &tbl) const |
858 | { |
859 | if (!isOpen()) |
860 | return QSqlRecord(); |
861 | |
862 | QString table = tbl; |
863 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
864 | table = stripDelimiters(table, QSqlDriver::TableName); |
865 | |
866 | QSqlQuery q(createResult()); |
867 | q.setForwardOnly(true); |
868 | return qGetTableInfo(q, table); |
869 | } |
870 | |
871 | QVariant QSQLiteDriver::handle() const |
872 | { |
873 | Q_D(const QSQLiteDriver); |
874 | return QVariant::fromValue(d->access); |
875 | } |
876 | |
877 | QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const |
878 | { |
879 | Q_UNUSED(type); |
880 | return _q_escapeIdentifier(identifier); |
881 | } |
882 | |
883 | QT_END_NAMESPACE |
884 | |