~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/sql/drivers/sqlite2/qsql_sqlite2.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the sql module of the Qt Toolkit.
 
6
**
 
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.
 
10
**
 
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.
 
15
**
 
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.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
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.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qsql_sqlite2.h"
 
30
 
 
31
#include <qcoreapplication.h>
 
32
#include <qvariant.h>
 
33
#include <qdatetime.h>
 
34
#include <qfile.h>
 
35
#include <qregexp.h>
 
36
#include <qsqlerror.h>
 
37
#include <qsqlfield.h>
 
38
#include <qsqlindex.h>
 
39
#include <qsqlquery.h>
 
40
#include <qstringlist.h>
 
41
#include <qvector.h>
 
42
 
 
43
#if !defined Q_WS_WIN32
 
44
# include <unistd.h>
 
45
#endif
 
46
#include <sqlite.h>
 
47
 
 
48
typedef struct sqlite_vm sqlite_vm;
 
49
 
 
50
Q_DECLARE_METATYPE(sqlite_vm*)
 
51
Q_DECLARE_METATYPE(sqlite*)
 
52
 
 
53
static QVariant::Type nameToType(const QString& typeName)
 
54
{
 
55
    QString tName = typeName.toUpper();
 
56
    if (tName.startsWith(QLatin1String("INT")))
 
57
        return QVariant::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;
 
64
}
 
65
 
 
66
class QSQLite2DriverPrivate
 
67
{
 
68
public:
 
69
    QSQLite2DriverPrivate();
 
70
    sqlite *access;
 
71
    bool utf8;
 
72
};
 
73
 
 
74
QSQLite2DriverPrivate::QSQLite2DriverPrivate() : access(0)
 
75
{
 
76
    utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
 
77
}
 
78
 
 
79
class QSQLite2ResultPrivate
 
80
{
 
81
public:
 
82
    QSQLite2ResultPrivate(QSQLite2Result *res);
 
83
    void cleanup();
 
84
    bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch);
 
85
    bool isSelect();
 
86
    // initializes the recordInfo and the cache
 
87
    void init(const char **cnames, int numCols);
 
88
    void finalize();
 
89
 
 
90
    QSQLite2Result* q;
 
91
    sqlite *access;
 
92
 
 
93
    // and we have too keep our own struct for the data (sqlite works via
 
94
    // callback.
 
95
    const char *currentTail;
 
96
    sqlite_vm *currentMachine;
 
97
 
 
98
    uint skippedStatus: 1; // the status of the fetchNext() that's skipped
 
99
    uint skipRow: 1; // skip the next fetchNext()?
 
100
    uint utf8: 1;
 
101
    QSqlRecord rInf;
 
102
};
 
103
 
 
104
static const uint initial_cache_size = 128;
 
105
 
 
106
QSQLite2ResultPrivate::QSQLite2ResultPrivate(QSQLite2Result* res) : q(res), access(0), currentTail(0),
 
107
    currentMachine(0), skippedStatus(false), skipRow(false), utf8(false)
 
108
{
 
109
}
 
110
 
 
111
void QSQLite2ResultPrivate::cleanup()
 
112
{
 
113
    finalize();
 
114
    rInf.clear();
 
115
    currentTail = 0;
 
116
    currentMachine = 0;
 
117
    skippedStatus = false;
 
118
    skipRow = false;
 
119
    q->setAt(QSql::BeforeFirstRow);
 
120
    q->setActive(false);
 
121
    q->cleanup();
 
122
}
 
123
 
 
124
void QSQLite2ResultPrivate::finalize()
 
125
{
 
126
    if (!currentMachine)
 
127
        return;
 
128
 
 
129
    char* err = 0;
 
130
    int res = sqlite_finalize(currentMachine, &err);
 
131
    if (err) {
 
132
        q->setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result",
 
133
                                  "Unable to fetch results"), QString::fromAscii(err),
 
134
                                  QSqlError::StatementError, res));
 
