summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
commitbd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch)
tree7a520322212d48ebcb9fbe1087e7fca28b76185c /src/sql/drivers
downloadqt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz
qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip
Add Qt3 development HEAD version
Diffstat (limited to 'src/sql/drivers')
-rw-r--r--src/sql/drivers/cache/qsqlcachedresult.cpp259
-rw-r--r--src/sql/drivers/cache/qsqlcachedresult.h104
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp1078
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.h117
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.cpp775
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.h131
-rw-r--r--src/sql/drivers/odbc/debian_qsql_odbc.h10
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.cpp2035
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.h162
-rw-r--r--src/sql/drivers/psql/qsql_psql.cpp1117
-rw-r--r--src/sql/drivers/psql/qsql_psql.h131
-rw-r--r--src/sql/drivers/sqlite/qsql_sqlite.cpp513
-rw-r--r--src/sql/drivers/sqlite/qsql_sqlite.h90
13 files changed, 6522 insertions, 0 deletions
diff --git a/src/sql/drivers/cache/qsqlcachedresult.cpp b/src/sql/drivers/cache/qsqlcachedresult.cpp
new file mode 100644
index 0000000..4891b97
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Implementation of cached Qt SQL result classes
+**
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlcachedresult.h"
+#include <qdatetime.h>
+
+#ifndef QT_NO_SQL
+static const uint initial_cache_size = 128;
+
+class QtSqlCachedResultPrivate
+{
+public:
+ QtSqlCachedResultPrivate();
+ bool seek(int i);
+ void init(int count, bool fo);
+ void cleanup();
+ QtSqlCachedResult::RowCache* next();
+ void revertLast();
+
+ QtSqlCachedResult::RowsetCache *cache;
+ QtSqlCachedResult::RowCache *current;
+ int rowCacheEnd;
+ int colCount;
+ bool forwardOnly;
+};
+
+QtSqlCachedResultPrivate::QtSqlCachedResultPrivate():
+ cache(0), current(0), rowCacheEnd(0), colCount(0), forwardOnly(FALSE)
+{
+}
+
+void QtSqlCachedResultPrivate::cleanup()
+{
+ if (cache) {
+ for (int i = 0; i < rowCacheEnd; ++i)
+ delete (*cache)[i];
+ delete cache;
+ cache = 0;
+ }
+ if (forwardOnly)
+ delete current;
+ current = 0;
+ forwardOnly = FALSE;
+ colCount = 0;
+ rowCacheEnd = 0;
+}
+
+void QtSqlCachedResultPrivate::init(int count, bool fo)
+{
+ cleanup();
+ forwardOnly = fo;
+ colCount = count;
+ if (fo)
+ current = new QtSqlCachedResult::RowCache(count);
+ else
+ cache = new QtSqlCachedResult::RowsetCache(initial_cache_size);
+}
+
+QtSqlCachedResult::RowCache *QtSqlCachedResultPrivate::next()
+{
+ if (forwardOnly)
+ return current;
+
+ Q_ASSERT(cache);
+ current = new QtSqlCachedResult::RowCache(colCount);
+ if (rowCacheEnd == (int)cache->size())
+ cache->resize(cache->size() * 2);
+ cache->insert(rowCacheEnd++, current);
+ return current;
+}
+
+bool QtSqlCachedResultPrivate::seek(int i)
+{
+ if (forwardOnly || i < 0)
+ return FALSE;
+ if (i >= rowCacheEnd)
+ return FALSE;
+ current = (*cache)[i];
+ return TRUE;
+}
+
+void QtSqlCachedResultPrivate::revertLast()
+{
+ if (forwardOnly)
+ return;
+ --rowCacheEnd;
+ delete current;
+ current = 0;
+}
+
+//////////////
+
+QtSqlCachedResult::QtSqlCachedResult(const QSqlDriver * db ): QSqlResult ( db )
+{
+ d = new QtSqlCachedResultPrivate();
+}
+
+QtSqlCachedResult::~QtSqlCachedResult()
+{
+ delete d;
+}
+
+void QtSqlCachedResult::init(int colCount)
+{
+ d->init(colCount, isForwardOnly());
+}
+
+bool QtSqlCachedResult::fetch(int i)
+{
+ if ((!isActive()) || (i < 0))
+ return FALSE;
+ if (at() == i)
+ return TRUE;
+ if (d->forwardOnly) {
+ // speed hack - do not copy values if not needed
+ if (at() > i || at() == QSql::AfterLast)
+ return FALSE;
+ while(at() < i - 1) {
+ if (!gotoNext(0))
+ return FALSE;
+ setAt(at() + 1);
+ }
+ if (!gotoNext(d->current))
+ return FALSE;
+ setAt(at() + 1);
+ return TRUE;
+ }
+ if (d->seek(i)) {
+ setAt(i);
+ return TRUE;
+ }
+ setAt(d->rowCacheEnd - 1);
+ while (at() < i) {
+ if (!cacheNext())
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QtSqlCachedResult::fetchNext()
+{
+ if (d->seek(at() + 1)) {
+ setAt(at() + 1);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool QtSqlCachedResult::fetchPrev()
+{
+ return fetch(at() - 1);
+}
+
+bool QtSqlCachedResult::fetchFirst()
+{
+ if (d->forwardOnly && at() != QSql::BeforeFirst) {
+ return FALSE;
+ }
+ if (d->seek(0)) {
+ setAt(0);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool QtSqlCachedResult::fetchLast()
+{
+ if (at() == QSql::AfterLast) {
+ if (d->forwardOnly)
+ return FALSE;
+ else
+ return fetch(d->rowCacheEnd - 1);
+ }
+
+ int i = at();
+ while (fetchNext())
+ i++; /* brute force */
+ if (d->forwardOnly && at() == QSql::AfterLast) {
+ setAt(i);
+ return TRUE;
+ } else {
+ return fetch(d->rowCacheEnd - 1);
+ }
+}
+
+QVariant QtSqlCachedResult::data(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return QVariant();
+
+ return (*d->current)[i];
+}
+
+bool QtSqlCachedResult::isNull(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return TRUE;
+
+ return (*d->current)[i].isNull();
+}
+
+void QtSqlCachedResult::cleanup()
+{
+ setAt(QSql::BeforeFirst);
+ setActive(FALSE);
+ d->cleanup();
+}
+
+bool QtSqlCachedResult::cacheNext()
+{
+ if (!gotoNext(d->next())) {
+ d->revertLast();
+ return FALSE;
+ }
+ setAt(at() + 1);
+ return TRUE;
+}
+
+int QtSqlCachedResult::colCount() const
+{
+ return d->colCount;
+}
+#endif // QT_NO_SQL
diff --git a/src/sql/drivers/cache/qsqlcachedresult.h b/src/sql/drivers/cache/qsqlcachedresult.h
new file mode 100644
index 0000000..e401eef
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Definition of shared Qt SQL module classes
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLCACHEDRESULT_H
+#define QSQLCACHEDRESULT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include <qglobal.h>
+#include <qvariant.h>
+#include <qptrvector.h>
+#include <qvaluevector.h>
+#include <qsqlresult.h>
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QtSqlCachedResultPrivate;
+
+class QM_EXPORT_SQL QtSqlCachedResult: public QSqlResult
+{
+public:
+ virtual ~QtSqlCachedResult();
+
+ typedef QValueVector<QVariant> RowCache;
+ typedef QPtrVector<RowCache> RowsetCache;
+
+protected:
+ QtSqlCachedResult(const QSqlDriver * db);
+
+ void init(int colCount);
+ void cleanup();
+ bool cacheNext();
+
+ virtual bool gotoNext(RowCache* row) = 0;
+
+ QVariant data(int i);
+ bool isNull(int i);
+ bool fetch(int i);
+ bool fetchNext();
+ bool fetchPrev();
+ bool fetchFirst();
+ bool fetchLast();
+
+ int colCount() const;
+
+private:
+ QtSqlCachedResultPrivate *d;
+};
+
+
+#endif
+
+#endif
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
new file mode 100644
index 0000000..6954ef8
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.cpp
@@ -0,0 +1,1078 @@
+/****************************************************************************
+**
+** Implementation of Interbase driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt 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_ibase.h"
+
+#include <qdatetime.h>
+#include <private/qsqlextension_p.h>
+
+#include <ibase.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+
+#define QIBASE_DRIVER_NAME "QIBASE"
+
+class QIBasePreparedExtension : public QSqlExtension
+{
+public:
+ QIBasePreparedExtension(QIBaseResult *r)
+ : result(r) {}
+
+ bool prepare(const QString &query)
+ {
+ return result->prepare(query);
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ QIBaseResult *result;
+};
+
+static bool getIBaseError(QString& msg, ISC_STATUS* status, long &sqlcode)
+{
+ if (status[0] != 1 || status[1] <= 0)
+ return FALSE;
+
+ sqlcode = isc_sqlcode(status);
+ char buf[512];
+ isc_sql_interprete(sqlcode, buf, 512);
+ msg = QString::fromUtf8(buf);
+ return TRUE;
+}
+
+static void createDA(XSQLDA *&sqlda)
+{
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
+ sqlda->sqln = 1;
+ sqlda->sqld = 0;
+ sqlda->version = SQLDA_VERSION1;
+ sqlda->sqlvar[0].sqlind = 0;
+ sqlda->sqlvar[0].sqldata = 0;
+}
+
+static void enlargeDA(XSQLDA *&sqlda, int n)
+{
+ free(sqlda);
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ sqlda->sqln = n;
+ sqlda->version = SQLDA_VERSION1;
+}
+
+static void initDA(XSQLDA *sqlda)
+{
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ switch (sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_INT64:
+ case SQL_LONG:
+ case SQL_SHORT:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIME:
+ case SQL_TYPE_DATE:
+ case SQL_TEXT:
+ case SQL_BLOB:
+ sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
+ break;
+ case SQL_VARYING:
+ sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
+ break;
+ default:
+ // not supported - do not bind.
+ sqlda->sqlvar[i].sqldata = 0;
+ break;
+ }
+ if (sqlda->sqlvar[i].sqltype & 1) {
+ sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short));
+ *(sqlda->sqlvar[i].sqlind) = 0;
+ } else {
+ sqlda->sqlvar[i].sqlind = 0;
+ }
+ }
+}
+
+static void delDA(XSQLDA *&sqlda)
+{
+ if (!sqlda)
+ return;
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ free(sqlda->sqlvar[i].sqlind);
+ free(sqlda->sqlvar[i].sqldata);
+ }
+ free(sqlda);
+ sqlda = 0;
+}
+
+static QVariant::Type qIBaseTypeName(int iType)
+{
+ switch (iType) {
+ case blr_varying:
+ case blr_varying2:
+ case blr_text:
+ case blr_cstring:
+ case blr_cstring2:
+ return QVariant::String;
+ case blr_sql_time:
+ return QVariant::Time;
+ case blr_sql_date:
+ return QVariant::Date;
+ case blr_timestamp:
+ return QVariant::DateTime;
+ case blr_blob:
+ return QVariant::ByteArray;
+ case blr_quad:
+ case blr_short:
+ case blr_long:
+ return QVariant::Int;
+ case blr_int64:
+ return QVariant::LongLong;
+ case blr_float:
+ case blr_d_float:
+ case blr_double:
+ return QVariant::Double;
+ }
+ return QVariant::Invalid;
+}
+
+static QVariant::Type qIBaseTypeName2(int iType)
+{
+ switch(iType & ~1) {
+ case SQL_VARYING:
+ case SQL_TEXT:
+ return QVariant::String;
+ case SQL_LONG:
+ case SQL_SHORT:
+ return QVariant::Int;
+ case SQL_INT64:
+ return QVariant::LongLong;
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return QVariant::Double;
+ case SQL_TIMESTAMP:
+ return QVariant::DateTime;
+ case SQL_TYPE_DATE:
+ return QVariant::Date;
+ case SQL_TYPE_TIME:
+ return QVariant::Time;
+ default:
+ return QVariant::Invalid;
+ }
+}
+
+static ISC_TIME toTime(const QTime &t)
+{
+ static const QTime midnight(0, 0, 0, 0);
+ return (ISC_TIME)midnight.msecsTo(t) * 10;
+}
+
+static ISC_DATE toDate(const QDate &d)
+{
+ static const QDate basedate(1858, 11, 17);
+ return (ISC_DATE)basedate.daysTo(d);
+}
+
+static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
+{
+ ISC_TIMESTAMP ts;
+ ts.timestamp_time = toTime(dt.time());
+ ts.timestamp_date = toDate(dt.date());
+ return ts;
+}
+
+static QTime toQTime(ISC_TIME time)
+{
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ static const QTime t;
+ return t.addMSecs(time / 10);
+}
+
+static QDate toQDate(ISC_DATE d)
+{
+ static const QDate bd(1858, 11, 17);
+ return bd.addDays(d);
+}
+
+static QDateTime toQDateTime(ISC_TIMESTAMP *ts)
+{
+ return QDateTime(toQDate(ts->timestamp_date), toQTime(ts->timestamp_time));
+}
+
+class QIBaseDriverPrivate
+{
+public:
+ QIBaseDriverPrivate(QIBaseDriver *d): q(d)
+ {
+ ibase = 0;
+ trans = 0;
+ }
+
+ bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
+ {
+ QString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+public:
+ QIBaseDriver* q;
+ isc_db_handle ibase;
+ isc_tr_handle trans;
+ ISC_STATUS status[20];
+};
+
+class QIBaseResultPrivate
+{
+public:
+ QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb);
+ ~QIBaseResultPrivate() { cleanup(); }
+
+ void cleanup();
+ bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
+ {
+ QString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+ bool transaction();
+ bool commit();
+
+ bool isSelect();
+ QVariant fetchBlob(ISC_QUAD *bId);
+ void writeBlob(int i, const QByteArray &ba);
+
+public:
+ QIBaseResult *q;
+ const QIBaseDriver *db;
+ ISC_STATUS status[20];
+ isc_tr_handle trans;
+ //indicator whether we have a local transaction or a transaction on driver level
+ bool localTransaction;
+ isc_stmt_handle stmt;
+ isc_db_handle ibase;
+ XSQLDA *sqlda; // output sqlda
+ XSQLDA *inda; // input parameters
+ int queryType;
+};
+
+QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb):
+ q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1)
+{
+ localTransaction = (ddb->d->ibase == 0);
+}
+
+void QIBaseResultPrivate::cleanup()
+{
+ if (stmt) {
+ isc_dsql_free_statement(status, &stmt, DSQL_drop);
+ stmt = 0;
+ }
+
+ commit();
+ if (!localTransaction)
+ trans = 0;
+
+ delDA(sqlda);
+ delDA(inda);
+
+ queryType = -1;
+ q->cleanup();
+}
+
+void QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
+{
+ isc_blob_handle handle = 0;
+ ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
+ isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (!isError("Unable to create BLOB", QSqlError::Statement)) {
+ uint i = 0;
+ while (i < ba.size()) {
+ isc_put_segment(status, &handle, QMIN(ba.size() - i, SHRT_MAX), ba.data());
+ if (isError("Unable to write BLOB"))
+ break;
+ i += SHRT_MAX;
+ }
+ }
+ isc_close_blob(status, &handle);
+}
+
+QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
+{
+ isc_blob_handle handle = 0;
+
+ isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (isError("Unable to open BLOB", QSqlError::Statement))
+ return QVariant();
+
+ unsigned short len = 0;
+ QByteArray ba(255);
+ ISC_STATUS stat = isc_get_segment(status, &handle, &len, ba.size(), ba.data());
+ while (status[1] == isc_segment) {
+ uint osize = ba.size();
+ // double the amount of data fetched on each iteration
+ ba.resize(QMIN(ba.size() * 2, SHRT_MAX));
+ stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize);
+ }
+ bool isErr = isError("Unable to read BLOB", QSqlError::Statement);
+ isc_close_blob(status, &handle);
+ if (isErr)
+ return QVariant();
+
+ if (ba.size() > 255)
+ ba.resize(ba.size() / 2 + len);
+ else
+ ba.resize(len);
+
+ return ba;
+}
+
+bool QIBaseResultPrivate::isSelect()
+{
+ char acBuffer[9];
+ char qType = isc_info_sql_stmt_type;
+ isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
+ if (isError("Could not get query info", QSqlError::Statement))
+ return FALSE;
+ int iLength = isc_vax_integer(&acBuffer[1], 2);
+ queryType = isc_vax_integer(&acBuffer[3], iLength);
+ return (queryType == isc_info_sql_stmt_select);
+}
+
+bool QIBaseResultPrivate::transaction()
+{
+ if (trans)
+ return TRUE;
+ if (db->d->trans) {
+ localTransaction = FALSE;
+ trans = db->d->trans;
+ return TRUE;
+ }
+ localTransaction = TRUE;
+
+ isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
+ if (isError("Could not start transaction", QSqlError::Statement))
+ return FALSE;
+
+ return TRUE;
+}
+
+// does nothing if the transaction is on the
+// driver level
+bool QIBaseResultPrivate::commit()
+{
+ if (!trans)
+ return FALSE;
+ // don't commit driver's transaction, the driver will do it for us
+ if (!localTransaction)
+ return TRUE;
+
+ isc_commit_transaction(status, &trans);
+ trans = 0;
+ return !isError("Unable to commit transaction", QSqlError::Statement);
+}
+
+//////////
+
+QIBaseResult::QIBaseResult(const QIBaseDriver* db):
+ QtSqlCachedResult(db)
+{
+ d = new QIBaseResultPrivate(this, db);
+ setExtension(new QIBasePreparedExtension(this));
+}
+
+QIBaseResult::~QIBaseResult()
+{
+ delete d;
+}
+
+bool QIBaseResult::prepare(const QString& query)
+{
+ //qDebug("prepare: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ createDA(d->sqlda);
+ createDA(d->inda);
+
+ if (!d->transaction())
+ return FALSE;
+
+ isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
+ if (d->isError("Could not allocate statement", QSqlError::Statement))
+ return FALSE;
+ isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
+ if (d->isError("Could not prepare statement", QSqlError::Statement))
+ return FALSE;
+
+ isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
+ if (d->isError("Could not describe input statement", QSqlError::Statement))
+ return FALSE;
+ if (d->inda->sqld > d->inda->sqln) {
+ enlargeDA(d->inda, d->inda->sqld);
+
+ isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
+ if (d->isError("Could not describe input statement", QSqlError::Statement))
+ return FALSE;
+ }
+ initDA(d->inda);
+ if (d->sqlda->sqld > d->sqlda->sqln) {
+ // need more field descriptors
+ enlargeDA(d->sqlda, d->sqlda->sqld);
+
+ isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
+ if (d->isError("Could not describe statement", QSqlError::Statement))
+ return FALSE;
+ }
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (!isSelect()) {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ return TRUE;
+}
+
+bool QIBaseResult::exec()
+{
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ if (d->inda && extension()->index.count() > 0) {
+ QMap<int, QString>::ConstIterator it;
+ if ((int)extension()->index.count() > d->inda->sqld) {
+ qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", d->inda->sqld, extension()->index.count());
+ return FALSE;
+ }
+ int para = 0;
+ for (it = extension()->index.constBegin(); it != extension()->index.constEnd(); ++it, ++para) {
+ if (para >= d->inda->sqld)
+ break;
+ if (!d->inda->sqlvar[para].sqldata)
+ continue;
+ const QVariant val(extension()->values[it.data()].value);
+ if (d->inda->sqlvar[para].sqltype & 1) {
+ if (val.isNull()) {
+ // set null indicator
+ *(d->inda->sqlvar[para].sqlind) = 1;
+ // and set the value to 0, otherwise it would count as empty string.
+ *((short*)d->inda->sqlvar[para].sqldata) = 0;
+ continue;
+ }
+ // a value of 0 means non-null.
+ *(d->inda->sqlvar[para].sqlind) = 0;
+ }
+ switch(d->inda->sqlvar[para].sqltype & ~1) {
+ case SQL_INT64:
+ if (d->inda->sqlvar[para].sqlscale < 0)
+ *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = Q_LLONG(val.toDouble() *
+ pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
+ else
+ *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
+ break;
+ case SQL_LONG:
+ *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
+ break;
+ case SQL_SHORT:
+ *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
+ break;
+ case SQL_FLOAT:
+ *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
+ break;
+ case SQL_DOUBLE:
+ *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
+ break;
+ case SQL_TIMESTAMP:
+ *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
+ break;
+ case SQL_TYPE_TIME:
+ *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
+ break;
+ case SQL_TYPE_DATE:
+ *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
+ break;
+ case SQL_VARYING: {
+ QCString str(val.toString().utf8()); // keep a copy of the string alive in this scope
+ short buflen = d->inda->sqlvar[para].sqllen;
+ if (str.length() < (uint)buflen)
+ buflen = str.length();
+ *(short*)d->inda->sqlvar[para].sqldata = buflen; // first two bytes is the length
+ memcpy(d->inda->sqlvar[para].sqldata + sizeof(short), str.data(), buflen);
+ break; }
+ case SQL_TEXT: {
+ QCString str(val.toString().utf8().leftJustify(d->inda->sqlvar[para].sqllen, ' ', TRUE));
+ memcpy(d->inda->sqlvar[para].sqldata, str.data(), d->inda->sqlvar[para].sqllen);
+ break; }
+ case SQL_BLOB:
+ d->writeBlob(para, val.toByteArray());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (colCount()) {
+ isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
+ if (d->isError("Unable to close statement"))
+ return FALSE;
+ cleanup();
+ }
+ if (d->sqlda)
+ init(d->sqlda->sqld);
+ isc_dsql_execute2(d->status, &d->trans, &d->stmt, 1, d->inda, 0);
+ if (d->isError("Unable to execute query"))
+ return FALSE;
+
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QIBaseResult::reset (const QString& query)
+{
+// qDebug("reset: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ createDA(d->sqlda);
+
+ if (!d->transaction())
+ return FALSE;
+
+ isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
+ if (d->isError("Could not allocate statement", QSqlError::Statement))
+ return FALSE;
+ isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
+ if (d->isError("Could not prepare statement", QSqlError::Statement))
+ return FALSE;
+
+ if (d->sqlda->sqld > d->sqlda->sqln) {
+ // need more field descriptors
+ int n = d->sqlda->sqld;
+ free(d->sqlda);
+ d->sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ d->sqlda->sqln = n;
+ d->sqlda->version = SQLDA_VERSION1;
+
+ isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
+ if (d->isError("Could not describe statement", QSqlError::Statement))
+ return FALSE;
+ }
+
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (isSelect()) {
+ init(d->sqlda->sqld);
+ } else {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ isc_dsql_execute(d->status, &d->trans, &d->stmt, 1, 0);
+ if (d->isError("Unable to execute query"))
+ return FALSE;
+
+ // commit non-select queries (if they are local)
+ if (!isSelect() && !d->commit())
+ return FALSE;
+
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QIBaseResult::gotoNext(QtSqlCachedResult::RowCache* row)
+{
+ ISC_STATUS stat = isc_dsql_fetch(d->status, &d->stmt, 1, d->sqlda);
+
+ if (stat == 100) {
+ // no more rows
+ setAt(QSql::AfterLast);
+ return FALSE;
+ }
+ if (d->isError("Could not fetch next item", QSqlError::Statement))
+ return FALSE;
+ if (!row) // not interested in actual values
+ return TRUE;
+
+ Q_ASSERT(row);
+ Q_ASSERT((int)row->size() == d->sqlda->sqld);
+ for (int i = 0; i < d->sqlda->sqld; ++i) {
+ char *buf = d->sqlda->sqlvar[i].sqldata;
+ int size = d->sqlda->sqlvar[i].sqllen;
+ Q_ASSERT(buf);
+
+ if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
+ // null value
+ QVariant v;
+ v.cast(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype));
+ (*row)[i] = v;
+ continue;
+ }
+
+ switch(d->sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_VARYING:
+ // pascal strings - a short with a length information followed by the data
+ (*row)[i] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
+ break;
+ case SQL_INT64:
+ if (d->sqlda->sqlvar[i].sqlscale < 0)
+ (*row)[i] = *(Q_LLONG*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
+ else
+ (*row)[i] = QVariant(*(Q_LLONG*)buf);
+ break;
+ case SQL_LONG:
+ if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out.
+ (*row)[i] = QVariant((int)(*(long*)buf));
+ else
+ (*row)[i] = QVariant((Q_LLONG)(*(long*)buf));
+ break;
+ case SQL_SHORT:
+ (*row)[i] = QVariant((int)(*(short*)buf));
+ break;
+ case SQL_FLOAT:
+ (*row)[i] = QVariant((double)(*(float*)buf));
+ break;
+ case SQL_DOUBLE:
+ (*row)[i] = QVariant(*(double*)buf);
+ break;
+ case SQL_TIMESTAMP:
+ (*row)[i] = toQDateTime((ISC_TIMESTAMP*)buf);
+ break;
+ case SQL_TYPE_TIME:
+ (*row)[i] = toQTime(*(ISC_TIME*)buf);
+ break;
+ case SQL_TYPE_DATE:
+ (*row)[i] = toQDate(*(ISC_DATE*)buf);
+ break;
+ case SQL_TEXT:
+ (*row)[i] = QString::fromUtf8(buf, size);
+ break;
+ case SQL_BLOB:
+ (*row)[i] = d->fetchBlob((ISC_QUAD*)buf);
+ break;
+ default:
+ // unknown type - don't even try to fetch
+ (*row)[i] = QVariant();
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+int QIBaseResult::size()
+{
+ static char sizeInfo[] = {isc_info_sql_records};
+ char buf[33];
+
+ if (!isActive() || !isSelect())
+ return -1;
+
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
+ for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
+ char ct = *c++;
+ short len = isc_vax_integer(c, 2);
+ c += 2;
+ int val = isc_vax_integer(c, len);
+ c += len;
+ if (ct == isc_info_req_select_count)
+ return val;
+ }
+ return -1;
+}
+
+int QIBaseResult::numRowsAffected()
+{
+ static char acCountInfo[] = {isc_info_sql_records};
+ char cCountType;
+
+ switch (d->queryType) {
+ case isc_info_sql_stmt_select:
+ cCountType = isc_info_req_select_count;
+ break;
+ case isc_info_sql_stmt_update:
+ cCountType = isc_info_req_update_count;
+ break;
+ case isc_info_sql_stmt_delete:
+ cCountType = isc_info_req_delete_count;
+ break;
+ case isc_info_sql_stmt_insert:
+ cCountType = isc_info_req_insert_count;
+ break;
+ }
+
+ char acBuffer[33];
+ int iResult = -1;
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
+ if (d->isError("Could not get statement info", QSqlError::Statement))
+ return -1;
+ for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
+ char cType = *pcBuf++;
+ short sLength = isc_vax_integer (pcBuf, 2);
+ pcBuf += 2;
+ int iValue = isc_vax_integer (pcBuf, sLength);
+ pcBuf += sLength;
+
+ if (cType == cCountType) {
+ iResult = iValue;
+ break;
+ }
+ }
+ return iResult;
+}
+
+/*********************************/
+
+QIBaseDriver::QIBaseDriver(QObject * parent, const char * name)
+ : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
+{
+ d = new QIBaseDriverPrivate(this);
+}
+
+QIBaseDriver::QIBaseDriver(void *connection, QObject *parent, const char *name)
+ : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
+{
+ d = new QIBaseDriverPrivate(this);
+ d->ibase = (isc_db_handle)(long int)connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+QIBaseDriver::~QIBaseDriver()
+{
+ delete d;
+}
+
+bool QIBaseDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+// case QuerySize:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case Unicode:
+ case BLOB:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QIBaseDriver::open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int /*port*/,
+ const QString & /* connOpts */)
+{
+ if (isOpen())
+ close();
+
+ static const char enc[8] = "UTF_FSS";
+ QCString usr = user.local8Bit();
+ QCString pass = password.local8Bit();
+ usr.truncate(255);
+ pass.truncate(255);
+
+ QByteArray ba(usr.length() + pass.length() + sizeof(enc) + 6);
+ int i = -1;
+ ba[++i] = isc_dpb_version1;
+ ba[++i] = isc_dpb_user_name;
+ ba[++i] = usr.length();
+ memcpy(&ba[++i], usr.data(), usr.length());
+ i += usr.length();
+ ba[i] = isc_dpb_password;
+ ba[++i] = pass.length();
+ memcpy(&ba[++i], pass.data(), pass.length());
+ i += pass.length();
+ ba[i] = isc_dpb_lc_ctype;
+ ba[++i] = sizeof(enc) - 1;
+ memcpy(&ba[++i], enc, sizeof(enc) - 1);
+ i += sizeof(enc) - 1;
+
+ QString ldb;
+ if (!host.isEmpty())
+ ldb += host + ":";
+ ldb += db;
+ isc_attach_database(d->status, 0, (char*)ldb.latin1(), &d->ibase, i, ba.data());
+ if (d->isError("Error opening database", QSqlError::Connection)) {
+ setOpenError(TRUE);
+ return FALSE;
+ }
+
+ setOpen(TRUE);
+ return TRUE;
+}
+
+void QIBaseDriver::close()
+{
+ if (isOpen()) {
+ isc_detach_database(d->status, &d->ibase);
+ d->ibase = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+QSqlQuery QIBaseDriver::createQuery() const
+{
+ return QSqlQuery(new QIBaseResult(this));
+}
+
+bool QIBaseDriver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (d->trans)
+ return FALSE;
+
+ isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
+ return !d->isError("Could not start transaction", QSqlError::Transaction);
+}
+
+bool QIBaseDriver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (!d->trans)
+ return FALSE;
+
+ isc_commit_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError("Unable to commit transaction", QSqlError::Transaction);
+}
+
+bool QIBaseDriver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (!d->trans)
+ return FALSE;
+
+ isc_rollback_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError("Unable to rollback transaction", QSqlError::Transaction);
+}
+
+QStringList QIBaseDriver::tables(const QString& typeName) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+
+ int type = typeName.isEmpty() ? (int)QSql::Tables | (int)QSql::Views : typeName.toInt();
+ QString typeFilter;
+
+ if (type == (int)QSql::SystemTables) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0";
+ } else if (type == ((int)QSql::SystemTables | (int)QSql::Views)) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL";
+ } else {
+ if (!(type & (int)QSql::SystemTables))
+ typeFilter += "RDB$SYSTEM_FLAG = 0 AND ";
+ if (!(type & (int)QSql::Views))
+ typeFilter += "RDB$VIEW_BLR IS NULL AND ";
+ if (!(type & (int)QSql::Tables))
+ typeFilter += "RDB$VIEW_BLR IS NOT NULL AND ";
+ if (!typeFilter.isEmpty())
+ typeFilter.truncate(typeFilter.length() - 5);
+ }
+ if (!typeFilter.isEmpty())
+ typeFilter.prepend("where ");
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ if (!q.exec("select rdb$relation_name from rdb$relations " + typeFilter))
+ return res;
+ while(q.next())
+ res << q.value(0).toString().stripWhiteSpace();
+
+ return res;
+}
+
+QSqlRecord QIBaseDriver::record(const QString& tablename) const
+{
+ QSqlRecord rec;
+ if (!isOpen())
+ return rec;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+
+ q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '" + tablename.upper()+ "' "
+ "ORDER BY RDB$FIELD_POSITION");
+ while (q.next()) {
+ QSqlField field(q.value(0).toString().stripWhiteSpace(), qIBaseTypeName(q.value(1).toInt()));
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QSqlRecordInfo QIBaseDriver::recordInfo(const QString& tablename) const
+{
+ QSqlRecordInfo rec;
+ if (!isOpen())
+ return rec;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+
+ q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, b.RDB$FIELD_SCALE, "
+ "b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '" + tablename.upper() + "' "
+ "ORDER BY a.RDB$FIELD_POSITION");
+
+ while (q.next()) {
+ QVariant::Type type = qIBaseTypeName(q.value(1).toInt());
+ QSqlFieldInfo field(q.value(0).toString().stripWhiteSpace(), type, q.value(5).toInt(),
+ q.value(2).toInt(), q.value(4).toInt(), QVariant());
+
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
+{
+ QSqlIndex index(table);
+ if (!isOpen())
+ return index;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE "
+ "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
+ "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
+ "AND a.RDB$RELATION_NAME = '" + table.upper() + "' "
+ "AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
+ "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
+ "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
+ "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
+ "ORDER BY b.RDB$FIELD_POSITION");
+
+ while (q.next()) {
+ QSqlField field(q.value(1).toString().stripWhiteSpace(), qIBaseTypeName(q.value(2).toInt()));
+ index.append(field); //TODO: asc? desc?
+ index.setName(q.value(0).toString());
+ }
+
+ return index;
+}
+
+QSqlRecord QIBaseDriver::record(const QSqlQuery& query) const
+{
+ QSqlRecord rec;
+ if (query.isActive() && query.driver() == this) {
+ QIBaseResult* result = (QIBaseResult*)query.result();
+ if (!result->d->sqlda)
+ return rec;
+ XSQLVAR v;
+ for (int i = 0; i < result->d->sqlda->sqld; ++i) {
+ v = result->d->sqlda->sqlvar[i];
+ QSqlField f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype));
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+QSqlRecordInfo QIBaseDriver::recordInfo(const QSqlQuery& query) const
+{
+ QSqlRecordInfo rec;
+ if (query.isActive() && query.driver() == this) {
+ QIBaseResult* result = (QIBaseResult*)query.result();
+ if (!result->d->sqlda)
+ return rec;
+ XSQLVAR v;
+ for (int i = 0; i < result->d->sqlda->sqld; ++i) {
+ v = result->d->sqlda->sqlvar[i];
+ QSqlFieldInfo f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype),
+ -1, v.sqllen, QABS(v.sqlscale), QVariant(), v.sqltype);
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+QString QIBaseDriver::formatValue(const QSqlField* field, bool trimStrings) const
+{
+ switch (field->type()) {
+ case QVariant::DateTime: {
+ QDateTime datetime = field->value().toDateTime();
+ if (datetime.isValid())
+ return "'" + QString::number(datetime.date().year()) + "-" +
+ QString::number(datetime.date().month()) + "-" +
+ QString::number(datetime.date().day()) + " " +
+ QString::number(datetime.time().hour()) + ":" +
+ QString::number(datetime.time().minute()) + ":" +
+ QString::number(datetime.time().second()) + "." +
+ QString::number(datetime.time().msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case QVariant::Time: {
+ QTime time = field->value().toTime();
+ if (time.isValid())
+ return "'" + QString::number(time.hour()) + ":" +
+ QString::number(time.minute()) + ":" +
+ QString::number(time.second()) + "." +
+ QString::number(time.msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case QVariant::Date: {
+ QDate date = field->value().toDate();
+ if (date.isValid())
+ return "'" + QString::number(date.year()) + "-" +
+ QString::number(date.month()) + "-" +
+ QString::number(date.day()) + "'";
+ else
+ return "NULL";
+ }
+ default:
+ return QSqlDriver::formatValue(field, trimStrings);
+ }
+}
diff --git a/src/sql/drivers/ibase/qsql_ibase.h b/src/sql/drivers/ibase/qsql_ibase.h
new file mode 100644
index 0000000..563f509
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Definition of Interbase driver classes
+**
+** Created : 030911
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_IBASE_H
+#define QSQL_IBASE_H
+
+#include "qsqlresult.h"
+#include "qsqldriver.h"
+#include "../cache/qsqlcachedresult.h"
+
+
+class QIBaseDriverPrivate;
+class QIBaseResultPrivate;
+class QIBaseDriver;
+
+class QIBaseResult : public QtSqlCachedResult
+{
+ friend class QIBaseDriver;
+ friend class QIBaseResultPrivate;
+
+public:
+ QIBaseResult(const QIBaseDriver* db);
+ virtual ~QIBaseResult();
+
+ bool prepare(const QString& query);
+ bool exec();
+
+protected:
+ bool gotoNext(QtSqlCachedResult::RowCache* row);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ QIBaseResultPrivate* d;
+};
+
+class QIBaseDriver : public QSqlDriver
+{
+ friend class QIBaseDriverPrivate;
+ friend class QIBaseResultPrivate;
+ friend class QIBaseResult;
+public:
+ QIBaseDriver(QObject *parent = 0, const char *name = 0);
+ QIBaseDriver(void *connection, QObject *parent = 0, const char *name = 0);
+ virtual ~QIBaseDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port ) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(const QString& typeName) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlRecordInfo recordInfo(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+ QSqlRecord record(const QSqlQuery& query) const;
+ QSqlRecordInfo recordInfo(const QSqlQuery& query) const;
+
+ QString formatValue(const QSqlField* field, bool trimStrings) const;
+
+private:
+ QIBaseDriverPrivate* d;
+};
+
+
+#endif
+
diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp
new file mode 100644
index 0000000..7cfe0a7
--- /dev/null
+++ b/src/sql/drivers/mysql/qsql_mysql.cpp
@@ -0,0 +1,775 @@
+/****************************************************************************
+**
+** Implementation of MYSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_mysql.h"
+#include <private/qsqlextension_p.h>
+
+#include <qdatetime.h>
+#include <qvaluevector.h>
+#include <qsqlrecord.h>
+
+#define QMYSQL_DRIVER_NAME "QMYSQL3"
+
+#ifdef Q_OS_WIN32
+// comment the next line out if you want to use MySQL/embedded on Win32 systems.
+// note that it will crash if you don't statically link to the mysql/e library!
+# define Q_NO_MYSQL_EMBEDDED
+#endif
+
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+static int qMySqlConnectionCount = 0;
+static bool qMySqlInitHandledByUser = FALSE;
+
+class QMYSQLOpenExtension : public QSqlOpenExtension
+{
+public:
+ QMYSQLOpenExtension( QMYSQLDriver *dri )
+ : QSqlOpenExtension(), driver(dri) {}
+ ~QMYSQLOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+private:
+ QMYSQLDriver *driver;
+};
+
+bool QMYSQLOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+class QMYSQLDriverPrivate
+{
+public:
+ QMYSQLDriverPrivate() : mysql(0) {}
+ MYSQL* mysql;
+};
+
+class QMYSQLResultPrivate : public QMYSQLDriverPrivate
+{
+public:
+ QMYSQLResultPrivate() : QMYSQLDriverPrivate(), result(0) {}
+ MYSQL_RES* result;
+ MYSQL_ROW row;
+ QValueVector<QVariant::Type> fieldTypes;
+};
+
+QSqlError qMakeError( const QString& err, int type, const QMYSQLDriverPrivate* p )
+{
+ return QSqlError(QMYSQL_DRIVER_NAME ": " + err, QString(mysql_error( p->mysql )), type, mysql_errno( p->mysql ));
+}
+
+QVariant::Type qDecodeMYSQLType( int mysqltype, uint flags )
+{
+ QVariant::Type type;
+ switch ( mysqltype ) {
+ case FIELD_TYPE_TINY :
+ case FIELD_TYPE_SHORT :
+ case FIELD_TYPE_LONG :
+ case FIELD_TYPE_INT24 :
+ type = (flags & UNSIGNED_FLAG) ? QVariant::UInt : QVariant::Int;
+ break;
+ case FIELD_TYPE_YEAR :
+ type = QVariant::Int;
+ break;
+ case FIELD_TYPE_LONGLONG :
+ type = (flags & UNSIGNED_FLAG) ? QVariant::ULongLong : QVariant::LongLong;
+ break;
+ case FIELD_TYPE_DECIMAL :
+ case FIELD_TYPE_FLOAT :
+ case FIELD_TYPE_DOUBLE :
+ type = QVariant::Double;
+ break;
+ case FIELD_TYPE_DATE :
+ type = QVariant::Date;
+ break;
+ case FIELD_TYPE_TIME :
+ type = QVariant::Time;
+ break;
+ case FIELD_TYPE_DATETIME :
+ case FIELD_TYPE_TIMESTAMP :
+ type = QVariant::DateTime;
+ break;
+ case FIELD_TYPE_BLOB :
+ case FIELD_TYPE_TINY_BLOB :
+ case FIELD_TYPE_MEDIUM_BLOB :
+ case FIELD_TYPE_LONG_BLOB :
+ type = (flags & BINARY_FLAG) ? QVariant::ByteArray : QVariant::CString;
+ break;
+ default:
+ case FIELD_TYPE_ENUM :
+ case FIELD_TYPE_SET :
+ case FIELD_TYPE_STRING :
+ case FIELD_TYPE_VAR_STRING :
+ type = QVariant::String;
+ break;
+ }
+ return type;
+}
+
+QMYSQLResult::QMYSQLResult( const QMYSQLDriver* db )
+: QSqlResult( db )
+{
+ d = new QMYSQLResultPrivate();
+ d->mysql = db->d->mysql;
+}
+
+QMYSQLResult::~QMYSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+MYSQL_RES* QMYSQLResult::result()
+{
+ return d->result;
+}
+
+void QMYSQLResult::cleanup()
+{
+ if ( d->result ) {
+ mysql_free_result( d->result );
+ }
+ d->result = NULL;
+ d->row = NULL;
+ setAt( -1 );
+ setActive( FALSE );
+}
+
+bool QMYSQLResult::fetch( int i )
+{
+ if ( isForwardOnly() ) { // fake a forward seek
+ if ( at() < i ) {
+ int x = i - at();
+ while ( --x && fetchNext() );
+ return fetchNext();
+ } else {
+ return FALSE;
+ }
+ }
+ if ( at() == i )
+ return TRUE;
+ mysql_data_seek( d->result, i );
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( i );
+ return TRUE;
+}
+
+bool QMYSQLResult::fetchNext()
+{
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool QMYSQLResult::fetchLast()
+{
+ if ( isForwardOnly() ) { // fake this since MySQL can't seek on forward only queries
+ bool success = fetchNext(); // did we move at all?
+ while ( fetchNext() );
+ return success;
+ }
+ my_ulonglong numRows = mysql_num_rows( d->result );
+ if ( !numRows )
+ return FALSE;
+ return fetch( numRows - 1 );
+}
+
+bool QMYSQLResult::fetchFirst()
+{
+ if ( isForwardOnly() ) // again, fake it
+ return fetchNext();
+ return fetch( 0 );
+}
+
+QVariant QMYSQLResult::data( int field )
+{
+ if ( !isSelect() || field >= (int) d->fieldTypes.count() ) {
+ qWarning( "QMYSQLResult::data: column %d out of range", field );
+ return QVariant();
+ }
+
+ QString val( d->row[field] );
+ switch ( d->fieldTypes.at( field ) ) {
+ case QVariant::LongLong:
+ return QVariant( val.toLongLong() );
+ case QVariant::ULongLong:
+ return QVariant( val.toULongLong() );
+ case QVariant::Int:
+ return QVariant( val.toInt() );
+ case QVariant::UInt:
+ return QVariant( val.toUInt() );
+ case QVariant::Double:
+ return QVariant( val.toDouble() );
+ case QVariant::Date:
+ if ( val.isEmpty() ) {
+ return QVariant( QDate() );
+ } else {
+ return QVariant( QDate::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::Time:
+ if ( val.isEmpty() ) {
+ return QVariant( QTime() );
+ } else {
+ return QVariant( QTime::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::DateTime:
+ if ( val.isEmpty() )
+ return QVariant( QDateTime() );
+ if ( val.length() == 14u )
+ // TIMESTAMPS have the format yyyyMMddhhmmss
+ val.insert(4, "-").insert(7, "-").insert(10, 'T').insert(13, ':').insert(16, ':');
+ return QVariant( QDateTime::fromString( val, Qt::ISODate ) );
+ case QVariant::ByteArray: {
+ unsigned long* fl = mysql_fetch_lengths( d->result );
+ QByteArray ba;
+ ba.duplicate( d->row[field], fl[field] );
+ return QVariant( ba );
+ }
+ default:
+ case QVariant::String:
+ case QVariant::CString:
+ return QVariant( val );
+ }
+#ifdef QT_CHECK_RANGE
+ qWarning("QMYSQLResult::data: unknown data type");
+#endif
+ return QVariant();
+}
+
+bool QMYSQLResult::isNull( int field )
+{
+ if ( d->row[field] == NULL )
+ return TRUE;
+ return FALSE;
+}
+
+bool QMYSQLResult::reset ( const QString& query )
+{
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()-> isOpen() || driver()->isOpenError() )
+ return FALSE;
+ cleanup();
+
+ const char *encQuery = query.ascii();
+ if ( mysql_real_query( d->mysql, encQuery, qstrlen(encQuery) ) ) {
+ setLastError( qMakeError("Unable to execute query", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ if ( isForwardOnly() ) {
+ if ( isActive() || isValid() ) // have to empty the results from previous query
+ fetchLast();
+ d->result = mysql_use_result( d->mysql );
+ } else {
+ d->result = mysql_store_result( d->mysql );
+ }
+ if ( !d->result && mysql_field_count( d->mysql ) > 0 ) {
+ setLastError( qMakeError( "Unable to store result", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ int numFields = mysql_field_count( d->mysql );
+ setSelect( !( numFields == 0) );
+ d->fieldTypes.resize( numFields );
+ if ( isSelect() ) {
+ for( int i = 0; i < numFields; i++) {
+ MYSQL_FIELD* field = mysql_fetch_field_direct( d->result, i );
+ if ( field->type == FIELD_TYPE_DECIMAL )
+ d->fieldTypes[i] = QVariant::String;
+ else
+ d->fieldTypes[i] = qDecodeMYSQLType( field->type, field->flags );
+ }
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+int QMYSQLResult::size()
+{
+ return isSelect() ? (int)mysql_num_rows( d->result ) : -1;
+}
+
+int QMYSQLResult::numRowsAffected()
+{
+ return (int)mysql_affected_rows( d->mysql );
+}
+
+/////////////////////////////////////////////////////////
+static void qServerEnd()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ mysql_server_end();
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+static void qServerInit()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ if ( qMySqlInitHandledByUser || qMySqlConnectionCount > 1 )
+ return;
+
+ // this should only be called once
+ // has no effect on client/server library
+ // but is vital for the embedded lib
+ if ( mysql_server_init( 0, 0, 0 ) ) {
+# ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::qServerInit: unable to start server." );
+# endif
+ }
+
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+QMYSQLDriver::QMYSQLDriver( QObject * parent, const char * name )
+ : QSqlDriver( parent, name ? name : QMYSQL_DRIVER_NAME )
+{
+ init();
+ qServerInit();
+}
+
+/*!
+ Create a driver instance with an already open connection handle.
+*/
+
+QMYSQLDriver::QMYSQLDriver( MYSQL * con, QObject * parent, const char * name )
+ : QSqlDriver( parent, name ? name : QMYSQL_DRIVER_NAME )
+{
+ init();
+ if ( con ) {
+ d->mysql = (MYSQL *) con;
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ if (qMySqlConnectionCount == 1)
+ qMySqlInitHandledByUser = TRUE;
+ } else {
+ qServerInit();
+ }
+}
+
+void QMYSQLDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new QMYSQLOpenExtension(this) );
+ d = new QMYSQLDriverPrivate();
+ d->mysql = 0;
+ qMySqlConnectionCount++;
+}
+
+QMYSQLDriver::~QMYSQLDriver()
+{
+ qMySqlConnectionCount--;
+ if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+ qServerEnd();
+
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool QMYSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
+#ifdef CLIENT_TRANSACTIONS
+ if ( d->mysql ) {
+ if ( ( d->mysql->server_capabilities & CLIENT_TRANSACTIONS ) == CLIENT_TRANSACTIONS )
+ return TRUE;
+ }
+#endif
+ return FALSE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QMYSQLDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QMYSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QMYSQLDriver::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+
+ unsigned int optionFlags = 0;
+
+ QStringList raw = QStringList::split( ';', connOpts );
+ QStringList opts;
+ QStringList::ConstIterator it;
+
+ // extract the real options from the string
+ for ( it = raw.begin(); it != raw.end(); ++it ) {
+ QString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 ) {
+ QString val( tmp.mid( idx + 1 ) );
+ val.simplifyWhiteSpace();
+ if ( val == "TRUE" || val == "1" )
+ opts << tmp.left( idx );
+ else
+ qWarning( "QMYSQLDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ } else {
+ opts << tmp;
+ }
+ }
+
+ for ( it = opts.begin(); it != opts.end(); ++it ) {
+ QString opt( (*it).upper() );
+ if ( opt == "CLIENT_COMPRESS" )
+ optionFlags |= CLIENT_COMPRESS;
+ else if ( opt == "CLIENT_FOUND_ROWS" )
+ optionFlags |= CLIENT_FOUND_ROWS;
+ else if ( opt == "CLIENT_IGNORE_SPACE" )
+ optionFlags |= CLIENT_IGNORE_SPACE;
+ else if ( opt == "CLIENT_INTERACTIVE" )
+ optionFlags |= CLIENT_INTERACTIVE;
+ else if ( opt == "CLIENT_NO_SCHEMA" )
+ optionFlags |= CLIENT_NO_SCHEMA;
+ else if ( opt == "CLIENT_ODBC" )
+ optionFlags |= CLIENT_ODBC;
+ else if ( opt == "CLIENT_SSL" )
+ optionFlags |= CLIENT_SSL;
+ else
+ qWarning( "QMYSQLDriver::open: Unknown connect option '%s'", (*it).latin1() );
+ }
+
+ if ( (d->mysql = mysql_init((MYSQL*) 0)) &&
+ mysql_real_connect( d->mysql,
+ host,
+ user,
+ password,
+ db.isNull() ? QString("") : db,
+ (port > -1) ? port : 0,
+ NULL,
+ optionFlags ) )
+ {
+ if ( !db.isEmpty() && mysql_select_db( d->mysql, db )) {
+ setLastError( qMakeError("Unable open database '" + db + "'", QSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ } else {
+ setLastError( qMakeError( "Unable to connect", QSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QMYSQLDriver::close()
+{
+ if ( isOpen() ) {
+ mysql_close( d->mysql );
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+QSqlQuery QMYSQLDriver::createQuery() const
+{
+ return QSqlQuery( new QMYSQLResult( this ) );
+}
+
+QStringList QMYSQLDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ if ( !typeName.isEmpty() && !(typeName.toInt() & (int)QSql::Tables) )
+ return tl;
+
+ MYSQL_RES* tableRes = mysql_list_tables( d->mysql, NULL );
+ MYSQL_ROW row;
+ int i = 0;
+ while ( tableRes && TRUE ) {
+ mysql_data_seek( tableRes, i );
+ row = mysql_fetch_row( tableRes );
+ if ( !row )
+ break;
+ tl.append( QString(row[0]) );
+ i++;
+ }
+ mysql_free_result( tableRes );
+ return tl;
+}
+
+QSqlIndex QMYSQLDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex idx;
+ if ( !isOpen() )
+ return idx;
+ QSqlQuery i = createQuery();
+ QString stmt( "show index from %1;" );
+ QSqlRecord fil = record( tablename );
+ i.exec( stmt.arg( tablename ) );
+ while ( i.isActive() && i.next() ) {
+ if ( i.value(2).toString() == "PRIMARY" ) {
+ idx.append( *fil.field( i.value(4).toString() ) );
+ idx.setCursorName( i.value(0).toString() );
+ idx.setName( i.value(2).toString() );
+ }
+ }
+ return idx;
+}
+
+QSqlRecord QMYSQLDriver::record( const QString& tablename ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return fil;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ QSqlField f ( QString( field->name ) , qDecodeMYSQLType( (int)field->type, field->flags ) );
+ fil.append ( f );
+ }
+ mysql_free_result( r );
+ return fil;
+}
+
+QSqlRecord QMYSQLDriver::record( const QSqlQuery& query ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ QMYSQLResult* result = (QMYSQLResult*)query.result();
+ QMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* f = mysql_fetch_field( p->result );
+ if ( f ) {
+ QSqlField fi( QString((const char*)f->name), qDecodeMYSQLType( f->type, f->flags ) );
+ fil.append( fi );
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return fil;
+}
+
+QSqlRecordInfo QMYSQLDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return info;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ info.append ( QSqlFieldInfo( QString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ QString( field->def ),
+ (int)field->type ) );
+ }
+ mysql_free_result( r );
+ return info;
+}
+
+QSqlRecordInfo QMYSQLDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ QMYSQLResult* result = (QMYSQLResult*)query.result();
+ QMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* field = mysql_fetch_field( p->result );
+ if ( field ) {
+ info.append ( QSqlFieldInfo( QString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ QVariant(),
+ (int)field->type ) );
+
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return info;
+}
+
+MYSQL* QMYSQLDriver::mysql()
+{
+ return d->mysql;
+}
+
+bool QMYSQLDriver::beginTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "BEGIN WORK" ) ) {
+ setLastError( qMakeError("Unable to begin transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QMYSQLDriver::commitTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "COMMIT" ) ) {
+ setLastError( qMakeError("Unable to commit transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QMYSQLDriver::rollbackTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "ROLLBACK" ) ) {
+ setLastError( qMakeError("Unable to rollback transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+QString QMYSQLDriver::formatValue( const QSqlField* field, bool trimStrings ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch( field->type() ) {
+ case QVariant::ByteArray: {
+
+ const QByteArray ba = field->value().toByteArray();
+ // buffer has to be at least length*2+1 bytes
+ char* buffer = new char[ ba.size() * 2 + 1 ];
+ /*uint escapedSize =*/ mysql_escape_string( buffer, ba.data(), ba.size() );
+ r.append("'").append(buffer).append("'");
+ delete[] buffer;
+ }
+ break;
+ case QVariant::String:
+ case QVariant::CString: {
+ // Escape '\' characters
+ r = QSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ default:
+ r = QSqlDriver::formatValue( field, trimStrings );
+ }
+ }
+ return r;
+}
diff --git a/src/sql/drivers/mysql/qsql_mysql.h b/src/sql/drivers/mysql/qsql_mysql.h
new file mode 100644
index 0000000..ff8ace6
--- /dev/null
+++ b/src/sql/drivers/mysql/qsql_mysql.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Definition of MySQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_MYSQL_H
+#define QSQL_MYSQL_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#endif
+
+#include <mysql.h>
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_MYSQL
+#else
+#define Q_EXPORT_SQLDRIVER_MYSQL Q_EXPORT
+#endif
+
+class QMYSQLDriverPrivate;
+class QMYSQLResultPrivate;
+class QMYSQLDriver;
+class QSqlRecordInfo;
+
+class QMYSQLResult : public QSqlResult
+{
+ friend class QMYSQLDriver;
+public:
+ QMYSQLResult( const QMYSQLDriver* db );
+ ~QMYSQLResult();
+
+ MYSQL_RES* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchNext();
+ bool fetchLast();
+ bool fetchFirst();
+ QVariant data( int field );
+ bool isNull( int field );
+ bool reset ( const QString& query );
+ int size();
+ int numRowsAffected();
+private:
+ QMYSQLResultPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_MYSQL QMYSQLDriver : public QSqlDriver
+{
+ friend class QMYSQLResult;
+public:
+ QMYSQLDriver( QObject * parent=0, const char * name=0 );
+ QMYSQLDriver( MYSQL * con, QObject * parent=0, const char * name=0 );
+ ~QMYSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+ MYSQL* mysql();
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ QMYSQLDriverPrivate* d;
+};
+
+
+#endif
diff --git a/src/sql/drivers/odbc/debian_qsql_odbc.h b/src/sql/drivers/odbc/debian_qsql_odbc.h
new file mode 100644
index 0000000..4b91f47
--- /dev/null
+++ b/src/sql/drivers/odbc/debian_qsql_odbc.h
@@ -0,0 +1,10 @@
+#ifdef UNICODE
+typedef SQLWCHAR SQLTCHAR;
+#else
+typedef SQLCHAR SQLTCHAR;
+#endif
+
+#define SQL_WCHAR (-8)
+#define SQL_WVARCHAR (-9)
+#define SQL_WLONGVARCHAR (-10)
+#define SQL_C_WCHAR SQL_WCHAR
diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp
new file mode 100644
index 0000000..b09afd2
--- /dev/null
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp
@@ -0,0 +1,2035 @@
+/****************************************************************************
+**
+** Implementation of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_odbc.h"
+#include <qsqlrecord.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#include <qapplication.h>
+#endif
+#include <qdatetime.h>
+#include <private/qsqlextension_p.h>
+#include <private/qinternal_p.h>
+#include <stdlib.h>
+
+// undefine this to prevent initial check of the ODBC driver
+#define ODBC_CHECK_DRIVER
+
+#if defined(Q_ODBC_VERSION_2)
+//crude hack to get non-unicode capable driver managers to work
+# undef UNICODE
+# define SQLTCHAR SQLCHAR
+# define SQL_C_WCHAR SQL_C_CHAR
+#endif
+
+// newer platform SDKs use SQLLEN instead of SQLINTEGER
+#ifdef SQLLEN
+# define QSQLLEN SQLLEN
+#else
+# define QSQLLEN SQLINTEGER
+#endif
+
+#ifdef SQLULEN
+# define QSQLULEN SQLULEN
+#else
+# define QSQLULEN SQLUINTEGER
+#endif
+
+
+static const QSQLLEN COLNAMESIZE = 256;
+//Map Qt parameter types to ODBC types
+static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
+
+class QODBCPrivate
+{
+public:
+ QODBCPrivate()
+ : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE)
+ {
+ sql_char_type = sql_varchar_type = sql_longvarchar_type = QVariant::CString;
+ unicode = FALSE;
+ }
+
+ SQLHANDLE hEnv;
+ SQLHANDLE hDbc;
+ SQLHANDLE hStmt;
+
+ bool unicode;
+ bool useSchema;
+ QVariant::Type sql_char_type;
+ QVariant::Type sql_varchar_type;
+ QVariant::Type sql_longvarchar_type;
+
+ QSqlRecordInfo rInf;
+
+ bool checkDriver() const;
+ void checkUnicode();
+ void checkSchemaUsage();
+ bool setConnectionOptions( const QString& connOpts );
+ void splitTableQualifier(const QString &qualifier, QString &catalog,
+ QString &schema, QString &table);
+};
+
+class QODBCPreparedExtension : public QSqlExtension
+{
+public:
+ QODBCPreparedExtension( QODBCResult * r )
+ : result( r ) {}
+
+ bool prepare( const QString& query )
+ {
+ return result->prepare( query );
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ QODBCResult * result;
+};
+
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+class QODBCOpenExtension : public QSqlOpenExtension
+{
+public:
+ QODBCOpenExtension( QODBCDriver *dri )
+ : QSqlOpenExtension(), driver(dri) {}
+ ~QODBCOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+private:
+ QODBCDriver *driver;
+};
+
+bool QODBCOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static QString qWarnODBCHandle(int handleType, SQLHANDLE handle)
+{
+ SQLINTEGER nativeCode_;
+ SQLSMALLINT msgLen;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
+ SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH];
+ r = SQLGetDiagRec( handleType,
+ handle,
+ 1,
+ (SQLTCHAR*)state_,
+ &nativeCode_,
+ (SQLTCHAR*)description_,
+ SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */
+ &msgLen);
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
+#ifdef UNICODE
+ return QString( (const QChar*)description_, (uint)msgLen );
+#else
+ return QString::fromLocal8Bit( (const char*)description_ );
+#endif
+ return QString::null;
+}
+
+static QString qODBCWarn( const QODBCPrivate* odbc)
+{
+ return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );
+}
+
+static void qSqlWarning( const QString& message, const QODBCPrivate* odbc )
+{
+#ifdef QT_CHECK_RANGE
+ qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );
+#endif
+}
+
+static QSqlError qMakeError( const QString& err, int type, const QODBCPrivate* p )
+{
+ return QSqlError( "QODBC3: " + err, qODBCWarn(p), type );
+}
+
+static QVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const QODBCPrivate* p )
+{
+ QVariant::Type type = QVariant::Invalid;
+ switch ( sqltype ) {
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ type = QVariant::Double;
+ break;
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIT:
+ case SQL_TINYINT:
+ type = QVariant::Int;
+ break;
+ case SQL_BIGINT:
+ type = QVariant::LongLong;
+ break;
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ type = QVariant::ByteArray;
+ break;
+ case SQL_DATE:
+ case SQL_TYPE_DATE:
+ type = QVariant::Date;
+ break;
+ case SQL_TIME:
+ case SQL_TYPE_TIME:
+ type = QVariant::Time;
+ break;
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIMESTAMP:
+ type = QVariant::DateTime;
+ break;
+#ifndef Q_ODBC_VERSION_2
+ case SQL_WCHAR:
+ case SQL_WVARCHAR:
+ case SQL_WLONGVARCHAR:
+ type = QVariant::String;
+ break;
+#endif
+ case SQL_CHAR:
+ type = p->sql_char_type;
+ break;
+ case SQL_VARCHAR:
+ type = p->sql_varchar_type;
+ break;
+ case SQL_LONGVARCHAR:
+ type = p->sql_longvarchar_type;
+ break;
+ default:
+ type = QVariant::CString;
+ break;
+ }
+ return type;
+}
+
+static QString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE )
+{
+ QString fieldVal;
+ SQLRETURN r = SQL_ERROR;
+ QSQLLEN lengthIndicator = 0;
+
+ if ( colSize <= 0 ) {
+ colSize = 256;
+ } else if ( colSize > 65536 ) { // limit buffer size to 64 KB
+ colSize = 65536;
+ } else {
+ colSize++; // make sure there is room for more than the 0 termination
+ if ( unicode ) {
+ colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call
+ }
+ }
+ char* buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = SQLGetData( hStmt,
+ column+1,
+ unicode ? SQL_C_WCHAR : SQL_C_CHAR,
+ (SQLPOINTER)buf,
+ (QSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) {
+ fieldVal = QString::null;
+ isNull = TRUE;
+ break;
+ }
+ // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it contains the
+ // total number of bytes that CAN be fetched
+ // colSize-1: remove 0 termination when there is more data to fetch
+ int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator;
+ if ( unicode ) {
+ fieldVal += QString( (QChar*) buf, rSize / 2 );
+ } else {
+ buf[ rSize ] = 0;
+ fieldVal += buf;
+ }
+ if ( lengthIndicator < colSize ) {
+ // workaround for Drivermanagers that don't return SQL_NO_DATA
+ break;
+ }
+ } else if ( r == SQL_NO_DATA ) {
+ break;
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "qGetStringData: Error while fetching data (%d)", r );
+#endif
+ fieldVal = QString::null;
+ break;
+ }
+ }
+ delete[] buf;
+ return fieldVal;
+}
+
+static QByteArray qGetBinaryData( SQLHANDLE hStmt, int column, QSQLLEN& lengthIndicator, bool& isNull )
+{
+ QByteArray fieldVal;
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ QSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+
+ SQLTCHAR colName[COLNAMESIZE];
+ r = SQLDescribeCol( hStmt,
+ column+1,
+ colName,
+ COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qWarning( "qGetBinaryData: Unable to describe column %d", column );
+#endif
+ // SQLDescribeCol may return 0 if size cannot be determined
+ if (!colSize) {
+ colSize = 256;
+ }
+ if ( colSize > 65536 ) { // read the field in 64 KB chunks
+ colSize = 65536;
+ }
+ char * buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_BINARY,
+ (SQLPOINTER) buf,
+ (QSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ break;
+ } else {
+ int rSize;
+ r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
+ if ( lengthIndicator == SQL_NO_TOTAL ) { // size cannot be determined
+ rSize = colSize;
+ }
+ // NB! This is not a memleak - the mem will be deleted by QByteArray when
+ // no longer ref'd
+ char * tmp = (char *) malloc( rSize + fieldVal.size() );
+ if ( fieldVal.size() ) {
+ memcpy( tmp, fieldVal.data(), fieldVal.size() );
+ }
+ memcpy( tmp + fieldVal.size(), buf, rSize );
+ fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize );
+
+ if ( r == SQL_SUCCESS ) { // the whole field was read in one chunk
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ delete [] buf;
+ return fieldVal;
+}
+
+static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ QSQLLEN intbuf = 0;
+ isNull = FALSE;
+ QSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SLONG,
+ (SQLPOINTER)&intbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0;
+ }
+ return (int)intbuf;
+}
+
+static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLDOUBLE dblbuf;
+ QSQLLEN lengthIndicator = 0;
+ isNull = FALSE;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_DOUBLE,
+ (SQLPOINTER)&dblbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0.0;
+ }
+
+ return (double) dblbuf;
+}
+
+static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLBIGINT lngbuf = Q_INT64_C( 0 );
+ isNull = FALSE;
+ QSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SBIGINT,
+ (SQLPOINTER) &lngbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA )
+ isNull = TRUE;
+
+ return lngbuf;
+}
+
+// creates a QSqlFieldInfo from a valid hStmt generated
+// by SQLColumns. The hStmt has to point to a valid position.
+static QSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const QODBCPrivate* p )
+{
+ bool isNull;
+ QString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode );
+ int type = qGetIntData( hStmt, 4, isNull ); // column type
+ int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag
+ // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ if ( required == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( required == SQL_NULLABLE ) {
+ required = 0;
+ } else {
+ required = -1;
+ }
+ int size = qGetIntData( hStmt, 6, isNull ); // column size
+ int prec = qGetIntData( hStmt, 8, isNull ); // precision
+ return QSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, QVariant(), type );
+}
+
+static QSqlFieldInfo qMakeFieldInfo( const QODBCPrivate* p, int i )
+{
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ QSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR colName[ COLNAMESIZE ];
+ r = SQLDescribeCol( p->hStmt,
+ i+1,
+ colName,
+ (QSQLULEN)COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable);
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( QString("qMakeField: Unable to describe column %1").arg(i), p );
+#endif
+ return QSqlFieldInfo();
+ }
+#ifdef UNICODE
+ QString qColName( (const QChar*)colName, (uint)colNameLen );
+#else
+ QString qColName = QString::fromLocal8Bit( (const char*)colName );
+#endif
+ // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ int required = -1;
+ if ( nullable == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( nullable == SQL_NULLABLE ) {
+ required = 0;
+ }
+ QVariant::Type type = qDecodeODBCType( colType, p );
+ return QSqlFieldInfo( qColName,
+ type,
+ required,
+ (int)colSize == 0 ? -1 : (int)colSize,
+ (int)colScale == 0 ? -1 : (int)colScale,
+ QVariant(),
+ (int)colType );
+}
+
+bool QODBCPrivate::setConnectionOptions( const QString& connOpts )
+{
+ // Set any connection attributes
+ QStringList raw = QStringList::split( ';', connOpts );
+ QStringList opts;
+ SQLRETURN r = SQL_SUCCESS;
+ QMap<QString, QString> connMap;
+ for ( QStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
+ QString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 )
+ connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
+ else
+ qWarning( "QODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ }
+ if ( connMap.count() ) {
+ QMap<QString, QString>::ConstIterator it;
+ QString opt, val;
+ SQLUINTEGER v = 0;
+ for ( it = connMap.begin(); it != connMap.end(); ++it ) {
+ opt = it.key().upper();
+ val = it.data().upper();
+ r = SQL_SUCCESS;
+ if ( opt == "SQL_ATTR_ACCESS_MODE" ) {
+ if ( val == "SQL_MODE_READ_ONLY" ) {
+ v = SQL_MODE_READ_ONLY;
+ } else if ( val == "SQL_MODE_READ_WRITE" ) {
+ v = SQL_MODE_READ_WRITE;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CONNECTION_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_LOGIN_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CURRENT_CATALOG" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CURRENT_CATALOG,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_METADATA_ID" ) {
+ if ( val == "SQL_TRUE" ) {
+ v = SQL_TRUE;
+ } else if ( val == "SQL_FALSE" ) {
+ v = SQL_FALSE;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_PACKET_SIZE" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_TRACEFILE" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACEFILE,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_TRACE" ) {
+ if ( val == "SQL_OPT_TRACE_OFF" ) {
+ v = SQL_OPT_TRACE_OFF;
+ } else if ( val == "SQL_OPT_TRACE_ON" ) {
+ v = SQL_OPT_TRACE_ON;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 );
+ }
+#ifdef QT_CHECK_RANGE
+ else {
+ qWarning( QString("QODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) );
+ }
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( QString("QODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this );
+#endif
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void QODBCPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
+ QString &schema, QString &table)
+{
+ if (!useSchema) {
+ table = qualifier;
+ return;
+ }
+ QStringList l = QStringList::split( ".", qualifier, TRUE );
+ if ( l.count() > 3 )
+ return; // can't possibly be a valid table qualifier
+ int i = 0, n = l.count();
+ if ( n == 1 ) {
+ table = qualifier;
+ } else {
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( n == 3 ) {
+ if ( i == 0 ) {
+ catalog = *it;
+ } else if ( i == 1 ) {
+ schema = *it;
+ } else if ( i == 2 ) {
+ table = *it;
+ }
+ } else if ( n == 2 ) {
+ if ( i == 0 ) {
+ schema = *it;
+ } else if ( i == 1 ) {
+ table = *it;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+QODBCResult::QODBCResult( const QODBCDriver * db, QODBCPrivate* p )
+: QSqlResult(db)
+{
+ d = new QODBCPrivate();
+ (*d) = (*p);
+ setExtension( new QODBCPreparedExtension( this ) );
+}
+
+QODBCResult::~QODBCResult()
+{
+ if ( d->hStmt && driver()->isOpen() ) {
+ SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
+#endif
+ }
+
+ delete d;
+}
+
+bool QODBCResult::reset ( const QString& query )
+{
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ // Always reallocate the statement handle - the statement attributes
+ // are not reset if SQLFreeStmt() is called which causes some problems.
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to free statement handle", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLExecDirect( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ QCString query8 = query.local8Bit();
+ r = SQLExecDirect( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+bool QODBCResult::fetch(int i)
+{
+ if ( isForwardOnly() && i < at() )
+ return FALSE;
+ if ( i == at() )
+ return TRUE;
+ fieldCache.clear();
+ nullCache.clear();
+ int actualIdx = i + 1;
+ if ( actualIdx <= 0 ) {
+ setAt( QSql::BeforeFirst );
+ return FALSE;
+ }
+ SQLRETURN r;
+ if ( isForwardOnly() ) {
+ bool ok = TRUE;
+ while ( ok && i > at() )
+ ok = fetchNext();
+ return ok;
+ } else {
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_ABSOLUTE,
+ actualIdx );
+ }
+ if ( r != SQL_SUCCESS ){
+ return FALSE;
+ }
+ setAt( i );
+ return TRUE;
+}
+
+bool QODBCResult::fetchNext()
+{
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchFirst()
+{
+ if ( isForwardOnly() && at() != QSql::BeforeFirst )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ if ( isForwardOnly() ) {
+ return fetchNext();
+ }
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_FIRST,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( 0 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchPrior()
+{
+ if ( isForwardOnly() )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_PRIOR,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() - 1 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchLast()
+{
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+
+ if ( isForwardOnly() ) {
+ // cannot seek to last row in forwardOnly mode, so we have to use brute force
+ int i = at();
+ if ( i == QSql::AfterLast )
+ return FALSE;
+ if ( i == QSql::BeforeFirst )
+ i = 0;
+ while ( fetchNext() )
+ ++i;
+ setAt( i );
+ return TRUE;
+ }
+
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_LAST,
+ 0 );
+ if ( r != SQL_SUCCESS ) {
+ return FALSE;
+ }
+ SQLINTEGER currRow;
+ r = SQLGetStmtAttr( d->hStmt,
+ SQL_ROW_NUMBER,
+ &currRow,
+ SQL_IS_INTEGER,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( currRow-1 );
+ return TRUE;
+}
+
+QVariant QODBCResult::data( int field )
+{
+ if ( field >= (int) d->rInf.count() ) {
+ qWarning( "QODBCResult::data: column %d out of range", field );
+ return QVariant();
+ }
+ if ( fieldCache.contains( field ) )
+ return fieldCache[ field ];
+ SQLRETURN r(0);
+ QSQLLEN lengthIndicator = 0;
+ bool isNull = FALSE;
+ int current = fieldCache.count();
+ for ( ; current < (field + 1); ++current ) {
+ const QSqlFieldInfo info = d->rInf[ current ];
+ switch ( info.type() ) {
+ case QVariant::LongLong:
+ fieldCache[ current ] = QVariant( (Q_LLONG) qGetBigIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Int:
+ fieldCache[ current ] = QVariant( qGetIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Date:
+ DATE_STRUCT dbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_DATE,
+ (SQLPOINTER)&dbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QDate( dbuf.year, dbuf.month, dbuf.day ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QDate() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::Time:
+ TIME_STRUCT tbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIME,
+ (SQLPOINTER)&tbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QTime( tbuf.hour, tbuf.minute, tbuf.second ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::DateTime:
+ TIMESTAMP_STRUCT dtbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIMESTAMP,
+ (SQLPOINTER)&dtbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QDateTime( QDate( dtbuf.year, dtbuf.month, dtbuf.day ), QTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QDateTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::ByteArray: {
+ isNull = FALSE;
+ QByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull );
+ fieldCache[ current ] = QVariant( val );
+ nullCache[ current ] = isNull;
+ break; }
+ case QVariant::String:
+ isNull = FALSE;
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, TRUE ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Double:
+ if ( info.typeID() == SQL_DECIMAL || info.typeID() == SQL_NUMERIC )
+ // bind Double values as string to prevent loss of precision
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma
+ else
+ fieldCache[ current ] = QVariant( qGetDoubleData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::CString:
+ default:
+ isNull = FALSE;
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, FALSE ) );
+ nullCache[ current ] = isNull;
+ break;
+ }
+ }
+ return fieldCache[ --current ];
+}
+
+bool QODBCResult::isNull( int field )
+{
+ if ( !fieldCache.contains( field ) ) {
+ // since there is no good way to find out whether the value is NULL
+ // without fetching the field we'll fetch it here.
+ // (data() also sets the NULL flag)
+ data( field );
+ }
+ return nullCache[ field ];
+}
+
+int QODBCResult::size()
+{
+ return -1;
+}
+
+int QODBCResult::numRowsAffected()
+{
+ QSQLLEN affectedRowCount(0);
+ SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount );
+ if ( r == SQL_SUCCESS )
+ return affectedRowCount;
+#ifdef QT_CHECK_RANGE
+ else
+ qSqlWarning( "QODBCResult::numRowsAffected: Unable to count affected rows", d );
+#endif
+ return -1;
+}
+
+bool QODBCResult::prepare( const QString& query )
+{
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to close statement", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLPrepare( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ QCString query8 = query.local8Bit();
+ r = SQLPrepare( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to prepare statement", d );
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QODBCResult::exec()
+{
+ SQLRETURN r;
+ QPtrList<QVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit
+ tmpStorage.setAutoDelete( TRUE );
+
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ d->rInf.clear();
+
+ if ( !d->hStmt ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::exec: No statement handle available", d );
+#endif
+ return FALSE;
+ } else {
+ r = SQLFreeStmt( d->hStmt, SQL_CLOSE );
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCResult::exec: Unable to close statement handle", d );
+ return FALSE;
+ }
+ }
+
+ // bind parameters - only positional binding allowed
+ if ( extension()->index.count() > 0 ) {
+ QMap<int, QString>::Iterator it;
+ int para = 1;
+ QVariant val;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+ val = extension()->values[ it.data() ].value;
+ QSQLLEN *ind = new QSQLLEN( SQL_NTS );
+ tmpStorage.append( qAutoDeleter(ind) );
+ if ( val.isNull() ) {
+ *ind = SQL_NULL_DATA;
+ }
+ switch ( val.type() ) {
+ case QVariant::Date: {
+ DATE_STRUCT * dt = new DATE_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QDate qdt = val.toDate();
+ dt->year = qdt.year();
+ dt->month = qdt.month();
+ dt->day = qdt.day();
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DATE,
+ SQL_DATE,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Time: {
+ TIME_STRUCT * dt = new TIME_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QTime qdt = val.toTime();
+ dt->hour = qdt.hour();
+ dt->minute = qdt.minute();
+ dt->second = qdt.second();
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIME,
+ SQL_TIME,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QDateTime qdt = val.toDateTime();
+ dt->year = qdt.date().year();
+ dt->month = qdt.date().month();
+ dt->day = qdt.date().day();
+ dt->hour = qdt.time().hour();
+ dt->minute = qdt.time().minute();
+ dt->second = qdt.time().second();
+ dt->fraction = 0;
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIMESTAMP,
+ SQL_TIMESTAMP,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Int: {
+ int * v = new int( val.toInt() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_SLONG,
+ SQL_INTEGER,
+ 0,
+ 0,
+ (void *) v,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Double: {
+ double * v = new double( val.toDouble() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DOUBLE,
+ SQL_DOUBLE,
+ 0,
+ 0,
+ (void *) v,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::ByteArray: {
+ if ( *ind != SQL_NULL_DATA ) {
+ *ind = val.asByteArray().size();
+ }
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_BINARY,
+ SQL_LONGVARBINARY,
+ val.asByteArray().size(),
+ 0,
+ (void *) val.asByteArray().data(),
+ (QSQLLEN)val.asByteArray().size(),
+ ind );
+ break; }
+#ifndef Q_ODBC_VERSION_2
+ case QVariant::String:
+ if ( d->unicode ) {
+ QString * str = new QString( val.asString() );
+ str->ucs2();
+ int len = str->length()*2;
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_WCHAR,
+ len > 8000 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ len > 8000 ? len : 0,
+ 0,
+ (void *) str->unicode(),
+ (QSQLLEN) len,
+ ind );
+ break;
+ }
+#endif
+ // fall through
+ default: {
+ QCString * str = new QCString( val.asString().local8Bit() );
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_CHAR,
+ str->length() > 4000 ? SQL_LONGVARCHAR : SQL_VARCHAR,
+ str->length() + 1,
+ 0,
+ (void *) str->data(),
+ (QSQLLEN)(str->length() + 1),
+ ind );
+ break; }
+ }
+ para++;
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to bind variable", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ }
+ }
+ r = SQLExecute( d->hStmt );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+
+ //get out parameters
+ if ( extension()->index.count() > 0 ) {
+ QMap<int, QString>::Iterator it;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+
+ SQLINTEGER* indPtr = qAutoDeleterData( (QAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() );
+ if ( !indPtr )
+ return FALSE;
+ bool isNull = (*indPtr == SQL_NULL_DATA);
+ tmpStorage.removeFirst();
+
+ QVariant::Type type = extension()->values[ it.data() ].value.type();
+ if ( isNull ) {
+ QVariant v;
+ v.cast(type);
+ extension()->values[ it.data() ].value = v;
+ if (type != QVariant::ByteArray)
+ tmpStorage.removeFirst();
+ continue;
+ }
+
+ switch (type) {
+ case QVariant::Date: {
+ DATE_STRUCT * ds = qAutoDeleterData( (QAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QDate( ds->year, ds->month, ds->day ) );
+ break; }
+ case QVariant::Time: {
+ TIME_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QTime( dt->hour, dt->minute, dt->second ) );
+ break; }
+ case QVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QDateTime( QDate( dt->year, dt->month, dt->day ),
+ QTime( dt->hour, dt->minute, dt->second ) ) );
+ break; }
+ case QVariant::Int: {
+ int * v = qAutoDeleterData( (QAutoDeleter<int>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *v );
+ break; }
+ case QVariant::Double: {
+ double * v = qAutoDeleterData( (QAutoDeleter<double>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *v );
+ break; }
+ case QVariant::ByteArray:
+ break;
+ case QVariant::String:
+ if ( d->unicode ) {
+ QString * str = qAutoDeleterData( (QAutoDeleter<QString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *str );
+ break;
+ }
+ // fall through
+ default: {
+ QCString * str = qAutoDeleterData( (QAutoDeleter<QCString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *str );
+ break; }
+ }
+ if (type != QVariant::ByteArray)
+ tmpStorage.removeFirst();
+ }
+ }
+
+ return TRUE;
+}
+
+////////////////////////////////////////
+
+
+QODBCDriver::QODBCDriver( QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QODBC")
+{
+ init();
+}
+
+QODBCDriver::QODBCDriver( SQLHANDLE env, SQLHANDLE con, QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QODBC")
+{
+ init();
+ d->hEnv = env;
+ d->hDbc = con;
+ if ( env && con ) {
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void QODBCDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new QODBCOpenExtension(this) );
+ d = new QODBCPrivate();
+}
+
+QODBCDriver::~QODBCDriver()
+{
+ cleanup();
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool QODBCDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions: {
+ if ( !d->hDbc )
+ return FALSE;
+ SQLUSMALLINT txn;
+ SQLSMALLINT t;
+ int r = SQLGetInfo( d->hDbc,
+ (SQLUSMALLINT)SQL_TXN_CAPABLE,
+ &txn,
+ sizeof(txn),
+ &t);
+ if ( r != SQL_SUCCESS || txn == SQL_TC_NONE )
+ return FALSE;
+ else
+ return TRUE;
+ }
+ case QuerySize:
+ return FALSE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return d->unicode;
+ case PreparedQueries:
+ return TRUE;
+ case PositionalPlaceholders:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QODBCDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QODBCDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QODBCDriver::open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString &,
+ int,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ SQLRETURN r;
+ r = SQLAllocHandle( SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &d->hEnv);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::open: Unable to allocate environment", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ r = SQLSetEnvAttr( d->hEnv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER)SQL_OV_ODBC2,
+ SQL_IS_UINTEGER );
+ r = SQLAllocHandle( SQL_HANDLE_DBC,
+ d->hEnv,
+ &d->hDbc);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::open: Unable to allocate connection", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->setConnectionOptions( connOpts ) )
+ return FALSE;
+
+ // Create the connection string
+ QString connQStr;
+ // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
+ if ( db.contains(".dsn") )
+ connQStr = "FILEDSN=" + db;
+ else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) )
+ connQStr = db;
+ else
+ connQStr = "DSN=" + db;
+ connQStr += ";UID=" + user + ";PWD=" + password;
+ SQLSMALLINT cb;
+ SQLTCHAR connOut[1024];
+ r = SQLDriverConnect( d->hDbc,
+ NULL,
+#ifdef UNICODE
+ (SQLWCHAR*)connQStr.unicode(),
+#else
+ (SQLCHAR*)connQStr.latin1(),
+#endif
+ (SQLSMALLINT)connQStr.length(),
+ connOut,
+ 1024,
+ &cb,
+ SQL_DRIVER_NOPROMPT );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to connect", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->checkDriver() ) {
+ setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ d->checkUnicode();
+ d->checkSchemaUsage();
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QODBCDriver::close()
+{
+ cleanup();
+ setOpen( FALSE );
+ setOpenError( FALSE );
+}
+
+void QODBCDriver::cleanup()
+{
+ SQLRETURN r;
+ if ( !d )
+ return;
+
+ if( d->hDbc ) {
+ // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
+ if ( isOpen() ) {
+ r = SQLDisconnect( d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::disconnect: Unable to disconnect datasource", d );
+#endif
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::cleanup: Unable to free connection handle", d );
+#endif
+ d->hDbc = 0;
+ }
+
+ if ( d->hEnv ) {
+ r = SQLFreeHandle( SQL_HANDLE_ENV, d->hEnv );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::cleanup: Unable to free environment handle", d );
+#endif
+ d->hEnv = 0;
+ }
+}
+
+// checks whether the server can return char, varchar and longvarchar
+// as two byte unicode characters
+void QODBCPrivate::checkUnicode()
+{
+#if defined(Q_WS_WIN)
+ if ( !qt_winunicode ) {
+ unicode = FALSE;
+ return;
+ }
+#endif
+ SQLRETURN r;
+ SQLUINTEGER fFunc;
+
+ unicode = FALSE;
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_CHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WCHAR ) ) {
+ sql_char_type = QVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_VARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WVARCHAR ) ) {
+ sql_varchar_type = QVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_LONGVARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WLONGVARCHAR ) ) {
+ sql_longvarchar_type = QVariant::String;
+ unicode = TRUE;
+ }
+}
+
+bool QODBCPrivate::checkDriver() const
+{
+#ifdef ODBC_CHECK_DRIVER
+ // do not query for SQL_API_SQLFETCHSCROLL because it can't be used at this time
+ static const SQLUSMALLINT reqFunc[] = {
+ SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
+ SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
+ SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
+ };
+
+ // these functions are optional
+ static const SQLUSMALLINT optFunc[] = {
+ SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
+ };
+
+ SQLRETURN r;
+ SQLUSMALLINT sup;
+
+
+ int i;
+ // check the required functions
+ for ( i = 0; reqFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning ( "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). "
+ "Please look at the Qt SQL Module Driver documentation for more information.", reqFunc[ i ] );
+#endif
+ return FALSE;
+ }
+ }
+
+ // these functions are optional and just generate a warning
+ for ( i = 0; optFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, optFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] );
+#endif
+ return TRUE;
+ }
+ }
+#endif //ODBC_CHECK_DRIVER
+
+ return TRUE;
+}
+
+void QODBCPrivate::checkSchemaUsage()
+{
+ SQLRETURN r;
+ SQLUINTEGER val;
+
+ r = SQLGetInfo(hDbc,
+ SQL_SCHEMA_USAGE,
+ (SQLPOINTER) &val,
+ sizeof(val),
+ NULL);
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ useSchema = (val != 0);
+}
+
+QSqlQuery QODBCDriver::createQuery() const
+{
+ return QSqlQuery( new QODBCResult( this, d ) );
+}
+
+bool QODBCDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac) );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to disable autocommit", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QODBCDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_COMMIT );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError("Unable to commit transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool QODBCDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_ROLLBACK );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to rollback transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool QODBCDriver::endTrans()
+{
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac));
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to enable autocommit", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+QStringList QODBCDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ SQLHANDLE hStmt;
+
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::tables: Unable to allocate handle", d );
+#endif
+ return tl;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ QString tableType;
+ if ( typeName.isEmpty() || ((type & (int)QSql::Tables) == (int)QSql::Tables) )
+ tableType += "TABLE,";
+ if ( (type & (int)QSql::Views) == (int)QSql::Views )
+ tableType += "VIEW,";
+ if ( (type & (int)QSql::SystemTables) == (int)QSql::SystemTables )
+ tableType += "SYSTEM TABLE,";
+ if ( tableType.isEmpty() )
+ return tl;
+ tableType.truncate( tableType.length() - 1 );
+
+ r = SQLTables( hStmt,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0,
+#ifdef UNICODE
+ (SQLWCHAR*)tableType.unicode(),
+#else
+ (SQLCHAR*)tableType.latin1(),
+#endif
+ tableType.length() /* characters, not bytes */ );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::tables Unable to execute table list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ while ( r == SQL_SUCCESS ) {
+ bool isNull;
+ QString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->unicode );
+ tl.append( fieldVal );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
+ return tl;
+}
+
+QSqlIndex QODBCDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex index( tablename );
+ if ( !isOpen() )
+ return index;
+ bool usingSpecialColumns = FALSE;
+ QSqlRecord rec = record( tablename );
+
+ SQLHANDLE hStmt;
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::primaryIndex: Unable to list primary key", d );
+#endif
+ return index;
+ }
+ QString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLPrimaryKeys( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length() /* in characters, not in bytes */);
+
+ // if the SQLPrimaryKeys() call does not succeed (e.g the driver
+ // does not support it) - try an alternative method to get hold of
+ // the primary index (e.g MS Access and FoxPro)
+ if ( r != SQL_SUCCESS ) {
+ r = SQLSpecialColumns( hStmt,
+ SQL_BEST_ROWID,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+
+ table.length(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE );
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::primaryIndex: Unable to execute primary key list", d );
+#endif
+ } else {
+ usingSpecialColumns = TRUE;
+ }
+ }
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ bool isNull;
+ int fakeId = 0;
+ QString cName, idxName;
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == SQL_SUCCESS ) {
+ if ( usingSpecialColumns ) {
+ cName = qGetStringData( hStmt, 1, -1, isNull, d->unicode ); // column name
+ idxName = QString::number( fakeId++ ); // invent a fake index name
+ } else {
+ cName = qGetStringData( hStmt, 3, -1, isNull, d->unicode ); // column name
+ idxName = qGetStringData( hStmt, 5, -1, isNull, d->unicode ); // pk index name
+ }
+ QSqlField *fld = rec.field(cName);
+ if (fld)
+ index.append(*fld);
+ index.setName( idxName );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ }
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
+ return index;
+}
+
+QSqlRecord QODBCDriver::record( const QString& tablename ) const
+{
+ return recordInfo( tablename ).toRecord();
+}
+
+QSqlRecord QODBCDriver::record( const QSqlQuery& query ) const
+{
+ return recordInfo( query ).toRecord();
+}
+
+QSqlRecordInfo QODBCDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+
+ SQLHANDLE hStmt;
+ QString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::record: Unable to allocate handle", d );
+#endif
+ return fil;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLColumns( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length(),
+ NULL,
+ 0 );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::record: Unable to execute column list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == SQL_SUCCESS ) {
+
+ fil.append( qMakeFieldInfo( hStmt, d ) );
+
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
+
+ return fil;
+}
+
+QSqlRecordInfo QODBCDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ QODBCResult* result = (QODBCResult*)query.result();
+ fil = result->d->rInf;
+ }
+ return fil;
+}
+
+SQLHANDLE QODBCDriver::environment()
+{
+ return d->hEnv;
+}
+
+SQLHANDLE QODBCDriver::connection()
+{
+ return d->hDbc;
+}
+
+QString QODBCDriver::formatValue( const QSqlField* field,
+ bool trimStrings ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else if ( field->type() == QVariant::DateTime ) {
+ // Use an escape sequence for the datetime fields
+ if ( field->value().toDateTime().isValid() ){
+ QDate dt = field->value().toDateTime().date();
+ QTime tm = field->value().toDateTime().time();
+ // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
+ r = "{ ts '" +
+ QString::number(dt.year()) + "-" +
+ QString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" +
+ QString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " +
+ tm.toString() +
+ "' }";
+ } else
+ r = nullText();
+ } else if ( field->type() == QVariant::ByteArray ) {
+ QByteArray ba = field->value().toByteArray();
+ QString res;
+ static const char hexchars[] = "0123456789abcdef";
+ for ( uint i = 0; i < ba.size(); ++i ) {
+ uchar s = (uchar) ba[(int)i];
+ res += hexchars[s >> 4];
+ res += hexchars[s & 0x0f];
+ }
+ r = "0x" + res;
+ } else {
+ r = QSqlDriver::formatValue( field, trimStrings );
+ }
+ return r;
+}
diff --git a/src/sql/drivers/odbc/qsql_odbc.h b/src/sql/drivers/odbc/qsql_odbc.h
new file mode 100644
index 0000000..67285c8
--- /dev/null
+++ b/src/sql/drivers/odbc/qsql_odbc.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Definition of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_ODBC_H
+#define QSQL_ODBC_H
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qsqldriver.h>
+#include <qsqlfield.h>
+#include <qsqlresult.h>
+#include <qsqlindex.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#endif
+
+#if defined (Q_OS_MAC)
+// assume we use iodbc on MAC
+// comment next line out if you use a
+// unicode compatible manager
+# define Q_ODBC_VERSION_2
+#endif
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_ODBC
+#else
+#define Q_EXPORT_SQLDRIVER_ODBC Q_EXPORT
+#endif
+
+#ifdef Q_OS_UNIX
+#define HAVE_LONG_LONG 1 // force UnixODBC NOT to fall back to a struct for BIGINTs
+#endif
+
+#if defined(Q_CC_BOR)
+// workaround for Borland to make sure that SQLBIGINT is defined
+# define _MSC_VER 900
+#endif
+#include <sql.h>
+#if defined(Q_CC_BOR)
+# undef _MSC_VER
+#endif
+
+#include <sqlext.h>
+#include "debian_qsql_odbc.h"
+
+class QODBCPrivate;
+class QODBCDriver;
+class QSqlRecordInfo;
+
+class QODBCResult : public QSqlResult
+{
+ friend class QODBCDriver;
+public:
+ QODBCResult( const QODBCDriver * db, QODBCPrivate* p );
+ ~QODBCResult();
+
+ SQLHANDLE statement();
+ bool prepare( const QString& query );
+ bool exec();
+
+protected:
+ bool fetchNext();
+ bool fetchFirst();
+ bool fetchLast();
+ bool fetchPrior();
+ bool fetch(int i);
+ bool reset ( const QString& query );
+ QVariant data( int field );
+ bool isNull( int field );
+ int size();
+ int numRowsAffected();
+private:
+ QODBCPrivate* d;
+ typedef QMap<int,QVariant> FieldCache;
+ FieldCache fieldCache;
+ typedef QMap<int,bool> NullCache;
+ NullCache nullCache;
+};
+
+class Q_EXPORT_SQLDRIVER_ODBC QODBCDriver : public QSqlDriver
+{
+public:
+ QODBCDriver( QObject * parent=0, const char * name=0 );
+ QODBCDriver( SQLHANDLE env, SQLHANDLE con, QObject * parent=0, const char * name=0 );
+ ~QODBCDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ SQLHANDLE environment();
+ SQLHANDLE connection();
+
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ bool endTrans();
+ void cleanup();
+ QODBCPrivate* d;
+};
+
+#endif
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp
new file mode 100644
index 0000000..7fe1a91
--- /dev/null
+++ b/src/sql/drivers/psql/qsql_psql.cpp
@@ -0,0 +1,1117 @@
+/****************************************************************************
+**
+** Implementation of PostgreSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_psql.h"
+#include <private/qsqlextension_p.h>
+
+#include <math.h>
+
+#include <qpointarray.h>
+#include <qsqlrecord.h>
+#include <qregexp.h>
+#include <qdatetime.h>
+// PostgreSQL header <utils/elog.h> included by <postgres.h> redefines DEBUG.
+#if defined(DEBUG)
+# undef DEBUG
+#endif
+#include <postgres.h>
+#include <libpq/libpq-fs.h>
+// PostgreSQL header <catalog/pg_type.h> redefines errno erroneously.
+#if defined(errno)
+# undef errno
+#endif
+#define errno qt_psql_errno
+#include <catalog/pg_type.h>
+#undef errno
+#ifdef open
+# undef open
+#endif
+
+QPtrDict<QSqlDriverExtension> *qSqlDriverExtDict();
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+class QPSQLPrivate
+{
+public:
+ QPSQLPrivate():connection(0), result(0), isUtf8(FALSE) {}
+ PGconn *connection;
+ PGresult *result;
+ bool isUtf8;
+};
+
+class QPSQLDriverExtension : public QSqlDriverExtension
+{
+public:
+ QPSQLDriverExtension( QPSQLDriver *dri )
+ : QSqlDriverExtension(), driver(dri) { }
+ ~QPSQLDriverExtension() {}
+
+ bool isOpen() const;
+private:
+ QPSQLDriver *driver;
+};
+
+bool QPSQLDriverExtension::isOpen() const
+{
+ return PQstatus( driver->connection() ) == CONNECTION_OK;
+}
+
+class QPSQLOpenExtension : public QSqlOpenExtension
+{
+public:
+ QPSQLOpenExtension( QPSQLDriver *dri )
+ : QSqlOpenExtension(), driver(dri) { }
+ ~QPSQLOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+private:
+ QPSQLDriver *driver;
+};
+
+bool QPSQLOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static QSqlError qMakeError( const QString& err, int type, const QPSQLPrivate* p )
+{
+ const char *s = PQerrorMessage(p->connection);
+ QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s);
+ return QSqlError("QPSQL: " + err, msg, type);
+}
+
+static QVariant::Type qDecodePSQLType( int t )
+{
+ QVariant::Type type = QVariant::Invalid;
+ switch ( t ) {
+ case BOOLOID :
+ type = QVariant::Bool;
+ break;
+ case INT8OID :
+ type = QVariant::LongLong;
+ break;
+ case INT2OID :
+ // case INT2VECTOROID : // 7.x
+ case INT4OID :
+ type = QVariant::Int;
+ break;
+ case NUMERICOID :
+ case FLOAT4OID :
+ case FLOAT8OID :
+ type = QVariant::Double;
+ break;
+ case ABSTIMEOID :
+ case RELTIMEOID :
+ case DATEOID :
+ type = QVariant::Date;
+ break;
+ case TIMEOID :
+#ifdef TIMETZOID // 7.x
+ case TIMETZOID :
+#endif
+ type = QVariant::Time;
+ break;
+ case TIMESTAMPOID :
+#ifdef DATETIMEOID
+ // Postgres 6.x datetime workaround.
+ // DATETIMEOID == TIMESTAMPOID (only the names have changed)
+ case DATETIMEOID :
+#endif
+#ifdef TIMESTAMPTZOID
+ // Postgres 7.2 workaround
+ // TIMESTAMPTZOID == TIMESTAMPOID == DATETIMEOID
+ case TIMESTAMPTZOID :
+#endif
+ type = QVariant::DateTime;
+ break;
+ // case ZPBITOID : // 7.x
+ // case VARBITOID : // 7.x
+ case OIDOID :
+ case BYTEAOID :
+ type = QVariant::ByteArray;
+ break;
+ case REGPROCOID :
+ case TIDOID :
+ case XIDOID :
+ case CIDOID :
+ // case OIDVECTOROID : // 7.x
+ case UNKNOWNOID :
+ // case TINTERVALOID : // 7.x
+ type = QVariant::Invalid;
+ break;
+ default:
+ case CHAROID :
+ case BPCHAROID :
+ // case LZTEXTOID : // 7.x
+ case VARCHAROID :
+ case TEXTOID :
+ case NAMEOID :
+ case CASHOID :
+ case INETOID :
+ case CIDROID :
+ case CIRCLEOID :
+ type = QVariant::String;
+ break;
+ }
+ return type;
+}
+
+QPSQLResult::QPSQLResult( const QPSQLDriver* db, const QPSQLPrivate* p )
+: QSqlResult( db ),
+ currentSize( 0 )
+{
+ d = new QPSQLPrivate();
+ (*d) = (*p);
+}
+
+QPSQLResult::~QPSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+PGresult* QPSQLResult::result()
+{
+ return d->result;
+}
+
+void QPSQLResult::cleanup()
+{
+ if ( d->result )
+ PQclear( d->result );
+ d->result = 0;
+ setAt( -1 );
+ currentSize = 0;
+ setActive( FALSE );
+}
+
+bool QPSQLResult::fetch( int i )
+{
+ if ( !isActive() )
+ return FALSE;
+ if ( i < 0 )
+ return FALSE;
+ if ( i >= currentSize )
+ return FALSE;
+ if ( at() == i )
+ return TRUE;
+ setAt( i );
+ return TRUE;
+}
+
+bool QPSQLResult::fetchFirst()
+{
+ return fetch( 0 );
+}
+
+bool QPSQLResult::fetchLast()
+{
+ return fetch( PQntuples( d->result ) - 1 );
+}
+
+// some Postgres conversions
+static QPoint pointFromString( const QString& s)
+{
+ // format '(x,y)'
+ int pivot = s.find( ',' );
+ if ( pivot != -1 ) {
+ int x = s.mid( 1, pivot-1 ).toInt();
+ int y = s.mid( pivot+1, s.length()-pivot-2 ).toInt();
+ return QPoint( x, y ) ;
+ } else
+ return QPoint();
+}
+
+QVariant QPSQLResult::data( int i )
+{
+ if ( i >= PQnfields( d->result ) ) {
+ qWarning( "QPSQLResult::data: column %d out of range", i );
+ return QVariant();
+ }
+ int ptype = PQftype( d->result, i );
+ QVariant::Type type = qDecodePSQLType( ptype );
+ const QString val = ( d->isUtf8 && ptype != BYTEAOID ) ?
+ QString::fromUtf8( PQgetvalue( d->result, at(), i ) ) :
+ QString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) );
+ if ( PQgetisnull( d->result, at(), i ) ) {
+ QVariant v;
+ v.cast( type );
+ return v;
+ }
+ switch ( type ) {
+ case QVariant::Bool:
+ {
+ QVariant b ( (bool)(val == "t"), 0 );
+ return ( b );
+ }
+ case QVariant::String:
+ return QVariant( val );
+ case QVariant::LongLong:
+ if ( val[0] == '-' )
+ return QVariant( val.toLongLong() );
+ else
+ return QVariant( val.toULongLong() );
+ case QVariant::Int:
+ return QVariant( val.toInt() );
+ case QVariant::Double:
+ if ( ptype == NUMERICOID )
+ return QVariant( val );
+ return QVariant( val.toDouble() );
+ case QVariant::Date:
+ if ( val.isEmpty() ) {
+ return QVariant( QDate() );
+ } else {
+ return QVariant( QDate::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::Time:
+ if ( val.isEmpty() )
+ return QVariant( QTime() );
+ if ( val.at( val.length() - 3 ) == '+' )
+ // strip the timezone
+ return QVariant( QTime::fromString( val.left( val.length() - 3 ), Qt::ISODate ) );
+ return QVariant( QTime::fromString( val, Qt::ISODate ) );
+ case QVariant::DateTime: {
+ if ( val.length() < 10 )
+ return QVariant( QDateTime() );
+ // remove the timezone
+ QString dtval = val;
+ if ( dtval.at( dtval.length() - 3 ) == '+' )
+ dtval.truncate( dtval.length() - 3 );
+ // milliseconds are sometimes returned with 2 digits only
+ if ( dtval.at( dtval.length() - 3 ).isPunct() )
+ dtval += '0';
+ if ( dtval.isEmpty() )
+ return QVariant( QDateTime() );
+ else
+ return QVariant( QDateTime::fromString( dtval, Qt::ISODate ) );
+ }
+ case QVariant::Point:
+ return QVariant( pointFromString( val ) );
+ case QVariant::Rect: // format '(x,y),(x',y')'
+ {
+ int pivot = val.find( "),(" );
+ if ( pivot != -1 )
+ return QVariant( QRect( pointFromString( val.mid(pivot+2,val.length()) ), pointFromString( val.mid(0,pivot+1) ) ) );
+ return QVariant( QRect() );
+ }
+ case QVariant::PointArray: // format '((x,y),(x1,y1),...,(xn,yn))'
+ {
+ QRegExp pointPattern("\\([0-9-]*,[0-9-]*\\)");
+ int points = val.contains( pointPattern );
+ QPointArray parray( points );
+ int idx = 1;
+ for ( int i = 0; i < points; i++ ){
+ int start = val.find( pointPattern, idx );
+ int end = -1;
+ if ( start != -1 ) {
+ end = val.find( ')', start+1 );
+ if ( end != -1 ) {
+ parray.setPoint( i, pointFromString( val.mid(idx, end-idx+1) ) );
+ }
+ else
+ parray.setPoint( i, QPoint() );
+ } else {
+ parray.setPoint( i, QPoint() );
+ break;
+ }
+ idx = end+2;
+ }
+ return QVariant( parray );
+ }
+ case QVariant::ByteArray: {
+ if ( ptype == BYTEAOID ) {
+ uint i = 0;
+ int index = 0;
+ uint len = val.length();
+ static const QChar backslash( '\\' );
+ QByteArray ba( (int)len );
+ while ( i < len ) {
+ if ( val.at( i ) == backslash ) {
+ if ( val.at( i + 1 ).isDigit() ) {
+ ba[ index++ ] = (char)(val.mid( i + 1, 3 ).toInt( 0, 8 ));
+ i += 4;
+ } else {
+ ba[ index++ ] = val.at( i + 1 );
+ i += 2;
+ }
+ } else {
+ ba[ index++ ] = val.at( i++ ).unicode();
+ }
+ }
+ ba.resize( index );
+ return QVariant( ba );
+ }
+
+ QByteArray ba;
+ ((QSqlDriver*)driver())->beginTransaction();
+ Oid oid = val.toInt();
+ int fd = lo_open( d->connection, oid, INV_READ );
+#ifdef QT_CHECK_RANGE
+ if ( fd < 0) {
+ qWarning( "QPSQLResult::data: unable to open large object for read" );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+#endif
+ int size = 0;
+ int retval = lo_lseek( d->connection, fd, 0L, SEEK_END );
+ if ( retval >= 0 ) {
+ size = lo_tell( d->connection, fd );
+ lo_lseek( d->connection, fd, 0L, SEEK_SET );
+ }
+ if ( size == 0 ) {
+ lo_close( d->connection, fd );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+ char * buf = new char[ size ];
+
+#ifdef Q_OS_WIN32
+ // ### For some reason lo_read() fails if we try to read more than
+ // ### 32760 bytes
+ char * p = buf;
+ int nread = 0;
+
+ while( size < nread ){
+ retval = lo_read( d->connection, fd, p, 32760 );
+ nread += retval;
+ p += retval;
+ }
+#else
+ retval = lo_read( d->connection, fd, buf, size );
+#endif
+
+ if (retval < 0) {
+ qWarning( "QPSQLResult::data: unable to read large object" );
+ } else {
+ ba.duplicate( buf, size );
+ }
+ delete [] buf;
+ lo_close( d->connection, fd );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+ default:
+ case QVariant::Invalid:
+#ifdef QT_CHECK_RANGE
+ qWarning("QPSQLResult::data: unknown data type");
+#endif
+ ;
+ }
+ return QVariant();
+}
+
+bool QPSQLResult::isNull( int field )
+{
+ PQgetvalue( d->result, at(), field );
+ return PQgetisnull( d->result, at(), field );
+}
+
+bool QPSQLResult::reset ( const QString& query )
+{
+ cleanup();
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()->isOpen() || driver()->isOpenError() )
+ return FALSE;
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ if ( d->result )
+ PQclear( d->result );
+ if ( d->isUtf8 ) {
+ d->result = PQexec( d->connection, query.utf8().data() );
+ } else {
+ d->result = PQexec( d->connection, query.local8Bit().data() );
+ }
+ int status = PQresultStatus( d->result );
+ if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) {
+ if ( status == PGRES_TUPLES_OK ) {
+ setSelect( TRUE );
+ currentSize = PQntuples( d->result );
+ } else {
+ setSelect( FALSE );
+ currentSize = -1;
+ }
+ setActive( TRUE );
+ return TRUE;
+ }
+ setLastError( qMakeError( "Unable to create query", QSqlError::Statement, d ) );
+ return FALSE;
+}
+
+int QPSQLResult::size()
+{
+ return currentSize;
+}
+
+int QPSQLResult::numRowsAffected()
+{
+ return QString( PQcmdTuples( d->result ) ).toInt();
+}
+
+///////////////////////////////////////////////////////////////////
+
+static bool setEncodingUtf8( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "SET CLIENT_ENCODING TO 'UNICODE'" );
+ int status = PQresultStatus( result );
+ PQclear( result );
+ return status == PGRES_COMMAND_OK;
+}
+
+static void setDatestyle( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "SET DATESTYLE TO 'ISO'" );
+#ifdef QT_CHECK_RANGE
+ int status = PQresultStatus( result );
+ if ( status != PGRES_COMMAND_OK )
+ qWarning( "%s", PQerrorMessage( connection ) );
+#endif
+ PQclear( result );
+}
+
+static QPSQLDriver::Protocol getPSQLVersion( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "select version()" );
+ int status = PQresultStatus( result );
+ if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) {
+ QString val( PQgetvalue( result, 0, 0 ) );
+ PQclear( result );
+ QRegExp rx( "(\\d+)\\.(\\d+)" );
+ rx.setMinimal ( TRUE ); // enforce non-greedy RegExp
+ if ( rx.search( val ) != -1 ) {
+ int vMaj = rx.cap( 1 ).toInt();
+ int vMin = rx.cap( 2 ).toInt();
+ if ( vMaj < 6 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "This version of PostgreSQL is not supported and may not work." );
+#endif
+ return QPSQLDriver::Version6;
+ }
+ if ( vMaj == 6 ) {
+ return QPSQLDriver::Version6;
+ } else if ( vMaj == 7 ) {
+ if ( vMin < 1 )
+ return QPSQLDriver::Version7;
+ else if ( vMin < 3 )
+ return QPSQLDriver::Version71;
+ }
+ return QPSQLDriver::Version73;
+ }
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "This version of PostgreSQL is not supported and may not work." );
+#endif
+ }
+
+ return QPSQLDriver::Version6;
+}
+
+QPSQLDriver::QPSQLDriver( QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QPSQL"), pro( QPSQLDriver::Version6 )
+{
+ init();
+}
+
+QPSQLDriver::QPSQLDriver( PGconn * conn, QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QPSQL"), pro( QPSQLDriver::Version6 )
+{
+ init();
+ d->connection = conn;
+ if ( conn ) {
+ pro = getPSQLVersion( d->connection );
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void QPSQLDriver::init()
+{
+ qSqlDriverExtDict()->insert( this, new QPSQLDriverExtension(this) );
+ qSqlOpenExtDict()->insert( this, new QPSQLOpenExtension(this) );
+
+ d = new QPSQLPrivate();
+}
+
+QPSQLDriver::~QPSQLDriver()
+{
+ if ( d->connection )
+ PQfinish( d->connection );
+ delete d;
+ if ( !qSqlDriverExtDict()->isEmpty() ) {
+ QSqlDriverExtension *ext = qSqlDriverExtDict()->take( this );
+ delete ext;
+ }
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+PGconn* QPSQLDriver::connection()
+{
+ return d->connection;
+}
+
+
+bool QPSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+ return TRUE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return pro >= QPSQLDriver::Version71;
+ case Unicode:
+ return d->isUtf8;
+ default:
+ return FALSE;
+ }
+}
+
+bool QPSQLDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QPSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QPSQLDriver::open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ QString connectString;
+ if ( host.length() )
+ connectString.append( "host=" ).append( host );
+ if ( db.length() )
+ connectString.append( " dbname=" ).append( db );
+ if ( user.length() )
+ connectString.append( " user=" ).append( user );
+ if ( password.length() )
+ connectString.append( " password=" ).append( password );
+ if ( port > -1 )
+ connectString.append( " port=" ).append( QString::number( port ) );
+
+ // add any connect options - the server will handle error detection
+ if ( !connOpts.isEmpty() )
+ connectString += " " + QStringList::split( ';', connOpts ).join( " " );
+
+ d->connection = PQconnectdb( connectString.local8Bit().data() );
+ if ( PQstatus( d->connection ) == CONNECTION_BAD ) {
+ setLastError( qMakeError("Unable to connect", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ pro = getPSQLVersion( d->connection );
+ d->isUtf8 = setEncodingUtf8( d->connection );
+ setDatestyle( d->connection );
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QPSQLDriver::close()
+{
+ if ( isOpen() ) {
+ if (d->connection)
+ PQfinish( d->connection );
+ d->connection = 0;
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+QSqlQuery QPSQLDriver::createQuery() const
+{
+ return QSqlQuery( new QPSQLResult( this, d ) );
+}
+
+bool QPSQLDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "BEGIN" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ PQclear( res );
+ setLastError( qMakeError( "Could not begin transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool QPSQLDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "COMMIT" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ PQclear( res );
+ setLastError( qMakeError( "Could not commit transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool QPSQLDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "ROLLBACK" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ setLastError( qMakeError( "Could not rollback transaction", QSqlError::Transaction, d ) );
+ PQclear( res );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+QStringList QPSQLDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ QSqlQuery t = createQuery();
+ t.setForwardOnly( TRUE );
+
+ if ( typeName.isEmpty() || ((type & (int)QSql::Tables) == (int)QSql::Tables) ) {
+
+ QString query("select relname from pg_class where (relkind = 'r') "
+ "and (relname !~ '^Inv') "
+ "and (relname !~ '^pg_') ");
+ if (pro >= QPSQLDriver::Version73)
+ query.append("and (relnamespace not in "
+ "(select oid from pg_namespace where nspname = 'information_schema')) "
+ "and pg_table_is_visible(pg_class.oid) ");
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+ if ( (type & (int)QSql::Views) == (int)QSql::Views ) {
+ QString query("select relname from pg_class where ( relkind = 'v' ) "
+ "and ( relname !~ '^Inv' ) "
+ "and ( relname !~ '^pg_' ) ");
+ if (pro >= QPSQLDriver::Version73)
+ query.append("and (relnamespace not in "
+ "(select oid from pg_namespace where nspname = 'information_schema')) "
+ "and pg_table_is_visible(pg_class.oid) ");
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+ if ( (type & (int)QSql::SystemTables) == (int)QSql::SystemTables ) {
+ QString query( "select relname from pg_class where ( relkind = 'r' ) "
+ "and ( relname like 'pg_%' ) " );
+ if (pro >= QPSQLDriver::Version73)
+ query.append( "and pg_table_is_visible(pg_class.oid) " );
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+
+ return tl;
+}
+
+QSqlIndex QPSQLDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex idx( tablename );
+ if ( !isOpen() )
+ return idx;
+ QSqlQuery i = createQuery();
+ QString stmt;
+
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_att1.attname, int(pg_att1.atttypid), pg_att2.attnum, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' ";
+ break;
+ case QPSQLDriver::Version7:
+ case QPSQLDriver::Version71:
+ stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' "
+ "and pg_table_is_visible(pg_cl.oid) "
+ "and pg_att1.attisdropped = false ";
+ break;
+ }
+ stmt += "and pg_cl.oid = pg_ind.indexrelid "
+ "and pg_att2.attrelid = pg_ind.indexrelid "
+ "and pg_att1.attrelid = pg_ind.indrelid "
+ "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
+ "order by pg_att2.attnum";
+
+ i.exec( stmt.arg( tablename.lower() ) );
+ while ( i.isActive() && i.next() ) {
+ QSqlField f( i.value(0).toString(), qDecodePSQLType( i.value(1).toInt() ) );
+ idx.append( f );
+ idx.setName( i.value(2).toString() );
+ }
+ return idx;
+}
+
+QSqlRecord QPSQLDriver::record( const QString& tablename ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ QString stmt;
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_attribute.attname, int(pg_attribute.atttypid) "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version7:
+ case QPSQLDriver::Version71:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_table_is_visible(pg_class.oid) "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attisdropped = false "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ }
+
+ QSqlQuery fi = createQuery();
+ fi.exec( stmt.arg( tablename.lower() ) );
+ while ( fi.next() ) {
+ QSqlField f( fi.value(0).toString(), qDecodePSQLType( fi.value(1).toInt() ) );
+ fil.append( f );
+ }
+ return fil;
+}
+
+QSqlRecord QPSQLDriver::record( const QSqlQuery& query ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ QPSQLResult* result = (QPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ QString name = PQfname( result->d->result, i );
+ QVariant::Type type = qDecodePSQLType( PQftype( result->d->result, i ) );
+ QSqlField rf( name, type );
+ fil.append( rf );
+ }
+ }
+ return fil;
+}
+
+QSqlRecordInfo QPSQLDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+
+ QString stmt;
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_attribute.attname, int(pg_attribute.atttypid), pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, int(pg_attribute.attrelid), pg_attribute.attnum "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version7:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attribute.attrelid::int, pg_attribute.attnum "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version71:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "order by pg_attribute.attnum ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_table_is_visible(pg_class.oid) "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "and pg_attribute.attisdropped = false "
+ "order by pg_attribute.attnum ";
+ break;
+ }
+
+ QSqlQuery query = createQuery();
+ query.exec( stmt.arg( tablename.lower() ) );
+ if ( pro >= QPSQLDriver::Version71 ) {
+ while ( query.next() ) {
+ int len = query.value( 3 ).toInt();
+ int precision = query.value( 4 ).toInt();
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ QString defVal = query.value( 5 ).toString();
+ if ( !defVal.isEmpty() && defVal.startsWith( "'" ) )
+ defVal = defVal.mid( 1, defVal.length() - 2 );
+ info.append( QSqlFieldInfo( query.value( 0 ).toString(),
+ qDecodePSQLType( query.value( 1 ).toInt() ),
+ query.value( 2 ).toBool(),
+ len,
+ precision,
+ defVal,
+ query.value( 1 ).toInt() ) );
+ }
+ } else {
+ // Postgres < 7.1 cannot handle outer joins
+ while ( query.next() ) {
+ QString defVal;
+ QString stmt2 = "select pg_attrdef.adsrc from pg_attrdef where "
+ "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ";
+ QSqlQuery query2 = createQuery();
+ query2.exec( stmt2.arg( query.value( 5 ).toInt() ).arg( query.value( 6 ).toInt() ) );
+ if ( query2.isActive() && query2.next() )
+ defVal = query2.value( 0 ).toString();
+ if ( !defVal.isEmpty() && defVal.startsWith( "'" ) )
+ defVal = defVal.mid( 1, defVal.length() - 2 );
+ int len = query.value( 3 ).toInt();
+ int precision = query.value( 4 ).toInt();
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ info.append( QSqlFieldInfo( query.value( 0 ).toString(),
+ qDecodePSQLType( query.value( 1 ).toInt() ),
+ query.value( 2 ).toBool(),
+ len,
+ precision,
+ defVal,
+ query.value( 1 ).toInt() ) );
+ }
+ }
+
+ return info;
+}
+
+QSqlRecordInfo QPSQLDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.driver() == this ) {
+ QPSQLResult* result = (QPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ QString name = PQfname( result->d->result, i );
+ int len = PQfsize( result->d->result, i );
+ int precision = PQfmod( result->d->result, i );
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ info.append( QSqlFieldInfo( name,
+ qDecodePSQLType( PQftype( result->d->result, i ) ),
+ -1,
+ len,
+ precision,
+ QVariant(),
+ PQftype( result->d->result, i ) ) );
+ }
+ }
+ return info;
+}
+
+QString QPSQLDriver::formatValue( const QSqlField* field,
+ bool ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch ( field->type() ) {
+ case QVariant::DateTime:
+ if ( field->value().toDateTime().isValid() ) {
+ QDate dt = field->value().toDateTime().date();
+ QTime tm = field->value().toDateTime().time();
+ // msecs need to be right aligned otherwise psql
+ // interpretes them wrong
+ r = "'" + QString::number( dt.year() ) + "-" +
+ QString::number( dt.month() ) + "-" +
+ QString::number( dt.day() ) + " " +
+ tm.toString() + "." +
+ QString::number( tm.msec() ).rightJustify( 3, '0' ) + "'";
+ } else {
+ r = nullText();
+ }
+ break;
+ case QVariant::Time:
+ if ( field->value().toTime().isValid() ) {
+ r = field->value().toTime().toString( Qt::ISODate );
+ } else {
+ r = nullText();
+ }
+ case QVariant::String:
+ case QVariant::CString: {
+ switch ( field->value().type() ) {
+ case QVariant::Rect: {
+ QRect rec = field->value().toRect();
+ // upper right corner then lower left according to psql docs
+ r = "'(" + QString::number( rec.right() ) +
+ "," + QString::number( rec.bottom() ) +
+ "),(" + QString::number( rec.left() ) +
+ "," + QString::number( rec.top() ) + ")'";
+ break;
+ }
+ case QVariant::Point: {
+ QPoint p = field->value().toPoint();
+ r = "'(" + QString::number( p.x() ) +
+ "," + QString::number( p.y() ) + ")'";
+ break;
+ }
+ case QVariant::PointArray: {
+ QPointArray pa = field->value().toPointArray();
+ r = "' ";
+ for ( int i = 0; i < (int)pa.size(); ++i ) {
+ r += "(" + QString::number( pa[i].x() ) +
+ "," + QString::number( pa[i].y() ) + "),";
+ }
+ r.truncate( r.length() - 1 );
+ r += "'";
+ break;
+ }
+ default:
+ // Escape '\' characters
+ r = QSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ break;
+ }
+ case QVariant::Bool:
+ if ( field->value().toBool() )
+ r = "TRUE";
+ else
+ r = "FALSE";
+ break;
+ case QVariant::ByteArray: {
+ QByteArray ba = field->value().asByteArray();
+ QString res;
+ r = "'";
+ unsigned char uc;
+ for ( int i = 0; i < (int)ba.size(); ++i ) {
+ uc = (unsigned char) ba[ i ];
+ if ( uc > 40 && uc < 92 ) {
+ r += uc;
+ } else {
+ r += "\\\\";
+ r += QString::number( (unsigned char) ba[ i ], 8 ).rightJustify( 3, '0', TRUE );
+ }
+ }
+ r += "'";
+ break;
+ }
+ default:
+ r = QSqlDriver::formatValue( field );
+ break;
+ }
+ }
+ return r;
+}
diff --git a/src/sql/drivers/psql/qsql_psql.h b/src/sql/drivers/psql/qsql_psql.h
new file mode 100644
index 0000000..fd9cbe6
--- /dev/null
+++ b/src/sql/drivers/psql/qsql_psql.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Definition of PostgreSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_PSQL_H
+#define QSQL_PSQL_H
+
+#include <qsqlresult.h>
+#include <qsqlfield.h>
+#include <qsqldriver.h>
+#include <libpq-fe.h>
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_PSQL
+#else
+#define Q_EXPORT_SQLDRIVER_PSQL Q_EXPORT
+#endif
+
+class QPSQLPrivate;
+class QPSQLDriver;
+class QSqlRecordInfo;
+
+class QPSQLResult : public QSqlResult
+{
+ friend class QPSQLDriver;
+public:
+ QPSQLResult( const QPSQLDriver* db, const QPSQLPrivate* p );
+ ~QPSQLResult();
+ PGresult* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchFirst();
+ bool fetchLast();
+ QVariant data( int i );
+ bool isNull( int field );
+ bool reset ( const QString& query );
+ int size();
+ int numRowsAffected();
+private:
+ int currentSize;
+ QPSQLPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_PSQL QPSQLDriver : public QSqlDriver
+{
+public:
+ enum Protocol {
+ Version6 = 6,
+ Version7 = 7,
+ Version71 = 8,
+ Version73 = 9
+ };
+
+ QPSQLDriver( QObject * parent=0, const char * name=0 );
+ QPSQLDriver( PGconn * conn, QObject * parent=0, const char * name=0 );
+ ~QPSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+
+ Protocol protocol() const { return pro; }
+ PGconn* connection();
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ Protocol pro;
+ QPSQLPrivate* d;
+};
+
+#endif
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp
new file mode 100644
index 0000000..f8c7ffa
--- /dev/null
+++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** Implementation of SQLite driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt 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_sqlite.h"
+
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <qfile.h>
+
+#if (QT_VERSION-0 < 0x030000)
+# include <qvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+# include "../../../3rdparty/libraries/sqlite/sqlite.h"
+#else
+# include <qptrvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+# include <sqlite.h>
+#endif
+
+typedef struct sqlite_vm sqlite_vm;
+
+#define QSQLITE_DRIVER_NAME "QSQLITE"
+
+static QSqlVariant::Type nameToType(const QString& typeName)
+{
+ QString tName = typeName.upper();
+ if (tName.startsWith("INT"))
+ return QSqlVariant::Int;
+ if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC"))
+ return QSqlVariant::Double;
+ if (tName.startsWith("BOOL"))
+ return QSqlVariant::Bool;
+ // SQLite is typeless - consider everything else as string
+ return QSqlVariant::String;
+}
+
+class QSQLiteDriverPrivate
+{
+public:
+ QSQLiteDriverPrivate();
+ sqlite *access;
+ bool utf8;
+};
+
+QSQLiteDriverPrivate::QSQLiteDriverPrivate() : access(0)
+{
+ utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
+}
+
+class QSQLiteResultPrivate
+{
+public:
+ QSQLiteResultPrivate(QSQLiteResult *res);
+ void cleanup();
+ bool fetchNext(QtSqlCachedResult::RowCache *row);
+ bool isSelect();
+ // initializes the recordInfo and the cache
+ void init(const char **cnames, int numCols, QtSqlCachedResult::RowCache **row = 0);
+ void finalize();
+
+ QSQLiteResult* 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 skippedStatus: 1; // the status of the fetchNext() that's skipped
+ QtSqlCachedResult::RowCache *skipRow;
+
+ uint utf8: 1;
+ QSqlRecordInfo rInf;
+};
+
+static const uint initial_cache_size = 128;
+
+QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0), currentTail(0),
+ currentMachine(0), skippedStatus(FALSE), skipRow(0), utf8(FALSE)
+{
+}
+
+void QSQLiteResultPrivate::cleanup()
+{
+ finalize();
+ rInf.clear();
+ currentTail = 0;
+ currentMachine = 0;
+ skippedStatus = FALSE;
+ delete skipRow;
+ skipRow = 0;
+ q->setAt(QSql::BeforeFirst);
+ q->setActive(FALSE);
+ q->cleanup();
+}
+
+void QSQLiteResultPrivate::finalize()
+{
+ if (!currentMachine)
+ return;
+
+ char* err = 0;
+ int res = sqlite_finalize(currentMachine, &err);
+ if (err) {
+ q->setLastError(QSqlError("Unable to fetch results", err, QSqlError::Statement, res));
+ sqlite_freemem(err);
+ }
+ currentMachine = 0;
+}
+
+// called on first fetch
+void QSQLiteResultPrivate::init(const char **cnames, int numCols, QtSqlCachedResult::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(QSqlFieldInfo(fieldName, nameToType(cnames[i+numCols])));
+ }
+ // skip the first fetch
+ if (row && !*row) {
+ *row = new QtSqlCachedResult::RowCache(numCols);
+ skipRow = *row;
+ }
+}
+
+bool QSQLiteResultPrivate::fetchNext(QtSqlCachedResult::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 skippedStatus;
+ }
+
+ if (!currentMachine)
+ return FALSE;
+
+ // keep trying while busy, wish I could implement this better.
+ while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == SQLITE_BUSY) {
+ // sleep instead requesting result again immidiately.
+#if defined Q_WS_WIN32
+ 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.
+ init(cnames, colNum, &row);
+ if (!fvals)
+ return FALSE;
+ if (!row)
+ return TRUE;
+ for (i = 0; i < colNum; ++i)
+ (*row)[i] = utf8 ? QString::fromUtf8(fvals[i]) : QString(fvals[i]);
+ return TRUE;
+ case SQLITE_DONE:
+ if (rInf.isEmpty())
+ // must be first call.
+ init(cnames, colNum);
+ q->setAt(QSql::AfterLast);
+ return FALSE;
+ case SQLITE_ERROR:
+ case SQLITE_MISUSE:
+ default:
+ // something wrong, don't get col info, but still return false
+ finalize(); // finalize to get the error message.
+ q->setAt(QSql::AfterLast);
+ return FALSE;
+ }
+ return FALSE;
+}
+
+QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
+: QtSqlCachedResult(db)
+{
+ d = new QSQLiteResultPrivate(this);
+ d->access = db->d->access;
+ d->utf8 = db->d->utf8;
+}
+
+QSQLiteResult::~QSQLiteResult()
+{
+ d->cleanup();
+ delete d;
+}
+
+/*
+ Execute \a query.
+*/
+bool QSQLiteResult::reset (const QString& 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 != SQLITE_OK || err) {
+ setLastError(QSqlError("Unable to execute statement", err, QSqlError::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 find out about
+ // the structure of the result set
+ d->skippedStatus = d->fetchNext(0);
+ setSelect(!d->rInf.isEmpty());
+ if (isSelect())
+ init(d->rInf.count());
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QSQLiteResult::gotoNext(QtSqlCachedResult::RowCache* row)
+{
+ return d->fetchNext(row);
+}
+
+int QSQLiteResult::size()
+{
+ return -1;
+}
+
+int QSQLiteResult::numRowsAffected()
+{
+ return sqlite_changes(d->access);
+}
+
+/////////////////////////////////////////////////////////
+
+QSQLiteDriver::QSQLiteDriver(QObject * parent, const char * name)
+ : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
+{
+ d = new QSQLiteDriverPrivate();
+}
+
+QSQLiteDriver::QSQLiteDriver(sqlite *connection, QObject *parent, const char *name)
+ : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
+{
+ d = new QSQLiteDriverPrivate();
+ d->access = connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+
+QSQLiteDriver::~QSQLiteDriver()
+{
+ delete d;
+}
+
+bool QSQLiteDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+ return TRUE;
+#if (QT_VERSION-0 >= 0x030000)
+ case Unicode:
+ return d->utf8;
+#endif
+// case BLOB:
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ SQLite dbs have no user name, passwords, hosts or ports.
+ just file names.
+*/
+bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
+{
+ if (isOpen())
+ close();
+
+ if (db.isEmpty())
+ return FALSE;
+
+ char* err = 0;
+ d->access = sqlite_open(QFile::encodeName(db), 0, &err);
+ if (err) {
+ setLastError(QSqlError("Error to open database", err, QSqlError::Connection));
+ sqlite_freemem(err);
+ err = 0;
+ }
+
+ if (d->access) {
+ setOpen(TRUE);
+ setOpenError(FALSE);
+ return TRUE;
+ }
+ setOpenError(TRUE);
+ return FALSE;
+}
+
+void QSQLiteDriver::close()
+{
+ if (isOpen()) {
+ sqlite_close(d->access);
+ d->access = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+QSqlQuery QSQLiteDriver::createQuery() const
+{
+ return QSqlQuery(new QSQLiteResult(this));
+}
+
+bool QSQLiteDriver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "BEGIN", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to begin transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool QSQLiteDriver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "COMMIT", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to commit transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool QSQLiteDriver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to rollback Transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+QStringList QSQLiteDriver::tables(const QString &typeName) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+ int type = typeName.toInt();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+#if (QT_VERSION-0 >= 0x030000)
+ if ((type & (int)QSql::Tables) && (type & (int)QSql::Views))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+ else if (typeName.isEmpty() || (type & (int)QSql::Tables))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table'");
+ else if (type & (int)QSql::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 (QT_VERSION-0 >= 0x030000)
+ if (type & (int)QSql::SystemTables) {
+ // there are no internal tables beside this one:
+ res.append("sqlite_master");
+ }
+#endif
+
+ return res;
+}
+
+QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
+{
+ QSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
+
+ if (!isOpen())
+ return QSqlIndex();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ // finrst find a UNIQUE INDEX
+ q.exec("PRAGMA index_list('" + tblname + "');");
+ QString indexname;
+ while(q.next()) {
+ if (q.value(2).toInt()==1) {
+ indexname = q.value(1).toString();
+ break;
+ }
+ }
+ if (indexname.isEmpty())
+ return QSqlIndex();
+
+ q.exec("PRAGMA index_info('" + indexname + "');");
+
+ QSqlIndex index(tblname, indexname);
+ while(q.next()) {
+ QString name = q.value(2).toString();
+ QSqlVariant::Type type = QSqlVariant::Invalid;
+ if (rec.contains(name))
+ type = rec.find(name).type();
+ index.append(QSqlField(name, type));
+ }
+ return index;
+}
+
+QSqlRecordInfo QSQLiteDriver::recordInfo(const QString &tbl) const
+{
+ if (!isOpen())
+ return QSqlRecordInfo();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT * FROM " + tbl + " LIMIT 1");
+ return recordInfo(q);
+}
+
+QSqlRecord QSQLiteDriver::record(const QString &tblname) const
+{
+ if (!isOpen())
+ return QSqlRecord();
+
+ return recordInfo(tblname).toRecord();
+}
+
+QSqlRecord QSQLiteDriver::record(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLiteResult* result = (QSQLiteResult*)query.result();
+ return result->d->rInf.toRecord();
+ }
+ return QSqlRecord();
+}
+
+QSqlRecordInfo QSQLiteDriver::recordInfo(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLiteResult* result = (QSQLiteResult*)query.result();
+ return result->d->rInf;
+ }
+ return QSqlRecordInfo();
+}
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.h b/src/sql/drivers/sqlite/qsql_sqlite.h
new file mode 100644
index 0000000..f3b9192
--- /dev/null
+++ b/src/sql/drivers/sqlite/qsql_sqlite.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Definition of SQLite driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt 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 QSQL_SQLITE_H
+#define QSQL_SQLITE_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlrecord.h>
+#include <qsqlindex.h>
+#include "../cache/qsqlcachedresult.h"
+
+#if (QT_VERSION-0 >= 0x030000)
+typedef QVariant QSqlVariant;
+#endif
+
+#if defined (Q_OS_WIN32)
+# include <qt_windows.h>
+#endif
+
+class QSQLiteDriverPrivate;
+class QSQLiteResultPrivate;
+class QSQLiteDriver;
+struct sqlite;
+
+class QSQLiteResult : public QtSqlCachedResult
+{
+ friend class QSQLiteDriver;
+ friend class QSQLiteResultPrivate;
+public:
+ QSQLiteResult(const QSQLiteDriver* db);
+ ~QSQLiteResult();
+
+protected:
+ bool gotoNext(QtSqlCachedResult::RowCache* row);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ QSQLiteResultPrivate* d;
+};
+
+class QSQLiteDriver : public QSqlDriver
+{
+ friend class QSQLiteResult;
+public:
+ QSQLiteDriver(QObject *parent = 0, const char *name = 0);
+ QSQLiteDriver(sqlite *connection, QObject *parent = 0, const char *name = 0);
+ ~QSQLiteDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port ) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(const QString& user) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlRecordInfo recordInfo(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+ QSqlRecord record(const QSqlQuery& query) const;
+ QSqlRecordInfo recordInfo(const QSqlQuery& query) const;
+
+private:
+ QSQLiteDriverPrivate* d;
+};
+#endif