source: trunk/src/sql/drivers/sqlite/qsql_sqlite.cpp

Last change on this file was 196, checked in by rudi, 14 years ago

Add SQL module (currently it isn't build by default, however it's needed for QtDesigner)

File size: 12.6 KB
Line 
1/****************************************************************************
2**
3** Implementation of SQLite driver classes.
4**
5** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
6**
7** This file is part of the sql module of the Qt GUI Toolkit.
8** EDITIONS: FREE, ENTERPRISE
9**
10** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12**
13****************************************************************************/
14
15#include "qsql_sqlite.h"
16
17#include <qdatetime.h>
18#include <qregexp.h>
19#include <qfile.h>
20
21#if (QT_VERSION-0 < 0x030000)
22# include <qvector.h>
23# if !defined Q_WS_WIN32
24# include <unistd.h>
25# endif
26# include "../../../3rdparty/libraries/sqlite/sqlite.h"
27#else
28# include <qptrvector.h>
29# if !defined Q_WS_WIN32
30# include <unistd.h>
31# endif
32# include <sqlite.h>
33#endif
34
35typedef struct sqlite_vm sqlite_vm;
36
37#define QSQLITE_DRIVER_NAME "QSQLITE"
38
39static QSqlVariant::Type nameToType(const QString& typeName)
40{
41 QString tName = typeName.upper();
42 if (tName.startsWith("INT"))
43 return QSqlVariant::Int;
44 if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC"))
45 return QSqlVariant::Double;
46 if (tName.startsWith("BOOL"))
47 return QSqlVariant::Bool;
48 // SQLite is typeless - consider everything else as string
49 return QSqlVariant::String;
50}
51
52class QSQLiteDriverPrivate
53{
54public:
55 QSQLiteDriverPrivate();
56 sqlite *access;
57 bool utf8;
58};
59
60QSQLiteDriverPrivate::QSQLiteDriverPrivate() : access(0)
61{
62 utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
63}
64
65class QSQLiteResultPrivate
66{
67public:
68 QSQLiteResultPrivate(QSQLiteResult *res);
69 void cleanup();
70 bool fetchNext(QtSqlCachedResult::RowCache *row);
71 bool isSelect();
72 // initializes the recordInfo and the cache
73 void init(const char **cnames, int numCols, QtSqlCachedResult::RowCache **row = 0);
74 void finalize();
75
76 QSQLiteResult* q;
77 sqlite *access;
78
79 // and we have too keep our own struct for the data (sqlite works via
80 // callback.
81 const char *currentTail;
82 sqlite_vm *currentMachine;
83
84 uint skippedStatus: 1; // the status of the fetchNext() that's skipped
85 QtSqlCachedResult::RowCache *skipRow;
86
87 uint utf8: 1;
88 QSqlRecordInfo rInf;
89};
90
91static const uint initial_cache_size = 128;
92
93QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0), currentTail(0),
94 currentMachine(0), skippedStatus(FALSE), skipRow(0), utf8(FALSE)
95{
96}
97
98void QSQLiteResultPrivate::cleanup()
99{
100 finalize();
101 rInf.clear();
102 currentTail = 0;
103 currentMachine = 0;
104 skippedStatus = FALSE;
105 delete skipRow;
106 skipRow = 0;
107 q->setAt(QSql::BeforeFirst);
108 q->setActive(FALSE);
109 q->cleanup();
110}
111
112void QSQLiteResultPrivate::finalize()
113{
114 if (!currentMachine)
115 return;
116
117 char* err = 0;
118 int res = sqlite_finalize(currentMachine, &err);
119 if (err) {
120 q->setLastError(QSqlError("Unable to fetch results", err, QSqlError::Statement, res));
121 sqlite_freemem(err);
122 }
123 currentMachine = 0;
124}
125
126// called on first fetch
127void QSQLiteResultPrivate::init(const char **cnames, int numCols, QtSqlCachedResult::RowCache **row)
128{
129 if (!cnames)
130 return;
131
132 rInf.clear();
133 if (numCols <= 0)
134 return;
135
136 for (int i = 0; i < numCols; ++i) {
137 const char* lastDot = strrchr(cnames[i], '.');
138 const char* fieldName = lastDot ? lastDot + 1 : cnames[i];
139 rInf.append(QSqlFieldInfo(fieldName, nameToType(cnames[i+numCols])));
140 }
141 // skip the first fetch
142 if (row && !*row) {
143 *row = new QtSqlCachedResult::RowCache(numCols);
144 skipRow = *row;
145 }
146}
147
148bool QSQLiteResultPrivate::fetchNext(QtSqlCachedResult::RowCache* row)
149{
150 // may be caching.
151 const char **fvals;
152 const char **cnames;
153 int colNum;
154 int res;
155 int i;
156
157 if (skipRow) {
158 // already fetched
159 if (row)
160 *row = *skipRow;
161 delete skipRow;
162 skipRow = 0;
163 return skippedStatus;
164 }
165
166 if (!currentMachine)
167 return FALSE;
168
169 // keep trying while busy, wish I could implement this better.
170 while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == SQLITE_BUSY) {
171 // sleep instead requesting result again immidiately.
172#if defined Q_WS_WIN32
173 Sleep(1000);
174#else
175 sleep(1);
176#endif
177 }
178
179 switch(res) {
180 case SQLITE_ROW:
181 // check to see if should fill out columns
182 if (rInf.isEmpty())
183 // must be first call.
184 init(cnames, colNum, &row);
185 if (!fvals)
186 return FALSE;
187 if (!row)
188 return TRUE;
189 for (i = 0; i < colNum; ++i)
190 (*row)[i] = utf8 ? QString::fromUtf8(fvals[i]) : QString(fvals[i]);
191 return TRUE;
192 case SQLITE_DONE:
193 if (rInf.isEmpty())
194 // must be first call.
195 init(cnames, colNum);
196 q->setAt(QSql::AfterLast);
197 return FALSE;
198 case SQLITE_ERROR:
199 case SQLITE_MISUSE:
200 default:
201 // something wrong, don't get col info, but still return false
202 finalize(); // finalize to get the error message.
203 q->setAt(QSql::AfterLast);
204 return FALSE;
205 }
206 return FALSE;
207}
208
209QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
210: QtSqlCachedResult(db)
211{
212 d = new QSQLiteResultPrivate(this);
213 d->access = db->d->access;
214 d->utf8 = db->d->utf8;
215}
216
217QSQLiteResult::~QSQLiteResult()
218{
219 d->cleanup();
220 delete d;
221}
222
223/*
224 Execute \a query.
225*/
226bool QSQLiteResult::reset (const QString& query)
227{
228 // this is where we build a query.
229 if (!driver())
230 return FALSE;
231 if (!driver()-> isOpen() || driver()->isOpenError())
232 return FALSE;
233
234 d->cleanup();
235
236 // Um, ok. callback based so.... pass private static function for this.
237 setSelect(FALSE);
238 char *err = 0;
239 int res = sqlite_compile(d->access,
240 d->utf8 ? (const char*)query.utf8().data() : query.ascii(),
241 &(d->currentTail),
242 &(d->currentMachine),
243 &err);
244 if (res != SQLITE_OK || err) {
245 setLastError(QSqlError("Unable to execute statement", err, QSqlError::Statement, res));
246 sqlite_freemem(err);
247 }
248 //if (*d->currentTail != '\000' then there is more sql to eval
249 if (!d->currentMachine) {
250 setActive(FALSE);
251 return FALSE;
252 }
253 // we have to fetch one row to find out about
254 // the structure of the result set
255 d->skippedStatus = d->fetchNext(0);
256 setSelect(!d->rInf.isEmpty());
257 if (isSelect())
258 init(d->rInf.count());
259 setActive(TRUE);
260 return TRUE;
261}
262
263bool QSQLiteResult::gotoNext(QtSqlCachedResult::RowCache* row)
264{
265 return d->fetchNext(row);
266}
267
268int QSQLiteResult::size()
269{
270 return -1;
271}
272
273int QSQLiteResult::numRowsAffected()
274{
275 return sqlite_changes(d->access);
276}
277
278/////////////////////////////////////////////////////////
279
280QSQLiteDriver::QSQLiteDriver(QObject * parent, const char * name)
281 : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
282{
283 d = new QSQLiteDriverPrivate();
284}
285
286QSQLiteDriver::QSQLiteDriver(sqlite *connection, QObject *parent, const char *name)
287 : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
288{
289 d = new QSQLiteDriverPrivate();
290 d->access = connection;
291 setOpen(TRUE);
292 setOpenError(FALSE);
293}
294
295
296QSQLiteDriver::~QSQLiteDriver()
297{
298 delete d;
299}
300
301bool QSQLiteDriver::hasFeature(DriverFeature f) const
302{
303 switch (f) {
304 case Transactions:
305 return TRUE;
306#if (QT_VERSION-0 >= 0x030000)
307 case Unicode:
308 return d->utf8;
309#endif
310// case BLOB:
311 default:
312 return FALSE;
313 }
314}
315
316/*
317 SQLite dbs have no user name, passwords, hosts or ports.
318 just file names.
319*/
320bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
321{
322 if (isOpen())
323 close();
324
325 if (db.isEmpty())
326 return FALSE;
327
328 char* err = 0;
329 d->access = sqlite_open(QFile::encodeName(db), 0, &err);
330 if (err) {
331 setLastError(QSqlError("Error to open database", err, QSqlError::Connection));
332 sqlite_freemem(err);
333 err = 0;
334 }
335
336 if (d->access) {
337 setOpen(TRUE);
338 setOpenError(FALSE);
339 return TRUE;
340 }
341 setOpenError(TRUE);
342 return FALSE;
343}
344
345void QSQLiteDriver::close()
346{
347 if (isOpen()) {
348 sqlite_close(d->access);
349 d->access = 0;
350 setOpen(FALSE);
351 setOpenError(FALSE);
352 }
353}
354
355QSqlQuery QSQLiteDriver::createQuery() const
356{
357 return QSqlQuery(new QSQLiteResult(this));
358}
359
360bool QSQLiteDriver::beginTransaction()
361{
362 if (!isOpen() || isOpenError())
363 return FALSE;
364
365 char* err;
366 int res = sqlite_exec(d->access, "BEGIN", 0, this, &err);
367
368 if (res == SQLITE_OK)
369 return TRUE;
370
371 setLastError(QSqlError("Unable to begin transaction", err, QSqlError::Transaction, res));
372 sqlite_freemem(err);
373 return FALSE;
374}
375
376bool QSQLiteDriver::commitTransaction()
377{
378 if (!isOpen() || isOpenError())
379 return FALSE;
380
381 char* err;
382 int res = sqlite_exec(d->access, "COMMIT", 0, this, &err);
383
384 if (res == SQLITE_OK)
385 return TRUE;
386
387 setLastError(QSqlError("Unable to commit transaction", err, QSqlError::Transaction, res));
388 sqlite_freemem(err);
389 return FALSE;
390}
391
392bool QSQLiteDriver::rollbackTransaction()
393{
394 if (!isOpen() || isOpenError())
395 return FALSE;
396
397 char* err;
398 int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err);
399
400 if (res == SQLITE_OK)
401 return TRUE;
402
403 setLastError(QSqlError("Unable to rollback Transaction", err, QSqlError::Transaction, res));
404 sqlite_freemem(err);
405 return FALSE;
406}
407
408QStringList QSQLiteDriver::tables(const QString &typeName) const
409{
410 QStringList res;
411 if (!isOpen())
412 return res;
413 int type = typeName.toInt();
414
415 QSqlQuery q = createQuery();
416 q.setForwardOnly(TRUE);
417#if (QT_VERSION-0 >= 0x030000)
418 if ((type & (int)QSql::Tables) && (type & (int)QSql::Views))
419 q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
420 else if (typeName.isEmpty() || (type & (int)QSql::Tables))
421 q.exec("SELECT name FROM sqlite_master WHERE type='table'");
422 else if (type & (int)QSql::Views)
423 q.exec("SELECT name FROM sqlite_master WHERE type='view'");
424#else
425 q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
426#endif
427
428
429 if (q.isActive()) {
430 while(q.next())
431 res.append(q.value(0).toString());
432 }
433
434#if (QT_VERSION-0 >= 0x030000)
435 if (type & (int)QSql::SystemTables) {
436 // there are no internal tables beside this one:
437 res.append("sqlite_master");
438 }
439#endif
440
441 return res;
442}
443
444QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
445{
446 QSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
447
448 if (!isOpen())
449 return QSqlIndex();
450
451 QSqlQuery q = createQuery();
452 q.setForwardOnly(TRUE);
453 // finrst find a UNIQUE INDEX
454 q.exec("PRAGMA index_list('" + tblname + "');");
455 QString indexname;
456 while(q.next()) {
457 if (q.value(2).toInt()==1) {
458 indexname = q.value(1).toString();
459 break;
460 }
461 }
462 if (indexname.isEmpty())
463 return QSqlIndex();
464
465 q.exec("PRAGMA index_info('" + indexname + "');");
466
467 QSqlIndex index(tblname, indexname);
468 while(q.next()) {
469 QString name = q.value(2).toString();
470 QSqlVariant::Type type = QSqlVariant::Invalid;
471 if (rec.contains(name))
472 type = rec.find(name).type();
473 index.append(QSqlField(name, type));
474 }
475 return index;
476}
477
478QSqlRecordInfo QSQLiteDriver::recordInfo(const QString &tbl) const
479{
480 if (!isOpen())
481 return QSqlRecordInfo();
482
483 QSqlQuery q = createQuery();
484 q.setForwardOnly(TRUE);
485 q.exec("SELECT * FROM " + tbl + " LIMIT 1");
486 return recordInfo(q);
487}
488
489QSqlRecord QSQLiteDriver::record(const QString &tblname) const
490{
491 if (!isOpen())
492 return QSqlRecord();
493
494 return recordInfo(tblname).toRecord();
495}
496
497QSqlRecord QSQLiteDriver::record(const QSqlQuery& query) const
498{
499 if (query.isActive() && query.driver() == this) {
500 QSQLiteResult* result = (QSQLiteResult*)query.result();
501 return result->d->rInf.toRecord();
502 }
503 return QSqlRecord();
504}
505
506QSqlRecordInfo QSQLiteDriver::recordInfo(const QSqlQuery& query) const
507{
508 if (query.isActive() && query.driver() == this) {
509 QSQLiteResult* result = (QSQLiteResult*)query.result();
510 return result->d->rInf;
511 }
512 return QSqlRecordInfo();
513}
Note: See TracBrowser for help on using the repository browser.