135
        sqlite_freemem(err);
 
136
    }
 
137
    currentMachine = 0;
 
138
}
 
139
 
 
140
// called on first fetch
 
141
void QSQLite2ResultPrivate::init(const char **cnames, int numCols)
 
142
{
 
143
    if (!cnames)
 
144
        return;
 
145
 
 
146
    rInf.clear();
 
147
    if (numCols <= 0)
 
148
        return;
 
149
    q->init(numCols);
 
150
 
 
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]))));
 
156
    }
 
157
}
 
158
 
 
159
bool QSQLite2ResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
 
160
{
 
161
    // may be caching.
 
162
    const char **fvals;
 
163
    const char **cnames;
 
164
    int colNum;
 
165
    int res;
 
166
    int i;
 
167
 
 
168
    if (skipRow) {
 
169
        // already fetched
 
170
        Q_ASSERT(!initialFetch);
 
171
        skipRow = false;
 
172
        return skippedStatus;
 
173
    }
 
174
    skipRow = initialFetch;
 
175
 
 
176
    if (!currentMachine)
 
177
        return false;
 
178
 
 
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
 
183
        Sleep(1000);
 
184
#else
 
185
        sleep(1);
 
186
#endif
 
187
    }
 
188
 
 
189
    switch(res) {
 
190
    case SQLITE_ROW:
 
191
        // check to see if should fill out columns
 
192
        if (rInf.isEmpty())
 
193
            // must be first call.
 
194
            init(cnames, colNum);
 
195
        if (!fvals)
 
196
            return false;
 
197
        if (idx < 0 && !initialFetch)
 
198
            return true;
 
199
        for (i = 0; i < colNum; ++i)
 
200
            values[i + idx] = utf8 ? QString::fromUtf8(fvals[i]) : QString::fromAscii(fvals[i]);
 
201
        return true;
 
202
    case SQLITE_DONE:
 
203
        if (rInf.isEmpty())
 
204
            // must be first call.
 
205
            init(cnames, colNum);
 
206
        q->setAt(QSql::AfterLastRow);
 
207
        return false;
 
208
    case SQLITE_ERROR:
 
209
    case SQLITE_MISUSE:
 
210
    default:
 
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);
 
214
        return false;
 
215
    }
 
216
    return false;
 
217
}
 
218
 
 
219
QSQLite2Result::QSQLite2Result(const QSQLite2Driver* db)
 
220
: QSqlCachedResult(db)
 
221
{
 
222
    d = new QSQLite2ResultPrivate(this);
 
223
    d->access = db->d->access;
 
224
    d->utf8 = db->d->utf8;
 
225
}
 
226
 
 
227
QSQLite2Result::~QSQLite2Result()
 
228
{
 
229
    d->cleanup();
 
230
    delete d;
 
231
}
 
232
 
 
233
/*
 
234
   Execute \a query.
 
235
*/
 
236
bool QSQLite2Result::reset (const QString& query)
 
237
{
 
238
    // this is where we build a query.
 
239
    if (!driver())
 
240
        return false;
 
241
    if (!driver()-> isOpen() || driver()->isOpenError())
 
242
        return false;
 
243
 
 
244
    d->cleanup();
 
245
 
 
246
    // Um, ok.  callback based so.... pass private static function for this.
 
247
    setSelect(false);
 
248
    char *err = 0;
 
249
    int res = sqlite_compile(d->access,
 
250
                                d->utf8 ? query.toUtf8().constData()
 
251
                                        : query.toAscii().constData(),
 
252
                                &(d->currentTail),
 
253
                                &(d->currentMachine),
 
254
                                &err);
 
255
    if (res != SQLITE_OK || err) {
 
256
        setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result",
 
257
                     "Unable to execute statement"), QString::fromAscii(err),
 
258
                     QSqlError::StatementError, res));
 
259
        sqlite_freemem(err);
 
260
    }
 
261
    //if (*d->currentTail != '\000' then there is more sql to eval
 
