diff options
Diffstat (limited to 'tqtinterface/qt4/src/sql/drivers')
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.cpp | 259 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.h | 104 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.cpp | 1078 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.h | 117 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.cpp | 775 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.h | 131 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp | 2035 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.h | 165 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.cpp | 1117 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.h | 131 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp | 513 | ||||
-rw-r--r-- | tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.h | 90 |
12 files changed, 6515 insertions, 0 deletions
diff --git a/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.cpp b/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.cpp new file mode 100644 index 0000000..39195e9 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Implementation of cached TQt SQL result classes +** +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 "tqsqlcachedresult.h" +#include <tqdatetime.h> + +#ifndef TQT_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; + + TQ_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 // TQT_NO_SQL diff --git a/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.h b/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.h new file mode 100644 index 0000000..c066770 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/cache/tqsqlcachedresult.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Definition of shared TQt SQL module classes +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 <tqglobal.h> +#include <tqvariant.h> +#include <tqptrvector.h> +#include <tqvaluevector.h> +#include <tqsqlresult.h> + +#if !defined( TQT_MODULE_SQL ) || defined( TQT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL TQ_EXPORT +#endif + +#ifndef TQT_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/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.cpp b/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.cpp new file mode 100644 index 0000000..955ecc4 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.cpp @@ -0,0 +1,1078 @@ +/**************************************************************************** +** +** Implementation of Interbase driver classes. +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "tqsql_ibase.h" + +#include <tqdatetime.h> +#include <private/tqsqlextension_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(XSTQLDA *&sqlda) +{ + sqlda = (XSTQLDA *) malloc(XSTQLDA_LENGTH(1)); + sqlda->sqln = 1; + sqlda->sqld = 0; + sqlda->version = STQLDA_VERSION1; + sqlda->sqlvar[0].sqlind = 0; + sqlda->sqlvar[0].sqldata = 0; +} + +static void enlargeDA(XSTQLDA *&sqlda, int n) +{ + free(sqlda); + sqlda = (XSTQLDA *) malloc(XSTQLDA_LENGTH(n)); + sqlda->sqln = n; + sqlda->version = STQLDA_VERSION1; +} + +static void initDA(XSTQLDA *sqlda) +{ + for (int i = 0; i < sqlda->sqld; ++i) { + switch (sqlda->sqlvar[i].sqltype & ~1) { + case STQL_INT64: + case STQL_LONG: + case STQL_SHORT: + case STQL_FLOAT: + case STQL_DOUBLE: + case STQL_TIMESTAMP: + case STQL_TYPE_TIME: + case STQL_TYPE_DATE: + case STQL_TEXT: + case STQL_BLOB: + sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen); + break; + case STQL_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(XSTQLDA *&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 STQL_VARYING: + case STQL_TEXT: + return TQVariant::String; + case STQL_LONG: + case STQL_SHORT: + return TQVariant::Int; + case STQL_INT64: + return TQVariant::LongLong; + case STQL_FLOAT: + case STQL_DOUBLE: + return TQVariant::Double; + case STQL_TIMESTAMP: + return TQVariant::DateTime; + case STQL_TYPE_DATE: + return TQVariant::Date; + case STQL_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_TQUAD *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; + XSTQLDA *sqlda; // output sqlda + XSTQLDA *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, DSTQL_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_TQUAD *bId = (ISC_TQUAD*)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_TQUAD *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 STQL_INT64: + if (d->inda->sqlvar[para].sqlscale < 0) + *((TQ_LLONG*)d->inda->sqlvar[para].sqldata) = TQ_LLONG(val.toDouble() * + pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); + else + *((TQ_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); + break; + case STQL_LONG: + *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong(); + break; + case STQL_SHORT: + *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt(); + break; + case STQL_FLOAT: + *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble(); + break; + case STQL_DOUBLE: + *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble(); + break; + case STQL_TIMESTAMP: + *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime()); + break; + case STQL_TYPE_TIME: + *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime()); + break; + case STQL_TYPE_DATE: + *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate()); + break; + case STQL_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 STQL_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 STQL_BLOB: + d->writeBlob(para, val.toByteArray()); + break; + default: + break; + } + } + } + + if (colCount()) { + isc_dsql_free_statement(d->status, &d->stmt, DSTQL_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 = (XSTQLDA *) malloc(XSTQLDA_LENGTH(n)); + d->sqlda->sqln = n; + d->sqlda->version = STQLDA_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; + + TQ_ASSERT(row); + TQ_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; + TQ_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 STQL_VARYING: + // pascal strings - a short with a length information followed by the data + (*row)[i] = TQString::fromUtf8(buf + sizeof(short), *(short*)buf); + break; + case STQL_INT64: + if (d->sqlda->sqlvar[i].sqlscale < 0) + (*row)[i] = *(TQ_LLONG*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale); + else + (*row)[i] = TQVariant(*(TQ_LLONG*)buf); + break; + case STQL_LONG: + if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out. + (*row)[i] = TQVariant((int)(*(long*)buf)); + else + (*row)[i] = TQVariant((TQ_LLONG)(*(long*)buf)); + break; + case STQL_SHORT: + (*row)[i] = TQVariant((int)(*(short*)buf)); + break; + case STQL_FLOAT: + (*row)[i] = TQVariant((double)(*(float*)buf)); + break; + case STQL_DOUBLE: + (*row)[i] = TQVariant(*(double*)buf); + break; + case STQL_TIMESTAMP: + (*row)[i] = toTQDateTime((ISC_TIMESTAMP*)buf); + break; + case STQL_TYPE_TIME: + (*row)[i] = toTQTime(*(ISC_TIME*)buf); + break; + case STQL_TYPE_DATE: + (*row)[i] = toTQDate(*(ISC_DATE*)buf); + break; + case STQL_TEXT: + (*row)[i] = TQString::fromUtf8(buf, size); + break; + case STQL_BLOB: + (*row)[i] = d->fetchBlob((ISC_TQUAD*)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 * tqparent, const char * name) + : TQSqlDriver(tqparent, name ? name : TQIBASE_DRIVER_NAME) +{ + d = new TQIBaseDriverPrivate(this); +} + +TQIBaseDriver::TQIBaseDriver(void *connection, TQObject *tqparent, const char *name) + : TQSqlDriver(tqparent, name ? name : TQIBASE_DRIVER_NAME) +{ + d = new TQIBaseDriverPrivate(this); + d->ibase = (isc_db_handle)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; + XSTQLVAR v; + for (int i = 0; i < result->d->sqlda->sqld; ++i) { + v = result->d->sqlda->sqlvar[i]; + TQSqlField f(TQString::tqfromLatin1(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; + XSTQLVAR v; + for (int i = 0; i < result->d->sqlda->sqld; ++i) { + v = result->d->sqlda->sqlvar[i]; + TQSqlFieldInfo f(TQString::tqfromLatin1(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/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.h b/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.h new file mode 100644 index 0000000..be5bfd5 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/ibase/tqsql_ibase.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Definition of Interbase driver classes +** +** Created : 030911 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 "tqsqlresult.h" +#include "tqsqldriver.h" +#include "../cache/tqsqlcachedresult.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 *tqparent = 0, const char *name = 0); + TQIBaseDriver(void *connection, TQObject *tqparent = 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/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.cpp b/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.cpp new file mode 100644 index 0000000..f97cd2e --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** Implementation of MYSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 "tqsql_mysql.h" +#include <private/tqsqlextension_p.h> + +#include <tqdatetime.h> +#include <tqvaluevector.h> +#include <tqsqlrecord.h> + +#define TQMYSTQL_DRIVER_NAME "TQMYSQL3" + +#ifdef TQ_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 TQ_NO_MYSTQL_EMBEDDED +#endif + +TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict(); + +static int qMySqlConnectionCount = 0; +static bool qMySqlInitHandledByUser = FALSE; + +class TQMYSTQLOpenExtension : public TQSqlOpenExtension +{ +public: + TQMYSTQLOpenExtension( TQMYSTQLDriver *dri ) + : TQSqlOpenExtension(), driver(dri) {} + ~TQMYSTQLOpenExtension() {} + + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); + +private: + TQMYSTQLDriver *driver; +}; + +bool TQMYSTQLOpenExtension::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 TQMYSTQLDriverPrivate +{ +public: + TQMYSTQLDriverPrivate() : mysql(0) {} + MYSQL* mysql; +}; + +class TQMYSTQLResultPrivate : public TQMYSTQLDriverPrivate +{ +public: + TQMYSTQLResultPrivate() : TQMYSTQLDriverPrivate(), result(0) {} + MYSTQL_RES* result; + MYSTQL_ROW row; + TQValueVector<TQVariant::Type> fieldTypes; +}; + +TQSqlError qMakeError( const TQString& err, int type, const TQMYSTQLDriverPrivate* p ) +{ + return TQSqlError(TQMYSTQL_DRIVER_NAME ": " + err, TQString(mysql_error( p->mysql )), type, mysql_errno( p->mysql )); +} + +TQVariant::Type qDecodeMYSTQLType( 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; +} + +TQMYSTQLResult::TQMYSTQLResult( const TQMYSTQLDriver* db ) +: TQSqlResult( db ) +{ + d = new TQMYSTQLResultPrivate(); + d->mysql = db->d->mysql; +} + +TQMYSTQLResult::~TQMYSTQLResult() +{ + cleanup(); + delete d; +} + +MYSTQL_RES* TQMYSTQLResult::result() +{ + return d->result; +} + +void TQMYSTQLResult::cleanup() +{ + if ( d->result ) { + mysql_free_result( d->result ); + } + d->result = NULL; + d->row = NULL; + setAt( -1 ); + setActive( FALSE ); +} + +bool TQMYSTQLResult::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 TQMYSTQLResult::fetchNext() +{ + d->row = mysql_fetch_row( d->result ); + if ( !d->row ) + return FALSE; + setAt( at() + 1 ); + return TRUE; +} + +bool TQMYSTQLResult::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 TQMYSTQLResult::fetchFirst() +{ + if ( isForwardOnly() ) // again, fake it + return fetchNext(); + return fetch( 0 ); +} + +TQVariant TQMYSTQLResult::data( int field ) +{ + if ( !isSelect() || field >= (int) d->fieldTypes.count() ) { + qWarning( "TQMYSTQLResult::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 TQT_CHECK_RANGE + qWarning("TQMYSTQLResult::data: unknown data type"); +#endif + return TQVariant(); +} + +bool TQMYSTQLResult::isNull( int field ) +{ + if ( d->row[field] == NULL ) + return TRUE; + return FALSE; +} + +bool TQMYSTQLResult::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, tqstrlen(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++) { + MYSTQL_FIELD* field = mysql_fetch_field_direct( d->result, i ); + if ( field->type == FIELD_TYPE_DECIMAL ) + d->fieldTypes[i] = TQVariant::String; + else + d->fieldTypes[i] = qDecodeMYSTQLType( field->type, field->flags ); + } + } + setActive( TRUE ); + return TRUE; +} + +int TQMYSTQLResult::size() +{ + return isSelect() ? (int)mysql_num_rows( d->result ) : -1; +} + +int TQMYSTQLResult::numRowsAffected() +{ + return (int)mysql_affected_rows( d->mysql ); +} + +///////////////////////////////////////////////////////// +static void qServerEnd() +{ +#ifndef TQ_NO_MYSTQL_EMBEDDED +# if MYSTQL_VERSION_ID >= 40000 + mysql_server_end(); +# endif // MYSTQL_VERSION_ID +#endif // TQ_NO_MYSTQL_EMBEDDED +} + +static void qServerInit() +{ +#ifndef TQ_NO_MYSTQL_EMBEDDED +# if MYSTQL_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 TQT_CHECK_RANGE + qWarning( "TQMYSTQLDriver::qServerInit: unable to start server." ); +# endif + } + +# endif // MYSTQL_VERSION_ID +#endif // TQ_NO_MYSTQL_EMBEDDED +} + +TQMYSTQLDriver::TQMYSTQLDriver( TQObject * tqparent, const char * name ) + : TQSqlDriver( tqparent, name ? name : TQMYSTQL_DRIVER_NAME ) +{ + init(); + qServerInit(); +} + +/*! + Create a driver instance with an already open connection handle. +*/ + +TQMYSTQLDriver::TQMYSTQLDriver( MYSQL * con, TQObject * tqparent, const char * name ) + : TQSqlDriver( tqparent, name ? name : TQMYSTQL_DRIVER_NAME ) +{ + init(); + if ( con ) { + d->mysql = (MYSQL *) con; + setOpen( TRUE ); + setOpenError( FALSE ); + if (qMySqlConnectionCount == 1) + qMySqlInitHandledByUser = TRUE; + } else { + qServerInit(); + } +} + +void TQMYSTQLDriver::init() +{ + qSqlOpenExtDict()->insert( this, new TQMYSTQLOpenExtension(this) ); + d = new TQMYSTQLDriverPrivate(); + d->mysql = 0; + qMySqlConnectionCount++; +} + +TQMYSTQLDriver::~TQMYSTQLDriver() +{ + qMySqlConnectionCount--; + if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser) + qServerEnd(); + + delete d; + if ( !qSqlOpenExtDict()->isEmpty() ) { + TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this ); + delete ext; + } +} + +bool TQMYSTQLDriver::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 TQMYSTQLDriver::open( const TQString&, + const TQString&, + const TQString&, + const TQString&, + int ) +{ + qWarning("TQMYSTQLDriver::open(): This version of open() is no longer supported." ); + return FALSE; +} + +bool TQMYSTQLDriver::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.tqfind( '=' )) != -1 ) { + TQString val( tmp.mid( idx + 1 ) ); + val.simplifyWhiteSpace(); + if ( val == "TRUE" || val == "1" ) + opts << tmp.left( idx ); + else + qWarning( "TQMYSTQLDriver::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( "TQMYSTQLDriver::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 TQMYSTQLDriver::close() +{ + if ( isOpen() ) { + mysql_close( d->mysql ); + setOpen( FALSE ); + setOpenError( FALSE ); + } +} + +TQSqlQuery TQMYSTQLDriver::createQuery() const +{ + return TQSqlQuery( new TQMYSTQLResult( this ) ); +} + +TQStringList TQMYSTQLDriver::tables( const TQString& typeName ) const +{ + TQStringList tl; + if ( !isOpen() ) + return tl; + if ( !typeName.isEmpty() && !(typeName.toInt() & (int)TQSql::Tables) ) + return tl; + + MYSTQL_RES* tableRes = mysql_list_tables( d->mysql, NULL ); + MYSTQL_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 TQMYSTQLDriver::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 TQMYSTQLDriver::record( const TQString& tablename ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + MYSTQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0); + if ( !r ) { + return fil; + } + MYSTQL_FIELD* field; + while ( (field = mysql_fetch_field( r ))) { + TQSqlField f ( TQString( field->name ) , qDecodeMYSTQLType( (int)field->type, field->flags ) ); + fil.append ( f ); + } + mysql_free_result( r ); + return fil; +} + +TQSqlRecord TQMYSTQLDriver::record( const TQSqlQuery& query ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + if ( query.isActive() && query.isSelect() && query.driver() == this ) { + TQMYSTQLResult* result = (TQMYSTQLResult*)query.result(); + TQMYSTQLResultPrivate* p = result->d; + if ( !mysql_errno( p->mysql ) ) { + for ( ;; ) { + MYSTQL_FIELD* f = mysql_fetch_field( p->result ); + if ( f ) { + TQSqlField fi( TQString((const char*)f->name), qDecodeMYSTQLType( f->type, f->flags ) ); + fil.append( fi ); + } else + break; + } + } + mysql_field_seek( p->result, 0 ); + } + return fil; +} + +TQSqlRecordInfo TQMYSTQLDriver::recordInfo( const TQString& tablename ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + MYSTQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0); + if ( !r ) { + return info; + } + MYSTQL_FIELD* field; + while ( (field = mysql_fetch_field( r ))) { + info.append ( TQSqlFieldInfo( TQString( field->name ), + qDecodeMYSTQLType( (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 TQMYSTQLDriver::recordInfo( const TQSqlQuery& query ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + if ( query.isActive() && query.isSelect() && query.driver() == this ) { + TQMYSTQLResult* result = (TQMYSTQLResult*)query.result(); + TQMYSTQLResultPrivate* p = result->d; + if ( !mysql_errno( p->mysql ) ) { + for ( ;; ) { + MYSTQL_FIELD* field = mysql_fetch_field( p->result ); + if ( field ) { + info.append ( TQSqlFieldInfo( TQString( field->name ), + qDecodeMYSTQLType( (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* TQMYSTQLDriver::mysql() +{ + return d->mysql; +} + +bool TQMYSTQLDriver::beginTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQMYSTQLDriver::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 TQMYSTQLDriver::commitTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQMYSTQLDriver::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 TQMYSTQLDriver::rollbackTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQMYSTQLDriver::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 TQMYSTQLDriver::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.tqreplace( "\\", "\\\\" ); + break; + } + default: + r = TQSqlDriver::formatValue( field, trimStrings ); + } + } + return r; +} diff --git a/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.h b/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.h new file mode 100644 index 0000000..ef4f4ce --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/mysql/tqsql_mysql.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Definition of MySQL driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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_MYSTQL_H +#define TQSQL_MYSTQL_H + +#include <tqsqldriver.h> +#include <tqsqlresult.h> +#include <tqsqlfield.h> +#include <tqsqlindex.h> + +#if defined (TQ_OS_WIN32) +#include <tqt_windows.h> +#endif + +#include <mysql.h> + +#ifdef TQT_PLUGIN +#define TQ_EXPORT_STQLDRIVER_MYSQL +#else +#define TQ_EXPORT_STQLDRIVER_MYSQL TQ_EXPORT +#endif + +class TQMYSTQLDriverPrivate; +class TQMYSTQLResultPrivate; +class TQMYSTQLDriver; +class TQSqlRecordInfo; + +class TQMYSTQLResult : public TQSqlResult +{ + friend class TQMYSTQLDriver; +public: + TQMYSTQLResult( const TQMYSTQLDriver* db ); + ~TQMYSTQLResult(); + + MYSTQL_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: + TQMYSTQLResultPrivate* d; +}; + +class TQ_EXPORT_STQLDRIVER_MYSQL TQMYSTQLDriver : public TQSqlDriver +{ + friend class TQMYSTQLResult; +public: + TQMYSTQLDriver( TQObject * tqparent=0, const char * name=0 ); + TQMYSTQLDriver( MYSQL * con, TQObject * tqparent=0, const char * name=0 ); + ~TQMYSTQLDriver(); + 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(); + TQMYSTQLDriverPrivate* d; +}; + + +#endif diff --git a/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp b/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp new file mode 100644 index 0000000..fda38a8 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.cpp @@ -0,0 +1,2035 @@ +/**************************************************************************** +** +** Implementation of ODBC driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 "tqsql_odbc.h" +#include <tqsqlrecord.h> + +#if defined (TQ_OS_WIN32) +#include <tqt_windows.h> +#include <tqapplication.h> +#endif +#include <tqdatetime.h> +#include <private/tqsqlextension_p.h> +#include <private/tqinternal_p.h> +#include <stdlib.h> + +// undefine this to prevent initial check of the ODBC driver +#define ODBC_CHECK_DRIVER + +#if defined(TQ_ODBC_VERSION_2) +//crude hack to get non-tqunicode capable driver managers to work +# undef UNICODE +# define STQLTCHAR STQLCHAR +# define STQL_C_WCHAR STQL_C_CHAR +#endif + +// newer platform SDKs use STQLLEN instead of STQLINTEGER +#ifdef STQLLEN +# define TQSTQLLEN STQLLEN +#else +# define TQSTQLLEN STQLINTEGER +#endif + +#ifdef STQLULEN +# define TQSQLULEN STQLULEN +#else +# define TQSQLULEN STQLUINTEGER +#endif + + +static const TQSTQLLEN COLNAMESIZE = 256; +//Map TQt parameter types to ODBC types +static const STQLSMALLINT qParamType[ 4 ] = { STQL_PARAM_INPUT, STQL_PARAM_INPUT, STQL_PARAM_OUTPUT, STQL_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; + tqunicode = FALSE; + } + + STQLHANDLE hEnv; + STQLHANDLE hDbc; + STQLHANDLE hStmt; + + bool tqunicode; + 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, STQLHANDLE handle) +{ + STQLINTEGER nativeCode_; + STQLSMALLINT msgLen; + STQLRETURN r = STQL_ERROR; + STQLTCHAR state_[STQL_STQLSTATE_SIZE+1]; + STQLTCHAR description_[STQL_MAX_MESSAGE_LENGTH]; + r = STQLGetDiagRec( handleType, + handle, + 1, + (STQLTCHAR*)state_, + &nativeCode_, + (STQLTCHAR*)description_, + STQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */ + &msgLen); + if ( r == STQL_SUCCESS || r == STQL_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( STQL_HANDLE_ENV, odbc->hEnv ) + " " + + qWarnODBCHandle( STQL_HANDLE_DBC, odbc->hDbc ) + " " + + qWarnODBCHandle( STQL_HANDLE_STMT, odbc->hStmt ) ); +} + +static void qSqlWarning( const TQString& message, const TQODBCPrivate* odbc ) +{ +#ifdef TQT_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( STQLSMALLINT sqltype, const TQODBCPrivate* p ) +{ + TQVariant::Type type = TQVariant::Invalid; + switch ( sqltype ) { + case STQL_DECIMAL: + case STQL_NUMERIC: + case STQL_REAL: + case STQL_FLOAT: + case STQL_DOUBLE: + type = TQVariant::Double; + break; + case STQL_SMALLINT: + case STQL_INTEGER: + case STQL_BIT: + case STQL_TINYINT: + type = TQVariant::Int; + break; + case STQL_BIGINT: + type = TQVariant::LongLong; + break; + case STQL_BINARY: + case STQL_VARBINARY: + case STQL_LONGVARBINARY: + type = TQVariant::ByteArray; + break; + case STQL_DATE: + case STQL_TYPE_DATE: + type = TQVariant::Date; + break; + case STQL_TIME: + case STQL_TYPE_TIME: + type = TQVariant::Time; + break; + case STQL_TIMESTAMP: + case STQL_TYPE_TIMESTAMP: + type = TQVariant::DateTime; + break; +#ifndef TQ_ODBC_VERSION_2 + case STQL_WCHAR: + case STQL_WVARCHAR: + case STQL_WLONGVARCHAR: + type = TQVariant::String; + break; +#endif + case STQL_CHAR: + type = p->sql_char_type; + break; + case STQL_VARCHAR: + type = p->sql_varchar_type; + break; + case STQL_LONGVARCHAR: + type = p->sql_longvarchar_type; + break; + default: + type = TQVariant::CString; + break; + } + return type; +} + +static TQString qGetStringData( STQLHANDLE hStmt, int column, int colSize, bool& isNull, bool tqunicode = FALSE ) +{ + TQString fieldVal; + STQLRETURN r = STQL_ERROR; + TQSTQLLEN 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 ( tqunicode ) { + colSize *= 2; // a tiny bit faster, since it saves a STQLGetData() call + } + } + char* buf = new char[ colSize ]; + while ( TRUE ) { + r = STQLGetData( hStmt, + column+1, + tqunicode ? STQL_C_WCHAR : STQL_C_CHAR, + (STQLPOINTER)buf, + (TQSTQLLEN)colSize, + &lengthIndicator ); + if ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) { + if ( lengthIndicator == STQL_NULL_DATA || lengthIndicator == STQL_NO_TOTAL ) { + fieldVal = TQString::null; + isNull = TRUE; + break; + } + // if STQL_SUCCESS_WITH_INFO is returned, indicating that + // more data can be fetched, the length indicator does NOT + // contain the number of bytes returned - it tqcontains the + // total number of bytes that CAN be fetched + // colSize-1: remove 0 termination when there is more data to fetch + int rSize = (r == STQL_SUCCESS_WITH_INFO) ? (tqunicode ? colSize-2 : colSize-1) : lengthIndicator; + if ( tqunicode ) { + fieldVal += TQString( (TQChar*) buf, rSize / 2 ); + } else { + buf[ rSize ] = 0; + fieldVal += buf; + } + if ( lengthIndicator < colSize ) { + // workaround for Drivermanagers that don't return STQL_NO_DATA + break; + } + } else if ( r == STQL_NO_DATA ) { + break; + } else { +#ifdef TQT_CHECK_RANGE + qWarning( "qGetStringData: Error while fetching data (%d)", r ); +#endif + fieldVal = TQString::null; + break; + } + } + delete[] buf; + return fieldVal; +} + +static TQByteArray qGetBinaryData( STQLHANDLE hStmt, int column, TQSTQLLEN& lengthIndicator, bool& isNull ) +{ + TQByteArray fieldVal; + STQLSMALLINT colNameLen; + STQLSMALLINT colType; + TQSQLULEN colSize; + STQLSMALLINT colScale; + STQLSMALLINT nullable; + STQLRETURN r = STQL_ERROR; + + STQLTCHAR colName[COLNAMESIZE]; + r = STQLDescribeCol( hStmt, + column+1, + colName, + COLNAMESIZE, + &colNameLen, + &colType, + &colSize, + &colScale, + &nullable ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) + qWarning( "qGetBinaryData: Unable to describe column %d", column ); +#endif + // STQLDescribeCol 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 = STQLGetData( hStmt, + column+1, + STQL_C_BINARY, + (STQLPOINTER) buf, + (TQSTQLLEN)colSize, + &lengthIndicator ); + if ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) { + if ( lengthIndicator == STQL_NULL_DATA ) { + isNull = TRUE; + break; + } else { + int rSize; + r == STQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize; + if ( lengthIndicator == STQL_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 == STQL_SUCCESS ) { // the whole field was read in one chunk + break; + } + } + } else { + break; + } + } + delete [] buf; + return fieldVal; +} + +static int qGetIntData( STQLHANDLE hStmt, int column, bool& isNull ) +{ + TQSTQLLEN intbuf = 0; + isNull = FALSE; + TQSTQLLEN lengthIndicator = 0; + STQLRETURN r = STQLGetData( hStmt, + column+1, + STQL_C_SLONG, + (STQLPOINTER)&intbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA ) { + isNull = TRUE; + return 0; + } + return (int)intbuf; +} + +static double qGetDoubleData( STQLHANDLE hStmt, int column, bool& isNull ) +{ + STQLDOUBLE dblbuf; + TQSTQLLEN lengthIndicator = 0; + isNull = FALSE; + STQLRETURN r = STQLGetData( hStmt, + column+1, + STQL_C_DOUBLE, + (STQLPOINTER)&dblbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA ) { + isNull = TRUE; + return 0.0; + } + + return (double) dblbuf; +} + +static STQLBIGINT qGetBigIntData( STQLHANDLE hStmt, int column, bool& isNull ) +{ + STQLBIGINT lngbuf = TQ_INT64_C( 0 ); + isNull = FALSE; + TQSTQLLEN lengthIndicator = 0; + STQLRETURN r = STQLGetData( hStmt, + column+1, + STQL_C_SBIGINT, + (STQLPOINTER) &lngbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) || lengthIndicator == STQL_NULL_DATA ) + isNull = TRUE; + + return lngbuf; +} + +// creates a TQSqlFieldInfo from a valid hStmt generated +// by STQLColumns. The hStmt has to point to a valid position. +static TQSqlFieldInfo qMakeFieldInfo( const STQLHANDLE hStmt, const TQODBCPrivate* p ) +{ + bool isNull; + TQString fname = qGetStringData( hStmt, 3, -1, isNull, p->tqunicode ); + int type = qGetIntData( hStmt, 4, isNull ); // column type + int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag + // required can be STQL_NO_NULLS, STQL_NULLABLE or STQL_NULLABLE_UNKNOWN + if ( required == STQL_NO_NULLS ) { + required = 1; + } else if ( required == STQL_NULLABLE ) { + required = 0; + } else { + required = -1; + } + int size = qGetIntData( hStmt, 6, isNull ); // column size + int prec = qGetIntData( hStmt, 8, isNull ); // precision + return TQSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, TQVariant(), type ); +} + +static TQSqlFieldInfo qMakeFieldInfo( const TQODBCPrivate* p, int i ) +{ + STQLSMALLINT colNameLen; + STQLSMALLINT colType; + TQSQLULEN colSize; + STQLSMALLINT colScale; + STQLSMALLINT nullable; + STQLRETURN r = STQL_ERROR; + STQLTCHAR colName[ COLNAMESIZE ]; + r = STQLDescribeCol( p->hStmt, + i+1, + colName, + (TQSQLULEN)COLNAMESIZE, + &colNameLen, + &colType, + &colSize, + &colScale, + &nullable); + + if ( r != STQL_SUCCESS ) { +#ifdef TQT_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 STQL_NO_NULLS, STQL_NULLABLE or STQL_NULLABLE_UNKNOWN + int required = -1; + if ( nullable == STQL_NO_NULLS ) { + required = 1; + } else if ( nullable == STQL_NULLABLE ) { + required = 0; + } + TQVariant::Type type = qDecodeODBCType( colType, p ); + return TQSqlFieldInfo( qColName, + type, + required, + (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; + STQLRETURN r = STQL_SUCCESS; + TQMap<TQString, TQString> connMap; + for ( TQStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) { + TQString tmp( *it ); + int idx; + if ( (idx = tmp.tqfind( '=' )) != -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; + STQLUINTEGER v = 0; + for ( it = connMap.begin(); it != connMap.end(); ++it ) { + opt = it.key().upper(); + val = it.data().upper(); + r = STQL_SUCCESS; + if ( opt == "STQL_ATTR_ACCESS_MODE" ) { + if ( val == "STQL_MODE_READ_ONLY" ) { + v = STQL_MODE_READ_ONLY; + } else if ( val == "STQL_MODE_READ_WRITE" ) { + v = STQL_MODE_READ_WRITE; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = STQLSetConnectAttr( hDbc, STQL_ATTR_ACCESS_MODE, (STQLPOINTER) v, 0 ); + } else if ( opt == "STQL_ATTR_CONNECTION_TIMEOUT" ) { + v = val.toUInt(); + r = STQLSetConnectAttr( hDbc, STQL_ATTR_CONNECTION_TIMEOUT, (STQLPOINTER) v, 0 ); + } else if ( opt == "STQL_ATTR_LOGIN_TIMEOUT" ) { + v = val.toUInt(); + r = STQLSetConnectAttr( hDbc, STQL_ATTR_LOGIN_TIMEOUT, (STQLPOINTER) v, 0 ); + } else if ( opt == "STQL_ATTR_CURRENT_CATALOG" ) { + val.ucs2(); // 0 terminate + r = STQLSetConnectAttr( hDbc, STQL_ATTR_CURRENT_CATALOG, +#ifdef UNICODE + (STQLWCHAR*) val.tqunicode(), +#else + (STQLCHAR*) val.latin1(), +#endif + STQL_NTS ); + } else if ( opt == "STQL_ATTR_METADATA_ID" ) { + if ( val == "STQL_TRUE" ) { + v = STQL_TRUE; + } else if ( val == "STQL_FALSE" ) { + v = STQL_FALSE; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = STQLSetConnectAttr( hDbc, STQL_ATTR_METADATA_ID, (STQLPOINTER) v, 0 ); + } else if ( opt == "STQL_ATTR_PACKET_SIZE" ) { + v = val.toUInt(); + r = STQLSetConnectAttr( hDbc, STQL_ATTR_PACKET_SIZE, (STQLPOINTER) v, 0 ); + } else if ( opt == "STQL_ATTR_TRACEFILE" ) { + val.ucs2(); // 0 terminate + r = STQLSetConnectAttr( hDbc, STQL_ATTR_TRACEFILE, +#ifdef UNICODE + (STQLWCHAR*) val.tqunicode(), +#else + (STQLCHAR*) val.latin1(), +#endif + STQL_NTS ); + } else if ( opt == "STQL_ATTR_TRACE" ) { + if ( val == "STQL_OPT_TRACE_OFF" ) { + v = STQL_OPT_TRACE_OFF; + } else if ( val == "STQL_OPT_TRACE_ON" ) { + v = STQL_OPT_TRACE_ON; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = STQLSetConnectAttr( hDbc, STQL_ATTR_TRACE, (STQLPOINTER) v, 0 ); + } +#ifdef TQT_CHECK_RANGE + else { + qWarning( TQString("TQODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) ); + } +#endif + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_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() ) { + STQLRETURN r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_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 ); + STQLRETURN r; + + d->rInf.clear(); + // Always reallocate the statement handle - the statement attributes + // are not reset if STQLFreeStmt() is called which causes some problems. + if ( d->hStmt ) { + r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to free statement handle", d ); +#endif + return FALSE; + } + } + r = STQLAllocHandle( STQL_HANDLE_STMT, + d->hDbc, + &d->hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to allocate statement handle", d ); +#endif + return FALSE; + } + + if ( isForwardOnly() ) { + r = STQLSetStmtAttr( d->hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY, + STQL_IS_UINTEGER ); + } else { + r = STQLSetStmtAttr( d->hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_STATIC, + STQL_IS_UINTEGER ); + } + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to set 'STQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d ); +#endif + return FALSE; + } + +#ifdef UNICODE + r = STQLExecDirect( d->hStmt, + (STQLWCHAR*) query.tqunicode(), + (STQLINTEGER) query.length() ); +#else + TQCString query8 = query.local8Bit(); + r = STQLExecDirect( d->hStmt, + (STQLCHAR*) query8.data(), + (STQLINTEGER) query8.length() ); +#endif + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { + setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) ); + return FALSE; + } + STQLSMALLINT count; + r = STQLNumResultCols( 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; + } + STQLRETURN r; + if ( isForwardOnly() ) { + bool ok = TRUE; + while ( ok && i > at() ) + ok = fetchNext(); + return ok; + } else { + r = STQLFetchScroll( d->hStmt, + STQL_FETCH_ABSOLUTE, + actualIdx ); + } + if ( r != STQL_SUCCESS ){ + return FALSE; + } + setAt( i ); + return TRUE; +} + +bool TQODBCResult::fetchNext() +{ + STQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + r = STQLFetchScroll( d->hStmt, + STQL_FETCH_NEXT, + 0 ); + if ( r != STQL_SUCCESS ) + return FALSE; + setAt( at() + 1 ); + return TRUE; +} + +bool TQODBCResult::fetchFirst() +{ + if ( isForwardOnly() && at() != TQSql::BeforeFirst ) + return FALSE; + STQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + if ( isForwardOnly() ) { + return fetchNext(); + } + r = STQLFetchScroll( d->hStmt, + STQL_FETCH_FIRST, + 0 ); + if ( r != STQL_SUCCESS ) + return FALSE; + setAt( 0 ); + return TRUE; +} + +bool TQODBCResult::fetchPrior() +{ + if ( isForwardOnly() ) + return FALSE; + STQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + r = STQLFetchScroll( d->hStmt, + STQL_FETCH_PRIOR, + 0 ); + if ( r != STQL_SUCCESS ) + return FALSE; + setAt( at() - 1 ); + return TRUE; +} + +bool TQODBCResult::fetchLast() +{ + STQLRETURN 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 = STQLFetchScroll( d->hStmt, + STQL_FETCH_LAST, + 0 ); + if ( r != STQL_SUCCESS ) { + return FALSE; + } + STQLINTEGER currRow; + r = STQLGetStmtAttr( d->hStmt, + STQL_ROW_NUMBER, + &currRow, + STQL_IS_INTEGER, + 0 ); + if ( r != STQL_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.tqcontains( field ) ) + return fieldCache[ field ]; + STQLRETURN r(0); + TQSTQLLEN 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( (TQ_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 = STQLGetData( d->hStmt, + current+1, + STQL_C_DATE, + (STQLPOINTER)&dbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_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 = STQLGetData( d->hStmt, + current+1, + STQL_C_TIME, + (STQLPOINTER)&tbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_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 = STQLGetData( d->hStmt, + current+1, + STQL_C_TIMESTAMP, + (STQLPOINTER)&dtbuf, + (TQSTQLLEN)0, + &lengthIndicator ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != STQL_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() == STQL_DECIMAL || info.typeID() == STQL_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.tqcontains( field ) ) { + // since there is no good way to tqfind 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() +{ + TQSTQLLEN affectedRowCount(0); + STQLRETURN r = STQLRowCount( d->hStmt, &affectedRowCount ); + if ( r == STQL_SUCCESS ) + return affectedRowCount; +#ifdef TQT_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 ); + STQLRETURN r; + + d->rInf.clear(); + if ( d->hStmt ) { + r = STQLFreeHandle( STQL_HANDLE_STMT, d->hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to close statement", d ); +#endif + return FALSE; + } + } + r = STQLAllocHandle( STQL_HANDLE_STMT, + d->hDbc, + &d->hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to allocate statement handle", d ); +#endif + return FALSE; + } + + if ( isForwardOnly() ) { + r = STQLSetStmtAttr( d->hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY, + STQL_IS_UINTEGER ); + } else { + r = STQLSetStmtAttr( d->hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_STATIC, + STQL_IS_UINTEGER ); + } + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to set 'STQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d ); +#endif + return FALSE; + } + +#ifdef UNICODE + r = STQLPrepare( d->hStmt, + (STQLWCHAR*) query.tqunicode(), + (STQLINTEGER) query.length() ); +#else + TQCString query8 = query.local8Bit(); + r = STQLPrepare( d->hStmt, + (STQLCHAR*) query8.data(), + (STQLINTEGER) query8.length() ); +#endif + + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to prepare statement", d ); +#endif + return FALSE; + } + return TRUE; +} + +bool TQODBCResult::exec() +{ + STQLRETURN 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 TQT_CHECK_RANGE + qSqlWarning( "TQODBCResult::exec: No statement handle available", d ); +#endif + return FALSE; + } else { + r = STQLFreeStmt( d->hStmt, STQL_CLOSE ); + if ( r != STQL_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; + TQSTQLLEN *ind = new TQSTQLLEN( STQL_NTS ); + tmpStorage.append( qAutoDeleter(ind) ); + if ( val.isNull() ) { + *ind = STQL_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 = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_DATE, + STQL_DATE, + 0, + 0, + (void *) dt, + (TQSTQLLEN)0, + *ind == STQL_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 = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_TIME, + STQL_TIME, + 0, + 0, + (void *) dt, + (TQSTQLLEN)0, + *ind == STQL_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 = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_TIMESTAMP, + STQL_TIMESTAMP, + 0, + 0, + (void *) dt, + (TQSTQLLEN)0, + *ind == STQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::Int: { + int * v = new int( val.toInt() ); + tmpStorage.append( qAutoDeleter(v) ); + r = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_SLONG, + STQL_INTEGER, + 0, + 0, + (void *) v, + (TQSTQLLEN)0, + *ind == STQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::Double: { + double * v = new double( val.toDouble() ); + tmpStorage.append( qAutoDeleter(v) ); + r = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_DOUBLE, + STQL_DOUBLE, + 0, + 0, + (void *) v, + (TQSTQLLEN)0, + *ind == STQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::ByteArray: { + if ( *ind != STQL_NULL_DATA ) { + *ind = val.asByteArray().size(); + } + r = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_BINARY, + STQL_LONGVARBINARY, + val.asByteArray().size(), + 0, + (void *) val.asByteArray().data(), + (TQSTQLLEN)val.asByteArray().size(), + ind ); + break; } +#ifndef TQ_ODBC_VERSION_2 + case TQVariant::String: + if ( d->tqunicode ) { + TQString * str = new TQString( val.asString() ); + str->ucs2(); + int len = str->length()*2; + tmpStorage.append( qAutoDeleter(str) ); + r = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_WCHAR, + len > 8000 ? STQL_WLONGVARCHAR : STQL_WVARCHAR, + len > 8000 ? len : 0, + 0, + (void *) str->tqunicode(), + (TQSTQLLEN) len, + ind ); + break; + } +#endif + // fall through + default: { + TQCString * str = new TQCString( val.asString().local8Bit() ); + tmpStorage.append( qAutoDeleter(str) ); + r = STQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + STQL_C_CHAR, + str->length() > 4000 ? STQL_LONGVARCHAR : STQL_VARCHAR, + str->length() + 1, + 0, + (void *) str->data(), + (TQSTQLLEN)(str->length() + 1), + ind ); + break; } + } + para++; + if ( r != STQL_SUCCESS ) { +#ifdef TQT_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 = STQLExecute( d->hStmt ); + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_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; + } + STQLSMALLINT count; + r = STQLNumResultCols( 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 ) { + + STQLINTEGER* indPtr = qAutoDeleterData( (TQAutoDeleter<STQLINTEGER>*)tmpStorage.getFirst() ); + if ( !indPtr ) + return FALSE; + bool isNull = (*indPtr == STQL_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->tqunicode ) { + 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 * tqparent, const char * name ) + : TQSqlDriver(tqparent,name ? name : "TQODBC") +{ + init(); +} + +TQODBCDriver::TQODBCDriver( STQLHANDLE env, STQLHANDLE con, TQObject * tqparent, const char * name ) + : TQSqlDriver(tqparent,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; + STQLUSMALLINT txn; + STQLSMALLINT t; + int r = STQLGetInfo( d->hDbc, + (STQLUSMALLINT)STQL_TXN_CAPABLE, + &txn, + sizeof(txn), + &t); + if ( r != STQL_SUCCESS || txn == STQL_TC_NONE ) + return FALSE; + else + return TRUE; + } + case QuerySize: + return FALSE; + case BLOB: + return TRUE; + case Unicode: + return d->tqunicode; + 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(); + STQLRETURN r; + r = STQLAllocHandle( STQL_HANDLE_ENV, + STQL_NULL_HANDLE, + &d->hEnv); + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::open: Unable to allocate environment", d ); +#endif + setOpenError( TRUE ); + return FALSE; + } + r = STQLSetEnvAttr( d->hEnv, + STQL_ATTR_ODBC_VERSION, + (STQLPOINTER)STQL_OV_ODBC2, + STQL_IS_UINTEGER ); + r = STQLAllocHandle( STQL_HANDLE_DBC, + d->hEnv, + &d->hDbc); + if ( r != STQL_SUCCESS && r != STQL_SUCCESS_WITH_INFO ) { +#ifdef TQT_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.tqcontains(".dsn") ) + connTQStr = "FILEDSN=" + db; + else if ( db.tqcontains( "DRIVER" ) || db.tqcontains( "SERVER" ) ) + connTQStr = db; + else + connTQStr = "DSN=" + db; + connTQStr += ";UID=" + user + ";PWD=" + password; + STQLSMALLINT cb; + STQLTCHAR connOut[1024]; + r = STQLDriverConnect( d->hDbc, + NULL, +#ifdef UNICODE + (STQLWCHAR*)connTQStr.tqunicode(), +#else + (STQLCHAR*)connTQStr.latin1(), +#endif + (STQLSMALLINT)connTQStr.length(), + connOut, + 1024, + &cb, + STQL_DRIVER_NOPROMPT ); + if ( r != STQL_SUCCESS && r != STQL_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() +{ + STQLRETURN r; + if ( !d ) + return; + + if( d->hDbc ) { + // Open statements/descriptors handles are automatically cleaned up by STQLDisconnect + if ( isOpen() ) { + r = STQLDisconnect( d->hDbc ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::disconnect: Unable to disconnect datasource", d ); +#endif + } + + r = STQLFreeHandle( STQL_HANDLE_DBC, d->hDbc ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::cleanup: Unable to free connection handle", d ); +#endif + d->hDbc = 0; + } + + if ( d->hEnv ) { + r = STQLFreeHandle( STQL_HANDLE_ENV, d->hEnv ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_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 tqunicode characters +void TQODBCPrivate::checkUnicode() +{ +#if defined(TQ_WS_WIN) + if ( !qt_wintqunicode ) { + tqunicode = FALSE; + return; + } +#endif + STQLRETURN r; + STQLUINTEGER fFunc; + + tqunicode = FALSE; + r = STQLGetInfo( hDbc, + STQL_CONVERT_CHAR, + (STQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WCHAR ) ) { + sql_char_type = TQVariant::String; + tqunicode = TRUE; + } + + r = STQLGetInfo( hDbc, + STQL_CONVERT_VARCHAR, + (STQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WVARCHAR ) ) { + sql_varchar_type = TQVariant::String; + tqunicode = TRUE; + } + + r = STQLGetInfo( hDbc, + STQL_CONVERT_LONGVARCHAR, + (STQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO ) && ( fFunc & STQL_CVT_WLONGVARCHAR ) ) { + sql_longvarchar_type = TQVariant::String; + tqunicode = TRUE; + } +} + +bool TQODBCPrivate::checkDriver() const +{ +#ifdef ODBC_CHECK_DRIVER + // do not query for STQL_API_STQLFETCHSCROLL because it can't be used at this time + static const STQLUSMALLINT reqFunc[] = { + STQL_API_STQLDESCRIBECOL, STQL_API_STQLGETDATA, STQL_API_STQLCOLUMNS, + STQL_API_STQLGETSTMTATTR, STQL_API_STQLGETDIAGREC, STQL_API_STQLEXECDIRECT, + STQL_API_STQLGETINFO, STQL_API_STQLTABLES, 0 + }; + + // these functions are optional + static const STQLUSMALLINT optFunc[] = { + STQL_API_STQLNUMRESULTCOLS, STQL_API_STQLROWCOUNT, 0 + }; + + STQLRETURN r; + STQLUSMALLINT sup; + + + int i; + // check the required functions + for ( i = 0; reqFunc[ i ] != 0; ++i ) { + + r = STQLGetFunctions( hDbc, reqFunc[ i ], &sup ); + +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) { + qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this ); + return FALSE; + } +#endif + if ( sup == STQL_FALSE ) { +#ifdef TQT_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 = STQLGetFunctions( hDbc, optFunc[ i ], &sup ); + +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) { + qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this ); + return FALSE; + } +#endif + if ( sup == STQL_FALSE ) { +#ifdef TQT_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() +{ + STQLRETURN r; + STQLUINTEGER val; + + r = STQLGetInfo(hDbc, + STQL_SCHEMA_USAGE, + (STQLPOINTER) &val, + sizeof(val), + NULL); + if (r == STQL_SUCCESS || r == STQL_SUCCESS_WITH_INFO) + useSchema = (val != 0); +} + +TQSqlQuery TQODBCDriver::createQuery() const +{ + return TQSqlQuery( new TQODBCResult( this, d ) ); +} + +bool TQODBCDriver::beginTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning(" TQODBCDriver::beginTransaction: Database not open" ); +#endif + return FALSE; + } + STQLUINTEGER ac(STQL_AUTOCOMMIT_OFF); + STQLRETURN r = STQLSetConnectAttr( d->hDbc, + STQL_ATTR_AUTOCOMMIT, + (STQLPOINTER)ac, + sizeof(ac) ); + if ( r != STQL_SUCCESS ) { + setLastError( qMakeError( "Unable to disable autocommit", TQSqlError::Transaction, d ) ); + return FALSE; + } + return TRUE; +} + +bool TQODBCDriver::commitTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning(" TQODBCDriver::commitTransaction: Database not open" ); +#endif + return FALSE; + } + STQLRETURN r = STQLEndTran( STQL_HANDLE_DBC, + d->hDbc, + STQL_COMMIT ); + if ( r != STQL_SUCCESS ) { + setLastError( qMakeError("Unable to commit transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + return endTrans(); +} + +bool TQODBCDriver::rollbackTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning(" TQODBCDriver::rollbackTransaction: Database not open" ); +#endif + return FALSE; + } + STQLRETURN r = STQLEndTran( STQL_HANDLE_DBC, + d->hDbc, + STQL_ROLLBACK ); + if ( r != STQL_SUCCESS ) { + setLastError( qMakeError( "Unable to rollback transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + return endTrans(); +} + +bool TQODBCDriver::endTrans() +{ + STQLUINTEGER ac(STQL_AUTOCOMMIT_ON); + STQLRETURN r = STQLSetConnectAttr( d->hDbc, + STQL_ATTR_AUTOCOMMIT, + (STQLPOINTER)ac, + sizeof(ac)); + if ( r != STQL_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(); + STQLHANDLE hStmt; + + STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::tables: Unable to allocate handle", d ); +#endif + return tl; + } + r = STQLSetStmtAttr( hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY, + STQL_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 = STQLTables( hStmt, + NULL, + 0, + NULL, + 0, + NULL, + 0, +#ifdef UNICODE + (STQLWCHAR*)tableType.tqunicode(), +#else + (STQLCHAR*)tableType.latin1(), +#endif + tableType.length() /* characters, not bytes */ ); + +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::tables Unable to execute table list", d ); +#endif + r = STQLFetchScroll( hStmt, + STQL_FETCH_NEXT, + 0); + while ( r == STQL_SUCCESS ) { + bool isNull; + TQString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->tqunicode ); + tl.append( fieldVal ); + r = STQLFetchScroll( hStmt, + STQL_FETCH_NEXT, + 0); + } + + r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt ); + if ( r!= STQL_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 ); + + STQLHANDLE hStmt; + STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_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 = STQLSetStmtAttr( hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY, + STQL_IS_UINTEGER ); + r = STQLPrimaryKeys( hStmt, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(), +#else + catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(), +#else + schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (STQLWCHAR*)table.tqunicode(), +#else + (STQLCHAR*)table.latin1(), +#endif + table.length() /* in characters, not in bytes */); + + // if the STQLPrimaryKeys() 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 != STQL_SUCCESS ) { + r = STQLSpecialColumns( hStmt, + STQL_BEST_ROWID, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(), +#else + catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(), +#else + schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (STQLWCHAR*)table.tqunicode(), +#else + (STQLCHAR*)table.latin1(), +#endif + + table.length(), + STQL_SCOPE_CURROW, + STQL_NULLABLE ); + + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::primaryIndex: Unable to execute primary key list", d ); +#endif + } else { + usingSpecialColumns = TRUE; + } + } + r = STQLFetchScroll( hStmt, + STQL_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 == STQL_SUCCESS ) { + if ( usingSpecialColumns ) { + cName = qGetStringData( hStmt, 1, -1, isNull, d->tqunicode ); // column name + idxName = TQString::number( fakeId++ ); // invent a fake index name + } else { + cName = qGetStringData( hStmt, 3, -1, isNull, d->tqunicode ); // column name + idxName = qGetStringData( hStmt, 5, -1, isNull, d->tqunicode ); // pk index name + } + TQSqlField *fld = rec.field(cName); + if (fld) + index.append(*fld); + index.setName( idxName ); + r = STQLFetchScroll( hStmt, + STQL_FETCH_NEXT, + 0 ); + } + r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt ); + if ( r!= STQL_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; + + STQLHANDLE hStmt; + TQString catalog, schema, table; + d->splitTableQualifier( tablename, catalog, schema, table ); + STQLRETURN r = STQLAllocHandle( STQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != STQL_SUCCESS ) { +#ifdef TQT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::record: Unable to allocate handle", d ); +#endif + return fil; + } + r = STQLSetStmtAttr( hStmt, + STQL_ATTR_CURSOR_TYPE, + (STQLPOINTER)STQL_CURSOR_FORWARD_ONLY, + STQL_IS_UINTEGER ); + r = STQLColumns( hStmt, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (STQLWCHAR*)catalog.tqunicode(), +#else + catalog.length() == 0 ? NULL : (STQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (STQLWCHAR*)schema.tqunicode(), +#else + schema.length() == 0 ? NULL : (STQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (STQLWCHAR*)table.tqunicode(), +#else + (STQLCHAR*)table.latin1(), +#endif + table.length(), + NULL, + 0 ); +#ifdef TQT_CHECK_RANGE + if ( r != STQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::record: Unable to execute column list", d ); +#endif + r = STQLFetchScroll( hStmt, + STQL_FETCH_NEXT, + 0); + // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop + while ( r == STQL_SUCCESS ) { + + fil.append( qMakeFieldInfo( hStmt, d ) ); + + r = STQLFetchScroll( hStmt, + STQL_FETCH_NEXT, + 0); + } + + r = STQLFreeHandle( STQL_HANDLE_STMT, hStmt ); + if ( r!= STQL_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; +} + +STQLHANDLE TQODBCDriver::environment() +{ + return d->hEnv; +} + +STQLHANDLE 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/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.h b/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.h new file mode 100644 index 0000000..74ab4e7 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/odbc/tqsql_odbc.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Definition of ODBC driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 <tqmap.h> +#include <tqstring.h> +#include <tqsqldriver.h> +#include <tqsqlfield.h> +#include <tqsqlresult.h> +#include <tqsqlindex.h> + +#if defined (TQ_OS_WIN32) +#include <tqt_windows.h> +#endif + +#if defined (TQ_OS_MAC) +// assume we use iodbc on MAC +// comment next line out if you use a +// tqunicode compatible manager +# define TQ_ODBC_VERSION_2 +#endif + +#ifdef TQT_PLUGIN +#define TQ_EXPORT_STQLDRIVER_ODBC +#else +#define TQ_EXPORT_STQLDRIVER_ODBC TQ_EXPORT +#endif + +#ifdef TQ_OS_UNIX +#define HAVE_LONG_LONG 1 // force UnixODBC NOT to fall back to a struct for BIGINTs +#endif + +#if defined(TQ_CC_BOR) +// workaround for Borland to make sure that STQLBIGINT is defined +# define _MSC_VER 900 +#endif +#include <sql.h> +#if defined(TQ_CC_BOR) +# undef _MSC_VER +#endif + +#ifndef TQ_ODBC_VERSION_2 +#include <sqlucode.h> +#endif + +#include <sqlext.h> + +class TQODBCPrivate; +class TQODBCDriver; +class TQSqlRecordInfo; + +class TQODBCResult : public TQSqlResult +{ + friend class TQODBCDriver; +public: + TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p ); + ~TQODBCResult(); + + STQLHANDLE 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 TQ_EXPORT_STQLDRIVER_ODBC TQODBCDriver : public TQSqlDriver +{ +public: + TQODBCDriver( TQObject * tqparent=0, const char * name=0 ); + TQODBCDriver( STQLHANDLE env, STQLHANDLE con, TQObject * tqparent=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; + STQLHANDLE environment(); + STQLHANDLE 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/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.cpp b/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.cpp new file mode 100644 index 0000000..db233ce --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.cpp @@ -0,0 +1,1117 @@ +/**************************************************************************** +** +** Implementation of PostgreSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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 "tqsql_psql.h" +#include <private/tqsqlextension_p.h> + +#include <math.h> + +#include <tqpointarray.h> +#include <tqsqlrecord.h> +#include <tqregexp.h> +#include <tqdatetime.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 TQPSTQLPrivate +{ +public: + TQPSTQLPrivate():connection(0), result(0), isUtf8(FALSE) {} + PGconn *connection; + PGresult *result; + bool isUtf8; +}; + +class TQPSTQLDriverExtension : public TQSqlDriverExtension +{ +public: + TQPSTQLDriverExtension( TQPSTQLDriver *dri ) + : TQSqlDriverExtension(), driver(dri) { } + ~TQPSTQLDriverExtension() {} + + bool isOpen() const; +private: + TQPSTQLDriver *driver; +}; + +bool TQPSTQLDriverExtension::isOpen() const +{ + return PQstatus( driver->connection() ) == CONNECTION_OK; +} + +class TQPSTQLOpenExtension : public TQSqlOpenExtension +{ +public: + TQPSTQLOpenExtension( TQPSTQLDriver *dri ) + : TQSqlOpenExtension(), driver(dri) { } + ~TQPSTQLOpenExtension() {} + + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); +private: + TQPSTQLDriver *driver; +}; + +bool TQPSTQLOpenExtension::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 TQPSTQLPrivate* 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 qDecodePSTQLType( 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; +} + +TQPSTQLResult::TQPSTQLResult( const TQPSTQLDriver* db, const TQPSTQLPrivate* p ) +: TQSqlResult( db ), + currentSize( 0 ) +{ + d = new TQPSTQLPrivate(); + (*d) = (*p); +} + +TQPSTQLResult::~TQPSTQLResult() +{ + cleanup(); + delete d; +} + +PGresult* TQPSTQLResult::result() +{ + return d->result; +} + +void TQPSTQLResult::cleanup() +{ + if ( d->result ) + PQclear( d->result ); + d->result = 0; + setAt( -1 ); + currentSize = 0; + setActive( FALSE ); +} + +bool TQPSTQLResult::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 TQPSTQLResult::fetchFirst() +{ + return fetch( 0 ); +} + +bool TQPSTQLResult::fetchLast() +{ + return fetch( PQntuples( d->result ) - 1 ); +} + +// some Postgres conversions +static TQPoint pointFromString( const TQString& s) +{ + // format '(x,y)' + int pivot = s.tqfind( ',' ); + 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 TQPSTQLResult::data( int i ) +{ + if ( i >= PQnfields( d->result ) ) { + qWarning( "TQPSTQLResult::data: column %d out of range", i ); + return TQVariant(); + } + int ptype = PQftype( d->result, i ); + TQVariant::Type type = qDecodePSTQLType( 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.tqfind( "),(" ); + 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.tqcontains( pointPattern ); + TQPointArray parray( points ); + int idx = 1; + for ( int i = 0; i < points; i++ ){ + int start = val.tqfind( pointPattern, idx ); + int end = -1; + if ( start != -1 ) { + end = val.tqfind( ')', 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++ ).tqunicode(); + } + } + 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 TQT_CHECK_RANGE + if ( fd < 0) { + qWarning( "TQPSTQLResult::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 TQ_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( "TQPSTQLResult::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 TQT_CHECK_RANGE + qWarning("TQPSTQLResult::data: unknown data type"); +#endif + ; + } + return TQVariant(); +} + +bool TQPSTQLResult::isNull( int field ) +{ + PQgetvalue( d->result, at(), field ); + return PQgetisnull( d->result, at(), field ); +} + +bool TQPSTQLResult::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 = PQresultqStatus( 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 TQPSTQLResult::size() +{ + return currentSize; +} + +int TQPSTQLResult::numRowsAffected() +{ + return TQString( PQcmdTuples( d->result ) ).toInt(); +} + +/////////////////////////////////////////////////////////////////// + +static bool setEncodingUtf8( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "SET CLIENT_ENCODING TO 'UNICODE'" ); + int status = PQresultqStatus( result ); + PQclear( result ); + return status == PGRES_COMMAND_OK; +} + +static void setDatestyle( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "SET DATESTYLE TO 'ISO'" ); +#ifdef TQT_CHECK_RANGE + int status = PQresultqStatus( result ); + if ( status != PGRES_COMMAND_OK ) + qWarning( "%s", PQerrorMessage( connection ) ); +#endif + PQclear( result ); +} + +static TQPSTQLDriver::Protocol getPSTQLVersion( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "select version()" ); + int status = PQresultqStatus( 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 TQT_CHECK_RANGE + qWarning( "This version of PostgreSQL is not supported and may not work." ); +#endif + return TQPSTQLDriver::Version6; + } + if ( vMaj == 6 ) { + return TQPSTQLDriver::Version6; + } else if ( vMaj == 7 ) { + if ( vMin < 1 ) + return TQPSTQLDriver::Version7; + else if ( vMin < 3 ) + return TQPSTQLDriver::Version71; + } + return TQPSTQLDriver::Version73; + } + } else { +#ifdef TQT_CHECK_RANGE + qWarning( "This version of PostgreSQL is not supported and may not work." ); +#endif + } + + return TQPSTQLDriver::Version6; +} + +TQPSTQLDriver::TQPSTQLDriver( TQObject * tqparent, const char * name ) + : TQSqlDriver(tqparent,name ? name : "TQPSQL"), pro( TQPSTQLDriver::Version6 ) +{ + init(); +} + +TQPSTQLDriver::TQPSTQLDriver( PGconn * conn, TQObject * tqparent, const char * name ) + : TQSqlDriver(tqparent,name ? name : "TQPSQL"), pro( TQPSTQLDriver::Version6 ) +{ + init(); + d->connection = conn; + if ( conn ) { + pro = getPSTQLVersion( d->connection ); + setOpen( TRUE ); + setOpenError( FALSE ); + } +} + +void TQPSTQLDriver::init() +{ + qSqlDriverExtDict()->insert( this, new TQPSTQLDriverExtension(this) ); + qSqlOpenExtDict()->insert( this, new TQPSTQLOpenExtension(this) ); + + d = new TQPSTQLPrivate(); +} + +TQPSTQLDriver::~TQPSTQLDriver() +{ + 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* TQPSTQLDriver::connection() +{ + return d->connection; +} + + +bool TQPSTQLDriver::hasFeature( DriverFeature f ) const +{ + switch ( f ) { + case Transactions: + return TRUE; + case QuerySize: + return TRUE; + case BLOB: + return pro >= TQPSTQLDriver::Version71; + case Unicode: + return d->isUtf8; + default: + return FALSE; + } +} + +bool TQPSTQLDriver::open( const TQString&, + const TQString&, + const TQString&, + const TQString&, + int ) +{ + qWarning("TQPSTQLDriver::open(): This version of open() is no longer supported." ); + return FALSE; +} + +bool TQPSTQLDriver::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 = getPSTQLVersion( d->connection ); + d->isUtf8 = setEncodingUtf8( d->connection ); + setDatestyle( d->connection ); + + setOpen( TRUE ); + setOpenError( FALSE ); + return TRUE; +} + +void TQPSTQLDriver::close() +{ + if ( isOpen() ) { + if (d->connection) + PQfinish( d->connection ); + d->connection = 0; + setOpen( FALSE ); + setOpenError( FALSE ); + } +} + +TQSqlQuery TQPSTQLDriver::createQuery() const +{ + return TQSqlQuery( new TQPSTQLResult( this, d ) ); +} + +bool TQPSTQLDriver::beginTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQPSTQLDriver::beginTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "BEGIN" ); + if ( !res || PQresultqStatus( res ) != PGRES_COMMAND_OK ) { + PQclear( res ); + setLastError( qMakeError( "Could not begin transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +bool TQPSTQLDriver::commitTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQPSTQLDriver::commitTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "COMMIT" ); + if ( !res || PQresultqStatus( res ) != PGRES_COMMAND_OK ) { + PQclear( res ); + setLastError( qMakeError( "Could not commit transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +bool TQPSTQLDriver::rollbackTransaction() +{ + if ( !isOpen() ) { +#ifdef TQT_CHECK_RANGE + qWarning( "TQPSTQLDriver::rollbackTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "ROLLBACK" ); + if ( !res || PQresultqStatus( res ) != PGRES_COMMAND_OK ) { + setLastError( qMakeError( "Could not rollback transaction", TQSqlError::Transaction, d ) ); + PQclear( res ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +TQStringList TQPSTQLDriver::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 >= TQPSTQLDriver::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 >= TQPSTQLDriver::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 >= TQPSTQLDriver::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 TQPSTQLDriver::primaryIndex( const TQString& tablename ) const +{ + TQSqlIndex idx( tablename ); + if ( !isOpen() ) + return idx; + TQSqlQuery i = createQuery(); + TQString stmt; + + switch( pro ) { + case TQPSTQLDriver::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 TQPSTQLDriver::Version7: + case TQPSTQLDriver::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 TQPSTQLDriver::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(), qDecodePSTQLType( i.value(1).toInt() ) ); + idx.append( f ); + idx.setName( i.value(2).toString() ); + } + return idx; +} + +TQSqlRecord TQPSTQLDriver::record( const TQString& tablename ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + TQString stmt; + switch( pro ) { + case TQPSTQLDriver::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 TQPSTQLDriver::Version7: + case TQPSTQLDriver::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 TQPSTQLDriver::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(), qDecodePSTQLType( fi.value(1).toInt() ) ); + fil.append( f ); + } + return fil; +} + +TQSqlRecord TQPSTQLDriver::record( const TQSqlQuery& query ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + if ( query.isActive() && query.driver() == this ) { + TQPSTQLResult* result = (TQPSTQLResult*)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 = qDecodePSTQLType( PQftype( result->d->result, i ) ); + TQSqlField rf( name, type ); + fil.append( rf ); + } + } + return fil; +} + +TQSqlRecordInfo TQPSTQLDriver::recordInfo( const TQString& tablename ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + + TQString stmt; + switch( pro ) { + case TQPSTQLDriver::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 TQPSTQLDriver::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 TQPSTQLDriver::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 TQPSTQLDriver::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 >= TQPSTQLDriver::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(), + qDecodePSTQLType( 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(), + qDecodePSTQLType( query.value( 1 ).toInt() ), + query.value( 2 ).toBool(), + len, + precision, + defVal, + query.value( 1 ).toInt() ) ); + } + } + + return info; +} + +TQSqlRecordInfo TQPSTQLDriver::recordInfo( const TQSqlQuery& query ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + if ( query.isActive() && query.driver() == this ) { + TQPSTQLResult* result = (TQPSTQLResult*)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, + qDecodePSTQLType( PQftype( result->d->result, i ) ), + -1, + len, + precision, + TQVariant(), + PQftype( result->d->result, i ) ) ); + } + } + return info; +} + +TQString TQPSTQLDriver::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.tqreplace( "\\", "\\\\" ); + 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/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.h b/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.h new file mode 100644 index 0000000..c005107 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/psql/tqsql_psql.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Definition of PostgreSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** 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 requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.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_PSTQL_H +#define TQSQL_PSTQL_H + +#include <tqsqlresult.h> +#include <tqsqlfield.h> +#include <tqsqldriver.h> +#include <libpq-fe.h> + +#ifdef TQT_PLUGIN +#define TQ_EXPORT_STQLDRIVER_PSQL +#else +#define TQ_EXPORT_STQLDRIVER_PSQL TQ_EXPORT +#endif + +class TQPSTQLPrivate; +class TQPSTQLDriver; +class TQSqlRecordInfo; + +class TQPSTQLResult : public TQSqlResult +{ + friend class TQPSTQLDriver; +public: + TQPSTQLResult( const TQPSTQLDriver* db, const TQPSTQLPrivate* p ); + ~TQPSTQLResult(); + 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; + TQPSTQLPrivate* d; +}; + +class TQ_EXPORT_STQLDRIVER_PSQL TQPSTQLDriver : public TQSqlDriver +{ +public: + enum Protocol { + Version6 = 6, + Version7 = 7, + Version71 = 8, + Version73 = 9 + }; + + TQPSTQLDriver( TQObject * tqparent=0, const char * name=0 ); + TQPSTQLDriver( PGconn * conn, TQObject * tqparent=0, const char * name=0 ); + ~TQPSTQLDriver(); + 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; + TQPSTQLPrivate* d; +}; + +#endif diff --git a/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp b/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp new file mode 100644 index 0000000..c94f595 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Implementation of STQLite driver classes. +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "tqsql_sqlite.h" + +#include <tqdatetime.h> +#include <tqregexp.h> +#include <tqfile.h> + +#if (TQT_VERSION-0 < 0x030000) +# include <tqvector.h> +# if !defined TQ_WS_WIN32 +# include <unistd.h> +# endif +# include "../../../3rdparty/libraries/sqlite/sqlite.h" +#else +# include <tqptrvector.h> +# if !defined TQ_WS_WIN32 +# include <unistd.h> +# endif +# include <sqlite.h> +#endif + +typedef struct sqlite_vm sqlite_vm; + +#define TQSQLITE_DRIVER_NAME "TQSQLITE" + +static TQSqlVariant::Type nameToType(const TQString& typeName) +{ + TQString tName = typeName.upper(); + if (tName.startsWith("INT")) + return TQSqlVariant::Int; + if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC")) + return TQSqlVariant::Double; + if (tName.startsWith("BOOL")) + return TQSqlVariant::Bool; + // STQLite is typeless - consider everything else as string + return TQSqlVariant::String; +} + +class TQSTQLiteDriverPrivate +{ +public: + TQSTQLiteDriverPrivate(); + sqlite *access; + bool utf8; +}; + +TQSTQLiteDriverPrivate::TQSTQLiteDriverPrivate() : access(0) +{ + utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0); +} + +class TQSTQLiteResultPrivate +{ +public: + TQSTQLiteResultPrivate(TQSTQLiteResult *res); + void cleanup(); + bool fetchNext(TQtSqlCachedResult::RowCache *row); + bool isSelect(); + // initializes the recordInfo and the cache + void init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row = 0); + void finalize(); + + TQSTQLiteResult* q; + sqlite *access; + + // and we have too keep our own struct for the data (sqlite works via + // callback. + const char *currentTail; + sqlite_vm *currentMachine; + + uint skippedtqStatus: 1; // the status of the fetchNext() that's skipped + TQtSqlCachedResult::RowCache *skipRow; + + uint utf8: 1; + TQSqlRecordInfo rInf; +}; + +static const uint initial_cache_size = 128; + +TQSTQLiteResultPrivate::TQSTQLiteResultPrivate(TQSTQLiteResult* res) : q(res), access(0), currentTail(0), + currentMachine(0), skippedtqStatus(FALSE), skipRow(0), utf8(FALSE) +{ +} + +void TQSTQLiteResultPrivate::cleanup() +{ + finalize(); + rInf.clear(); + currentTail = 0; + currentMachine = 0; + skippedtqStatus = FALSE; + delete skipRow; + skipRow = 0; + q->setAt(TQSql::BeforeFirst); + q->setActive(FALSE); + q->cleanup(); +} + +void TQSTQLiteResultPrivate::finalize() +{ + if (!currentMachine) + return; + + char* err = 0; + int res = sqlite_finalize(currentMachine, &err); + if (err) { + q->setLastError(TQSqlError("Unable to fetch results", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + currentMachine = 0; +} + +// called on first fetch +void TQSTQLiteResultPrivate::init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row) +{ + if (!cnames) + return; + + rInf.clear(); + if (numCols <= 0) + return; + + for (int i = 0; i < numCols; ++i) { + const char* lastDot = strrchr(cnames[i], '.'); + const char* fieldName = lastDot ? lastDot + 1 : cnames[i]; + rInf.append(TQSqlFieldInfo(fieldName, nameToType(cnames[i+numCols]))); + } + // skip the first fetch + if (row && !*row) { + *row = new TQtSqlCachedResult::RowCache(numCols); + skipRow = *row; + } +} + +bool TQSTQLiteResultPrivate::fetchNext(TQtSqlCachedResult::RowCache* row) +{ + // may be caching. + const char **fvals; + const char **cnames; + int colNum; + int res; + int i; + + if (skipRow) { + // already fetched + if (row) + *row = *skipRow; + delete skipRow; + skipRow = 0; + return skippedtqStatus; + } + + if (!currentMachine) + return FALSE; + + // keep trying while busy, wish I could implement this better. + while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == STQLITE_BUSY) { + // sleep instead requesting result again immidiately. +#if defined TQ_WS_WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + + switch(res) { + case STQLITE_ROW: + // check to see if should fill out columns + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum, &row); + if (!fvals) + return FALSE; + if (!row) + return TRUE; + for (i = 0; i < colNum; ++i) + (*row)[i] = utf8 ? TQString::fromUtf8(fvals[i]) : TQString(fvals[i]); + return TRUE; + case STQLITE_DONE: + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum); + q->setAt(TQSql::AfterLast); + return FALSE; + case STQLITE_ERROR: + case STQLITE_MISUSE: + default: + // something wrong, don't get col info, but still return false + finalize(); // finalize to get the error message. + q->setAt(TQSql::AfterLast); + return FALSE; + } + return FALSE; +} + +TQSTQLiteResult::TQSTQLiteResult(const TQSTQLiteDriver* db) +: TQtSqlCachedResult(db) +{ + d = new TQSTQLiteResultPrivate(this); + d->access = db->d->access; + d->utf8 = db->d->utf8; +} + +TQSTQLiteResult::~TQSTQLiteResult() +{ + d->cleanup(); + delete d; +} + +/* + Execute \a query. +*/ +bool TQSTQLiteResult::reset (const TQString& query) +{ + // this is where we build a query. + if (!driver()) + return FALSE; + if (!driver()-> isOpen() || driver()->isOpenError()) + return FALSE; + + d->cleanup(); + + // Um, ok. callback based so.... pass private static function for this. + setSelect(FALSE); + char *err = 0; + int res = sqlite_compile(d->access, + d->utf8 ? (const char*)query.utf8().data() : query.ascii(), + &(d->currentTail), + &(d->currentMachine), + &err); + if (res != STQLITE_OK || err) { + setLastError(TQSqlError("Unable to execute statement", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + //if (*d->currentTail != '\000' then there is more sql to eval + if (!d->currentMachine) { + setActive(FALSE); + return FALSE; + } + // we have to fetch one row to tqfind out about + // the structure of the result set + d->skippedtqStatus = d->fetchNext(0); + setSelect(!d->rInf.isEmpty()); + if (isSelect()) + init(d->rInf.count()); + setActive(TRUE); + return TRUE; +} + +bool TQSTQLiteResult::gotoNext(TQtSqlCachedResult::RowCache* row) +{ + return d->fetchNext(row); +} + +int TQSTQLiteResult::size() +{ + return -1; +} + +int TQSTQLiteResult::numRowsAffected() +{ + return sqlite_changes(d->access); +} + +///////////////////////////////////////////////////////// + +TQSTQLiteDriver::TQSTQLiteDriver(TQObject * tqparent, const char * name) + : TQSqlDriver(tqparent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSTQLiteDriverPrivate(); +} + +TQSTQLiteDriver::TQSTQLiteDriver(sqlite *connection, TQObject *tqparent, const char *name) + : TQSqlDriver(tqparent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSTQLiteDriverPrivate(); + d->access = connection; + setOpen(TRUE); + setOpenError(FALSE); +} + + +TQSTQLiteDriver::~TQSTQLiteDriver() +{ + delete d; +} + +bool TQSTQLiteDriver::hasFeature(DriverFeature f) const +{ + switch (f) { + case Transactions: + return TRUE; +#if (TQT_VERSION-0 >= 0x030000) + case Unicode: + return d->utf8; +#endif +// case BLOB: + default: + return FALSE; + } +} + +/* + STQLite dbs have no user name, passwords, hosts or ports. + just file names. +*/ +bool TQSTQLiteDriver::open(const TQString & db, const TQString &, const TQString &, const TQString &, int, const TQString &) +{ + if (isOpen()) + close(); + + if (db.isEmpty()) + return FALSE; + + char* err = 0; + d->access = sqlite_open(TQFile::encodeName(db), 0, &err); + if (err) { + setLastError(TQSqlError("Error to open database", err, TQSqlError::Connection)); + sqlite_freemem(err); + err = 0; + } + + if (d->access) { + setOpen(TRUE); + setOpenError(FALSE); + return TRUE; + } + setOpenError(TRUE); + return FALSE; +} + +void TQSTQLiteDriver::close() +{ + if (isOpen()) { + sqlite_close(d->access); + d->access = 0; + setOpen(FALSE); + setOpenError(FALSE); + } +} + +TQSqlQuery TQSTQLiteDriver::createQuery() const +{ + return TQSqlQuery(new TQSTQLiteResult(this)); +} + +bool TQSTQLiteDriver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "BEGIN", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to begin transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSTQLiteDriver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "COMMIT", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to commit transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSTQLiteDriver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err); + + if (res == STQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to rollback Transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +TQStringList TQSTQLiteDriver::tables(const TQString &typeName) const +{ + TQStringList res; + if (!isOpen()) + return res; + int type = typeName.toInt(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); +#if (TQT_VERSION-0 >= 0x030000) + if ((type & (int)TQSql::Tables) && (type & (int)TQSql::Views)) + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); + else if (typeName.isEmpty() || (type & (int)TQSql::Tables)) + q.exec("SELECT name FROM sqlite_master WHERE type='table'"); + else if (type & (int)TQSql::Views) + q.exec("SELECT name FROM sqlite_master WHERE type='view'"); +#else + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); +#endif + + + if (q.isActive()) { + while(q.next()) + res.append(q.value(0).toString()); + } + +#if (TQT_VERSION-0 >= 0x030000) + if (type & (int)TQSql::SystemTables) { + // there are no internal tables beside this one: + res.append("sqlite_master"); + } +#endif + + return res; +} + +TQSqlIndex TQSTQLiteDriver::primaryIndex(const TQString &tblname) const +{ + TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :( + + if (!isOpen()) + return TQSqlIndex(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + // finrst tqfind a UNITQUE INDEX + q.exec("PRAGMA index_list('" + tblname + "');"); + TQString indexname; + while(q.next()) { + if (q.value(2).toInt()==1) { + indexname = q.value(1).toString(); + break; + } + } + if (indexname.isEmpty()) + return TQSqlIndex(); + + q.exec("PRAGMA index_info('" + indexname + "');"); + + TQSqlIndex index(tblname, indexname); + while(q.next()) { + TQString name = q.value(2).toString(); + TQSqlVariant::Type type = TQSqlVariant::Invalid; + if (rec.tqcontains(name)) + type = rec.tqfind(name).type(); + index.append(TQSqlField(name, type)); + } + return index; +} + +TQSqlRecordInfo TQSTQLiteDriver::recordInfo(const TQString &tbl) const +{ + if (!isOpen()) + return TQSqlRecordInfo(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + q.exec("SELECT * FROM " + tbl + " LIMIT 1"); + return recordInfo(q); +} + +TQSqlRecord TQSTQLiteDriver::record(const TQString &tblname) const +{ + if (!isOpen()) + return TQSqlRecord(); + + return recordInfo(tblname).toRecord(); +} + +TQSqlRecord TQSTQLiteDriver::record(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSTQLiteResult* result = (TQSTQLiteResult*)query.result(); + return result->d->rInf.toRecord(); + } + return TQSqlRecord(); +} + +TQSqlRecordInfo TQSTQLiteDriver::recordInfo(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSTQLiteResult* result = (TQSTQLiteResult*)query.result(); + return result->d->rInf; + } + return TQSqlRecordInfo(); +} diff --git a/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.h b/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.h new file mode 100644 index 0000000..5350ba1 --- /dev/null +++ b/tqtinterface/qt4/src/sql/drivers/sqlite/tqsql_sqlite.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Definition of STQLite driver classes. +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef TQSQL_STQLITE_H +#define TQSQL_STQLITE_H + +#include <tqsqldriver.h> +#include <tqsqlresult.h> +#include <tqsqlrecord.h> +#include <tqsqlindex.h> +#include "../cache/tqsqlcachedresult.h" + +#if (TQT_VERSION-0 >= 0x030000) +typedef TQVariant TQSqlVariant; +#endif + +#if defined (TQ_OS_WIN32) +# include <tqt_windows.h> +#endif + +class TQSTQLiteDriverPrivate; +class TQSTQLiteResultPrivate; +class TQSTQLiteDriver; +struct sqlite; + +class TQSTQLiteResult : public TQtSqlCachedResult +{ + friend class TQSTQLiteDriver; + friend class TQSTQLiteResultPrivate; +public: + TQSTQLiteResult(const TQSTQLiteDriver* db); + ~TQSTQLiteResult(); + +protected: + bool gotoNext(TQtSqlCachedResult::RowCache* row); + bool reset (const TQString& query); + int size(); + int numRowsAffected(); + +private: + TQSTQLiteResultPrivate* d; +}; + +class TQSTQLiteDriver : public TQSqlDriver +{ + friend class TQSTQLiteResult; +public: + TQSTQLiteDriver(TQObject *tqparent = 0, const char *name = 0); + TQSTQLiteDriver(sqlite *connection, TQObject *tqparent = 0, const char *name = 0); + ~TQSTQLiteDriver(); + 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: + TQSTQLiteDriverPrivate* d; +}; +#endif |