summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-08 12:31:36 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-08 12:31:36 -0600
commitd796c9dd933ab96ec83b9a634feedd5d32e1ba3f (patch)
tree6e3dcca4f77e20ec8966c666aac7c35bd4704053 /src/sql/drivers
downloadtqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.tar.gz
tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.zip
Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731
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 000000000..5e46b9b49
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Implementation of cached TQt SQL result classes
+**
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE 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 TQtSqlCachedResultPrivate
+{
+public:
+ TQtSqlCachedResultPrivate();
+ bool seek(int i);
+ void init(int count, bool fo);
+ void cleanup();
+ TQtSqlCachedResult::RowCache* next();
+ void revertLast();
+
+ TQtSqlCachedResult::RowsetCache *cache;
+ TQtSqlCachedResult::RowCache *current;
+ int rowCacheEnd;
+ int colCount;
+ bool forwardOnly;
+};
+
+TQtSqlCachedResultPrivate::TQtSqlCachedResultPrivate():
+ cache(0), current(0), rowCacheEnd(0), colCount(0), forwardOnly(FALSE)
+{
+}
+
+void TQtSqlCachedResultPrivate::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 TQtSqlCachedResultPrivate::init(int count, bool fo)
+{
+ cleanup();
+ forwardOnly = fo;
+ colCount = count;
+ if (fo)
+ current = new TQtSqlCachedResult::RowCache(count);
+ else
+ cache = new TQtSqlCachedResult::RowsetCache(initial_cache_size);
+}
+
+TQtSqlCachedResult::RowCache *TQtSqlCachedResultPrivate::next()
+{
+ if (forwardOnly)
+ return current;
+
+ Q_ASSERT(cache);
+ current = new TQtSqlCachedResult::RowCache(colCount);
+ if (rowCacheEnd == (int)cache->size())
+ cache->resize(cache->size() * 2);
+ cache->insert(rowCacheEnd++, current);
+ return current;
+}
+
+bool TQtSqlCachedResultPrivate::seek(int i)
+{
+ if (forwardOnly || i < 0)
+ return FALSE;
+ if (i >= rowCacheEnd)
+ return FALSE;
+ current = (*cache)[i];
+ return TRUE;
+}
+
+void TQtSqlCachedResultPrivate::revertLast()
+{
+ if (forwardOnly)
+ return;
+ --rowCacheEnd;
+ delete current;
+ current = 0;
+}
+
+//////////////
+
+TQtSqlCachedResult::TQtSqlCachedResult(const TQSqlDriver * db ): TQSqlResult ( db )
+{
+ d = new TQtSqlCachedResultPrivate();
+}
+
+TQtSqlCachedResult::~TQtSqlCachedResult()
+{
+ delete d;
+}
+
+void TQtSqlCachedResult::init(int colCount)
+{
+ d->init(colCount, isForwardOnly());
+}
+
+bool TQtSqlCachedResult::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() == TQSql::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 TQtSqlCachedResult::fetchNext()
+{
+ if (d->seek(at() + 1)) {
+ setAt(at() + 1);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool TQtSqlCachedResult::fetchPrev()
+{
+ return fetch(at() - 1);
+}
+
+bool TQtSqlCachedResult::fetchFirst()
+{
+ if (d->forwardOnly && at() != TQSql::BeforeFirst) {
+ return FALSE;
+ }
+ if (d->seek(0)) {
+ setAt(0);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool TQtSqlCachedResult::fetchLast()
+{
+ if (at() == TQSql::AfterLast) {
+ if (d->forwardOnly)
+ return FALSE;
+ else
+ return fetch(d->rowCacheEnd - 1);
+ }
+
+ int i = at();
+ while (fetchNext())
+ i++; /* brute force */
+ if (d->forwardOnly && at() == TQSql::AfterLast) {
+ setAt(i);
+ return TRUE;
+ } else {
+ return fetch(d->rowCacheEnd - 1);
+ }
+}
+
+TQVariant TQtSqlCachedResult::data(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return TQVariant();
+
+ return (*d->current)[i];
+}
+
+bool TQtSqlCachedResult::isNull(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return TRUE;
+
+ return (*d->current)[i].isNull();
+}
+
+void TQtSqlCachedResult::cleanup()
+{
+ setAt(TQSql::BeforeFirst);
+ setActive(FALSE);
+ d->cleanup();
+}
+
+bool TQtSqlCachedResult::cacheNext()
+{
+ if (!gotoNext(d->next())) {
+ d->revertLast();
+ return FALSE;
+ }
+ setAt(at() + 1);
+ return TRUE;
+}
+
+int TQtSqlCachedResult::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 000000000..1cdae7ae2
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Definition of shared TQt SQL module classes
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSQLCACHEDRESULT_H
+#define TQSQLCACHEDRESULT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the TQt API. It exists for the convenience
+// of other TQt 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 TQM_EXPORT_SQL
+#else
+#define TQM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class TQtSqlCachedResultPrivate;
+
+class TQM_EXPORT_SQL TQtSqlCachedResult: public TQSqlResult
+{
+public:
+ virtual ~TQtSqlCachedResult();
+
+ typedef TQValueVector<TQVariant> RowCache;
+ typedef TQPtrVector<RowCache> RowsetCache;
+
+protected:
+ TQtSqlCachedResult(const TQSqlDriver * db);
+
+ void init(int colCount);
+ void cleanup();
+ bool cacheNext();
+
+ virtual bool gotoNext(RowCache* row) = 0;
+
+ TQVariant data(int i);
+ bool isNull(int i);
+ bool fetch(int i);
+ bool fetchNext();
+ bool fetchPrev();
+ bool fetchFirst();
+ bool fetchLast();
+
+ int colCount() const;
+
+private:
+ TQtSqlCachedResultPrivate *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 000000000..22c41064d
--- /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 TQt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsql_ibase.h"
+
+#include <qdatetime.h>
+#include <private/qsqlextension_p.h>
+
+#include <ibase.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+
+#define TQIBASE_DRIVER_NAME "TQIBASE"
+
+class TQIBasePreparedExtension : public TQSqlExtension
+{
+public:
+ TQIBasePreparedExtension(TQIBaseResult *r)
+ : result(r) {}
+
+ bool prepare(const TQString &query)
+ {
+ return result->prepare(query);
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ TQIBaseResult *result;
+};
+
+static bool getIBaseError(TQString& 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 = TQString::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 TQVariant::Type qIBaseTypeName(int iType)
+{
+ switch (iType) {
+ case blr_varying:
+ case blr_varying2:
+ case blr_text:
+ case blr_cstring:
+ case blr_cstring2:
+ return TQVariant::String;
+ case blr_sql_time:
+ return TQVariant::Time;
+ case blr_sql_date:
+ return TQVariant::Date;
+ case blr_timestamp:
+ return TQVariant::DateTime;
+ case blr_blob:
+ return TQVariant::ByteArray;
+ case blr_quad:
+ case blr_short:
+ case blr_long:
+ return TQVariant::Int;
+ case blr_int64:
+ return TQVariant::LongLong;
+ case blr_float:
+ case blr_d_float:
+ case blr_double:
+ return TQVariant::Double;
+ }
+ return TQVariant::Invalid;
+}
+
+static TQVariant::Type qIBaseTypeName2(int iType)
+{
+ switch(iType & ~1) {
+ case SQL_VARYING:
+ case SQL_TEXT:
+ return TQVariant::String;
+ case SQL_LONG:
+ case SQL_SHORT:
+ return TQVariant::Int;
+ case SQL_INT64:
+ return TQVariant::LongLong;
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return TQVariant::Double;
+ case SQL_TIMESTAMP:
+ return TQVariant::DateTime;
+ case SQL_TYPE_DATE:
+ return TQVariant::Date;
+ case SQL_TYPE_TIME:
+ return TQVariant::Time;
+ default:
+ return TQVariant::Invalid;
+ }
+}
+
+static ISC_TIME toTime(const TQTime &t)
+{
+ static const TQTime midnight(0, 0, 0, 0);
+ return (ISC_TIME)midnight.msecsTo(t) * 10;
+}
+
+static ISC_DATE toDate(const TQDate &d)
+{
+ static const TQDate basedate(1858, 11, 17);
+ return (ISC_DATE)basedate.daysTo(d);
+}
+
+static ISC_TIMESTAMP toTimeStamp(const TQDateTime &dt)
+{
+ ISC_TIMESTAMP ts;
+ ts.timestamp_time = toTime(dt.time());
+ ts.timestamp_date = toDate(dt.date());
+ return ts;
+}
+
+static TQTime toTQTime(ISC_TIME time)
+{
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ static const TQTime t;
+ return t.addMSecs(time / 10);
+}
+
+static TQDate toTQDate(ISC_DATE d)
+{
+ static const TQDate bd(1858, 11, 17);
+ return bd.addDays(d);
+}
+
+static TQDateTime toTQDateTime(ISC_TIMESTAMP *ts)
+{
+ return TQDateTime(toTQDate(ts->timestamp_date), toTQTime(ts->timestamp_time));
+}
+
+class TQIBaseDriverPrivate
+{
+public:
+ TQIBaseDriverPrivate(TQIBaseDriver *d): q(d)
+ {
+ ibase = 0;
+ trans = 0;
+ }
+
+ bool isError(const TQString &msg = TQString::null, TQSqlError::Type typ = TQSqlError::Unknown)
+ {
+ TQString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(TQSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+public:
+ TQIBaseDriver* q;
+ isc_db_handle ibase;
+ isc_tr_handle trans;
+ ISC_STATUS status[20];
+};
+
+class TQIBaseResultPrivate
+{
+public:
+ TQIBaseResultPrivate(TQIBaseResult *d, const TQIBaseDriver *ddb);
+ ~TQIBaseResultPrivate() { cleanup(); }
+
+ void cleanup();
+ bool isError(const TQString &msg = TQString::null, TQSqlError::Type typ = TQSqlError::Unknown)
+ {
+ TQString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(TQSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+ bool transaction();
+ bool commit();
+
+ bool isSelect();
+ TQVariant fetchBlob(ISC_QUAD *bId);
+ void writeBlob(int i, const TQByteArray &ba);
+
+public:
+ TQIBaseResult *q;
+ const TQIBaseDriver *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;
+};
+
+TQIBaseResultPrivate::TQIBaseResultPrivate(TQIBaseResult *d, const TQIBaseDriver *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 TQIBaseResultPrivate::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 TQIBaseResultPrivate::writeBlob(int i, const TQByteArray &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", TQSqlError::Statement)) {
+ uint i = 0;
+ while (i < ba.size()) {
+ isc_put_segment(status, &handle, TQMIN(ba.size() - i, SHRT_MAX), ba.data());
+ if (isError("Unable to write BLOB"))
+ break;
+ i += SHRT_MAX;
+ }
+ }
+ isc_close_blob(status, &handle);
+}
+
+TQVariant TQIBaseResultPrivate::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", TQSqlError::Statement))
+ return TQVariant();
+
+ unsigned short len = 0;
+ TQByteArray 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(TQMIN(ba.size() * 2, SHRT_MAX));
+ stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize);
+ }
+ bool isErr = isError("Unable to read BLOB", TQSqlError::Statement);
+ isc_close_blob(status, &handle);
+ if (isErr)
+ return TQVariant();
+
+ if (ba.size() > 255)
+ ba.resize(ba.size() / 2 + len);
+ else
+ ba.resize(len);
+
+ return ba;
+}
+
+bool TQIBaseResultPrivate::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", TQSqlError::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 TQIBaseResultPrivate::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", TQSqlError::Statement))
+ return FALSE;
+
+ return TRUE;
+}
+
+// does nothing if the transaction is on the
+// driver level
+bool TQIBaseResultPrivate::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", TQSqlError::Statement);
+}
+
+//////////
+
+TQIBaseResult::TQIBaseResult(const TQIBaseDriver* db):
+ TQtSqlCachedResult(db)
+{
+ d = new TQIBaseResultPrivate(this, db);
+ setExtension(new TQIBasePreparedExtension(this));
+}
+
+TQIBaseResult::~TQIBaseResult()
+{
+ delete d;
+}
+
+bool TQIBaseResult::prepare(const TQString& query)
+{
+ //qDebug("prepare: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(TQSql::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", TQSqlError::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", TQSqlError::Statement))
+ return FALSE;
+
+ isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
+ if (d->isError("Could not describe input statement", TQSqlError::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", TQSqlError::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", TQSqlError::Statement))
+ return FALSE;
+ }
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (!isSelect()) {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ return TRUE;
+}
+
+bool TQIBaseResult::exec()
+{
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ setActive(FALSE);
+ setAt(TQSql::BeforeFirst);
+
+ if (d->inda && extension()->index.count() > 0) {
+ TQMap<int, TQString>::ConstIterator it;
+ if ((int)extension()->index.count() > d->inda->sqld) {
+ qWarning("TQIBaseResult::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 TQVariant 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: {
+ TQCString 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: {
+ TQCString 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 TQIBaseResult::reset (const TQString& query)
+{
+// qDebug("reset: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(TQSql::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", TQSqlError::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", TQSqlError::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", TQSqlError::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 TQIBaseResult::gotoNext(TQtSqlCachedResult::RowCache* row)
+{
+ ISC_STATUS stat = isc_dsql_fetch(d->status, &d->stmt, 1, d->sqlda);
+
+ if (stat == 100) {
+ // no more rows
+ setAt(TQSql::AfterLast);
+ return FALSE;
+ }
+ if (d->isError("Could not fetch next item", TQSqlError::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
+ TQVariant 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] = TQString::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] = TQVariant(*(Q_LLONG*)buf);
+ break;
+ case SQL_LONG:
+ if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out.
+ (*row)[i] = TQVariant((int)(*(long*)buf));
+ else
+ (*row)[i] = TQVariant((Q_LLONG)(*(long*)buf));
+ break;
+ case SQL_SHORT:
+ (*row)[i] = TQVariant((int)(*(short*)buf));
+ break;
+ case SQL_FLOAT:
+ (*row)[i] = TQVariant((double)(*(float*)buf));
+ break;
+ case SQL_DOUBLE:
+ (*row)[i] = TQVariant(*(double*)buf);
+ break;
+ case SQL_TIMESTAMP:
+ (*row)[i] = toTQDateTime((ISC_TIMESTAMP*)buf);
+ break;
+ case SQL_TYPE_TIME:
+ (*row)[i] = toTQTime(*(ISC_TIME*)buf);
+ break;
+ case SQL_TYPE_DATE:
+ (*row)[i] = toTQDate(*(ISC_DATE*)buf);
+ break;
+ case SQL_TEXT:
+ (*row)[i] = TQString::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] = TQVariant();
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+int TQIBaseResult::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 TQIBaseResult::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", TQSqlError::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;
+}
+
+/*********************************/
+
+TQIBaseDriver::TQIBaseDriver(TQObject * parent, const char * name)
+ : TQSqlDriver(parent, name ? name : TQIBASE_DRIVER_NAME)
+{
+ d = new TQIBaseDriverPrivate(this);
+}
+
+TQIBaseDriver::TQIBaseDriver(void *connection, TQObject *parent, const char *name)
+ : TQSqlDriver(parent, name ? name : TQIBASE_DRIVER_NAME)
+{
+ d = new TQIBaseDriverPrivate(this);
+ d->ibase = (isc_db_handle)(long int)connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+TQIBaseDriver::~TQIBaseDriver()
+{
+ delete d;
+}
+
+bool TQIBaseDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+// case QuerySize:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case Unicode:
+ case BLOB:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool TQIBaseDriver::open(const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int /*port*/,
+ const TQString & /* connOpts */)
+{
+ if (isOpen())
+ close();
+
+ static const char enc[8] = "UTF_FSS";
+ TQCString usr = user.local8Bit();
+ TQCString pass = password.local8Bit();
+ usr.truncate(255);
+ pass.truncate(255);
+
+ TQByteArray 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;
+
+ TQString 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", TQSqlError::Connection)) {
+ setOpenError(TRUE);
+ return FALSE;
+ }
+
+ setOpen(TRUE);
+ return TRUE;
+}
+
+void TQIBaseDriver::close()
+{
+ if (isOpen()) {
+ isc_detach_database(d->status, &d->ibase);
+ d->ibase = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+TQSqlQuery TQIBaseDriver::createQuery() const
+{
+ return TQSqlQuery(new TQIBaseResult(this));
+}
+
+bool TQIBaseDriver::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", TQSqlError::Transaction);
+}
+
+bool TQIBaseDriver::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", TQSqlError::Transaction);
+}
+
+bool TQIBaseDriver::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", TQSqlError::Transaction);
+}
+
+TQStringList TQIBaseDriver::tables(const TQString& typeName) const
+{
+ TQStringList res;
+ if (!isOpen())
+ return res;
+
+ int type = typeName.isEmpty() ? (int)TQSql::Tables | (int)TQSql::Views : typeName.toInt();
+ TQString typeFilter;
+
+ if (type == (int)TQSql::SystemTables) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0";
+ } else if (type == ((int)TQSql::SystemTables | (int)TQSql::Views)) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL";
+ } else {
+ if (!(type & (int)TQSql::SystemTables))
+ typeFilter += "RDB$SYSTEM_FLAG = 0 AND ";
+ if (!(type & (int)TQSql::Views))
+ typeFilter += "RDB$VIEW_BLR IS NULL AND ";
+ if (!(type & (int)TQSql::Tables))
+ typeFilter += "RDB$VIEW_BLR IS NOT NULL AND ";
+ if (!typeFilter.isEmpty())
+ typeFilter.truncate(typeFilter.length() - 5);
+ }
+ if (!typeFilter.isEmpty())
+ typeFilter.prepend("where ");
+
+ TQSqlQuery 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;
+}
+
+TQSqlRecord TQIBaseDriver::record(const TQString& tablename) const
+{
+ TQSqlRecord rec;
+ if (!isOpen())
+ return rec;
+
+ TQSqlQuery 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()) {
+ TQSqlField field(q.value(0).toString().stripWhiteSpace(), qIBaseTypeName(q.value(1).toInt()));
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+TQSqlRecordInfo TQIBaseDriver::recordInfo(const TQString& tablename) const
+{
+ TQSqlRecordInfo rec;
+ if (!isOpen())
+ return rec;
+
+ TQSqlQuery 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()) {
+ TQVariant::Type type = qIBaseTypeName(q.value(1).toInt());
+ TQSqlFieldInfo field(q.value(0).toString().stripWhiteSpace(), type, q.value(5).toInt(),
+ q.value(2).toInt(), q.value(4).toInt(), TQVariant());
+
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+TQSqlIndex TQIBaseDriver::primaryIndex(const TQString &table) const
+{
+ TQSqlIndex index(table);
+ if (!isOpen())
+ return index;
+
+ TQSqlQuery 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()) {
+ TQSqlField 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;
+}
+
+TQSqlRecord TQIBaseDriver::record(const TQSqlQuery& query) const
+{
+ TQSqlRecord rec;
+ if (query.isActive() && query.driver() == this) {
+ TQIBaseResult* result = (TQIBaseResult*)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];
+ TQSqlField f(TQString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype));
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+TQSqlRecordInfo TQIBaseDriver::recordInfo(const TQSqlQuery& query) const
+{
+ TQSqlRecordInfo rec;
+ if (query.isActive() && query.driver() == this) {
+ TQIBaseResult* result = (TQIBaseResult*)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];
+ TQSqlFieldInfo f(TQString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype),
+ -1, v.sqllen, TQABS(v.sqlscale), TQVariant(), v.sqltype);
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+TQString TQIBaseDriver::formatValue(const TQSqlField* field, bool trimStrings) const
+{
+ switch (field->type()) {
+ case TQVariant::DateTime: {
+ TQDateTime datetime = field->value().toDateTime();
+ if (datetime.isValid())
+ return "'" + TQString::number(datetime.date().year()) + "-" +
+ TQString::number(datetime.date().month()) + "-" +
+ TQString::number(datetime.date().day()) + " " +
+ TQString::number(datetime.time().hour()) + ":" +
+ TQString::number(datetime.time().minute()) + ":" +
+ TQString::number(datetime.time().second()) + "." +
+ TQString::number(datetime.time().msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case TQVariant::Time: {
+ TQTime time = field->value().toTime();
+ if (time.isValid())
+ return "'" + TQString::number(time.hour()) + ":" +
+ TQString::number(time.minute()) + ":" +
+ TQString::number(time.second()) + "." +
+ TQString::number(time.msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case TQVariant::Date: {
+ TQDate date = field->value().toDate();
+ if (date.isValid())
+ return "'" + TQString::number(date.year()) + "-" +
+ TQString::number(date.month()) + "-" +
+ TQString::number(date.day()) + "'";
+ else
+ return "NULL";
+ }
+ default:
+ return TQSqlDriver::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 000000000..c0e8717de
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSQL_IBASE_H
+#define TQSQL_IBASE_H
+
+#include "qsqlresult.h"
+#include "qsqldriver.h"
+#include "../cache/qsqlcachedresult.h"
+
+
+class TQIBaseDriverPrivate;
+class TQIBaseResultPrivate;
+class TQIBaseDriver;
+
+class TQIBaseResult : public TQtSqlCachedResult
+{
+ friend class TQIBaseDriver;
+ friend class TQIBaseResultPrivate;
+
+public:
+ TQIBaseResult(const TQIBaseDriver* db);
+ virtual ~TQIBaseResult();
+
+ bool prepare(const TQString& query);
+ bool exec();
+
+protected:
+ bool gotoNext(TQtSqlCachedResult::RowCache* row);
+ bool reset (const TQString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ TQIBaseResultPrivate* d;
+};
+
+class TQIBaseDriver : public TQSqlDriver
+{
+ friend class TQIBaseDriverPrivate;
+ friend class TQIBaseResultPrivate;
+ friend class TQIBaseResult;
+public:
+ TQIBaseDriver(TQObject *parent = 0, const char *name = 0);
+ TQIBaseDriver(void *connection, TQObject *parent = 0, const char *name = 0);
+ virtual ~TQIBaseDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int port,
+ const TQString & connOpts);
+ bool open( const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int port ) { return open (db, user, password, host, port, TQString()); }
+ void close();
+ TQSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ TQStringList tables(const TQString& typeName) const;
+
+ TQSqlRecord record(const TQString& tablename) const;
+ TQSqlRecordInfo recordInfo(const TQString& tablename) const;
+ TQSqlIndex primaryIndex(const TQString &table) const;
+ TQSqlRecord record(const TQSqlQuery& query) const;
+ TQSqlRecordInfo recordInfo(const TQSqlQuery& query) const;
+
+ TQString formatValue(const TQSqlField* field, bool trimStrings) const;
+
+private:
+ TQIBaseDriverPrivate* 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 000000000..b0afb436d
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE 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 TQMYSQL_DRIVER_NAME "TQMYSQL3"
+
+#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
+
+TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict();
+
+static int qMySqlConnectionCount = 0;
+static bool qMySqlInitHandledByUser = FALSE;
+
+class TQMYSQLOpenExtension : public TQSqlOpenExtension
+{
+public:
+ TQMYSQLOpenExtension( TQMYSQLDriver *dri )
+ : TQSqlOpenExtension(), driver(dri) {}
+ ~TQMYSQLOpenExtension() {}
+
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+
+private:
+ TQMYSQLDriver *driver;
+};
+
+bool TQMYSQLOpenExtension::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+class TQMYSQLDriverPrivate
+{
+public:
+ TQMYSQLDriverPrivate() : mysql(0) {}
+ MYSQL* mysql;
+};
+
+class TQMYSQLResultPrivate : public TQMYSQLDriverPrivate
+{
+public:
+ TQMYSQLResultPrivate() : TQMYSQLDriverPrivate(), result(0) {}
+ MYSQL_RES* result;
+ MYSQL_ROW row;
+ TQValueVector<TQVariant::Type> fieldTypes;
+};
+
+TQSqlError qMakeError( const TQString& err, int type, const TQMYSQLDriverPrivate* p )
+{
+ return TQSqlError(TQMYSQL_DRIVER_NAME ": " + err, TQString(mysql_error( p->mysql )), type, mysql_errno( p->mysql ));
+}
+
+TQVariant::Type qDecodeMYSQLType( int mysqltype, uint flags )
+{
+ TQVariant::Type type;
+ switch ( mysqltype ) {
+ case FIELD_TYPE_TINY :
+ case FIELD_TYPE_SHORT :
+ case FIELD_TYPE_LONG :
+ case FIELD_TYPE_INT24 :
+ type = (flags & UNSIGNED_FLAG) ? TQVariant::UInt : TQVariant::Int;
+ break;
+ case FIELD_TYPE_YEAR :
+ type = TQVariant::Int;
+ break;
+ case FIELD_TYPE_LONGLONG :
+ type = (flags & UNSIGNED_FLAG) ? TQVariant::ULongLong : TQVariant::LongLong;
+ break;
+ case FIELD_TYPE_DECIMAL :
+ case FIELD_TYPE_FLOAT :
+ case FIELD_TYPE_DOUBLE :
+ type = TQVariant::Double;
+ break;
+ case FIELD_TYPE_DATE :
+ type = TQVariant::Date;
+ break;
+ case FIELD_TYPE_TIME :
+ type = TQVariant::Time;
+ break;
+ case FIELD_TYPE_DATETIME :
+ case FIELD_TYPE_TIMESTAMP :
+ type = TQVariant::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) ? TQVariant::ByteArray : TQVariant::CString;
+ break;
+ default:
+ case FIELD_TYPE_ENUM :
+ case FIELD_TYPE_SET :
+ case FIELD_TYPE_STRING :
+ case FIELD_TYPE_VAR_STRING :
+ type = TQVariant::String;
+ break;
+ }
+ return type;
+}
+
+TQMYSQLResult::TQMYSQLResult( const TQMYSQLDriver* db )
+: TQSqlResult( db )
+{
+ d = new TQMYSQLResultPrivate();
+ d->mysql = db->d->mysql;
+}
+
+TQMYSQLResult::~TQMYSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+MYSQL_RES* TQMYSQLResult::result()
+{
+ return d->result;
+}
+
+void TQMYSQLResult::cleanup()
+{
+ if ( d->result ) {
+ mysql_free_result( d->result );
+ }
+ d->result = NULL;
+ d->row = NULL;
+ setAt( -1 );
+ setActive( FALSE );
+}
+
+bool TQMYSQLResult::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 TQMYSQLResult::fetchNext()
+{
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool TQMYSQLResult::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 TQMYSQLResult::fetchFirst()
+{
+ if ( isForwardOnly() ) // again, fake it
+ return fetchNext();
+ return fetch( 0 );
+}
+
+TQVariant TQMYSQLResult::data( int field )
+{
+ if ( !isSelect() || field >= (int) d->fieldTypes.count() ) {
+ qWarning( "TQMYSQLResult::data: column %d out of range", field );
+ return TQVariant();
+ }
+
+ TQString val( d->row[field] );
+ switch ( d->fieldTypes.at( field ) ) {
+ case TQVariant::LongLong:
+ return TQVariant( val.toLongLong() );
+ case TQVariant::ULongLong:
+ return TQVariant( val.toULongLong() );
+ case TQVariant::Int:
+ return TQVariant( val.toInt() );
+ case TQVariant::UInt:
+ return TQVariant( val.toUInt() );
+ case TQVariant::Double:
+ return TQVariant( val.toDouble() );
+ case TQVariant::Date:
+ if ( val.isEmpty() ) {
+ return TQVariant( TQDate() );
+ } else {
+ return TQVariant( TQDate::fromString( val, TQt::ISODate ) );
+ }
+ case TQVariant::Time:
+ if ( val.isEmpty() ) {
+ return TQVariant( TQTime() );
+ } else {
+ return TQVariant( TQTime::fromString( val, TQt::ISODate ) );
+ }
+ case TQVariant::DateTime:
+ if ( val.isEmpty() )
+ return TQVariant( TQDateTime() );
+ if ( val.length() == 14u )
+ // TIMESTAMPS have the format yyyyMMddhhmmss
+ val.insert(4, "-").insert(7, "-").insert(10, 'T').insert(13, ':').insert(16, ':');
+ return TQVariant( TQDateTime::fromString( val, TQt::ISODate ) );
+ case TQVariant::ByteArray: {
+ unsigned long* fl = mysql_fetch_lengths( d->result );
+ TQByteArray ba;
+ ba.duplicate( d->row[field], fl[field] );
+ return TQVariant( ba );
+ }
+ default:
+ case TQVariant::String:
+ case TQVariant::CString:
+ return TQVariant( val );
+ }
+#ifdef QT_CHECK_RANGE
+ qWarning("TQMYSQLResult::data: unknown data type");
+#endif
+ return TQVariant();
+}
+
+bool TQMYSQLResult::isNull( int field )
+{
+ if ( d->row[field] == NULL )
+ return TRUE;
+ return FALSE;
+}
+
+bool TQMYSQLResult::reset ( const TQString& 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", TQSqlError::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", TQSqlError::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] = TQVariant::String;
+ else
+ d->fieldTypes[i] = qDecodeMYSQLType( field->type, field->flags );
+ }
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+int TQMYSQLResult::size()
+{
+ return isSelect() ? (int)mysql_num_rows( d->result ) : -1;
+}
+
+int TQMYSQLResult::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( "TQMYSQLDriver::qServerInit: unable to start server." );
+# endif
+ }
+
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+TQMYSQLDriver::TQMYSQLDriver( TQObject * parent, const char * name )
+ : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME )
+{
+ init();
+ qServerInit();
+}
+
+/*!
+ Create a driver instance with an already open connection handle.
+*/
+
+TQMYSQLDriver::TQMYSQLDriver( MYSQL * con, TQObject * parent, const char * name )
+ : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME )
+{
+ init();
+ if ( con ) {
+ d->mysql = (MYSQL *) con;
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ if (qMySqlConnectionCount == 1)
+ qMySqlInitHandledByUser = TRUE;
+ } else {
+ qServerInit();
+ }
+}
+
+void TQMYSQLDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new TQMYSQLOpenExtension(this) );
+ d = new TQMYSQLDriverPrivate();
+ d->mysql = 0;
+ qMySqlConnectionCount++;
+}
+
+TQMYSQLDriver::~TQMYSQLDriver()
+{
+ qMySqlConnectionCount--;
+ if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+ qServerEnd();
+
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool TQMYSQLDriver::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 TQMYSQLDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ qWarning("TQMYSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool TQMYSQLDriver::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ if ( isOpen() )
+ close();
+
+ unsigned int optionFlags = 0;
+
+ TQStringList raw = TQStringList::split( ';', connOpts );
+ TQStringList opts;
+ TQStringList::ConstIterator it;
+
+ // extract the real options from the string
+ for ( it = raw.begin(); it != raw.end(); ++it ) {
+ TQString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 ) {
+ TQString val( tmp.mid( idx + 1 ) );
+ val.simplifyWhiteSpace();
+ if ( val == "TRUE" || val == "1" )
+ opts << tmp.left( idx );
+ else
+ qWarning( "TQMYSQLDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ } else {
+ opts << tmp;
+ }
+ }
+
+ for ( it = opts.begin(); it != opts.end(); ++it ) {
+ TQString 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( "TQMYSQLDriver::open: Unknown connect option '%s'", (*it).latin1() );
+ }
+
+ if ( (d->mysql = mysql_init((MYSQL*) 0)) &&
+ mysql_real_connect( d->mysql,
+ host,
+ user,
+ password,
+ db.isNull() ? TQString("") : db,
+ (port > -1) ? port : 0,
+ NULL,
+ optionFlags ) )
+ {
+ if ( !db.isEmpty() && mysql_select_db( d->mysql, db )) {
+ setLastError( qMakeError("Unable open database '" + db + "'", TQSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ } else {
+ setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void TQMYSQLDriver::close()
+{
+ if ( isOpen() ) {
+ mysql_close( d->mysql );
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+TQSqlQuery TQMYSQLDriver::createQuery() const
+{
+ return TQSqlQuery( new TQMYSQLResult( this ) );
+}
+
+TQStringList TQMYSQLDriver::tables( const TQString& typeName ) const
+{
+ TQStringList tl;
+ if ( !isOpen() )
+ return tl;
+ if ( !typeName.isEmpty() && !(typeName.toInt() & (int)TQSql::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( TQString(row[0]) );
+ i++;
+ }
+ mysql_free_result( tableRes );
+ return tl;
+}
+
+TQSqlIndex TQMYSQLDriver::primaryIndex( const TQString& tablename ) const
+{
+ TQSqlIndex idx;
+ if ( !isOpen() )
+ return idx;
+ TQSqlQuery i = createQuery();
+ TQString stmt( "show index from %1;" );
+ TQSqlRecord 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;
+}
+
+TQSqlRecord TQMYSQLDriver::record( const TQString& tablename ) const
+{
+ TQSqlRecord 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 ))) {
+ TQSqlField f ( TQString( field->name ) , qDecodeMYSQLType( (int)field->type, field->flags ) );
+ fil.append ( f );
+ }
+ mysql_free_result( r );
+ return fil;
+}
+
+TQSqlRecord TQMYSQLDriver::record( const TQSqlQuery& query ) const
+{
+ TQSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ TQMYSQLResult* result = (TQMYSQLResult*)query.result();
+ TQMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* f = mysql_fetch_field( p->result );
+ if ( f ) {
+ TQSqlField fi( TQString((const char*)f->name), qDecodeMYSQLType( f->type, f->flags ) );
+ fil.append( fi );
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return fil;
+}
+
+TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQString& tablename ) const
+{
+ TQSqlRecordInfo 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 ( TQSqlFieldInfo( TQString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ TQString( field->def ),
+ (int)field->type ) );
+ }
+ mysql_free_result( r );
+ return info;
+}
+
+TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQSqlQuery& query ) const
+{
+ TQSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ TQMYSQLResult* result = (TQMYSQLResult*)query.result();
+ TQMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* field = mysql_fetch_field( p->result );
+ if ( field ) {
+ info.append ( TQSqlFieldInfo( TQString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ TQVariant(),
+ (int)field->type ) );
+
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return info;
+}
+
+MYSQL* TQMYSQLDriver::mysql()
+{
+ return d->mysql;
+}
+
+bool TQMYSQLDriver::beginTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "BEGIN WORK" ) ) {
+ setLastError( qMakeError("Unable to begin transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQMYSQLDriver::commitTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "COMMIT" ) ) {
+ setLastError( qMakeError("Unable to commit transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQMYSQLDriver::rollbackTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQMYSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "ROLLBACK" ) ) {
+ setLastError( qMakeError("Unable to rollback transaction", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+TQString TQMYSQLDriver::formatValue( const TQSqlField* field, bool trimStrings ) const
+{
+ TQString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch( field->type() ) {
+ case TQVariant::ByteArray: {
+
+ const TQByteArray 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 TQVariant::String:
+ case TQVariant::CString: {
+ // Escape '\' characters
+ r = TQSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ default:
+ r = TQSqlDriver::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 000000000..d47aa6ea9
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSQL_MYSQL_H
+#define TQSQL_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 TQMYSQLDriverPrivate;
+class TQMYSQLResultPrivate;
+class TQMYSQLDriver;
+class TQSqlRecordInfo;
+
+class TQMYSQLResult : public TQSqlResult
+{
+ friend class TQMYSQLDriver;
+public:
+ TQMYSQLResult( const TQMYSQLDriver* db );
+ ~TQMYSQLResult();
+
+ MYSQL_RES* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchNext();
+ bool fetchLast();
+ bool fetchFirst();
+ TQVariant data( int field );
+ bool isNull( int field );
+ bool reset ( const TQString& query );
+ int size();
+ int numRowsAffected();
+private:
+ TQMYSQLResultPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_MYSQL TQMYSQLDriver : public TQSqlDriver
+{
+ friend class TQMYSQLResult;
+public:
+ TQMYSQLDriver( TQObject * parent=0, const char * name=0 );
+ TQMYSQLDriver( MYSQL * con, TQObject * parent=0, const char * name=0 );
+ ~TQMYSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const TQString & db,
+ const TQString & user = TQString::null,
+ const TQString & password = TQString::null,
+ const TQString & host = TQString::null,
+ int port = -1 );
+ void close();
+ TQSqlQuery createQuery() const;
+ TQStringList tables( const TQString& user ) const;
+ TQSqlIndex primaryIndex( const TQString& tablename ) const;
+ TQSqlRecord record( const TQString& tablename ) const;
+ TQSqlRecord record( const TQSqlQuery& query ) const;
+ TQSqlRecordInfo recordInfo( const TQString& tablename ) const;
+ TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const;
+ TQString formatValue( const TQSqlField* field,
+ bool trimStrings ) const;
+ MYSQL* mysql();
+ // ### remove me for 4.0
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ TQMYSQLDriverPrivate* 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 000000000..4b91f475c
--- /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 000000000..4d0f7969f
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE 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 TQSQLLEN SQLLEN
+#else
+# define TQSQLLEN SQLINTEGER
+#endif
+
+#ifdef SQLULEN
+# define TQSQLULEN SQLULEN
+#else
+# define TQSQLULEN SQLUINTEGER
+#endif
+
+
+static const TQSQLLEN COLNAMESIZE = 256;
+//Map TQt parameter types to ODBC types
+static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
+
+class TQODBCPrivate
+{
+public:
+ TQODBCPrivate()
+ : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE)
+ {
+ sql_char_type = sql_varchar_type = sql_longvarchar_type = TQVariant::CString;
+ unicode = FALSE;
+ }
+
+ SQLHANDLE hEnv;
+ SQLHANDLE hDbc;
+ SQLHANDLE hStmt;
+
+ bool unicode;
+ bool useSchema;
+ TQVariant::Type sql_char_type;
+ TQVariant::Type sql_varchar_type;
+ TQVariant::Type sql_longvarchar_type;
+
+ TQSqlRecordInfo rInf;
+
+ bool checkDriver() const;
+ void checkUnicode();
+ void checkSchemaUsage();
+ bool setConnectionOptions( const TQString& connOpts );
+ void splitTableQualifier(const TQString &qualifier, TQString &catalog,
+ TQString &schema, TQString &table);
+};
+
+class TQODBCPreparedExtension : public TQSqlExtension
+{
+public:
+ TQODBCPreparedExtension( TQODBCResult * r )
+ : result( r ) {}
+
+ bool prepare( const TQString& query )
+ {
+ return result->prepare( query );
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ TQODBCResult * result;
+};
+
+TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict();
+
+class TQODBCOpenExtension : public TQSqlOpenExtension
+{
+public:
+ TQODBCOpenExtension( TQODBCDriver *dri )
+ : TQSqlOpenExtension(), driver(dri) {}
+ ~TQODBCOpenExtension() {}
+
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+private:
+ TQODBCDriver *driver;
+};
+
+bool TQODBCOpenExtension::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static TQString 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 TQString( (const TQChar*)description_, (uint)msgLen );
+#else
+ return TQString::fromLocal8Bit( (const char*)description_ );
+#endif
+ return TQString::null;
+}
+
+static TQString qODBCWarn( const TQODBCPrivate* odbc)
+{
+ return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );
+}
+
+static void qSqlWarning( const TQString& message, const TQODBCPrivate* odbc )
+{
+#ifdef QT_CHECK_RANGE
+ qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );
+#endif
+}
+
+static TQSqlError qMakeError( const TQString& err, int type, const TQODBCPrivate* p )
+{
+ return TQSqlError( "TQODBC3: " + err, qODBCWarn(p), type );
+}
+
+static TQVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const TQODBCPrivate* p )
+{
+ TQVariant::Type type = TQVariant::Invalid;
+ switch ( sqltype ) {
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ type = TQVariant::Double;
+ break;
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIT:
+ case SQL_TINYINT:
+ type = TQVariant::Int;
+ break;
+ case SQL_BIGINT:
+ type = TQVariant::LongLong;
+ break;
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ type = TQVariant::ByteArray;
+ break;
+ case SQL_DATE:
+ case SQL_TYPE_DATE:
+ type = TQVariant::Date;
+ break;
+ case SQL_TIME:
+ case SQL_TYPE_TIME:
+ type = TQVariant::Time;
+ break;
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIMESTAMP:
+ type = TQVariant::DateTime;
+ break;
+#ifndef Q_ODBC_VERSION_2
+ case SQL_WCHAR:
+ case SQL_WVARCHAR:
+ case SQL_WLONGVARCHAR:
+ type = TQVariant::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 = TQVariant::CString;
+ break;
+ }
+ return type;
+}
+
+static TQString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE )
+{
+ TQString fieldVal;
+ SQLRETURN r = SQL_ERROR;
+ TQSQLLEN 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,
+ (TQSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) {
+ fieldVal = TQString::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 += TQString( (TQChar*) 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 = TQString::null;
+ break;
+ }
+ }
+ delete[] buf;
+ return fieldVal;
+}
+
+static TQByteArray qGetBinaryData( SQLHANDLE hStmt, int column, TQSQLLEN& lengthIndicator, bool& isNull )
+{
+ TQByteArray fieldVal;
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ TQSQLULEN 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,
+ (TQSQLLEN)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 TQByteArray 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 )
+{
+ TQSQLLEN intbuf = 0;
+ isNull = FALSE;
+ TQSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SLONG,
+ (SQLPOINTER)&intbuf,
+ (TQSQLLEN)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;
+ TQSQLLEN lengthIndicator = 0;
+ isNull = FALSE;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_DOUBLE,
+ (SQLPOINTER)&dblbuf,
+ (TQSQLLEN)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;
+ TQSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SBIGINT,
+ (SQLPOINTER) &lngbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA )
+ isNull = TRUE;
+
+ return lngbuf;
+}
+
+// creates a TQSqlFieldInfo from a valid hStmt generated
+// by SQLColumns. The hStmt has to point to a valid position.
+static TQSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const TQODBCPrivate* p )
+{
+ bool isNull;
+ TQString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode );
+ int type = qGetIntData( hStmt, 4, isNull ); // column type
+ int retquired = qGetIntData( hStmt, 10, isNull ); // nullable-flag
+ // retquired can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ if ( retquired == SQL_NO_NULLS ) {
+ retquired = 1;
+ } else if ( retquired == SQL_NULLABLE ) {
+ retquired = 0;
+ } else {
+ retquired = -1;
+ }
+ int size = qGetIntData( hStmt, 6, isNull ); // column size
+ int prec = qGetIntData( hStmt, 8, isNull ); // precision
+ return TQSqlFieldInfo( fname, qDecodeODBCType( type, p ), retquired, size, prec, TQVariant(), type );
+}
+
+static TQSqlFieldInfo qMakeFieldInfo( const TQODBCPrivate* p, int i )
+{
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ TQSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR colName[ COLNAMESIZE ];
+ r = SQLDescribeCol( p->hStmt,
+ i+1,
+ colName,
+ (TQSQLULEN)COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable);
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( TQString("qMakeField: Unable to describe column %1").arg(i), p );
+#endif
+ return TQSqlFieldInfo();
+ }
+#ifdef UNICODE
+ TQString qColName( (const TQChar*)colName, (uint)colNameLen );
+#else
+ TQString qColName = TQString::fromLocal8Bit( (const char*)colName );
+#endif
+ // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ int retquired = -1;
+ if ( nullable == SQL_NO_NULLS ) {
+ retquired = 1;
+ } else if ( nullable == SQL_NULLABLE ) {
+ retquired = 0;
+ }
+ TQVariant::Type type = qDecodeODBCType( colType, p );
+ return TQSqlFieldInfo( qColName,
+ type,
+ retquired,
+ (int)colSize == 0 ? -1 : (int)colSize,
+ (int)colScale == 0 ? -1 : (int)colScale,
+ TQVariant(),
+ (int)colType );
+}
+
+bool TQODBCPrivate::setConnectionOptions( const TQString& connOpts )
+{
+ // Set any connection attributes
+ TQStringList raw = TQStringList::split( ';', connOpts );
+ TQStringList opts;
+ SQLRETURN r = SQL_SUCCESS;
+ TQMap<TQString, TQString> connMap;
+ for ( TQStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
+ TQString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 )
+ connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
+ else
+ qWarning( "TQODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ }
+ if ( connMap.count() ) {
+ TQMap<TQString, TQString>::ConstIterator it;
+ TQString 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( TQString( "TQODBCDriver::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( TQString( "TQODBCDriver::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( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 );
+ }
+#ifdef QT_CHECK_RANGE
+ else {
+ qWarning( TQString("TQODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) );
+ }
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( TQString("TQODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this );
+#endif
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void TQODBCPrivate::splitTableQualifier(const TQString & qualifier, TQString &catalog,
+ TQString &schema, TQString &table)
+{
+ if (!useSchema) {
+ table = qualifier;
+ return;
+ }
+ TQStringList l = TQStringList::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 ( TQStringList::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++;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+TQODBCResult::TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p )
+: TQSqlResult(db)
+{
+ d = new TQODBCPrivate();
+ (*d) = (*p);
+ setExtension( new TQODBCPreparedExtension( this ) );
+}
+
+TQODBCResult::~TQODBCResult()
+{
+ if ( d->hStmt && driver()->isOpen() ) {
+ SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d );
+#endif
+ }
+
+ delete d;
+}
+
+bool TQODBCResult::reset ( const TQString& query )
+{
+ setActive( FALSE );
+ setAt( TQSql::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( "TQODBCResult::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( "TQODBCResult::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( "TQODBCResult::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
+ TQCString 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", TQSqlError::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 TQODBCResult::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( TQSql::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 TQODBCResult::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 TQODBCResult::fetchFirst()
+{
+ if ( isForwardOnly() && at() != TQSql::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 TQODBCResult::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 TQODBCResult::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 == TQSql::AfterLast )
+ return FALSE;
+ if ( i == TQSql::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;
+}
+
+TQVariant TQODBCResult::data( int field )
+{
+ if ( field >= (int) d->rInf.count() ) {
+ qWarning( "TQODBCResult::data: column %d out of range", field );
+ return TQVariant();
+ }
+ if ( fieldCache.contains( field ) )
+ return fieldCache[ field ];
+ SQLRETURN r(0);
+ TQSQLLEN lengthIndicator = 0;
+ bool isNull = FALSE;
+ int current = fieldCache.count();
+ for ( ; current < (field + 1); ++current ) {
+ const TQSqlFieldInfo info = d->rInf[ current ];
+ switch ( info.type() ) {
+ case TQVariant::LongLong:
+ fieldCache[ current ] = TQVariant( (Q_LLONG) qGetBigIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Int:
+ fieldCache[ current ] = TQVariant( qGetIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Date:
+ DATE_STRUCT dbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_DATE,
+ (SQLPOINTER)&dbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQDate( dbuf.year, dbuf.month, dbuf.day ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQDate() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::Time:
+ TIME_STRUCT tbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIME,
+ (SQLPOINTER)&tbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQTime( tbuf.hour, tbuf.minute, tbuf.second ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::DateTime:
+ TIMESTAMP_STRUCT dtbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIMESTAMP,
+ (SQLPOINTER)&dtbuf,
+ (TQSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = TQVariant( TQDateTime( TQDate( dtbuf.year, dtbuf.month, dtbuf.day ), TQTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = TQVariant( TQDateTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case TQVariant::ByteArray: {
+ isNull = FALSE;
+ TQByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull );
+ fieldCache[ current ] = TQVariant( val );
+ nullCache[ current ] = isNull;
+ break; }
+ case TQVariant::String:
+ isNull = FALSE;
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, TRUE ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::Double:
+ if ( info.typeID() == SQL_DECIMAL || info.typeID() == SQL_NUMERIC )
+ // bind Double values as string to prevent loss of precision
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma
+ else
+ fieldCache[ current ] = TQVariant( qGetDoubleData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case TQVariant::CString:
+ default:
+ isNull = FALSE;
+ fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, FALSE ) );
+ nullCache[ current ] = isNull;
+ break;
+ }
+ }
+ return fieldCache[ --current ];
+}
+
+bool TQODBCResult::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 TQODBCResult::size()
+{
+ return -1;
+}
+
+int TQODBCResult::numRowsAffected()
+{
+ TQSQLLEN affectedRowCount(0);
+ SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount );
+ if ( r == SQL_SUCCESS )
+ return affectedRowCount;
+#ifdef QT_CHECK_RANGE
+ else
+ qSqlWarning( "TQODBCResult::numRowsAffected: Unable to count affected rows", d );
+#endif
+ return -1;
+}
+
+bool TQODBCResult::prepare( const TQString& query )
+{
+ setActive( FALSE );
+ setAt( TQSql::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( "TQODBCResult::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( "TQODBCResult::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( "TQODBCResult::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
+ TQCString query8 = query.local8Bit();
+ r = SQLPrepare( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::prepare: Unable to prepare statement", d );
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCResult::exec()
+{
+ SQLRETURN r;
+ TQPtrList<TQVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit
+ tmpStorage.setAutoDelete( TRUE );
+
+ setActive( FALSE );
+ setAt( TQSql::BeforeFirst );
+ d->rInf.clear();
+
+ if ( !d->hStmt ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCResult::exec: No statement handle available", d );
+#endif
+ return FALSE;
+ } else {
+ r = SQLFreeStmt( d->hStmt, SQL_CLOSE );
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "TQODBCResult::exec: Unable to close statement handle", d );
+ return FALSE;
+ }
+ }
+
+ // bind parameters - only positional binding allowed
+ if ( extension()->index.count() > 0 ) {
+ TQMap<int, TQString>::Iterator it;
+ int para = 1;
+ TQVariant val;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+ val = extension()->values[ it.data() ].value;
+ TQSQLLEN *ind = new TQSQLLEN( SQL_NTS );
+ tmpStorage.append( qAutoDeleter(ind) );
+ if ( val.isNull() ) {
+ *ind = SQL_NULL_DATA;
+ }
+ switch ( val.type() ) {
+ case TQVariant::Date: {
+ DATE_STRUCT * dt = new DATE_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQDate 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,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::Time: {
+ TIME_STRUCT * dt = new TIME_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQTime 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,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ TQDateTime 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,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::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,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::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,
+ (TQSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case TQVariant::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(),
+ (TQSQLLEN)val.asByteArray().size(),
+ ind );
+ break; }
+#ifndef Q_ODBC_VERSION_2
+ case TQVariant::String:
+ if ( d->unicode ) {
+ TQString * str = new TQString( 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(),
+ (TQSQLLEN) len,
+ ind );
+ break;
+ }
+#endif
+ // fall through
+ default: {
+ TQCString * str = new TQCString( 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(),
+ (TQSQLLEN)(str->length() + 1),
+ ind );
+ break; }
+ }
+ para++;
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to bind variable", TQSqlError::Statement, d ) );
+ return FALSE;
+ }
+ }
+ }
+ r = SQLExecute( d->hStmt );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to execute statement", TQSqlError::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 ) {
+ TQMap<int, TQString>::Iterator it;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+
+ SQLINTEGER* indPtr = qAutoDeleterData( (TQAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() );
+ if ( !indPtr )
+ return FALSE;
+ bool isNull = (*indPtr == SQL_NULL_DATA);
+ tmpStorage.removeFirst();
+
+ TQVariant::Type type = extension()->values[ it.data() ].value.type();
+ if ( isNull ) {
+ TQVariant v;
+ v.cast(type);
+ extension()->values[ it.data() ].value = v;
+ if (type != TQVariant::ByteArray)
+ tmpStorage.removeFirst();
+ continue;
+ }
+
+ switch (type) {
+ case TQVariant::Date: {
+ DATE_STRUCT * ds = qAutoDeleterData( (TQAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQDate( ds->year, ds->month, ds->day ) );
+ break; }
+ case TQVariant::Time: {
+ TIME_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQTime( dt->hour, dt->minute, dt->second ) );
+ break; }
+ case TQVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( TQDateTime( TQDate( dt->year, dt->month, dt->day ),
+ TQTime( dt->hour, dt->minute, dt->second ) ) );
+ break; }
+ case TQVariant::Int: {
+ int * v = qAutoDeleterData( (TQAutoDeleter<int>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *v );
+ break; }
+ case TQVariant::Double: {
+ double * v = qAutoDeleterData( (TQAutoDeleter<double>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *v );
+ break; }
+ case TQVariant::ByteArray:
+ break;
+ case TQVariant::String:
+ if ( d->unicode ) {
+ TQString * str = qAutoDeleterData( (TQAutoDeleter<TQString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *str );
+ break;
+ }
+ // fall through
+ default: {
+ TQCString * str = qAutoDeleterData( (TQAutoDeleter<TQCString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = TQVariant( *str );
+ break; }
+ }
+ if (type != TQVariant::ByteArray)
+ tmpStorage.removeFirst();
+ }
+ }
+
+ return TRUE;
+}
+
+////////////////////////////////////////
+
+
+TQODBCDriver::TQODBCDriver( TQObject * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQODBC")
+{
+ init();
+}
+
+TQODBCDriver::TQODBCDriver( SQLHANDLE env, SQLHANDLE con, TQObject * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQODBC")
+{
+ init();
+ d->hEnv = env;
+ d->hDbc = con;
+ if ( env && con ) {
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void TQODBCDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new TQODBCOpenExtension(this) );
+ d = new TQODBCPrivate();
+}
+
+TQODBCDriver::~TQODBCDriver()
+{
+ cleanup();
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool TQODBCDriver::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 TQODBCDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ qWarning("TQODBCDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool TQODBCDriver::open( const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString &,
+ int,
+ const TQString& 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( "TQODBCDriver::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( "TQODBCDriver::open: Unable to allocate connection", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->setConnectionOptions( connOpts ) )
+ return FALSE;
+
+ // Create the connection string
+ TQString connTQStr;
+ // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
+ if ( db.contains(".dsn") )
+ connTQStr = "FILEDSN=" + db;
+ else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) )
+ connTQStr = db;
+ else
+ connTQStr = "DSN=" + db;
+ connTQStr += ";UID=" + user + ";PWD=" + password;
+ SQLSMALLINT cb;
+ SQLTCHAR connOut[1024];
+ r = SQLDriverConnect( d->hDbc,
+ NULL,
+#ifdef UNICODE
+ (SQLWCHAR*)connTQStr.unicode(),
+#else
+ (SQLCHAR*)connTQStr.latin1(),
+#endif
+ (SQLSMALLINT)connTQStr.length(),
+ connOut,
+ 1024,
+ &cb,
+ SQL_DRIVER_NOPROMPT );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->checkDriver() ) {
+ setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", TQSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ d->checkUnicode();
+ d->checkSchemaUsage();
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void TQODBCDriver::close()
+{
+ cleanup();
+ setOpen( FALSE );
+ setOpenError( FALSE );
+}
+
+void TQODBCDriver::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( "TQODBCDriver::disconnect: Unable to disconnect datasource", d );
+#endif
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "TQODBCDriver::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( "TQODBCDriver::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 TQODBCPrivate::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 = TQVariant::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 = TQVariant::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 = TQVariant::String;
+ unicode = TRUE;
+ }
+}
+
+bool TQODBCPrivate::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 retquired functions
+ for ( i = 0; reqFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning ( "TQODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). "
+ "Please look at the TQt 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( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] );
+#endif
+ return TRUE;
+ }
+ }
+#endif //ODBC_CHECK_DRIVER
+
+ return TRUE;
+}
+
+void TQODBCPrivate::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);
+}
+
+TQSqlQuery TQODBCDriver::createQuery() const
+{
+ return TQSqlQuery( new TQODBCResult( this, d ) );
+}
+
+bool TQODBCDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" TQODBCDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool TQODBCDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" TQODBCDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" TQODBCDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool TQODBCDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+TQStringList TQODBCDriver::tables( const TQString& typeName ) const
+{
+ TQStringList 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( "TQODBCDriver::tables: Unable to allocate handle", d );
+#endif
+ return tl;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ TQString tableType;
+ if ( typeName.isEmpty() || ((type & (int)TQSql::Tables) == (int)TQSql::Tables) )
+ tableType += "TABLE,";
+ if ( (type & (int)TQSql::Views) == (int)TQSql::Views )
+ tableType += "VIEW,";
+ if ( (type & (int)TQSql::SystemTables) == (int)TQSql::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( "TQODBCDriver::tables Unable to execute table list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ while ( r == SQL_SUCCESS ) {
+ bool isNull;
+ TQString 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( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d );
+ return tl;
+}
+
+TQSqlIndex TQODBCDriver::primaryIndex( const TQString& tablename ) const
+{
+ TQSqlIndex index( tablename );
+ if ( !isOpen() )
+ return index;
+ bool usingSpecialColumns = FALSE;
+ TQSqlRecord rec = record( tablename );
+
+ SQLHANDLE hStmt;
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "TQODBCDriver::primaryIndex: Unable to list primary key", d );
+#endif
+ return index;
+ }
+ TQString 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( "TQODBCDriver::primaryIndex: Unable to execute primary key list", d );
+#endif
+ } else {
+ usingSpecialColumns = TRUE;
+ }
+ }
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ bool isNull;
+ int fakeId = 0;
+ TQString 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 = TQString::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
+ }
+ TQSqlField *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( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d );
+ return index;
+}
+
+TQSqlRecord TQODBCDriver::record( const TQString& tablename ) const
+{
+ return recordInfo( tablename ).toRecord();
+}
+
+TQSqlRecord TQODBCDriver::record( const TQSqlQuery& query ) const
+{
+ return recordInfo( query ).toRecord();
+}
+
+TQSqlRecordInfo TQODBCDriver::recordInfo( const TQString& tablename ) const
+{
+ TQSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+
+ SQLHANDLE hStmt;
+ TQString 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( "TQODBCDriver::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( "TQODBCDriver::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( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d );
+
+ return fil;
+}
+
+TQSqlRecordInfo TQODBCDriver::recordInfo( const TQSqlQuery& query ) const
+{
+ TQSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ TQODBCResult* result = (TQODBCResult*)query.result();
+ fil = result->d->rInf;
+ }
+ return fil;
+}
+
+SQLHANDLE TQODBCDriver::environment()
+{
+ return d->hEnv;
+}
+
+SQLHANDLE TQODBCDriver::connection()
+{
+ return d->hDbc;
+}
+
+TQString TQODBCDriver::formatValue( const TQSqlField* field,
+ bool trimStrings ) const
+{
+ TQString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else if ( field->type() == TQVariant::DateTime ) {
+ // Use an escape sequence for the datetime fields
+ if ( field->value().toDateTime().isValid() ){
+ TQDate dt = field->value().toDateTime().date();
+ TQTime 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 '" +
+ TQString::number(dt.year()) + "-" +
+ TQString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" +
+ TQString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " +
+ tm.toString() +
+ "' }";
+ } else
+ r = nullText();
+ } else if ( field->type() == TQVariant::ByteArray ) {
+ TQByteArray ba = field->value().toByteArray();
+ TQString 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 = TQSqlDriver::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 000000000..b65acf6ca
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSQL_ODBC_H
+#define TQSQL_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 TQODBCPrivate;
+class TQODBCDriver;
+class TQSqlRecordInfo;
+
+class TQODBCResult : public TQSqlResult
+{
+ friend class TQODBCDriver;
+public:
+ TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p );
+ ~TQODBCResult();
+
+ SQLHANDLE statement();
+ bool prepare( const TQString& query );
+ bool exec();
+
+protected:
+ bool fetchNext();
+ bool fetchFirst();
+ bool fetchLast();
+ bool fetchPrior();
+ bool fetch(int i);
+ bool reset ( const TQString& query );
+ TQVariant data( int field );
+ bool isNull( int field );
+ int size();
+ int numRowsAffected();
+private:
+ TQODBCPrivate* d;
+ typedef TQMap<int,TQVariant> FieldCache;
+ FieldCache fieldCache;
+ typedef TQMap<int,bool> NullCache;
+ NullCache nullCache;
+};
+
+class Q_EXPORT_SQLDRIVER_ODBC TQODBCDriver : public TQSqlDriver
+{
+public:
+ TQODBCDriver( TQObject * parent=0, const char * name=0 );
+ TQODBCDriver( SQLHANDLE env, SQLHANDLE con, TQObject * parent=0, const char * name=0 );
+ ~TQODBCDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const TQString & db,
+ const TQString & user = TQString::null,
+ const TQString & password = TQString::null,
+ const TQString & host = TQString::null,
+ int port = -1 );
+ void close();
+ TQSqlQuery createQuery() const;
+ TQStringList tables( const TQString& user ) const;
+ TQSqlRecord record( const TQString& tablename ) const;
+ TQSqlRecord record( const TQSqlQuery& query ) const;
+ TQSqlRecordInfo recordInfo( const TQString& tablename ) const;
+ TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const;
+ TQSqlIndex primaryIndex( const TQString& tablename ) const;
+ SQLHANDLE environment();
+ SQLHANDLE connection();
+
+ TQString formatValue( const TQSqlField* field,
+ bool trimStrings ) const;
+ // ### remove me for 4.0
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ bool endTrans();
+ void cleanup();
+ TQODBCPrivate* 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 000000000..99df0fdfb
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE 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
+
+TQPtrDict<TQSqlDriverExtension> *qSqlDriverExtDict();
+TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict();
+
+class TQPSQLPrivate
+{
+public:
+ TQPSQLPrivate():connection(0), result(0), isUtf8(FALSE) {}
+ PGconn *connection;
+ PGresult *result;
+ bool isUtf8;
+};
+
+class TQPSQLDriverExtension : public TQSqlDriverExtension
+{
+public:
+ TQPSQLDriverExtension( TQPSQLDriver *dri )
+ : TQSqlDriverExtension(), driver(dri) { }
+ ~TQPSQLDriverExtension() {}
+
+ bool isOpen() const;
+private:
+ TQPSQLDriver *driver;
+};
+
+bool TQPSQLDriverExtension::isOpen() const
+{
+ return PQstatus( driver->connection() ) == CONNECTION_OK;
+}
+
+class TQPSQLOpenExtension : public TQSqlOpenExtension
+{
+public:
+ TQPSQLOpenExtension( TQPSQLDriver *dri )
+ : TQSqlOpenExtension(), driver(dri) { }
+ ~TQPSQLOpenExtension() {}
+
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+private:
+ TQPSQLDriver *driver;
+};
+
+bool TQPSQLOpenExtension::open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static TQSqlError qMakeError( const TQString& err, int type, const TQPSQLPrivate* p )
+{
+ const char *s = PQerrorMessage(p->connection);
+ TQString msg = p->isUtf8 ? TQString::fromUtf8(s) : TQString::fromLocal8Bit(s);
+ return TQSqlError("TQPSQL: " + err, msg, type);
+}
+
+static TQVariant::Type qDecodePSQLType( int t )
+{
+ TQVariant::Type type = TQVariant::Invalid;
+ switch ( t ) {
+ case BOOLOID :
+ type = TQVariant::Bool;
+ break;
+ case INT8OID :
+ type = TQVariant::LongLong;
+ break;
+ case INT2OID :
+ // case INT2VECTOROID : // 7.x
+ case INT4OID :
+ type = TQVariant::Int;
+ break;
+ case NUMERICOID :
+ case FLOAT4OID :
+ case FLOAT8OID :
+ type = TQVariant::Double;
+ break;
+ case ABSTIMEOID :
+ case RELTIMEOID :
+ case DATEOID :
+ type = TQVariant::Date;
+ break;
+ case TIMEOID :
+#ifdef TIMETZOID // 7.x
+ case TIMETZOID :
+#endif
+ type = TQVariant::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 = TQVariant::DateTime;
+ break;
+ // case ZPBITOID : // 7.x
+ // case VARBITOID : // 7.x
+ case OIDOID :
+ case BYTEAOID :
+ type = TQVariant::ByteArray;
+ break;
+ case REGPROCOID :
+ case TIDOID :
+ case XIDOID :
+ case CIDOID :
+ // case OIDVECTOROID : // 7.x
+ case UNKNOWNOID :
+ // case TINTERVALOID : // 7.x
+ type = TQVariant::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 = TQVariant::String;
+ break;
+ }
+ return type;
+}
+
+TQPSQLResult::TQPSQLResult( const TQPSQLDriver* db, const TQPSQLPrivate* p )
+: TQSqlResult( db ),
+ currentSize( 0 )
+{
+ d = new TQPSQLPrivate();
+ (*d) = (*p);
+}
+
+TQPSQLResult::~TQPSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+PGresult* TQPSQLResult::result()
+{
+ return d->result;
+}
+
+void TQPSQLResult::cleanup()
+{
+ if ( d->result )
+ PQclear( d->result );
+ d->result = 0;
+ setAt( -1 );
+ currentSize = 0;
+ setActive( FALSE );
+}
+
+bool TQPSQLResult::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 TQPSQLResult::fetchFirst()
+{
+ return fetch( 0 );
+}
+
+bool TQPSQLResult::fetchLast()
+{
+ return fetch( PQntuples( d->result ) - 1 );
+}
+
+// some Postgres conversions
+static TQPoint pointFromString( const TQString& 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 TQPoint( x, y ) ;
+ } else
+ return TQPoint();
+}
+
+TQVariant TQPSQLResult::data( int i )
+{
+ if ( i >= PQnfields( d->result ) ) {
+ qWarning( "TQPSQLResult::data: column %d out of range", i );
+ return TQVariant();
+ }
+ int ptype = PQftype( d->result, i );
+ TQVariant::Type type = qDecodePSQLType( ptype );
+ const TQString val = ( d->isUtf8 && ptype != BYTEAOID ) ?
+ TQString::fromUtf8( PQgetvalue( d->result, at(), i ) ) :
+ TQString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) );
+ if ( PQgetisnull( d->result, at(), i ) ) {
+ TQVariant v;
+ v.cast( type );
+ return v;
+ }
+ switch ( type ) {
+ case TQVariant::Bool:
+ {
+ TQVariant b ( (bool)(val == "t"), 0 );
+ return ( b );
+ }
+ case TQVariant::String:
+ return TQVariant( val );
+ case TQVariant::LongLong:
+ if ( val[0] == '-' )
+ return TQVariant( val.toLongLong() );
+ else
+ return TQVariant( val.toULongLong() );
+ case TQVariant::Int:
+ return TQVariant( val.toInt() );
+ case TQVariant::Double:
+ if ( ptype == NUMERICOID )
+ return TQVariant( val );
+ return TQVariant( val.toDouble() );
+ case TQVariant::Date:
+ if ( val.isEmpty() ) {
+ return TQVariant( TQDate() );
+ } else {
+ return TQVariant( TQDate::fromString( val, TQt::ISODate ) );
+ }
+ case TQVariant::Time:
+ if ( val.isEmpty() )
+ return TQVariant( TQTime() );
+ if ( val.at( val.length() - 3 ) == '+' )
+ // strip the timezone
+ return TQVariant( TQTime::fromString( val.left( val.length() - 3 ), TQt::ISODate ) );
+ return TQVariant( TQTime::fromString( val, TQt::ISODate ) );
+ case TQVariant::DateTime: {
+ if ( val.length() < 10 )
+ return TQVariant( TQDateTime() );
+ // remove the timezone
+ TQString 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 TQVariant( TQDateTime() );
+ else
+ return TQVariant( TQDateTime::fromString( dtval, TQt::ISODate ) );
+ }
+ case TQVariant::Point:
+ return TQVariant( pointFromString( val ) );
+ case TQVariant::Rect: // format '(x,y),(x',y')'
+ {
+ int pivot = val.find( "),(" );
+ if ( pivot != -1 )
+ return TQVariant( TQRect( pointFromString( val.mid(pivot+2,val.length()) ), pointFromString( val.mid(0,pivot+1) ) ) );
+ return TQVariant( TQRect() );
+ }
+ case TQVariant::PointArray: // format '((x,y),(x1,y1),...,(xn,yn))'
+ {
+ TQRegExp pointPattern("\\([0-9-]*,[0-9-]*\\)");
+ int points = val.contains( pointPattern );
+ TQPointArray 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, TQPoint() );
+ } else {
+ parray.setPoint( i, TQPoint() );
+ break;
+ }
+ idx = end+2;
+ }
+ return TQVariant( parray );
+ }
+ case TQVariant::ByteArray: {
+ if ( ptype == BYTEAOID ) {
+ uint i = 0;
+ int index = 0;
+ uint len = val.length();
+ static const TQChar backslash( '\\' );
+ TQByteArray 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 TQVariant( ba );
+ }
+
+ TQByteArray ba;
+ ((TQSqlDriver*)driver())->beginTransaction();
+ Oid oid = val.toInt();
+ int fd = lo_open( d->connection, oid, INV_READ );
+#ifdef QT_CHECK_RANGE
+ if ( fd < 0) {
+ qWarning( "TQPSQLResult::data: unable to open large object for read" );
+ ((TQSqlDriver*)driver())->commitTransaction();
+ return TQVariant( 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 );
+ ((TQSqlDriver*)driver())->commitTransaction();
+ return TQVariant( 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( "TQPSQLResult::data: unable to read large object" );
+ } else {
+ ba.duplicate( buf, size );
+ }
+ delete [] buf;
+ lo_close( d->connection, fd );
+ ((TQSqlDriver*)driver())->commitTransaction();
+ return TQVariant( ba );
+ }
+ default:
+ case TQVariant::Invalid:
+#ifdef QT_CHECK_RANGE
+ qWarning("TQPSQLResult::data: unknown data type");
+#endif
+ ;
+ }
+ return TQVariant();
+}
+
+bool TQPSQLResult::isNull( int field )
+{
+ PQgetvalue( d->result, at(), field );
+ return PQgetisnull( d->result, at(), field );
+}
+
+bool TQPSQLResult::reset ( const TQString& query )
+{
+ cleanup();
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()->isOpen() || driver()->isOpenError() )
+ return FALSE;
+ setActive( FALSE );
+ setAt( TQSql::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", TQSqlError::Statement, d ) );
+ return FALSE;
+}
+
+int TQPSQLResult::size()
+{
+ return currentSize;
+}
+
+int TQPSQLResult::numRowsAffected()
+{
+ return TQString( 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 TQPSQLDriver::Protocol getPSQLVersion( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "select version()" );
+ int status = PQresultStatus( result );
+ if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) {
+ TQString val( PQgetvalue( result, 0, 0 ) );
+ PQclear( result );
+ TQRegExp 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 TQPSQLDriver::Version6;
+ }
+ if ( vMaj == 6 ) {
+ return TQPSQLDriver::Version6;
+ } else if ( vMaj == 7 ) {
+ if ( vMin < 1 )
+ return TQPSQLDriver::Version7;
+ else if ( vMin < 3 )
+ return TQPSQLDriver::Version71;
+ }
+ return TQPSQLDriver::Version73;
+ }
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "This version of PostgreSQL is not supported and may not work." );
+#endif
+ }
+
+ return TQPSQLDriver::Version6;
+}
+
+TQPSQLDriver::TQPSQLDriver( TQObject * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQPSQL"), pro( TQPSQLDriver::Version6 )
+{
+ init();
+}
+
+TQPSQLDriver::TQPSQLDriver( PGconn * conn, TQObject * parent, const char * name )
+ : TQSqlDriver(parent,name ? name : "TQPSQL"), pro( TQPSQLDriver::Version6 )
+{
+ init();
+ d->connection = conn;
+ if ( conn ) {
+ pro = getPSQLVersion( d->connection );
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void TQPSQLDriver::init()
+{
+ qSqlDriverExtDict()->insert( this, new TQPSQLDriverExtension(this) );
+ qSqlOpenExtDict()->insert( this, new TQPSQLOpenExtension(this) );
+
+ d = new TQPSQLPrivate();
+}
+
+TQPSQLDriver::~TQPSQLDriver()
+{
+ if ( d->connection )
+ PQfinish( d->connection );
+ delete d;
+ if ( !qSqlDriverExtDict()->isEmpty() ) {
+ TQSqlDriverExtension *ext = qSqlDriverExtDict()->take( this );
+ delete ext;
+ }
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+PGconn* TQPSQLDriver::connection()
+{
+ return d->connection;
+}
+
+
+bool TQPSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+ return TRUE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return pro >= TQPSQLDriver::Version71;
+ case Unicode:
+ return d->isUtf8;
+ default:
+ return FALSE;
+ }
+}
+
+bool TQPSQLDriver::open( const TQString&,
+ const TQString&,
+ const TQString&,
+ const TQString&,
+ int )
+{
+ qWarning("TQPSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool TQPSQLDriver::open( const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int port,
+ const TQString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ TQString 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( TQString::number( port ) );
+
+ // add any connect options - the server will handle error detection
+ if ( !connOpts.isEmpty() )
+ connectString += " " + TQStringList::split( ';', connOpts ).join( " " );
+
+ d->connection = PQconnectdb( connectString.local8Bit().data() );
+ if ( PQstatus( d->connection ) == CONNECTION_BAD ) {
+ setLastError( qMakeError("Unable to connect", TQSqlError::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 TQPSQLDriver::close()
+{
+ if ( isOpen() ) {
+ if (d->connection)
+ PQfinish( d->connection );
+ d->connection = 0;
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+TQSqlQuery TQPSQLDriver::createQuery() const
+{
+ return TQSqlQuery( new TQPSQLResult( this, d ) );
+}
+
+bool TQPSQLDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQPSQLDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool TQPSQLDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQPSQLDriver::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", TQSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool TQPSQLDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "TQPSQLDriver::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", TQSqlError::Transaction, d ) );
+ PQclear( res );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+TQStringList TQPSQLDriver::tables( const TQString& typeName ) const
+{
+ TQStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ TQSqlQuery t = createQuery();
+ t.setForwardOnly( TRUE );
+
+ if ( typeName.isEmpty() || ((type & (int)TQSql::Tables) == (int)TQSql::Tables) ) {
+
+ TQString query("select relname from pg_class where (relkind = 'r') "
+ "and (relname !~ '^Inv') "
+ "and (relname !~ '^pg_') ");
+ if (pro >= TQPSQLDriver::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)TQSql::Views) == (int)TQSql::Views ) {
+ TQString query("select relname from pg_class where ( relkind = 'v' ) "
+ "and ( relname !~ '^Inv' ) "
+ "and ( relname !~ '^pg_' ) ");
+ if (pro >= TQPSQLDriver::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)TQSql::SystemTables) == (int)TQSql::SystemTables ) {
+ TQString query( "select relname from pg_class where ( relkind = 'r' ) "
+ "and ( relname like 'pg_%' ) " );
+ if (pro >= TQPSQLDriver::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;
+}
+
+TQSqlIndex TQPSQLDriver::primaryIndex( const TQString& tablename ) const
+{
+ TQSqlIndex idx( tablename );
+ if ( !isOpen() )
+ return idx;
+ TQSqlQuery i = createQuery();
+ TQString stmt;
+
+ switch( pro ) {
+ case TQPSQLDriver::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 TQPSQLDriver::Version7:
+ case TQPSQLDriver::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 TQPSQLDriver::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() ) {
+ TQSqlField f( i.value(0).toString(), qDecodePSQLType( i.value(1).toInt() ) );
+ idx.append( f );
+ idx.setName( i.value(2).toString() );
+ }
+ return idx;
+}
+
+TQSqlRecord TQPSQLDriver::record( const TQString& tablename ) const
+{
+ TQSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ TQString stmt;
+ switch( pro ) {
+ case TQPSQLDriver::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 TQPSQLDriver::Version7:
+ case TQPSQLDriver::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 TQPSQLDriver::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;
+ }
+
+ TQSqlQuery fi = createQuery();
+ fi.exec( stmt.arg( tablename.lower() ) );
+ while ( fi.next() ) {
+ TQSqlField f( fi.value(0).toString(), qDecodePSQLType( fi.value(1).toInt() ) );
+ fil.append( f );
+ }
+ return fil;
+}
+
+TQSqlRecord TQPSQLDriver::record( const TQSqlQuery& query ) const
+{
+ TQSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ TQPSQLResult* result = (TQPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ TQString name = PQfname( result->d->result, i );
+ TQVariant::Type type = qDecodePSQLType( PQftype( result->d->result, i ) );
+ TQSqlField rf( name, type );
+ fil.append( rf );
+ }
+ }
+ return fil;
+}
+
+TQSqlRecordInfo TQPSQLDriver::recordInfo( const TQString& tablename ) const
+{
+ TQSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+
+ TQString stmt;
+ switch( pro ) {
+ case TQPSQLDriver::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 TQPSQLDriver::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 TQPSQLDriver::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 TQPSQLDriver::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;
+ }
+
+ TQSqlQuery query = createQuery();
+ query.exec( stmt.arg( tablename.lower() ) );
+ if ( pro >= TQPSQLDriver::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;
+ }
+ TQString defVal = query.value( 5 ).toString();
+ if ( !defVal.isEmpty() && defVal.startsWith( "'" ) )
+ defVal = defVal.mid( 1, defVal.length() - 2 );
+ info.append( TQSqlFieldInfo( 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() ) {
+ TQString defVal;
+ TQString stmt2 = "select pg_attrdef.adsrc from pg_attrdef where "
+ "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ";
+ TQSqlQuery 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( TQSqlFieldInfo( query.value( 0 ).toString(),
+ qDecodePSQLType( query.value( 1 ).toInt() ),
+ query.value( 2 ).toBool(),
+ len,
+ precision,
+ defVal,
+ query.value( 1 ).toInt() ) );
+ }
+ }
+
+ return info;
+}
+
+TQSqlRecordInfo TQPSQLDriver::recordInfo( const TQSqlQuery& query ) const
+{
+ TQSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.driver() == this ) {
+ TQPSQLResult* result = (TQPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ TQString 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( TQSqlFieldInfo( name,
+ qDecodePSQLType( PQftype( result->d->result, i ) ),
+ -1,
+ len,
+ precision,
+ TQVariant(),
+ PQftype( result->d->result, i ) ) );
+ }
+ }
+ return info;
+}
+
+TQString TQPSQLDriver::formatValue( const TQSqlField* field,
+ bool ) const
+{
+ TQString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch ( field->type() ) {
+ case TQVariant::DateTime:
+ if ( field->value().toDateTime().isValid() ) {
+ TQDate dt = field->value().toDateTime().date();
+ TQTime tm = field->value().toDateTime().time();
+ // msecs need to be right aligned otherwise psql
+ // interpretes them wrong
+ r = "'" + TQString::number( dt.year() ) + "-" +
+ TQString::number( dt.month() ) + "-" +
+ TQString::number( dt.day() ) + " " +
+ tm.toString() + "." +
+ TQString::number( tm.msec() ).rightJustify( 3, '0' ) + "'";
+ } else {
+ r = nullText();
+ }
+ break;
+ case TQVariant::Time:
+ if ( field->value().toTime().isValid() ) {
+ r = field->value().toTime().toString( TQt::ISODate );
+ } else {
+ r = nullText();
+ }
+ case TQVariant::String:
+ case TQVariant::CString: {
+ switch ( field->value().type() ) {
+ case TQVariant::Rect: {
+ TQRect rec = field->value().toRect();
+ // upper right corner then lower left according to psql docs
+ r = "'(" + TQString::number( rec.right() ) +
+ "," + TQString::number( rec.bottom() ) +
+ "),(" + TQString::number( rec.left() ) +
+ "," + TQString::number( rec.top() ) + ")'";
+ break;
+ }
+ case TQVariant::Point: {
+ TQPoint p = field->value().toPoint();
+ r = "'(" + TQString::number( p.x() ) +
+ "," + TQString::number( p.y() ) + ")'";
+ break;
+ }
+ case TQVariant::PointArray: {
+ TQPointArray pa = field->value().toPointArray();
+ r = "' ";
+ for ( int i = 0; i < (int)pa.size(); ++i ) {
+ r += "(" + TQString::number( pa[i].x() ) +
+ "," + TQString::number( pa[i].y() ) + "),";
+ }
+ r.truncate( r.length() - 1 );
+ r += "'";
+ break;
+ }
+ default:
+ // Escape '\' characters
+ r = TQSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ break;
+ }
+ case TQVariant::Bool:
+ if ( field->value().toBool() )
+ r = "TRUE";
+ else
+ r = "FALSE";
+ break;
+ case TQVariant::ByteArray: {
+ TQByteArray ba = field->value().asByteArray();
+ TQString 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 += TQString::number( (unsigned char) ba[ i ], 8 ).rightJustify( 3, '0', TRUE );
+ }
+ }
+ r += "'";
+ break;
+ }
+ default:
+ r = TQSqlDriver::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 000000000..217cfe45e
--- /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 TQt 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 TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements 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.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSQL_PSQL_H
+#define TQSQL_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 TQPSQLPrivate;
+class TQPSQLDriver;
+class TQSqlRecordInfo;
+
+class TQPSQLResult : public TQSqlResult
+{
+ friend class TQPSQLDriver;
+public:
+ TQPSQLResult( const TQPSQLDriver* db, const TQPSQLPrivate* p );
+ ~TQPSQLResult();
+ PGresult* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchFirst();
+ bool fetchLast();
+ TQVariant data( int i );
+ bool isNull( int field );
+ bool reset ( const TQString& query );
+ int size();
+ int numRowsAffected();
+private:
+ int currentSize;
+ TQPSQLPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_PSQL TQPSQLDriver : public TQSqlDriver
+{
+public:
+ enum Protocol {
+ Version6 = 6,
+ Version7 = 7,
+ Version71 = 8,
+ Version73 = 9
+ };
+
+ TQPSQLDriver( TQObject * parent=0, const char * name=0 );
+ TQPSQLDriver( PGconn * conn, TQObject * parent=0, const char * name=0 );
+ ~TQPSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const TQString & db,
+ const TQString & user = TQString::null,
+ const TQString & password = TQString::null,
+ const TQString & host = TQString::null,
+ int port = -1 );
+ void close();
+ TQSqlQuery createQuery() const;
+ TQStringList tables( const TQString& user ) const;
+ TQSqlIndex primaryIndex( const TQString& tablename ) const;
+ TQSqlRecord record( const TQString& tablename ) const;
+ TQSqlRecord record( const TQSqlQuery& query ) const;
+ TQSqlRecordInfo recordInfo( const TQString& tablename ) const;
+ TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const;
+
+ Protocol protocol() const { return pro; }
+ PGconn* connection();
+ TQString formatValue( const TQSqlField* field,
+ bool trimStrings ) const;
+
+ // ### remove me for 4.0
+ bool open( const TQString& db,
+ const TQString& user,
+ const TQString& password,
+ const TQString& host,
+ int port,
+ const TQString& connOpts );
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ Protocol pro;
+ TQPSQLPrivate* 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 000000000..ecdda1c0c
--- /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 TQt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsql_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 TQSQLITE_DRIVER_NAME "TQSQLITE"
+
+static TQSqlVariant::Type nameToType(const TQString& typeName)
+{
+ TQString tName = typeName.upper();
+ if (tName.startsWith("INT"))
+ return TQSqlVariant::Int;
+ if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC"))
+ return TQSqlVariant::Double;
+ if (tName.startsWith("BOOL"))
+ return TQSqlVariant::Bool;
+ // SQLite is typeless - consider everything else as string
+ return TQSqlVariant::String;
+}
+
+class TQSQLiteDriverPrivate
+{
+public:
+ TQSQLiteDriverPrivate();
+ sqlite *access;
+ bool utf8;
+};
+
+TQSQLiteDriverPrivate::TQSQLiteDriverPrivate() : access(0)
+{
+ utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
+}
+
+class TQSQLiteResultPrivate
+{
+public:
+ TQSQLiteResultPrivate(TQSQLiteResult *res);
+ void cleanup();
+ bool fetchNext(TQtSqlCachedResult::RowCache *row);
+ bool isSelect();
+ // initializes the recordInfo and the cache
+ void init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row = 0);
+ void finalize();
+
+ TQSQLiteResult* 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
+ TQtSqlCachedResult::RowCache *skipRow;
+
+ uint utf8: 1;
+ TQSqlRecordInfo rInf;
+};
+
+static const uint initial_cache_size = 128;
+
+TQSQLiteResultPrivate::TQSQLiteResultPrivate(TQSQLiteResult* res) : q(res), access(0), currentTail(0),
+ currentMachine(0), skippedStatus(FALSE), skipRow(0), utf8(FALSE)
+{
+}
+
+void TQSQLiteResultPrivate::cleanup()
+{
+ finalize();
+ rInf.clear();
+ currentTail = 0;
+ currentMachine = 0;
+ skippedStatus = FALSE;
+ delete skipRow;
+ skipRow = 0;
+ q->setAt(TQSql::BeforeFirst);
+ q->setActive(FALSE);
+ q->cleanup();
+}
+
+void TQSQLiteResultPrivate::finalize()
+{
+ if (!currentMachine)
+ return;
+
+ char* err = 0;
+ int res = sqlite_finalize(currentMachine, &err);
+ if (err) {
+ q->setLastError(TQSqlError("Unable to fetch results", err, TQSqlError::Statement, res));
+ sqlite_freemem(err);
+ }
+ currentMachine = 0;
+}
+
+// called on first fetch
+void TQSQLiteResultPrivate::init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row)
+{
+ if (!cnames)
+ return;
+
+ rInf.clear();
+ if (numCols <= 0)
+ return;
+
+ for (int i = 0; i < numCols; ++i) {
+ const char* lastDot = strrchr(cnames[i], '.');
+ const char* fieldName = lastDot ? lastDot + 1 : cnames[i];
+ rInf.append(TQSqlFieldInfo(fieldName, nameToType(cnames[i+numCols])));
+ }
+ // skip the first fetch
+ if (row && !*row) {
+ *row = new TQtSqlCachedResult::RowCache(numCols);
+ skipRow = *row;
+ }
+}
+
+bool TQSQLiteResultPrivate::fetchNext(TQtSqlCachedResult::RowCache* row)
+{
+ // may be caching.
+ const char **fvals;
+ const char **cnames;
+ int colNum;
+ int res;
+ int i;
+
+ if (skipRow) {
+ // already fetched
+ if (row)
+ *row = *skipRow;
+ delete skipRow;
+ skipRow = 0;
+ return 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 ? TQString::fromUtf8(fvals[i]) : TQString(fvals[i]);
+ return TRUE;
+ case SQLITE_DONE:
+ if (rInf.isEmpty())
+ // must be first call.
+ init(cnames, colNum);
+ q->setAt(TQSql::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(TQSql::AfterLast);
+ return FALSE;
+ }
+ return FALSE;
+}
+
+TQSQLiteResult::TQSQLiteResult(const TQSQLiteDriver* db)
+: TQtSqlCachedResult(db)
+{
+ d = new TQSQLiteResultPrivate(this);
+ d->access = db->d->access;
+ d->utf8 = db->d->utf8;
+}
+
+TQSQLiteResult::~TQSQLiteResult()
+{
+ d->cleanup();
+ delete d;
+}
+
+/*
+ Execute \a query.
+*/
+bool TQSQLiteResult::reset (const TQString& query)
+{
+ // this is where we build a query.
+ if (!driver())
+ return FALSE;
+ if (!driver()-> isOpen() || driver()->isOpenError())
+ return FALSE;
+
+ d->cleanup();
+
+ // Um, ok. callback based so.... pass private static function for this.
+ setSelect(FALSE);
+ char *err = 0;
+ int res = sqlite_compile(d->access,
+ d->utf8 ? (const char*)query.utf8().data() : query.ascii(),
+ &(d->currentTail),
+ &(d->currentMachine),
+ &err);
+ if (res != SQLITE_OK || err) {
+ setLastError(TQSqlError("Unable to execute statement", err, TQSqlError::Statement, res));
+ sqlite_freemem(err);
+ }
+ //if (*d->currentTail != '\000' then there is more sql to eval
+ if (!d->currentMachine) {
+ setActive(FALSE);
+ return FALSE;
+ }
+ // we have to fetch one row to 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 TQSQLiteResult::gotoNext(TQtSqlCachedResult::RowCache* row)
+{
+ return d->fetchNext(row);
+}
+
+int TQSQLiteResult::size()
+{
+ return -1;
+}
+
+int TQSQLiteResult::numRowsAffected()
+{
+ return sqlite_changes(d->access);
+}
+
+/////////////////////////////////////////////////////////
+
+TQSQLiteDriver::TQSQLiteDriver(TQObject * parent, const char * name)
+ : TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME)
+{
+ d = new TQSQLiteDriverPrivate();
+}
+
+TQSQLiteDriver::TQSQLiteDriver(sqlite *connection, TQObject *parent, const char *name)
+ : TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME)
+{
+ d = new TQSQLiteDriverPrivate();
+ d->access = connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+
+TQSQLiteDriver::~TQSQLiteDriver()
+{
+ delete d;
+}
+
+bool TQSQLiteDriver::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 TQSQLiteDriver::open(const TQString & db, const TQString &, const TQString &, const TQString &, int, const TQString &)
+{
+ if (isOpen())
+ close();
+
+ if (db.isEmpty())
+ return FALSE;
+
+ char* err = 0;
+ d->access = sqlite_open(TQFile::encodeName(db), 0, &err);
+ if (err) {
+ setLastError(TQSqlError("Error to open database", err, TQSqlError::Connection));
+ sqlite_freemem(err);
+ err = 0;
+ }
+
+ if (d->access) {
+ setOpen(TRUE);
+ setOpenError(FALSE);
+ return TRUE;
+ }
+ setOpenError(TRUE);
+ return FALSE;
+}
+
+void TQSQLiteDriver::close()
+{
+ if (isOpen()) {
+ sqlite_close(d->access);
+ d->access = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+TQSqlQuery TQSQLiteDriver::createQuery() const
+{
+ return TQSqlQuery(new TQSQLiteResult(this));
+}
+
+bool TQSQLiteDriver::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(TQSqlError("Unable to begin transaction", err, TQSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool TQSQLiteDriver::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(TQSqlError("Unable to commit transaction", err, TQSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool TQSQLiteDriver::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(TQSqlError("Unable to rollback Transaction", err, TQSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+TQStringList TQSQLiteDriver::tables(const TQString &typeName) const
+{
+ TQStringList res;
+ if (!isOpen())
+ return res;
+ int type = typeName.toInt();
+
+ TQSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+#if (QT_VERSION-0 >= 0x030000)
+ if ((type & (int)TQSql::Tables) && (type & (int)TQSql::Views))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+ else if (typeName.isEmpty() || (type & (int)TQSql::Tables))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table'");
+ else if (type & (int)TQSql::Views)
+ q.exec("SELECT name FROM sqlite_master WHERE type='view'");
+#else
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+#endif
+
+
+ if (q.isActive()) {
+ while(q.next())
+ res.append(q.value(0).toString());
+ }
+
+#if (QT_VERSION-0 >= 0x030000)
+ if (type & (int)TQSql::SystemTables) {
+ // there are no internal tables beside this one:
+ res.append("sqlite_master");
+ }
+#endif
+
+ return res;
+}
+
+TQSqlIndex TQSQLiteDriver::primaryIndex(const TQString &tblname) const
+{
+ TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
+
+ if (!isOpen())
+ return TQSqlIndex();
+
+ TQSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ // finrst find a UNITQUE INDEX
+ q.exec("PRAGMA index_list('" + tblname + "');");
+ TQString indexname;
+ while(q.next()) {
+ if (q.value(2).toInt()==1) {
+ indexname = q.value(1).toString();
+ break;
+ }
+ }
+ if (indexname.isEmpty())
+ return TQSqlIndex();
+
+ q.exec("PRAGMA index_info('" + indexname + "');");
+
+ TQSqlIndex index(tblname, indexname);
+ while(q.next()) {
+ TQString name = q.value(2).toString();
+ TQSqlVariant::Type type = TQSqlVariant::Invalid;
+ if (rec.contains(name))
+ type = rec.find(name).type();
+ index.append(TQSqlField(name, type));
+ }
+ return index;
+}
+
+TQSqlRecordInfo TQSQLiteDriver::recordInfo(const TQString &tbl) const
+{
+ if (!isOpen())
+ return TQSqlRecordInfo();
+
+ TQSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT * FROM " + tbl + " LIMIT 1");
+ return recordInfo(q);
+}
+
+TQSqlRecord TQSQLiteDriver::record(const TQString &tblname) const
+{
+ if (!isOpen())
+ return TQSqlRecord();
+
+ return recordInfo(tblname).toRecord();
+}
+
+TQSqlRecord TQSQLiteDriver::record(const TQSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ TQSQLiteResult* result = (TQSQLiteResult*)query.result();
+ return result->d->rInf.toRecord();
+ }
+ return TQSqlRecord();
+}
+
+TQSqlRecordInfo TQSQLiteDriver::recordInfo(const TQSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ TQSQLiteResult* result = (TQSQLiteResult*)query.result();
+ return result->d->rInf;
+ }
+ return TQSqlRecordInfo();
+}
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.h b/src/sql/drivers/sqlite/qsql_sqlite.h
new file mode 100644
index 000000000..ccde9b865
--- /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 TQt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef TQSQL_SQLITE_H
+#define TQSQL_SQLITE_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlrecord.h>
+#include <qsqlindex.h>
+#include "../cache/qsqlcachedresult.h"
+
+#if (QT_VERSION-0 >= 0x030000)
+typedef TQVariant TQSqlVariant;
+#endif
+
+#if defined (Q_OS_WIN32)
+# include <qt_windows.h>
+#endif
+
+class TQSQLiteDriverPrivate;
+class TQSQLiteResultPrivate;
+class TQSQLiteDriver;
+struct sqlite;
+
+class TQSQLiteResult : public TQtSqlCachedResult
+{
+ friend class TQSQLiteDriver;
+ friend class TQSQLiteResultPrivate;
+public:
+ TQSQLiteResult(const TQSQLiteDriver* db);
+ ~TQSQLiteResult();
+
+protected:
+ bool gotoNext(TQtSqlCachedResult::RowCache* row);
+ bool reset (const TQString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ TQSQLiteResultPrivate* d;
+};
+
+class TQSQLiteDriver : public TQSqlDriver
+{
+ friend class TQSQLiteResult;
+public:
+ TQSQLiteDriver(TQObject *parent = 0, const char *name = 0);
+ TQSQLiteDriver(sqlite *connection, TQObject *parent = 0, const char *name = 0);
+ ~TQSQLiteDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int port,
+ const TQString & connOpts);
+ bool open( const TQString & db,
+ const TQString & user,
+ const TQString & password,
+ const TQString & host,
+ int port ) { return open (db, user, password, host, port, TQString()); }
+ void close();
+ TQSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ TQStringList tables(const TQString& user) const;
+
+ TQSqlRecord record(const TQString& tablename) const;
+ TQSqlRecordInfo recordInfo(const TQString& tablename) const;
+ TQSqlIndex primaryIndex(const TQString &table) const;
+ TQSqlRecord record(const TQSqlQuery& query) const;
+ TQSqlRecordInfo recordInfo(const TQSqlQuery& query) const;
+
+private:
+ TQSQLiteDriverPrivate* d;
+};
+#endif