262
    if (!d->currentMachine) {
 
263
        setActive(false);
 
264
        return false;
 
265
    }
 
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());
 
270
    setActive(true);
 
271
    return true;
 
272
}
 
273
 
 
274
bool QSQLite2Result::gotoNext(QSqlCachedResult::ValueCache& row, int idx)
 
275
{
 
276
    return d->fetchNext(row, idx, false);
 
277
}
 
278
 
 
279
int QSQLite2Result::size()
 
280
{
 
281
    return -1;
 
282
}
 
283
 
 
284
int QSQLite2Result::numRowsAffected()
 
285
{
 
286
    return sqlite_changes(d->access);
 
287
}
 
288
 
 
289
QSqlRecord QSQLite2Result::record() const
 
290
{
 
291
    if (!isActive() || !isSelect())
 
292
        return QSqlRecord();
 
293
    return d->rInf;
 
294
}
 
295
 
 
296
QVariant QSQLite2Result::handle() const
 
297
{
 
298
    return qVariantFromValue(d->currentMachine);
 
299
}
 
300
 
 
301
/////////////////////////////////////////////////////////
 
302
 
 
303
QSQLite2Driver::QSQLite2Driver(QObject * parent)
 
304
    : QSqlDriver(parent)
 
305
{
 
306
    d = new QSQLite2DriverPrivate();
 
307
}
 
308
 
 
309
QSQLite2Driver::QSQLite2Driver(sqlite *connection, QObject *parent)
 
310
    : QSqlDriver(parent)
 
311
{
 
312
    d = new QSQLite2DriverPrivate();
 
313
    d->access = connection;
 
314
    setOpen(true);
 
315
    setOpenError(false);
 
316
}
 
317
 
 
318
 
 
319
QSQLite2Driver::~QSQLite2Driver()
 
320
{
 
321
    delete d;
 
322
}
 
323
 
 
324
bool QSQLite2Driver::hasFeature(DriverFeature f) const
 
325
{
 
326
    switch (f) {
 
327
    case Transactions:
 
328
        return true;
 
329
    case Unicode:
 
330
        return d->utf8;
 
331
//   case BLOB:
 
332
    default:
 
333
        return false;
 
334
    }
 
335
}
 
336
 
 
337
/*
 
338
   SQLite dbs have no user name, passwords, hosts or ports.
 
339
   just file names.
 
340
*/
 
341
bool QSQLite2Driver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
 
342
{
 
343
    if (isOpen())
 
344
        close();
 
345
 
 
346
    if (db.isEmpty())
 
347
        return false;
 
348
 
 
349
    char* err = 0;
 
350
    d->access = sqlite_open(QFile::encodeName(db), 0, &err);
 
351
    if (err) {
 
352
        setLastError(QSqlError(tr("Error to open database"), QString::fromAscii(err),
 
353
                     QSqlError::ConnectionError));
 
354
        sqlite_freemem(err);
 
355
        err = 0;
 
356
    }
 
357
 
 
358
    if (d->access) {
 
359
        setOpen(true);
 
360
        setOpenError(false);
 
361
        return true;
 
362
    }
 
363
    setOpenError(true);
 
364
    return false;
 
365
}
 
366
 
 
367
void QSQLite2Driver::close()
 
368
{
 
369
    if (isOpen()) {
 
370
        sqlite_close(d->access);
 
371
        d->access = 0;
 
372
        setOpen(false);
 
373
        setOpenError(false);
 
374
    }
 
375
}
 
376
 
 
377
QSqlResult *QSQLite2Driver::createResult() const
 
378
{
 
379
    return new QSQLite2Result(this);
 
380
}
 
381
 
 
382
bool QSQLite2Driver::beginTransaction()
 
