diff options
Diffstat (limited to 'experimental/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp')
-rw-r--r-- | experimental/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/experimental/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp b/experimental/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp new file mode 100644 index 000000000..c94f595a7 --- /dev/null +++ b/experimental/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Implementation of STQLite driver classes. +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "tqsql_sqlite.h" + +#include <tqdatetime.h> +#include <tqregexp.h> +#include <tqfile.h> + +#if (TQT_VERSION-0 < 0x030000) +# include <tqvector.h> +# if !defined TQ_WS_WIN32 +# include <unistd.h> +# endif +# include "../../../3rdparty/libraries/sqlite/sqlite.h" +#else +# include <tqptrvector.h> +# if !defined TQ_WS_WIN32 +# include <unistd.h> +# endif +# include <sqlite.h> +#endif + +typedef struct sqlite_vm sqlite_vm; + +#define TQSQLITE_DRIVER_NAME "TQSQLITE" + +static TQSqlVariant::Type nameToType(const TQString& typeName) +{ + TQString tName = typeName.upper(); + if (tName.startsWith("INT")) + return TQSqlVariant::Int; + if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC")) + return TQSqlVariant::Double; + if (tName.startsWith("BOOL")) + return TQSqlVariant::Bool; + // STQLite is typeless - consider everything else as string + return TQSqlVariant::String; +} + +class TQSTQLiteDriverPrivate +{ +public: + TQSTQLiteDriverPrivate(); + sqlite *access; + bool utf8; +}; + +TQSTQLiteDriverPrivate::TQSTQLiteDriverPrivate() : access(0) +{ + utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0); +} + +class TQSTQLiteResultPrivate +{ +public: + TQSTQLiteResultPrivate(TQSTQLiteResult *res); + void cleanup(); + bool fetchNext(TQtSqlCachedResult::RowCache *row); + bool isSelect(); + // initializes the recordInfo and the cache + void init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row = 0); + void finalize(); + + TQSTQLiteResult* q; + sqlite *access; + + // and we have too keep our own struct for the data (sqlite works via + // callback. + const char *currentTail; + sqlite_vm *currentMachine; + + uint skippedtqStatus: 1; // the status of the fetchNext() that's skipped + TQtSqlCachedResult::RowCache *skipRow; + + uint utf8: 1; + TQSqlRecordInfo rInf; +}; + +static const uint initial_cache_size = 128; + +TQSTQLiteResultPrivate::TQSTQLiteResultPrivate(TQSTQLiteResult* res) : q(res), access(0), currentTail(0), + currentMachine(0), skippedtqStatus(FALSE), skipRow(0), utf8(FALSE) +{ +} + +void TQSTQLiteResultPrivate::cleanup() +{ + finalize(); + rInf.clear(); + currentTail = 0; + currentMachine = 0; + skippedtqStatus = FALSE; + delete skipRow; + skipRow = 0; + q->setAt(TQSql::BeforeFirst); + q->setActive(FALSE); + q->cleanup(); +} + +void TQSTQLiteResultPrivate::finalize() +{ + if (!currentMachine) + return; + + char* err = 0; + int res = sqlite_finalize(currentMachine, &err); + if (err) { + q->setLastError(TQSqlError("Unable to fetch results", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + currentMachine = 0; +} + +// called on first fetch +void TQSTQLiteResultPrivate::init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row) +{ + if (!cnames) + return; + + rInf.clear(); + if (numCols <= 0) + return; + + for (int i = 0; i < numCols; ++i) { + const char* lastDot = strrchr(cnames[i], '.'); + const char* fieldName = lastDot ? lastDot + 1 : cnames[i]; + rInf.append(TQSqlFieldInfo(fieldName, nameToType(cnames[i+numCols]))); + } + // skip the first fetch + if (row && !*row) { + *row = new TQtSqlCachedResult::RowCache(numCols); + skipRow = *row; + } +} + +bool TQSTQLiteResultPrivate::fetchNext(TQtSqlCachedResult::RowCache* row) +{ + // may be caching. + const char **fvals; + const char **cnames; + int colNum; + int res; + int i; + + if (skipRow) { + // already fetched + if (row) + *row = *skipRow; + delete skipRow; + skipRow = 0; + return skippedtqStatus; + } + + if (!currentMachine) + return FALSE; + + // keep trying while busy, wish I could implement this better. + while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == STQLITE_BUSY) { + // sleep instead requesting result again immidiately. +#if defined TQ_WS_WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + + switch(res) { + case STQLITE_ROW: + // check to see if should fill out columns + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum, &row); + if (!fvals) + return FALSE; + if (!row) + return TRUE; + for (i = 0; i < colNum; ++i) + (*row)[i] = utf8 ? TQString::fromUtf8(fvals[i]) : TQString(fvals[i]); + return TRUE; + case STQLITE_DONE: + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum); + q->setAt(TQSql::AfterLast); + return FALSE; + case STQLITE_ERROR: + case STQLITE_MISUSE: + default: + // something wrong, don't get col info, but still return false + finalize(); // finalize to get the error message. + q->setAt(TQSql::AfterLast); + return FALSE; + } + return FALSE; +} + +TQSTQLiteResult::TQSTQLiteResult(const TQSTQLiteDriver* db) +: TQtSqlCachedResult(db) +{ + d = new TQSTQLiteResultPrivate(this); + d->access = db->d->access; + d->utf8 = db->d->utf8; +} + +TQSTQLiteResult::~TQSTQLiteResult() +{ + d->cleanup(); + delete d; +} + +/* + Execute \a query. +*/ +bool TQSTQLiteResult::reset (const TQString& query) +{ + // this is where we build a query. + if (!driver()) + return FALSE; + if (!driver()-> isOpen() || driver()->isOpenError()) + return FALSE; + + d->cleanup(); + + // Um, ok. callback based so.... pass private static function for this. + setSelect(FALSE); + char *err = 0; + int res = sqlite_compile(d->access, + d->utf8 ? (const char*)query.utf8().data() : query.ascii(), + &(d->currentTail), + &(d->currentMachine), + &err); + if (res != STQLITE_OK || err) { + setLastError(TQSqlError("Unable to execute statement", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + //if (*d->currentTail != '\000' then there is more sql to eval + if (!d->currentMachine) { + setActive(FALSE); + return FALSE; + } + // we have to fetch one row to tqfind out about + // the structure of the result set + d->skippedtqStatus = d->fetchNext(0); + setSelect(!d->rInf.isEmpty()); + if (isSelect()) + init(d->rInf.count()); + setActive(TRUE); + return TRUE; +} + +bool TQSTQLiteResult::gotoNext(TQtSqlCachedResult::RowCache* row) +{ + return d->fetchNext(row); +} + +int TQSTQLiteResult::size() +{ + return -1; +} + +int TQSTQLiteResult::numRowsAffected() +{ + return sqlite_changes(d->access); +} + +///////////////////////////////////////////////////////// + +TQSTQLiteDriver::TQSTQLiteDriver(TQObject * tqparent, const char * name) + : TQSqlDriver(tqparent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSTQLiteDriverPrivate(); +} + +TQSTQLiteDriver::TQSTQLiteDriver(sqlite *connection, TQObject *tqparent, const char *name) + : TQSqlDriver(tqparent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSTQLiteDriverPrivate(); + d->access = connection; + setOpen(TRUE); + setOpenError(FALSE); +} + + +TQSTQLiteDriver::~TQSTQLiteDriver() +{ + delete d; +} + +bool TQSTQLiteDriver::hasFeature(DriverFeature f) const +{ + switch (f) { + case Transactions: + return TRUE; +#if (TQT_VERSION-0 >= 0x030000) + case Unicode: + return d->utf8; +#endif +// case BLOB: + default: + return FALSE; + } +} + +/* + STQLite dbs have no user name, passwords, hosts or ports. + just file names. +*/ +bool TQSTQLiteDriver::open(const TQString & db, const TQString &, const TQString &, const TQString &, int, const TQString &) +{ + if (isOpen()) + close(); + + if (db.isEmpty()) + return FALSE; + + char* err = 0; + d->access = sqlite_open(TQFile::encodeName(db), 0, &err); + if (err) { + setLastError(TQSqlError("Error to open database", err, TQSqlError::Connection)); + sqlite_freemem(err); + err = 0; + } + + if (d->access) { + setOpen(TRUE); + setOpenError(FALSE); + return TRUE; + } + setOpenError(TRUE); + return FALSE; +} + +void TQSTQLiteDriver::close() +{ + if (isOpen()) { + sqlite_close(d->access); + d->access = 0; + setOpen(FALSE); + setOpenError(FALSE); + } +} + +TQSqlQuery TQSTQLiteDriver::createQuery() const +{ + return TQSqlQuery(new TQSTQLiteResult(this)); +} + +bool TQSTQLiteDriver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "BEGIN", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to begin transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSTQLiteDriver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "COMMIT", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to commit transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSTQLiteDriver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to rollback Transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +TQStringList TQSTQLiteDriver::tables(const TQString &typeName) const +{ + TQStringList res; + if (!isOpen()) + return res; + int type = typeName.toInt(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); +#if (TQT_VERSION-0 >= 0x030000) + if ((type & (int)TQSql::Tables) && (type & (int)TQSql::Views)) + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); + else if (typeName.isEmpty() || (type & (int)TQSql::Tables)) + q.exec("SELECT name FROM sqlite_master WHERE type='table'"); + else if (type & (int)TQSql::Views) + q.exec("SELECT name FROM sqlite_master WHERE type='view'"); +#else + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); +#endif + + + if (q.isActive()) { + while(q.next()) + res.append(q.value(0).toString()); + } + +#if (TQT_VERSION-0 >= 0x030000) + if (type & (int)TQSql::SystemTables) { + // there are no internal tables beside this one: + res.append("sqlite_master"); + } +#endif + + return res; +} + +TQSqlIndex TQSTQLiteDriver::primaryIndex(const TQString &tblname) const +{ + TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :( + + if (!isOpen()) + return TQSqlIndex(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + // finrst tqfind a UNITQUE INDEX + q.exec("PRAGMA index_list('" + tblname + "');"); + TQString indexname; + while(q.next()) { + if (q.value(2).toInt()==1) { + indexname = q.value(1).toString(); + break; + } + } + if (indexname.isEmpty()) + return TQSqlIndex(); + + q.exec("PRAGMA index_info('" + indexname + "');"); + + TQSqlIndex index(tblname, indexname); + while(q.next()) { + TQString name = q.value(2).toString(); + TQSqlVariant::Type type = TQSqlVariant::Invalid; + if (rec.tqcontains(name)) + type = rec.tqfind(name).type(); + index.append(TQSqlField(name, type)); + } + return index; +} + +TQSqlRecordInfo TQSTQLiteDriver::recordInfo(const TQString &tbl) const +{ + if (!isOpen()) + return TQSqlRecordInfo(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + q.exec("SELECT * FROM " + tbl + " LIMIT 1"); + return recordInfo(q); +} + +TQSqlRecord TQSTQLiteDriver::record(const TQString &tblname) const +{ + if (!isOpen()) + return TQSqlRecord(); + + return recordInfo(tblname).toRecord(); +} + +TQSqlRecord TQSTQLiteDriver::record(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSTQLiteResult* result = (TQSTQLiteResult*)query.result(); + return result->d->rInf.toRecord(); + } + return TQSqlRecord(); +} + +TQSqlRecordInfo TQSTQLiteDriver::recordInfo(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSTQLiteResult* result = (TQSTQLiteResult*)query.result(); + return result->d->rInf; + } + return TQSqlRecordInfo(); +} |