diff --git a/plugins/src/sqldrivers/sqldrivers.pro b/plugins/src/sqldrivers/sqldrivers.pro index 616b23fc..0f2e3223 100644 --- a/plugins/src/sqldrivers/sqldrivers.pro +++ b/plugins/src/sqldrivers/sqldrivers.pro @@ -7,4 +7,5 @@ contains(sql-plugins, tds) : SUBDIRS += tds contains(sql-plugins, oci) : SUBDIRS += oci contains(sql-plugins, db2) : SUBDIRS += db2 contains(sql-plugins, sqlite) : SUBDIRS += sqlite +contains(sql-plugins, sqlite) : SUBDIRS += sqlite3 contains(sql-plugins, ibase) : SUBDIRS += ibase diff --git a/plugins/src/sqldrivers/sqlite3/smain.cpp b/plugins/src/sqldrivers/sqlite3/smain.cpp new file mode 100644 index 00000000..b3da4943 --- /dev/null +++ b/plugins/src/sqldrivers/sqlite3/smain.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Implementation of SQLite driver plugin +** +** Created : 031106 +** +** Copyright (C) 1992-2003 Trolltech AS. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.TQPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid TQt Enterprise Edition licenses may use this +** file in accordance with the TQt Commercial License Agreement provided +** with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about TQt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for TQPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include +#include "../../../../src/sql/drivers/sqlite3/qsql_sqlite3.h" + +class TQSQLite3DriverPlugin : public TQSqlDriverPlugin +{ +public: + TQSQLite3DriverPlugin(); + + TQSqlDriver* create( const TQString & ); + TQStringList keys() const; +}; + +TQSQLite3DriverPlugin::TQSQLite3DriverPlugin() + : TQSqlDriverPlugin() +{ +} + +TQSqlDriver* TQSQLite3DriverPlugin::create( const TQString &name ) +{ + if ( name == "TQSQLITE3" ) { + TQSQLite3Driver* driver = new TQSQLite3Driver(); + return driver; + } + return 0; +} + +TQStringList TQSQLite3DriverPlugin::keys() const +{ + TQStringList l; + l << "TQSQLITE3"; + return l; +} + +Q_EXPORT_PLUGIN( TQSQLite3DriverPlugin ) diff --git a/plugins/src/sqldrivers/sqlite3/sqlite3.pro b/plugins/src/sqldrivers/sqlite3/sqlite3.pro new file mode 100644 index 00000000..c3184f93 --- /dev/null +++ b/plugins/src/sqldrivers/sqlite3/sqlite3.pro @@ -0,0 +1,33 @@ +TEMPLATE = lib +TARGET = qsqlite3 + +CONFIG += qt plugin +DESTDIR = ../../../sqldrivers + +HEADERS = ../../../../src/sql/drivers/sqlite3/qsql_sqlite3.h +SOURCES = smain.cpp \ + ../../../../src/sql/drivers/sqlite3/qsql_sqlite3.cpp + +unix { + OBJECTS_DIR = .obj + !contains( LIBS, .*sqlite3.* ) { + LIBS *= -lsqlite3 + } +} + +win32 { + OBJECTS_DIR = obj + LIBS *= sqlite3.lib +# win32-msvc: { +# LIBS *= delayimp.lib +# QMAKE_LFLAGS += /DELAYLOAD:sqlite3.dll +# } +# win32-borland: { +# QMAKE_LFLAGS += /dsqlite3.dll +# } +} + +REQUIRES = sql + +target.path += $$plugins.path/sqldrivers +INSTALLS += target diff --git a/src/sql/drivers/sqlite3/qsql_sqlite3.cpp b/src/sql/drivers/sqlite3/qsql_sqlite3.cpp new file mode 100644 index 00000000..a756c7da --- /dev/null +++ b/src/sql/drivers/sqlite3/qsql_sqlite3.cpp @@ -0,0 +1,494 @@ +/**************************************************************************** +** +** Implementation of SQLite driver classes. +** +** Copyright (C) 1992-2003 Trolltech AS. All rights reserved. +** +** 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 "qsql_sqlite3.h" + +#include +#include +#include +#include +#include + +#if (TQT_VERSION-0 < 0x030200) +# include +# if !defined Q_WS_WIN32 +# include +# endif +#else +# include +# if !defined Q_WS_WIN32 +# include +# endif +#endif + +typedef struct sqlite3_stmt sqlite3_stmt; + +#define TQSQLITE3_DRIVER_NAME "TQSQLITE3" + +static TQVariant::Type qSqliteType(int tp) +{ + switch (tp) { + case SQLITE_INTEGER: + return TQVariant::Int; + case SQLITE_FLOAT: + return TQVariant::Double; + case SQLITE_BLOB: + return TQVariant::ByteArray; + case SQLITE_TEXT: + default: + return TQVariant::String; + } +} + +static TQSqlError qMakeError(sqlite3 *access, const TQString &descr, TQSqlError::Type type, + int errorCode = -1) +{ + return TQSqlError(descr, + TQString::fromUtf8(sqlite3_errmsg(access)), + type, errorCode); +} + +class TQSQLite3DriverPrivate +{ +public: + TQSQLite3DriverPrivate(); + sqlite3 *access; + bool utf8; +}; + +TQSQLite3DriverPrivate::TQSQLite3DriverPrivate() : access(0) +{ + utf8 = true; +} + +class TQSQLite3ResultPrivate +{ +public: + TQSQLite3ResultPrivate(TQSQLite3Result *res); + void cleanup(); + bool fetchNext(TQtSqlCachedResult::RowCache *row); + bool isSelect(); + // initializes the recordInfo and the cache + void initColumns(TQtSqlCachedResult::RowCache **row = 0); + void finalize(); + + TQSQLite3Result* q; + sqlite3 *access; + + sqlite3_stmt *stmt; + + uint skippedStatus: 1; // the status of the fetchNext() that's skipped + TQtSqlCachedResult::RowCache *skipRow; + + uint utf8: 1; + TQSqlRecord rInf; +}; + +static const uint initial_cache_size = 128; + +TQSQLite3ResultPrivate::TQSQLite3ResultPrivate(TQSQLite3Result* res) : q(res), access(0), + stmt(0), skippedStatus(false), skipRow(0), utf8(false) +{ +} + +void TQSQLite3ResultPrivate::cleanup() +{ + finalize(); + rInf.clear(); + skippedStatus = false; + delete skipRow; + skipRow = 0; + q->setAt(TQSql::BeforeFirst); + q->setActive(false); + q->cleanup(); +} + +void TQSQLite3ResultPrivate::finalize() +{ + if (!stmt) + return; + + sqlite3_finalize(stmt); + stmt = 0; +} + +// called on first fetch +void TQSQLite3ResultPrivate::initColumns(TQtSqlCachedResult::RowCache** row) +{ + rInf.clear(); + + int nCols = sqlite3_column_count(stmt); + if (nCols <= 0) + return; + + q->init(nCols); + + for (int i = 0; i < nCols; ++i) { + TQString colName = TQString::fromUtf8(sqlite3_column_name(stmt, i)); + + int dotIdx = colName.findRev('.'); + rInf.append(TQSqlField(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), + qSqliteType(sqlite3_column_type(stmt, i)))); + } + + // skip the first fetch + if (row && !*row) { + *row = new TQtSqlCachedResult::RowCache(nCols); + skipRow = *row; + } +} + +bool TQSQLite3ResultPrivate::fetchNext(TQtSqlCachedResult::RowCache* row) +{ + int res; + unsigned int i; + + if (skipRow) { + // already fetched + if (row) + *row = *skipRow; + delete skipRow; + skipRow = 0; + return skippedStatus; + } + + if (!stmt) + return false; + + // keep trying while busy, wish I could implement this better. + while ((res = sqlite3_step(stmt)) == SQLITE_BUSY) { + // sleep instead requesting result again immidiately. +#if defined Q_OS_WIN + Sleep(1000); +#else + sleep(1); +#endif + } + + switch(res) { + case SQLITE_ROW: + // check to see if should fill out columns + if (rInf.isEmpty()) + // must be first call. + initColumns(&row); + if (!row) + return true; + for (i = 0; i < rInf.count(); ++i) + // todo - handle other types + (*row)[i] = TQString::fromUtf8((char *)(sqlite3_column_text(stmt, i))); + // values[i + idx] = utf8 ? TQString::fromUtf8(fvals[i]) : TQString::fromAscii(fvals[i]); + return true; + case SQLITE_DONE: + if (rInf.isEmpty()) + // must be first call. + initColumns(&row); + q->setAt(TQSql::AfterLast); + return false; + case SQLITE_ERROR: + case SQLITE_MISUSE: + default: + // something wrong, don't get col info, but still return false + q->setLastError(qMakeError(access, "Unable to fetch row", TQSqlError::Connection, res)); + finalize(); + q->setAt(TQSql::AfterLast); + return false; + } + return false; +} + +TQSQLite3Result::TQSQLite3Result(const TQSQLite3Driver* db) + : TQtSqlCachedResult(db) +{ + d = new TQSQLite3ResultPrivate(this); + d->access = db->d->access; +} + +TQSQLite3Result::~TQSQLite3Result() +{ + d->cleanup(); + delete d; +} + +/* + Execute \a query. +*/ +bool TQSQLite3Result::reset (const TQString &query) +{ + // this is where we build a query. + if (!driver() || !driver()->isOpen() || driver()->isOpenError()) + return false; + + d->cleanup(); + + setSelect(false); + + int res = sqlite3_prepare(d->access, query.utf8().data(), (query.length() + 1) * sizeof(TQChar), + &d->stmt, 0); + + if (res != SQLITE_OK) { + setLastError(qMakeError(d->access, "Unable to execute statement", TQSqlError::Statement, res)); + d->finalize(); + return false; + } + + d->skippedStatus = d->fetchNext(0); + + setSelect(!d->rInf.isEmpty()); + setActive(true); + return true; +} + +bool TQSQLite3Result::gotoNext(TQtSqlCachedResult::RowCache* row) +{ + return d->fetchNext(row); +} + +int TQSQLite3Result::size() +{ + return -1; +} + +int TQSQLite3Result::numRowsAffected() +{ + return sqlite3_changes(d->access); +} + +///////////////////////////////////////////////////////// + +TQSQLite3Driver::TQSQLite3Driver(TQObject * parent, const char *name) + : TQSqlDriver(parent, name) +{ + d = new TQSQLite3DriverPrivate(); +} + +TQSQLite3Driver::TQSQLite3Driver(sqlite3 *connection, TQObject *parent, const char *name) + : TQSqlDriver(parent, name) +{ + d = new TQSQLite3DriverPrivate(); + d->access = connection; + setOpen(true); + setOpenError(false); +} + + +TQSQLite3Driver::~TQSQLite3Driver() +{ + delete d; +} + +bool TQSQLite3Driver::hasFeature(DriverFeature f) const +{ + switch (f) { + case Transactions: + case Unicode: + case BLOB: + return true; + default: + break; + } + return false; +} + +/* + SQLite dbs have no user name, passwords, hosts or ports. + just file names. +*/ +bool TQSQLite3Driver::open(const TQString & db, const TQString &, const TQString &, const TQString &, int, const TQString &) +{ + if (isOpen()) + close(); + + if (db.isEmpty()) + return false; + + if (sqlite3_open(TQFile::encodeName(db), &d->access) == SQLITE_OK) { + setOpen(true); + setOpenError(false); + return true; + } else { + setLastError(qMakeError(d->access, "Error opening database", + TQSqlError::Connection)); + setOpenError(true); + return false; + } +} + +void TQSQLite3Driver::close() +{ + if (isOpen()) { + if (sqlite3_close(d->access) != SQLITE_OK) + setLastError(qMakeError(d->access, "Error closing database", + TQSqlError::Connection)); + d->access = 0; + setOpen(false); + setOpenError(false); + } +} + +TQSqlQuery TQSQLite3Driver::createQuery() const +{ + return TQSqlQuery(new TQSQLite3Result(this)); +} + +bool TQSQLite3Driver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TQSqlQuery q(createQuery()); + if (!q.exec("BEGIN")) { + setLastError(TQSqlError("Unable to begin transaction", + q.lastError().databaseText(), TQSqlError::Transaction)); + return false; + } + + return true; +} + +bool TQSQLite3Driver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TQSqlQuery q(createQuery()); + if (!q.exec("COMMIT")) { + setLastError(TQSqlError("Unable to begin transaction", + q.lastError().databaseText(), TQSqlError::Transaction)); + return false; + } + + return true; +} + +bool TQSQLite3Driver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TQSqlQuery q(createQuery()); + if (!q.exec("ROLLBACK")) { + setLastError(TQSqlError("Unable to begin transaction", + q.lastError().databaseText(), TQSqlError::Transaction)); + return false; + } + + return true; +} + +TQStringList TQSQLite3Driver::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 >= 0x030200) + 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 >= 0x030200) + if (type & (int)TQSql::SystemTables) { + // there are no internal tables beside this one: + res.append("sqlite_master"); + } +#endif + + return res; +} + +TQSqlIndex TQSQLite3Driver::primaryIndex(const TQString &tblname) const +{ + TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :( + + if (!isOpen()) + return TQSqlIndex(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + // finrst find a UNIQUE 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(indexname); + while(q.next()) { + TQString name = q.value(2).toString(); + TQSqlVariant::Type type = TQSqlVariant::Invalid; + if (rec.contains(name)) + type = rec.find(name).type(); + index.append(TQSqlField(name, type)); + } + return index; +} + +TQSqlRecordInfo TQSQLite3Driver::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 TQSQLite3Driver::record(const TQString &tblname) const +{ + if (!isOpen()) + return TQSqlRecord(); + + return recordInfo(tblname).toRecord(); +} + +TQSqlRecord TQSQLite3Driver::record(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSQLite3Result* result = (TQSQLite3Result*)query.result(); + return result->d->rInf; + } + return TQSqlRecord(); +} + +TQSqlRecordInfo TQSQLite3Driver::recordInfo(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSQLite3Result* result = (TQSQLite3Result*)query.result(); + return TQSqlRecordInfo(result->d->rInf); + } + return TQSqlRecordInfo(); +} diff --git a/src/sql/drivers/sqlite3/qsql_sqlite3.h b/src/sql/drivers/sqlite3/qsql_sqlite3.h new file mode 100644 index 00000000..4d79176a --- /dev/null +++ b/src/sql/drivers/sqlite3/qsql_sqlite3.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Definition of SQLite driver classes. +** +** Copyright (C) 1992-2003 Trolltech AS. All rights reserved. +** +** 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. +** +****************************************************************************/ + +#ifndef TQSQL_SQLITE3_H +#define TQSQL_SQLITE3_H + +#include +#include +#include +#include +#include "qsqlcachedresult.h" + +#if (TQT_VERSION-0 >= 0x030200) +typedef TQVariant TQSqlVariant; +#endif + +#if defined (Q_OS_WIN32) +# include +#endif + +class TQSQLite3DriverPrivate; +class TQSQLite3ResultPrivate; +class TQSQLite3Driver; +struct sqlite3; + +class TQSQLite3Result : public TQtSqlCachedResult +{ + friend class TQSQLite3Driver; + friend class TQSQLite3ResultPrivate; +public: + TQSQLite3Result(const TQSQLite3Driver* db); + ~TQSQLite3Result(); + +protected: + bool gotoNext(TQtSqlCachedResult::RowCache* row); + bool reset (const TQString& query); + int size(); + int numRowsAffected(); + +private: + TQSQLite3ResultPrivate* d; +}; + +class TQSQLite3Driver : public TQSqlDriver +{ + friend class TQSQLite3Result; +public: + TQSQLite3Driver(TQObject *parent = 0, const char *name = 0); + TQSQLite3Driver(sqlite3 *connection, TQObject *parent = 0, const char *name = 0); + ~TQSQLite3Driver(); + bool hasFeature(DriverFeature f) const; + bool open(const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port, + const TQString & connOpts); + bool open( const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port ) { return open (db, user, password, host, port, TQString()); } + void close(); + TQSqlQuery createQuery() const; + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); + TQStringList tables(const TQString &user) const; + + TQSqlRecord record(const TQString& tablename) const; + TQSqlRecordInfo recordInfo(const TQString& tablename) const; + TQSqlIndex primaryIndex(const TQString &table) const; + TQSqlRecord record(const TQSqlQuery& query) const; + TQSqlRecordInfo recordInfo(const TQSqlQuery& query) const; + +private: + TQSQLite3DriverPrivate* d; +}; +#endif diff --git a/src/sql/qsqldatabase.cpp b/src/sql/qsqldatabase.cpp index 798e7948..606adb9f 100644 --- a/src/sql/qsqldatabase.cpp +++ b/src/sql/qsqldatabase.cpp @@ -68,6 +68,9 @@ #ifdef QT_SQL_SQLITE #include "drivers/sqlite/qsql_sqlite.h" #endif +#ifdef QT_SQL_SQLITE3 +#include "drivers/sqlite3/qsql_sqlite3.h" +#endif #ifdef QT_SQL_IBASE #include "drivers/ibase/qsql_ibase.h" #endif @@ -525,6 +528,10 @@ TQStringList TQSqlDatabase::drivers() if ( !l.contains( "TQSQLITE" ) ) l << "TQSQLITE"; #endif +#ifdef QT_SQL_SQLITE3 + if ( !l.contains( "TQSQLITE3" ) ) + l << "TQSQLITE3"; +#endif #ifdef QT_SQL_IBASE if ( !l.contains( "TQIBASE" ) ) l << "TQIBASE"; @@ -665,6 +672,11 @@ void TQSqlDatabase::init( const TQString& type, const TQString& ) d->driver = new TQSQLiteDriver(); #endif +#ifdef QT_SQL_SQLITE3 + if ( type == "TQSQLITE3" ) + d->driver = new TQSQLite3Driver(); +#endif + #ifdef QT_SQL_IBASE if ( type == "TQIBASE" ) d->driver = new TQIBaseDriver(); diff --git a/src/sql/qt_sql.pri b/src/sql/qt_sql.pri index f5bd9487..9ad50537 100644 --- a/src/sql/qt_sql.pri +++ b/src/sql/qt_sql.pri @@ -250,5 +250,11 @@ sql { SOURCES += $$SQL_CPP/drivers/sqlite/qsql_sqlite.cpp DEFINES += QT_SQL_SQLITE } + + contains(sql-drivers, sqlite3) { + HEADERS += $$SQL_CPP/drivers/sqlite3/qsql_sqlite3.h + SOURCES += $$SQL_CPP/drivers/sqlite3/qsql_sqlite3.cpp + DEFINES += QT_SQL_SQLITE3 + } }