383
{
 
384
    if (!isOpen() || isOpenError())
 
385
        return false;
 
386
 
 
387
    char* err;
 
388
    int res = sqlite_exec(d->access, "BEGIN", 0, this, &err);
 
389
 
 
390
    if (res == SQLITE_OK)
 
391
        return true;
 
392
 
 
393
    setLastError(QSqlError(tr("Unable to begin transaction"),
 
394
                           QString::fromAscii(err), QSqlError::TransactionError, res));
 
395
    sqlite_freemem(err);
 
396
    return false;
 
397
}
 
398
 
 
399
bool QSQLite2Driver::commitTransaction()
 
400
{
 
401
    if (!isOpen() || isOpenError())
 
402
        return false;
 
403
 
 
404
    char* err;
 
405
    int res = sqlite_exec(d->access, "COMMIT", 0, this, &err);
 
406
 
 
407
    if (res == SQLITE_OK)
 
408
        return true;
 
409
 
 
410
    setLastError(QSqlError(tr("Unable to commit transaction"),
 
411
                QString::fromAscii(err), QSqlError::TransactionError, res));
 
412
    sqlite_freemem(err);
 
413
    return false;
 
414
}
 
415
 
 
416
bool QSQLite2Driver::rollbackTransaction()
 
417
{
 
418
    if (!isOpen() || isOpenError())
 
419
        return false;
 
420
 
 
421
    char* err;
 
422
    int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err);
 
423
 
 
424
    if (res == SQLITE_OK)
 
425
        return true;
 
426
 
 
427
    setLastError(QSqlError(tr("Unable to rollback Transaction"),
 
428
                           QString::fromAscii(err), QSqlError::TransactionError, res));
 
429
    sqlite_freemem(err);
 
430
    return false;
 
431
}
 
432
 
 
433
QStringList QSQLite2Driver::tables(QSql::TableType type) const
 
434
{
 
435
    QStringList res;
 
436
    if (!isOpen())
 
437
        return res;
 
438
 
 
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'"));
 
447
 
 
448
    if (q.isActive()) {
 
449
        while(q.next())
 
450
            res.append(q.value(0).toString());
 
451
    }
 
452
 
 
453
    if (type & QSql::SystemTables) {
 
454
        // there are no internal tables beside this one:
 
455
        res.append(QLatin1String("sqlite_master"));
 
456
    }
 
457
 
 
458
    return res;
 
459
}
 
460
 
 
461
QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const
 
462
{
 
463
    QSqlRecord rec(record(tblname)); // expensive :(
 
464
 
 
465
    if (!isOpen())
 
466
        return QSqlIndex();
 
467
 
 
468
    QSqlQuery q(createResult());
 
469
    q.setForwardOnly(true);
 
470
    // finrst find a UNIQUE INDEX
 
471
    q.exec(QLatin1String("PRAGMA index_list('") + tblname + QLatin1String("');"));
 
472
    QString indexname;
 
473
    while(q.next()) {
 
474
        if (q.value(2).toInt()==1) {
 
475
            indexname = q.value(1).toString();
 
476
            break;
 
477
        }
 
478
    }
 
479
    if (indexname.isEmpty())
 
480
        return QSqlIndex();
 
481
 
 
482
    q.exec(QLatin1String("PRAGMA index_info('") + indexname + QLatin1String("');"));
 
483
 
 
484
    QSqlIndex index(tblname, indexname);
 
485
    while(q.next()) {
 
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));
 
491
    }
 
492
    return index;
 
493
}
 
494
 
 
495
QSqlRecord QSQLite2Driver::record(const QString &tbl) const
 
496
{
 
497
    if (!isOpen())
 
498
        return QSqlRecord();
 
499
 
 
500
    QSqlQuery q(createResult());
 
501
    q.setForwardOnly(true);
 
502
    q.exec(QLatin1String("SELECT * FROM ") + tbl + QLatin1String(" LIMIT 1"));
 
503
    return q.record();
 
504
}
 
505
 
 
506
QVariant QSQLite2Driver::handle() const
 
507
{
 
508
    return qVariantFromValue(d->access);
 
509
}
 
510