1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the sql module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qsql_sqlite2.h"
31
#include <qcoreapplication.h>
33
#include <qdatetime.h>
36
#include <qsqlerror.h>
37
#include <qsqlfield.h>
38
#include <qsqlindex.h>
39
#include <qsqlquery.h>
40
#include <qstringlist.h>
43
#if !defined Q_WS_WIN32
48
typedef struct sqlite_vm sqlite_vm;
50
Q_DECLARE_METATYPE(sqlite_vm*)
51
Q_DECLARE_METATYPE(sqlite*)
53
static QVariant::Type nameToType(const QString& typeName)
55
QString tName = typeName.toUpper();
56
if (tName.startsWith(QLatin1String("INT")))
58
if (tName.startsWith(QLatin1String("FLOAT")) || tName.startsWith(QLatin1String("NUMERIC")))
59
return QVariant::Double;
60
if (tName.startsWith(QLatin1String("BOOL")))
61
return QVariant::Bool;
62
// SQLite is typeless - consider everything else as string
63
return QVariant::String;
66
class QSQLite2DriverPrivate
69
QSQLite2DriverPrivate();
74
QSQLite2DriverPrivate::QSQLite2DriverPrivate() : access(0)
76
utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
79
class QSQLite2ResultPrivate
82
QSQLite2ResultPrivate(QSQLite2Result *res);
84
bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch);
86
// initializes the recordInfo and the cache
87
void init(const char **cnames, int numCols);
93
// and we have too keep our own struct for the data (sqlite works via
95
const char *currentTail;
96
sqlite_vm *currentMachine;
98
uint skippedStatus: 1; // the status of the fetchNext() that's skipped
99
uint skipRow: 1; // skip the next fetchNext()?
104
static const uint initial_cache_size = 128;
106
QSQLite2ResultPrivate::QSQLite2ResultPrivate(QSQLite2Result* res) : q(res), access(0), currentTail(0),
107
currentMachine(0), skippedStatus(false), skipRow(false), utf8(false)
111
void QSQLite2ResultPrivate::cleanup()
117
skippedStatus = false;
119
q->setAt(QSql::BeforeFirstRow);
124
void QSQLite2ResultPrivate::finalize()
130
int res = sqlite_finalize(currentMachine, &err);
132
q->setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result",
133
"Unable to fetch results"), QString::fromAscii(err),
134
QSqlError::StatementError, res));
140
// called on first fetch
141
void QSQLite2ResultPrivate::init(const char **cnames, int numCols)
151
for (int i = 0; i < numCols; ++i) {
152
const char* lastDot = strrchr(cnames[i], '.');
153
const char* fieldName = lastDot ? lastDot + 1 : cnames[i];
154
rInf.append(QSqlField(QString::fromAscii(fieldName),
155
nameToType(QString::fromAscii(cnames[i+numCols]))));
159
bool QSQLite2ResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
170
Q_ASSERT(!initialFetch);
172
return skippedStatus;
174
skipRow = initialFetch;
179
// keep trying while busy, wish I could implement this better.
180
while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == SQLITE_BUSY) {
181
// sleep instead requesting result again immidiately.
182
#if defined Q_WS_WIN32
191
// check to see if should fill out columns
193
// must be first call.
194
init(cnames, colNum);
197
if (idx < 0 && !initialFetch)
199
for (i = 0; i < colNum; ++i)
200
values[i + idx] = utf8 ? QString::fromUtf8(fvals[i]) : QString::fromAscii(fvals[i]);
204
// must be first call.
205
init(cnames, colNum);
206
q->setAt(QSql::AfterLastRow);
211
// something wrong, don't get col info, but still return false
212
finalize(); // finalize to get the error message.
213
q->setAt(QSql::AfterLastRow);
219
QSQLite2Result::QSQLite2Result(const QSQLite2Driver* db)
220
: QSqlCachedResult(db)
222
d = new QSQLite2ResultPrivate(this);
223
d->access = db->d->access;
224
d->utf8 = db->d->utf8;
227
QSQLite2Result::~QSQLite2Result()
236
bool QSQLite2Result::reset (const QString& query)
238
// this is where we build a query.
241
if (!driver()-> isOpen() || driver()->isOpenError())
246
// Um, ok. callback based so.... pass private static function for this.
249
int res = sqlite_compile(d->access,
250
d->utf8 ? query.toUtf8().constData()
251
: query.toAscii().constData(),
253
&(d->currentMachine),
255
if (res != SQLITE_OK || err) {
256
setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result",
257
"Unable to execute statement"), QString::fromAscii(err),
258
QSqlError::StatementError, res));
261
//if (*d->currentTail != '\000' then there is more sql to eval
262
if (!d->currentMachine) {
266
// we have to fetch one row to find out about
267
// the structure of the result set
268
d->skippedStatus = d->fetchNext(cache(), 0, true);
269
setSelect(!d->rInf.isEmpty());
274
bool QSQLite2Result::gotoNext(QSqlCachedResult::ValueCache& row, int idx)
276
return d->fetchNext(row, idx, false);
279
int QSQLite2Result::size()
284
int QSQLite2Result::numRowsAffected()
286
return sqlite_changes(d->access);
289
QSqlRecord QSQLite2Result::record() const
291
if (!isActive() || !isSelect())
296
QVariant QSQLite2Result::handle() const
298
return qVariantFromValue(d->currentMachine);
301
/////////////////////////////////////////////////////////
303
QSQLite2Driver::QSQLite2Driver(QObject * parent)
306
d = new QSQLite2DriverPrivate();
309
QSQLite2Driver::QSQLite2Driver(sqlite *connection, QObject *parent)
312
d = new QSQLite2DriverPrivate();
313
d->access = connection;
319
QSQLite2Driver::~QSQLite2Driver()
324
bool QSQLite2Driver::hasFeature(DriverFeature f) const
338
SQLite dbs have no user name, passwords, hosts or ports.
341
bool QSQLite2Driver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
350
d->access = sqlite_open(QFile::encodeName(db), 0, &err);
352
setLastError(QSqlError(tr("Error to open database"), QString::fromAscii(err),
353
QSqlError::ConnectionError));
367
void QSQLite2Driver::close()
370
sqlite_close(d->access);
377
QSqlResult *QSQLite2Driver::createResult() const
379
return new QSQLite2Result(this);
382
bool QSQLite2Driver::beginTransaction()
384
if (!isOpen() || isOpenError())
388
int res = sqlite_exec(d->access, "BEGIN", 0, this, &err);
390
if (res == SQLITE_OK)
393
setLastError(QSqlError(tr("Unable to begin transaction"),
394
QString::fromAscii(err), QSqlError::TransactionError, res));
399
bool QSQLite2Driver::commitTransaction()
401
if (!isOpen() || isOpenError())
405
int res = sqlite_exec(d->access, "COMMIT", 0, this, &err);
407
if (res == SQLITE_OK)
410
setLastError(QSqlError(tr("Unable to commit transaction"),
411
QString::fromAscii(err), QSqlError::TransactionError, res));
416
bool QSQLite2Driver::rollbackTransaction()
418
if (!isOpen() || isOpenError())
422
int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err);
424
if (res == SQLITE_OK)
427
setLastError(QSqlError(tr("Unable to rollback Transaction"),
428
QString::fromAscii(err), QSqlError::TransactionError, res));
433
QStringList QSQLite2Driver::tables(QSql::TableType type) const
439
QSqlQuery q(createResult());
440
q.setForwardOnly(true);
441
if ((type & QSql::Tables) && (type & QSql::Views))
442
q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"));
443
else if (type & QSql::Tables)
444
q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='table'"));
445
else if (type & QSql::Views)
446
q.exec(QLatin1String("SELECT name FROM sqlite_master WHERE type='view'"));
450
res.append(q.value(0).toString());
453
if (type & QSql::SystemTables) {
454
// there are no internal tables beside this one:
455
res.append(QLatin1String("sqlite_master"));
461
QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const
463
QSqlRecord rec(record(tblname)); // expensive :(
468
QSqlQuery q(createResult());
469
q.setForwardOnly(true);
470
// finrst find a UNIQUE INDEX
471
q.exec(QLatin1String("PRAGMA index_list('") + tblname + QLatin1String("');"));
474
if (q.value(2).toInt()==1) {
475
indexname = q.value(1).toString();
479
if (indexname.isEmpty())
482
q.exec(QLatin1String("PRAGMA index_info('") + indexname + QLatin1String("');"));
484
QSqlIndex index(tblname, indexname);
486
QString name = q.value(2).toString();
487
QVariant::Type type = QVariant::Invalid;
488
if (rec.contains(name))
489
type = rec.field(name).type();
490
index.append(QSqlField(name, type));
495
QSqlRecord QSQLite2Driver::record(const QString &tbl) const
500
QSqlQuery q(createResult());
501
q.setForwardOnly(true);
502
q.exec(QLatin1String("SELECT * FROM ") + tbl + QLatin1String(" LIMIT 1"));
506
QVariant QSQLite2Driver::handle() const
508
return qVariantFromValue(d->access);