diff options
Diffstat (limited to 'src/sql')
58 files changed, 22917 insertions, 0 deletions
diff --git a/src/sql/README.module b/src/sql/README.module new file mode 100644 index 000000000..308098bf9 --- /dev/null +++ b/src/sql/README.module @@ -0,0 +1,37 @@ +Before building the Qt library, the Qt SQL module can be enabled for +specific databases using 'configure'. 'configure' is located at the +top of your QTDIR. + +Specific databases drivers can be enabled using one of the following +options: + + ./configure [-qt-sql-<driver>] [-plugin-sql-<driver>] + +or disabled using the following option: + + ./configure [-no-sql-<driver>] + +Where <driver> is the name of the driver, for example 'psql'. This +will configure the Qt library to compile the specified driver into +the Qt lib itself. + +For example, to build the PostgreSQL driver directly into the Qt +library, configure Qt like this: + + ./configure -qt-sql-psql + +In addition, you may need to specify an extra include path, as some +database drivers retquire headers for the database they are using, +for example: + + ./configure -qt-sql-psql -I/usr/local/include + +If instead you need to build the PostgreSQL driver as a dynamically +loaded plugin, configure Qt like this: + + ./configure -plugin-sql-psql + +To compile drivers as dynamically loaded plugins, see the +QTDIR/plugins/src/sqldrivers directory. Use 'configure -help' +for a complete list of configure options. See the Qt documentation +for a complete list of supported database drivers. diff --git a/src/sql/drivers/cache/qsqlcachedresult.cpp b/src/sql/drivers/cache/qsqlcachedresult.cpp new file mode 100644 index 000000000..5e46b9b49 --- /dev/null +++ b/src/sql/drivers/cache/qsqlcachedresult.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Implementation of cached TQt SQL result classes +** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlcachedresult.h" +#include <qdatetime.h> + +#ifndef QT_NO_SQL +static const uint initial_cache_size = 128; + +class TQtSqlCachedResultPrivate +{ +public: + TQtSqlCachedResultPrivate(); + bool seek(int i); + void init(int count, bool fo); + void cleanup(); + TQtSqlCachedResult::RowCache* next(); + void revertLast(); + + TQtSqlCachedResult::RowsetCache *cache; + TQtSqlCachedResult::RowCache *current; + int rowCacheEnd; + int colCount; + bool forwardOnly; +}; + +TQtSqlCachedResultPrivate::TQtSqlCachedResultPrivate(): + cache(0), current(0), rowCacheEnd(0), colCount(0), forwardOnly(FALSE) +{ +} + +void TQtSqlCachedResultPrivate::cleanup() +{ + if (cache) { + for (int i = 0; i < rowCacheEnd; ++i) + delete (*cache)[i]; + delete cache; + cache = 0; + } + if (forwardOnly) + delete current; + current = 0; + forwardOnly = FALSE; + colCount = 0; + rowCacheEnd = 0; +} + +void TQtSqlCachedResultPrivate::init(int count, bool fo) +{ + cleanup(); + forwardOnly = fo; + colCount = count; + if (fo) + current = new TQtSqlCachedResult::RowCache(count); + else + cache = new TQtSqlCachedResult::RowsetCache(initial_cache_size); +} + +TQtSqlCachedResult::RowCache *TQtSqlCachedResultPrivate::next() +{ + if (forwardOnly) + return current; + + Q_ASSERT(cache); + current = new TQtSqlCachedResult::RowCache(colCount); + if (rowCacheEnd == (int)cache->size()) + cache->resize(cache->size() * 2); + cache->insert(rowCacheEnd++, current); + return current; +} + +bool TQtSqlCachedResultPrivate::seek(int i) +{ + if (forwardOnly || i < 0) + return FALSE; + if (i >= rowCacheEnd) + return FALSE; + current = (*cache)[i]; + return TRUE; +} + +void TQtSqlCachedResultPrivate::revertLast() +{ + if (forwardOnly) + return; + --rowCacheEnd; + delete current; + current = 0; +} + +////////////// + +TQtSqlCachedResult::TQtSqlCachedResult(const TQSqlDriver * db ): TQSqlResult ( db ) +{ + d = new TQtSqlCachedResultPrivate(); +} + +TQtSqlCachedResult::~TQtSqlCachedResult() +{ + delete d; +} + +void TQtSqlCachedResult::init(int colCount) +{ + d->init(colCount, isForwardOnly()); +} + +bool TQtSqlCachedResult::fetch(int i) +{ + if ((!isActive()) || (i < 0)) + return FALSE; + if (at() == i) + return TRUE; + if (d->forwardOnly) { + // speed hack - do not copy values if not needed + if (at() > i || at() == TQSql::AfterLast) + return FALSE; + while(at() < i - 1) { + if (!gotoNext(0)) + return FALSE; + setAt(at() + 1); + } + if (!gotoNext(d->current)) + return FALSE; + setAt(at() + 1); + return TRUE; + } + if (d->seek(i)) { + setAt(i); + return TRUE; + } + setAt(d->rowCacheEnd - 1); + while (at() < i) { + if (!cacheNext()) + return FALSE; + } + return TRUE; +} + +bool TQtSqlCachedResult::fetchNext() +{ + if (d->seek(at() + 1)) { + setAt(at() + 1); + return TRUE; + } + return cacheNext(); +} + +bool TQtSqlCachedResult::fetchPrev() +{ + return fetch(at() - 1); +} + +bool TQtSqlCachedResult::fetchFirst() +{ + if (d->forwardOnly && at() != TQSql::BeforeFirst) { + return FALSE; + } + if (d->seek(0)) { + setAt(0); + return TRUE; + } + return cacheNext(); +} + +bool TQtSqlCachedResult::fetchLast() +{ + if (at() == TQSql::AfterLast) { + if (d->forwardOnly) + return FALSE; + else + return fetch(d->rowCacheEnd - 1); + } + + int i = at(); + while (fetchNext()) + i++; /* brute force */ + if (d->forwardOnly && at() == TQSql::AfterLast) { + setAt(i); + return TRUE; + } else { + return fetch(d->rowCacheEnd - 1); + } +} + +TQVariant TQtSqlCachedResult::data(int i) +{ + if (!d->current || i >= (int)d->current->size() || i < 0) + return TQVariant(); + + return (*d->current)[i]; +} + +bool TQtSqlCachedResult::isNull(int i) +{ + if (!d->current || i >= (int)d->current->size() || i < 0) + return TRUE; + + return (*d->current)[i].isNull(); +} + +void TQtSqlCachedResult::cleanup() +{ + setAt(TQSql::BeforeFirst); + setActive(FALSE); + d->cleanup(); +} + +bool TQtSqlCachedResult::cacheNext() +{ + if (!gotoNext(d->next())) { + d->revertLast(); + return FALSE; + } + setAt(at() + 1); + return TRUE; +} + +int TQtSqlCachedResult::colCount() const +{ + return d->colCount; +} +#endif // QT_NO_SQL diff --git a/src/sql/drivers/cache/qsqlcachedresult.h b/src/sql/drivers/cache/qsqlcachedresult.h new file mode 100644 index 000000000..1cdae7ae2 --- /dev/null +++ b/src/sql/drivers/cache/qsqlcachedresult.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Definition of shared TQt SQL module classes +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLCACHEDRESULT_H +#define TQSQLCACHEDRESULT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt API. It exists for the convenience +// of other TQt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include <qglobal.h> +#include <qvariant.h> +#include <qptrvector.h> +#include <qvaluevector.h> +#include <qsqlresult.h> + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQtSqlCachedResultPrivate; + +class TQM_EXPORT_SQL TQtSqlCachedResult: public TQSqlResult +{ +public: + virtual ~TQtSqlCachedResult(); + + typedef TQValueVector<TQVariant> RowCache; + typedef TQPtrVector<RowCache> RowsetCache; + +protected: + TQtSqlCachedResult(const TQSqlDriver * db); + + void init(int colCount); + void cleanup(); + bool cacheNext(); + + virtual bool gotoNext(RowCache* row) = 0; + + TQVariant data(int i); + bool isNull(int i); + bool fetch(int i); + bool fetchNext(); + bool fetchPrev(); + bool fetchFirst(); + bool fetchLast(); + + int colCount() const; + +private: + TQtSqlCachedResultPrivate *d; +}; + + +#endif + +#endif diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp new file mode 100644 index 000000000..22c41064d --- /dev/null +++ b/src/sql/drivers/ibase/qsql_ibase.cpp @@ -0,0 +1,1078 @@ +/**************************************************************************** +** +** Implementation of Interbase driver classes. +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsql_ibase.h" + +#include <qdatetime.h> +#include <private/qsqlextension_p.h> + +#include <ibase.h> +#include <stdlib.h> +#include <limits.h> +#include <math.h> + +#define TQIBASE_DRIVER_NAME "TQIBASE" + +class TQIBasePreparedExtension : public TQSqlExtension +{ +public: + TQIBasePreparedExtension(TQIBaseResult *r) + : result(r) {} + + bool prepare(const TQString &query) + { + return result->prepare(query); + } + + bool exec() + { + return result->exec(); + } + + TQIBaseResult *result; +}; + +static bool getIBaseError(TQString& msg, ISC_STATUS* status, long &sqlcode) +{ + if (status[0] != 1 || status[1] <= 0) + return FALSE; + + sqlcode = isc_sqlcode(status); + char buf[512]; + isc_sql_interprete(sqlcode, buf, 512); + msg = TQString::fromUtf8(buf); + return TRUE; +} + +static void createDA(XSQLDA *&sqlda) +{ + sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1)); + sqlda->sqln = 1; + sqlda->sqld = 0; + sqlda->version = SQLDA_VERSION1; + sqlda->sqlvar[0].sqlind = 0; + sqlda->sqlvar[0].sqldata = 0; +} + +static void enlargeDA(XSQLDA *&sqlda, int n) +{ + free(sqlda); + sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n)); + sqlda->sqln = n; + sqlda->version = SQLDA_VERSION1; +} + +static void initDA(XSQLDA *sqlda) +{ + for (int i = 0; i < sqlda->sqld; ++i) { + switch (sqlda->sqlvar[i].sqltype & ~1) { + case SQL_INT64: + case SQL_LONG: + case SQL_SHORT: + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_TIMESTAMP: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_TEXT: + case SQL_BLOB: + sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen); + break; + case SQL_VARYING: + sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short)); + break; + default: + // not supported - do not bind. + sqlda->sqlvar[i].sqldata = 0; + break; + } + if (sqlda->sqlvar[i].sqltype & 1) { + sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short)); + *(sqlda->sqlvar[i].sqlind) = 0; + } else { + sqlda->sqlvar[i].sqlind = 0; + } + } +} + +static void delDA(XSQLDA *&sqlda) +{ + if (!sqlda) + return; + for (int i = 0; i < sqlda->sqld; ++i) { + free(sqlda->sqlvar[i].sqlind); + free(sqlda->sqlvar[i].sqldata); + } + free(sqlda); + sqlda = 0; +} + +static TQVariant::Type qIBaseTypeName(int iType) +{ + switch (iType) { + case blr_varying: + case blr_varying2: + case blr_text: + case blr_cstring: + case blr_cstring2: + return TQVariant::String; + case blr_sql_time: + return TQVariant::Time; + case blr_sql_date: + return TQVariant::Date; + case blr_timestamp: + return TQVariant::DateTime; + case blr_blob: + return TQVariant::ByteArray; + case blr_quad: + case blr_short: + case blr_long: + return TQVariant::Int; + case blr_int64: + return TQVariant::LongLong; + case blr_float: + case blr_d_float: + case blr_double: + return TQVariant::Double; + } + return TQVariant::Invalid; +} + +static TQVariant::Type qIBaseTypeName2(int iType) +{ + switch(iType & ~1) { + case SQL_VARYING: + case SQL_TEXT: + return TQVariant::String; + case SQL_LONG: + case SQL_SHORT: + return TQVariant::Int; + case SQL_INT64: + return TQVariant::LongLong; + case SQL_FLOAT: + case SQL_DOUBLE: + return TQVariant::Double; + case SQL_TIMESTAMP: + return TQVariant::DateTime; + case SQL_TYPE_DATE: + return TQVariant::Date; + case SQL_TYPE_TIME: + return TQVariant::Time; + default: + return TQVariant::Invalid; + } +} + +static ISC_TIME toTime(const TQTime &t) +{ + static const TQTime midnight(0, 0, 0, 0); + return (ISC_TIME)midnight.msecsTo(t) * 10; +} + +static ISC_DATE toDate(const TQDate &d) +{ + static const TQDate basedate(1858, 11, 17); + return (ISC_DATE)basedate.daysTo(d); +} + +static ISC_TIMESTAMP toTimeStamp(const TQDateTime &dt) +{ + ISC_TIMESTAMP ts; + ts.timestamp_time = toTime(dt.time()); + ts.timestamp_date = toDate(dt.date()); + return ts; +} + +static TQTime toTQTime(ISC_TIME time) +{ + // have to demangle the structure ourselves because isc_decode_time + // strips the msecs + static const TQTime t; + return t.addMSecs(time / 10); +} + +static TQDate toTQDate(ISC_DATE d) +{ + static const TQDate bd(1858, 11, 17); + return bd.addDays(d); +} + +static TQDateTime toTQDateTime(ISC_TIMESTAMP *ts) +{ + return TQDateTime(toTQDate(ts->timestamp_date), toTQTime(ts->timestamp_time)); +} + +class TQIBaseDriverPrivate +{ +public: + TQIBaseDriverPrivate(TQIBaseDriver *d): q(d) + { + ibase = 0; + trans = 0; + } + + bool isError(const TQString &msg = TQString::null, TQSqlError::Type typ = TQSqlError::Unknown) + { + TQString imsg; + long sqlcode; + if (!getIBaseError(imsg, status, sqlcode)) + return FALSE; + + q->setLastError(TQSqlError(msg, imsg, typ, (int)sqlcode)); + return TRUE; + } + +public: + TQIBaseDriver* q; + isc_db_handle ibase; + isc_tr_handle trans; + ISC_STATUS status[20]; +}; + +class TQIBaseResultPrivate +{ +public: + TQIBaseResultPrivate(TQIBaseResult *d, const TQIBaseDriver *ddb); + ~TQIBaseResultPrivate() { cleanup(); } + + void cleanup(); + bool isError(const TQString &msg = TQString::null, TQSqlError::Type typ = TQSqlError::Unknown) + { + TQString imsg; + long sqlcode; + if (!getIBaseError(imsg, status, sqlcode)) + return FALSE; + + q->setLastError(TQSqlError(msg, imsg, typ, (int)sqlcode)); + return TRUE; + } + + bool transaction(); + bool commit(); + + bool isSelect(); + TQVariant fetchBlob(ISC_QUAD *bId); + void writeBlob(int i, const TQByteArray &ba); + +public: + TQIBaseResult *q; + const TQIBaseDriver *db; + ISC_STATUS status[20]; + isc_tr_handle trans; + //indicator whether we have a local transaction or a transaction on driver level + bool localTransaction; + isc_stmt_handle stmt; + isc_db_handle ibase; + XSQLDA *sqlda; // output sqlda + XSQLDA *inda; // input parameters + int queryType; +}; + +TQIBaseResultPrivate::TQIBaseResultPrivate(TQIBaseResult *d, const TQIBaseDriver *ddb): + q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1) +{ + localTransaction = (ddb->d->ibase == 0); +} + +void TQIBaseResultPrivate::cleanup() +{ + if (stmt) { + isc_dsql_free_statement(status, &stmt, DSQL_drop); + stmt = 0; + } + + commit(); + if (!localTransaction) + trans = 0; + + delDA(sqlda); + delDA(inda); + + queryType = -1; + q->cleanup(); +} + +void TQIBaseResultPrivate::writeBlob(int i, const TQByteArray &ba) +{ + isc_blob_handle handle = 0; + ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata; + isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0); + if (!isError("Unable to create BLOB", TQSqlError::Statement)) { + uint i = 0; + while (i < ba.size()) { + isc_put_segment(status, &handle, TQMIN(ba.size() - i, SHRT_MAX), ba.data()); + if (isError("Unable to write BLOB")) + break; + i += SHRT_MAX; + } + } + isc_close_blob(status, &handle); +} + +TQVariant TQIBaseResultPrivate::fetchBlob(ISC_QUAD *bId) +{ + isc_blob_handle handle = 0; + + isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0); + if (isError("Unable to open BLOB", TQSqlError::Statement)) + return TQVariant(); + + unsigned short len = 0; + TQByteArray ba(255); + ISC_STATUS stat = isc_get_segment(status, &handle, &len, ba.size(), ba.data()); + while (status[1] == isc_segment) { + uint osize = ba.size(); + // double the amount of data fetched on each iteration + ba.resize(TQMIN(ba.size() * 2, SHRT_MAX)); + stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize); + } + bool isErr = isError("Unable to read BLOB", TQSqlError::Statement); + isc_close_blob(status, &handle); + if (isErr) + return TQVariant(); + + if (ba.size() > 255) + ba.resize(ba.size() / 2 + len); + else + ba.resize(len); + + return ba; +} + +bool TQIBaseResultPrivate::isSelect() +{ + char acBuffer[9]; + char qType = isc_info_sql_stmt_type; + isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer); + if (isError("Could not get query info", TQSqlError::Statement)) + return FALSE; + int iLength = isc_vax_integer(&acBuffer[1], 2); + queryType = isc_vax_integer(&acBuffer[3], iLength); + return (queryType == isc_info_sql_stmt_select); +} + +bool TQIBaseResultPrivate::transaction() +{ + if (trans) + return TRUE; + if (db->d->trans) { + localTransaction = FALSE; + trans = db->d->trans; + return TRUE; + } + localTransaction = TRUE; + + isc_start_transaction(status, &trans, 1, &ibase, 0, NULL); + if (isError("Could not start transaction", TQSqlError::Statement)) + return FALSE; + + return TRUE; +} + +// does nothing if the transaction is on the +// driver level +bool TQIBaseResultPrivate::commit() +{ + if (!trans) + return FALSE; + // don't commit driver's transaction, the driver will do it for us + if (!localTransaction) + return TRUE; + + isc_commit_transaction(status, &trans); + trans = 0; + return !isError("Unable to commit transaction", TQSqlError::Statement); +} + +////////// + +TQIBaseResult::TQIBaseResult(const TQIBaseDriver* db): + TQtSqlCachedResult(db) +{ + d = new TQIBaseResultPrivate(this, db); + setExtension(new TQIBasePreparedExtension(this)); +} + +TQIBaseResult::~TQIBaseResult() +{ + delete d; +} + +bool TQIBaseResult::prepare(const TQString& query) +{ + //qDebug("prepare: %s", query.ascii()); + if (!driver() || !driver()->isOpen() || driver()->isOpenError()) + return FALSE; + d->cleanup(); + setActive(FALSE); + setAt(TQSql::BeforeFirst); + + createDA(d->sqlda); + createDA(d->inda); + + if (!d->transaction()) + return FALSE; + + isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt); + if (d->isError("Could not allocate statement", TQSqlError::Statement)) + return FALSE; + isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda); + if (d->isError("Could not prepare statement", TQSqlError::Statement)) + return FALSE; + + isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda); + if (d->isError("Could not describe input statement", TQSqlError::Statement)) + return FALSE; + if (d->inda->sqld > d->inda->sqln) { + enlargeDA(d->inda, d->inda->sqld); + + isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda); + if (d->isError("Could not describe input statement", TQSqlError::Statement)) + return FALSE; + } + initDA(d->inda); + if (d->sqlda->sqld > d->sqlda->sqln) { + // need more field descriptors + enlargeDA(d->sqlda, d->sqlda->sqld); + + isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda); + if (d->isError("Could not describe statement", TQSqlError::Statement)) + return FALSE; + } + initDA(d->sqlda); + + setSelect(d->isSelect()); + if (!isSelect()) { + free(d->sqlda); + d->sqlda = 0; + } + + return TRUE; +} + +bool TQIBaseResult::exec() +{ + if (!driver() || !driver()->isOpen() || driver()->isOpenError()) + return FALSE; + setActive(FALSE); + setAt(TQSql::BeforeFirst); + + if (d->inda && extension()->index.count() > 0) { + TQMap<int, TQString>::ConstIterator it; + if ((int)extension()->index.count() > d->inda->sqld) { + qWarning("TQIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", d->inda->sqld, extension()->index.count()); + return FALSE; + } + int para = 0; + for (it = extension()->index.constBegin(); it != extension()->index.constEnd(); ++it, ++para) { + if (para >= d->inda->sqld) + break; + if (!d->inda->sqlvar[para].sqldata) + continue; + const TQVariant val(extension()->values[it.data()].value); + if (d->inda->sqlvar[para].sqltype & 1) { + if (val.isNull()) { + // set null indicator + *(d->inda->sqlvar[para].sqlind) = 1; + // and set the value to 0, otherwise it would count as empty string. + *((short*)d->inda->sqlvar[para].sqldata) = 0; + continue; + } + // a value of 0 means non-null. + *(d->inda->sqlvar[para].sqlind) = 0; + } + switch(d->inda->sqlvar[para].sqltype & ~1) { + case SQL_INT64: + if (d->inda->sqlvar[para].sqlscale < 0) + *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = Q_LLONG(val.toDouble() * + pow(10.0, d->inda->sqlvar[para].sqlscale * -1)); + else + *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); + break; + case SQL_LONG: + *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong(); + break; + case SQL_SHORT: + *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt(); + break; + case SQL_FLOAT: + *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble(); + break; + case SQL_DOUBLE: + *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble(); + break; + case SQL_TIMESTAMP: + *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime()); + break; + case SQL_TYPE_TIME: + *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime()); + break; + case SQL_TYPE_DATE: + *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate()); + break; + case SQL_VARYING: { + TQCString str(val.toString().utf8()); // keep a copy of the string alive in this scope + short buflen = d->inda->sqlvar[para].sqllen; + if (str.length() < (uint)buflen) + buflen = str.length(); + *(short*)d->inda->sqlvar[para].sqldata = buflen; // first two bytes is the length + memcpy(d->inda->sqlvar[para].sqldata + sizeof(short), str.data(), buflen); + break; } + case SQL_TEXT: { + TQCString str(val.toString().utf8().leftJustify(d->inda->sqlvar[para].sqllen, ' ', TRUE)); + memcpy(d->inda->sqlvar[para].sqldata, str.data(), d->inda->sqlvar[para].sqllen); + break; } + case SQL_BLOB: + d->writeBlob(para, val.toByteArray()); + break; + default: + break; + } + } + } + + if (colCount()) { + isc_dsql_free_statement(d->status, &d->stmt, DSQL_close); + if (d->isError("Unable to close statement")) + return FALSE; + cleanup(); + } + if (d->sqlda) + init(d->sqlda->sqld); + isc_dsql_execute2(d->status, &d->trans, &d->stmt, 1, d->inda, 0); + if (d->isError("Unable to execute query")) + return FALSE; + + setActive(TRUE); + return TRUE; +} + +bool TQIBaseResult::reset (const TQString& query) +{ +// qDebug("reset: %s", query.ascii()); + if (!driver() || !driver()->isOpen() || driver()->isOpenError()) + return FALSE; + d->cleanup(); + setActive(FALSE); + setAt(TQSql::BeforeFirst); + + createDA(d->sqlda); + + if (!d->transaction()) + return FALSE; + + isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt); + if (d->isError("Could not allocate statement", TQSqlError::Statement)) + return FALSE; + isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda); + if (d->isError("Could not prepare statement", TQSqlError::Statement)) + return FALSE; + + if (d->sqlda->sqld > d->sqlda->sqln) { + // need more field descriptors + int n = d->sqlda->sqld; + free(d->sqlda); + d->sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n)); + d->sqlda->sqln = n; + d->sqlda->version = SQLDA_VERSION1; + + isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda); + if (d->isError("Could not describe statement", TQSqlError::Statement)) + return FALSE; + } + + initDA(d->sqlda); + + setSelect(d->isSelect()); + if (isSelect()) { + init(d->sqlda->sqld); + } else { + free(d->sqlda); + d->sqlda = 0; + } + + isc_dsql_execute(d->status, &d->trans, &d->stmt, 1, 0); + if (d->isError("Unable to execute query")) + return FALSE; + + // commit non-select queries (if they are local) + if (!isSelect() && !d->commit()) + return FALSE; + + setActive(TRUE); + return TRUE; +} + +bool TQIBaseResult::gotoNext(TQtSqlCachedResult::RowCache* row) +{ + ISC_STATUS stat = isc_dsql_fetch(d->status, &d->stmt, 1, d->sqlda); + + if (stat == 100) { + // no more rows + setAt(TQSql::AfterLast); + return FALSE; + } + if (d->isError("Could not fetch next item", TQSqlError::Statement)) + return FALSE; + if (!row) // not interested in actual values + return TRUE; + + Q_ASSERT(row); + Q_ASSERT((int)row->size() == d->sqlda->sqld); + for (int i = 0; i < d->sqlda->sqld; ++i) { + char *buf = d->sqlda->sqlvar[i].sqldata; + int size = d->sqlda->sqlvar[i].sqllen; + Q_ASSERT(buf); + + if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) { + // null value + TQVariant v; + v.cast(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype)); + (*row)[i] = v; + continue; + } + + switch(d->sqlda->sqlvar[i].sqltype & ~1) { + case SQL_VARYING: + // pascal strings - a short with a length information followed by the data + (*row)[i] = TQString::fromUtf8(buf + sizeof(short), *(short*)buf); + break; + case SQL_INT64: + if (d->sqlda->sqlvar[i].sqlscale < 0) + (*row)[i] = *(Q_LLONG*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale); + else + (*row)[i] = TQVariant(*(Q_LLONG*)buf); + break; + case SQL_LONG: + if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out. + (*row)[i] = TQVariant((int)(*(long*)buf)); + else + (*row)[i] = TQVariant((Q_LLONG)(*(long*)buf)); + break; + case SQL_SHORT: + (*row)[i] = TQVariant((int)(*(short*)buf)); + break; + case SQL_FLOAT: + (*row)[i] = TQVariant((double)(*(float*)buf)); + break; + case SQL_DOUBLE: + (*row)[i] = TQVariant(*(double*)buf); + break; + case SQL_TIMESTAMP: + (*row)[i] = toTQDateTime((ISC_TIMESTAMP*)buf); + break; + case SQL_TYPE_TIME: + (*row)[i] = toTQTime(*(ISC_TIME*)buf); + break; + case SQL_TYPE_DATE: + (*row)[i] = toTQDate(*(ISC_DATE*)buf); + break; + case SQL_TEXT: + (*row)[i] = TQString::fromUtf8(buf, size); + break; + case SQL_BLOB: + (*row)[i] = d->fetchBlob((ISC_QUAD*)buf); + break; + default: + // unknown type - don't even try to fetch + (*row)[i] = TQVariant(); + break; + } + } + + return TRUE; +} + +int TQIBaseResult::size() +{ + static char sizeInfo[] = {isc_info_sql_records}; + char buf[33]; + + if (!isActive() || !isSelect()) + return -1; + + isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf); + for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) { + char ct = *c++; + short len = isc_vax_integer(c, 2); + c += 2; + int val = isc_vax_integer(c, len); + c += len; + if (ct == isc_info_req_select_count) + return val; + } + return -1; +} + +int TQIBaseResult::numRowsAffected() +{ + static char acCountInfo[] = {isc_info_sql_records}; + char cCountType; + + switch (d->queryType) { + case isc_info_sql_stmt_select: + cCountType = isc_info_req_select_count; + break; + case isc_info_sql_stmt_update: + cCountType = isc_info_req_update_count; + break; + case isc_info_sql_stmt_delete: + cCountType = isc_info_req_delete_count; + break; + case isc_info_sql_stmt_insert: + cCountType = isc_info_req_insert_count; + break; + } + + char acBuffer[33]; + int iResult = -1; + isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer); + if (d->isError("Could not get statement info", TQSqlError::Statement)) + return -1; + for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) { + char cType = *pcBuf++; + short sLength = isc_vax_integer (pcBuf, 2); + pcBuf += 2; + int iValue = isc_vax_integer (pcBuf, sLength); + pcBuf += sLength; + + if (cType == cCountType) { + iResult = iValue; + break; + } + } + return iResult; +} + +/*********************************/ + +TQIBaseDriver::TQIBaseDriver(TQObject * parent, const char * name) + : TQSqlDriver(parent, name ? name : TQIBASE_DRIVER_NAME) +{ + d = new TQIBaseDriverPrivate(this); +} + +TQIBaseDriver::TQIBaseDriver(void *connection, TQObject *parent, const char *name) + : TQSqlDriver(parent, name ? name : TQIBASE_DRIVER_NAME) +{ + d = new TQIBaseDriverPrivate(this); + d->ibase = (isc_db_handle)(long int)connection; + setOpen(TRUE); + setOpenError(FALSE); +} + +TQIBaseDriver::~TQIBaseDriver() +{ + delete d; +} + +bool TQIBaseDriver::hasFeature(DriverFeature f) const +{ + switch (f) { + case Transactions: +// case QuerySize: + case PreparedQueries: + case PositionalPlaceholders: + case Unicode: + case BLOB: + return TRUE; + default: + return FALSE; + } +} + +bool TQIBaseDriver::open(const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int /*port*/, + const TQString & /* connOpts */) +{ + if (isOpen()) + close(); + + static const char enc[8] = "UTF_FSS"; + TQCString usr = user.local8Bit(); + TQCString pass = password.local8Bit(); + usr.truncate(255); + pass.truncate(255); + + TQByteArray ba(usr.length() + pass.length() + sizeof(enc) + 6); + int i = -1; + ba[++i] = isc_dpb_version1; + ba[++i] = isc_dpb_user_name; + ba[++i] = usr.length(); + memcpy(&ba[++i], usr.data(), usr.length()); + i += usr.length(); + ba[i] = isc_dpb_password; + ba[++i] = pass.length(); + memcpy(&ba[++i], pass.data(), pass.length()); + i += pass.length(); + ba[i] = isc_dpb_lc_ctype; + ba[++i] = sizeof(enc) - 1; + memcpy(&ba[++i], enc, sizeof(enc) - 1); + i += sizeof(enc) - 1; + + TQString ldb; + if (!host.isEmpty()) + ldb += host + ":"; + ldb += db; + isc_attach_database(d->status, 0, (char*)ldb.latin1(), &d->ibase, i, ba.data()); + if (d->isError("Error opening database", TQSqlError::Connection)) { + setOpenError(TRUE); + return FALSE; + } + + setOpen(TRUE); + return TRUE; +} + +void TQIBaseDriver::close() +{ + if (isOpen()) { + isc_detach_database(d->status, &d->ibase); + d->ibase = 0; + setOpen(FALSE); + setOpenError(FALSE); + } +} + +TQSqlQuery TQIBaseDriver::createQuery() const +{ + return TQSqlQuery(new TQIBaseResult(this)); +} + +bool TQIBaseDriver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + if (d->trans) + return FALSE; + + isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL); + return !d->isError("Could not start transaction", TQSqlError::Transaction); +} + +bool TQIBaseDriver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + if (!d->trans) + return FALSE; + + isc_commit_transaction(d->status, &d->trans); + d->trans = 0; + return !d->isError("Unable to commit transaction", TQSqlError::Transaction); +} + +bool TQIBaseDriver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + if (!d->trans) + return FALSE; + + isc_rollback_transaction(d->status, &d->trans); + d->trans = 0; + return !d->isError("Unable to rollback transaction", TQSqlError::Transaction); +} + +TQStringList TQIBaseDriver::tables(const TQString& typeName) const +{ + TQStringList res; + if (!isOpen()) + return res; + + int type = typeName.isEmpty() ? (int)TQSql::Tables | (int)TQSql::Views : typeName.toInt(); + TQString typeFilter; + + if (type == (int)TQSql::SystemTables) { + typeFilter += "RDB$SYSTEM_FLAG != 0"; + } else if (type == ((int)TQSql::SystemTables | (int)TQSql::Views)) { + typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL"; + } else { + if (!(type & (int)TQSql::SystemTables)) + typeFilter += "RDB$SYSTEM_FLAG = 0 AND "; + if (!(type & (int)TQSql::Views)) + typeFilter += "RDB$VIEW_BLR IS NULL AND "; + if (!(type & (int)TQSql::Tables)) + typeFilter += "RDB$VIEW_BLR IS NOT NULL AND "; + if (!typeFilter.isEmpty()) + typeFilter.truncate(typeFilter.length() - 5); + } + if (!typeFilter.isEmpty()) + typeFilter.prepend("where "); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + if (!q.exec("select rdb$relation_name from rdb$relations " + typeFilter)) + return res; + while(q.next()) + res << q.value(0).toString().stripWhiteSpace(); + + return res; +} + +TQSqlRecord TQIBaseDriver::record(const TQString& tablename) const +{ + TQSqlRecord rec; + if (!isOpen()) + return rec; + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + + q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE " + "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b " + "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE " + "AND a.RDB$RELATION_NAME = '" + tablename.upper()+ "' " + "ORDER BY RDB$FIELD_POSITION"); + while (q.next()) { + TQSqlField field(q.value(0).toString().stripWhiteSpace(), qIBaseTypeName(q.value(1).toInt())); + rec.append(field); + } + + return rec; +} + +TQSqlRecordInfo TQIBaseDriver::recordInfo(const TQString& tablename) const +{ + TQSqlRecordInfo rec; + if (!isOpen()) + return rec; + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + + q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, b.RDB$FIELD_SCALE, " + "b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG " + "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b " + "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE " + "AND a.RDB$RELATION_NAME = '" + tablename.upper() + "' " + "ORDER BY a.RDB$FIELD_POSITION"); + + while (q.next()) { + TQVariant::Type type = qIBaseTypeName(q.value(1).toInt()); + TQSqlFieldInfo field(q.value(0).toString().stripWhiteSpace(), type, q.value(5).toInt(), + q.value(2).toInt(), q.value(4).toInt(), TQVariant()); + + rec.append(field); + } + + return rec; +} + +TQSqlIndex TQIBaseDriver::primaryIndex(const TQString &table) const +{ + TQSqlIndex index(table); + if (!isOpen()) + return index; + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE " + "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d " + "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' " + "AND a.RDB$RELATION_NAME = '" + table.upper() + "' " + "AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME " + "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME " + "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME " + "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE " + "ORDER BY b.RDB$FIELD_POSITION"); + + while (q.next()) { + TQSqlField field(q.value(1).toString().stripWhiteSpace(), qIBaseTypeName(q.value(2).toInt())); + index.append(field); //TODO: asc? desc? + index.setName(q.value(0).toString()); + } + + return index; +} + +TQSqlRecord TQIBaseDriver::record(const TQSqlQuery& query) const +{ + TQSqlRecord rec; + if (query.isActive() && query.driver() == this) { + TQIBaseResult* result = (TQIBaseResult*)query.result(); + if (!result->d->sqlda) + return rec; + XSQLVAR v; + for (int i = 0; i < result->d->sqlda->sqld; ++i) { + v = result->d->sqlda->sqlvar[i]; + TQSqlField f(TQString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(), + qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype)); + rec.append(f); + } + } + return rec; +} + +TQSqlRecordInfo TQIBaseDriver::recordInfo(const TQSqlQuery& query) const +{ + TQSqlRecordInfo rec; + if (query.isActive() && query.driver() == this) { + TQIBaseResult* result = (TQIBaseResult*)query.result(); + if (!result->d->sqlda) + return rec; + XSQLVAR v; + for (int i = 0; i < result->d->sqlda->sqld; ++i) { + v = result->d->sqlda->sqlvar[i]; + TQSqlFieldInfo f(TQString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(), + qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype), + -1, v.sqllen, TQABS(v.sqlscale), TQVariant(), v.sqltype); + rec.append(f); + } + } + return rec; +} + +TQString TQIBaseDriver::formatValue(const TQSqlField* field, bool trimStrings) const +{ + switch (field->type()) { + case TQVariant::DateTime: { + TQDateTime datetime = field->value().toDateTime(); + if (datetime.isValid()) + return "'" + TQString::number(datetime.date().year()) + "-" + + TQString::number(datetime.date().month()) + "-" + + TQString::number(datetime.date().day()) + " " + + TQString::number(datetime.time().hour()) + ":" + + TQString::number(datetime.time().minute()) + ":" + + TQString::number(datetime.time().second()) + "." + + TQString::number(datetime.time().msec()).rightJustify(3, '0', TRUE) + "'"; + else + return "NULL"; + } + case TQVariant::Time: { + TQTime time = field->value().toTime(); + if (time.isValid()) + return "'" + TQString::number(time.hour()) + ":" + + TQString::number(time.minute()) + ":" + + TQString::number(time.second()) + "." + + TQString::number(time.msec()).rightJustify(3, '0', TRUE) + "'"; + else + return "NULL"; + } + case TQVariant::Date: { + TQDate date = field->value().toDate(); + if (date.isValid()) + return "'" + TQString::number(date.year()) + "-" + + TQString::number(date.month()) + "-" + + TQString::number(date.day()) + "'"; + else + return "NULL"; + } + default: + return TQSqlDriver::formatValue(field, trimStrings); + } +} diff --git a/src/sql/drivers/ibase/qsql_ibase.h b/src/sql/drivers/ibase/qsql_ibase.h new file mode 100644 index 000000000..c0e8717de --- /dev/null +++ b/src/sql/drivers/ibase/qsql_ibase.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Definition of Interbase driver classes +** +** Created : 030911 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQL_IBASE_H +#define TQSQL_IBASE_H + +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "../cache/qsqlcachedresult.h" + + +class TQIBaseDriverPrivate; +class TQIBaseResultPrivate; +class TQIBaseDriver; + +class TQIBaseResult : public TQtSqlCachedResult +{ + friend class TQIBaseDriver; + friend class TQIBaseResultPrivate; + +public: + TQIBaseResult(const TQIBaseDriver* db); + virtual ~TQIBaseResult(); + + bool prepare(const TQString& query); + bool exec(); + +protected: + bool gotoNext(TQtSqlCachedResult::RowCache* row); + bool reset (const TQString& query); + int size(); + int numRowsAffected(); + +private: + TQIBaseResultPrivate* d; +}; + +class TQIBaseDriver : public TQSqlDriver +{ + friend class TQIBaseDriverPrivate; + friend class TQIBaseResultPrivate; + friend class TQIBaseResult; +public: + TQIBaseDriver(TQObject *parent = 0, const char *name = 0); + TQIBaseDriver(void *connection, TQObject *parent = 0, const char *name = 0); + virtual ~TQIBaseDriver(); + bool hasFeature(DriverFeature f) const; + bool open(const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port, + const TQString & connOpts); + bool open( const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port ) { return open (db, user, password, host, port, TQString()); } + void close(); + TQSqlQuery createQuery() const; + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); + TQStringList tables(const TQString& typeName) const; + + TQSqlRecord record(const TQString& tablename) const; + TQSqlRecordInfo recordInfo(const TQString& tablename) const; + TQSqlIndex primaryIndex(const TQString &table) const; + TQSqlRecord record(const TQSqlQuery& query) const; + TQSqlRecordInfo recordInfo(const TQSqlQuery& query) const; + + TQString formatValue(const TQSqlField* field, bool trimStrings) const; + +private: + TQIBaseDriverPrivate* d; +}; + + +#endif + diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp new file mode 100644 index 000000000..b0afb436d --- /dev/null +++ b/src/sql/drivers/mysql/qsql_mysql.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** Implementation of MYSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsql_mysql.h" +#include <private/qsqlextension_p.h> + +#include <qdatetime.h> +#include <qvaluevector.h> +#include <qsqlrecord.h> + +#define TQMYSQL_DRIVER_NAME "TQMYSQL3" + +#ifdef Q_OS_WIN32 +// comment the next line out if you want to use MySQL/embedded on Win32 systems. +// note that it will crash if you don't statically link to the mysql/e library! +# define Q_NO_MYSQL_EMBEDDED +#endif + +TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict(); + +static int qMySqlConnectionCount = 0; +static bool qMySqlInitHandledByUser = FALSE; + +class TQMYSQLOpenExtension : public TQSqlOpenExtension +{ +public: + TQMYSQLOpenExtension( TQMYSQLDriver *dri ) + : TQSqlOpenExtension(), driver(dri) {} + ~TQMYSQLOpenExtension() {} + + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); + +private: + TQMYSQLDriver *driver; +}; + +bool TQMYSQLOpenExtension::open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) +{ + return driver->open( db, user, password, host, port, connOpts ); +} + +class TQMYSQLDriverPrivate +{ +public: + TQMYSQLDriverPrivate() : mysql(0) {} + MYSQL* mysql; +}; + +class TQMYSQLResultPrivate : public TQMYSQLDriverPrivate +{ +public: + TQMYSQLResultPrivate() : TQMYSQLDriverPrivate(), result(0) {} + MYSQL_RES* result; + MYSQL_ROW row; + TQValueVector<TQVariant::Type> fieldTypes; +}; + +TQSqlError qMakeError( const TQString& err, int type, const TQMYSQLDriverPrivate* p ) +{ + return TQSqlError(TQMYSQL_DRIVER_NAME ": " + err, TQString(mysql_error( p->mysql )), type, mysql_errno( p->mysql )); +} + +TQVariant::Type qDecodeMYSQLType( int mysqltype, uint flags ) +{ + TQVariant::Type type; + switch ( mysqltype ) { + case FIELD_TYPE_TINY : + case FIELD_TYPE_SHORT : + case FIELD_TYPE_LONG : + case FIELD_TYPE_INT24 : + type = (flags & UNSIGNED_FLAG) ? TQVariant::UInt : TQVariant::Int; + break; + case FIELD_TYPE_YEAR : + type = TQVariant::Int; + break; + case FIELD_TYPE_LONGLONG : + type = (flags & UNSIGNED_FLAG) ? TQVariant::ULongLong : TQVariant::LongLong; + break; + case FIELD_TYPE_DECIMAL : + case FIELD_TYPE_FLOAT : + case FIELD_TYPE_DOUBLE : + type = TQVariant::Double; + break; + case FIELD_TYPE_DATE : + type = TQVariant::Date; + break; + case FIELD_TYPE_TIME : + type = TQVariant::Time; + break; + case FIELD_TYPE_DATETIME : + case FIELD_TYPE_TIMESTAMP : + type = TQVariant::DateTime; + break; + case FIELD_TYPE_BLOB : + case FIELD_TYPE_TINY_BLOB : + case FIELD_TYPE_MEDIUM_BLOB : + case FIELD_TYPE_LONG_BLOB : + type = (flags & BINARY_FLAG) ? TQVariant::ByteArray : TQVariant::CString; + break; + default: + case FIELD_TYPE_ENUM : + case FIELD_TYPE_SET : + case FIELD_TYPE_STRING : + case FIELD_TYPE_VAR_STRING : + type = TQVariant::String; + break; + } + return type; +} + +TQMYSQLResult::TQMYSQLResult( const TQMYSQLDriver* db ) +: TQSqlResult( db ) +{ + d = new TQMYSQLResultPrivate(); + d->mysql = db->d->mysql; +} + +TQMYSQLResult::~TQMYSQLResult() +{ + cleanup(); + delete d; +} + +MYSQL_RES* TQMYSQLResult::result() +{ + return d->result; +} + +void TQMYSQLResult::cleanup() +{ + if ( d->result ) { + mysql_free_result( d->result ); + } + d->result = NULL; + d->row = NULL; + setAt( -1 ); + setActive( FALSE ); +} + +bool TQMYSQLResult::fetch( int i ) +{ + if ( isForwardOnly() ) { // fake a forward seek + if ( at() < i ) { + int x = i - at(); + while ( --x && fetchNext() ); + return fetchNext(); + } else { + return FALSE; + } + } + if ( at() == i ) + return TRUE; + mysql_data_seek( d->result, i ); + d->row = mysql_fetch_row( d->result ); + if ( !d->row ) + return FALSE; + setAt( i ); + return TRUE; +} + +bool TQMYSQLResult::fetchNext() +{ + d->row = mysql_fetch_row( d->result ); + if ( !d->row ) + return FALSE; + setAt( at() + 1 ); + return TRUE; +} + +bool TQMYSQLResult::fetchLast() +{ + if ( isForwardOnly() ) { // fake this since MySQL can't seek on forward only queries + bool success = fetchNext(); // did we move at all? + while ( fetchNext() ); + return success; + } + my_ulonglong numRows = mysql_num_rows( d->result ); + if ( !numRows ) + return FALSE; + return fetch( numRows - 1 ); +} + +bool TQMYSQLResult::fetchFirst() +{ + if ( isForwardOnly() ) // again, fake it + return fetchNext(); + return fetch( 0 ); +} + +TQVariant TQMYSQLResult::data( int field ) +{ + if ( !isSelect() || field >= (int) d->fieldTypes.count() ) { + qWarning( "TQMYSQLResult::data: column %d out of range", field ); + return TQVariant(); + } + + TQString val( d->row[field] ); + switch ( d->fieldTypes.at( field ) ) { + case TQVariant::LongLong: + return TQVariant( val.toLongLong() ); + case TQVariant::ULongLong: + return TQVariant( val.toULongLong() ); + case TQVariant::Int: + return TQVariant( val.toInt() ); + case TQVariant::UInt: + return TQVariant( val.toUInt() ); + case TQVariant::Double: + return TQVariant( val.toDouble() ); + case TQVariant::Date: + if ( val.isEmpty() ) { + return TQVariant( TQDate() ); + } else { + return TQVariant( TQDate::fromString( val, TQt::ISODate ) ); + } + case TQVariant::Time: + if ( val.isEmpty() ) { + return TQVariant( TQTime() ); + } else { + return TQVariant( TQTime::fromString( val, TQt::ISODate ) ); + } + case TQVariant::DateTime: + if ( val.isEmpty() ) + return TQVariant( TQDateTime() ); + if ( val.length() == 14u ) + // TIMESTAMPS have the format yyyyMMddhhmmss + val.insert(4, "-").insert(7, "-").insert(10, 'T').insert(13, ':').insert(16, ':'); + return TQVariant( TQDateTime::fromString( val, TQt::ISODate ) ); + case TQVariant::ByteArray: { + unsigned long* fl = mysql_fetch_lengths( d->result ); + TQByteArray ba; + ba.duplicate( d->row[field], fl[field] ); + return TQVariant( ba ); + } + default: + case TQVariant::String: + case TQVariant::CString: + return TQVariant( val ); + } +#ifdef QT_CHECK_RANGE + qWarning("TQMYSQLResult::data: unknown data type"); +#endif + return TQVariant(); +} + +bool TQMYSQLResult::isNull( int field ) +{ + if ( d->row[field] == NULL ) + return TRUE; + return FALSE; +} + +bool TQMYSQLResult::reset ( const TQString& query ) +{ + if ( !driver() ) + return FALSE; + if ( !driver()-> isOpen() || driver()->isOpenError() ) + return FALSE; + cleanup(); + + const char *encQuery = query.ascii(); + if ( mysql_real_query( d->mysql, encQuery, qstrlen(encQuery) ) ) { + setLastError( qMakeError("Unable to execute query", TQSqlError::Statement, d ) ); + return FALSE; + } + if ( isForwardOnly() ) { + if ( isActive() || isValid() ) // have to empty the results from previous query + fetchLast(); + d->result = mysql_use_result( d->mysql ); + } else { + d->result = mysql_store_result( d->mysql ); + } + if ( !d->result && mysql_field_count( d->mysql ) > 0 ) { + setLastError( qMakeError( "Unable to store result", TQSqlError::Statement, d ) ); + return FALSE; + } + int numFields = mysql_field_count( d->mysql ); + setSelect( !( numFields == 0) ); + d->fieldTypes.resize( numFields ); + if ( isSelect() ) { + for( int i = 0; i < numFields; i++) { + MYSQL_FIELD* field = mysql_fetch_field_direct( d->result, i ); + if ( field->type == FIELD_TYPE_DECIMAL ) + d->fieldTypes[i] = TQVariant::String; + else + d->fieldTypes[i] = qDecodeMYSQLType( field->type, field->flags ); + } + } + setActive( TRUE ); + return TRUE; +} + +int TQMYSQLResult::size() +{ + return isSelect() ? (int)mysql_num_rows( d->result ) : -1; +} + +int TQMYSQLResult::numRowsAffected() +{ + return (int)mysql_affected_rows( d->mysql ); +} + +///////////////////////////////////////////////////////// +static void qServerEnd() +{ +#ifndef Q_NO_MYSQL_EMBEDDED +# if MYSQL_VERSION_ID >= 40000 + mysql_server_end(); +# endif // MYSQL_VERSION_ID +#endif // Q_NO_MYSQL_EMBEDDED +} + +static void qServerInit() +{ +#ifndef Q_NO_MYSQL_EMBEDDED +# if MYSQL_VERSION_ID >= 40000 + if ( qMySqlInitHandledByUser || qMySqlConnectionCount > 1 ) + return; + + // this should only be called once + // has no effect on client/server library + // but is vital for the embedded lib + if ( mysql_server_init( 0, 0, 0 ) ) { +# ifdef QT_CHECK_RANGE + qWarning( "TQMYSQLDriver::qServerInit: unable to start server." ); +# endif + } + +# endif // MYSQL_VERSION_ID +#endif // Q_NO_MYSQL_EMBEDDED +} + +TQMYSQLDriver::TQMYSQLDriver( TQObject * parent, const char * name ) + : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME ) +{ + init(); + qServerInit(); +} + +/*! + Create a driver instance with an already open connection handle. +*/ + +TQMYSQLDriver::TQMYSQLDriver( MYSQL * con, TQObject * parent, const char * name ) + : TQSqlDriver( parent, name ? name : TQMYSQL_DRIVER_NAME ) +{ + init(); + if ( con ) { + d->mysql = (MYSQL *) con; + setOpen( TRUE ); + setOpenError( FALSE ); + if (qMySqlConnectionCount == 1) + qMySqlInitHandledByUser = TRUE; + } else { + qServerInit(); + } +} + +void TQMYSQLDriver::init() +{ + qSqlOpenExtDict()->insert( this, new TQMYSQLOpenExtension(this) ); + d = new TQMYSQLDriverPrivate(); + d->mysql = 0; + qMySqlConnectionCount++; +} + +TQMYSQLDriver::~TQMYSQLDriver() +{ + qMySqlConnectionCount--; + if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser) + qServerEnd(); + + delete d; + if ( !qSqlOpenExtDict()->isEmpty() ) { + TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this ); + delete ext; + } +} + +bool TQMYSQLDriver::hasFeature( DriverFeature f ) const +{ + switch ( f ) { + case Transactions: +// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34 +#ifdef CLIENT_TRANSACTIONS + if ( d->mysql ) { + if ( ( d->mysql->server_capabilities & CLIENT_TRANSACTIONS ) == CLIENT_TRANSACTIONS ) + return TRUE; + } +#endif + return FALSE; + case QuerySize: + return TRUE; + case BLOB: + return TRUE; + case Unicode: + return FALSE; + default: + return FALSE; + } +} + +bool TQMYSQLDriver::open( const TQString&, + const TQString&, + const TQString&, + const TQString&, + int ) +{ + qWarning("TQMYSQLDriver::open(): This version of open() is no longer supported." ); + return FALSE; +} + +bool TQMYSQLDriver::open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) +{ + if ( isOpen() ) + close(); + + unsigned int optionFlags = 0; + + TQStringList raw = TQStringList::split( ';', connOpts ); + TQStringList opts; + TQStringList::ConstIterator it; + + // extract the real options from the string + for ( it = raw.begin(); it != raw.end(); ++it ) { + TQString tmp( *it ); + int idx; + if ( (idx = tmp.find( '=' )) != -1 ) { + TQString val( tmp.mid( idx + 1 ) ); + val.simplifyWhiteSpace(); + if ( val == "TRUE" || val == "1" ) + opts << tmp.left( idx ); + else + qWarning( "TQMYSQLDriver::open: Illegal connect option value '%s'", tmp.latin1() ); + } else { + opts << tmp; + } + } + + for ( it = opts.begin(); it != opts.end(); ++it ) { + TQString opt( (*it).upper() ); + if ( opt == "CLIENT_COMPRESS" ) + optionFlags |= CLIENT_COMPRESS; + else if ( opt == "CLIENT_FOUND_ROWS" ) + optionFlags |= CLIENT_FOUND_ROWS; + else if ( opt == "CLIENT_IGNORE_SPACE" ) + optionFlags |= CLIENT_IGNORE_SPACE; + else if ( opt == "CLIENT_INTERACTIVE" ) + optionFlags |= CLIENT_INTERACTIVE; + else if ( opt == "CLIENT_NO_SCHEMA" ) + optionFlags |= CLIENT_NO_SCHEMA; + else if ( opt == "CLIENT_ODBC" ) + optionFlags |= CLIENT_ODBC; + else if ( opt == "CLIENT_SSL" ) + optionFlags |= CLIENT_SSL; + else + qWarning( "TQMYSQLDriver::open: Unknown connect option '%s'", (*it).latin1() ); + } + + if ( (d->mysql = mysql_init((MYSQL*) 0)) && + mysql_real_connect( d->mysql, + host, + user, + password, + db.isNull() ? TQString("") : db, + (port > -1) ? port : 0, + NULL, + optionFlags ) ) + { + if ( !db.isEmpty() && mysql_select_db( d->mysql, db )) { + setLastError( qMakeError("Unable open database '" + db + "'", TQSqlError::Connection, d ) ); + mysql_close( d->mysql ); + setOpenError( TRUE ); + return FALSE; + } + } else { + setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) ); + mysql_close( d->mysql ); + setOpenError( TRUE ); + return FALSE; + } + setOpen( TRUE ); + setOpenError( FALSE ); + return TRUE; +} + +void TQMYSQLDriver::close() +{ + if ( isOpen() ) { + mysql_close( d->mysql ); + setOpen( FALSE ); + setOpenError( FALSE ); + } +} + +TQSqlQuery TQMYSQLDriver::createQuery() const +{ + return TQSqlQuery( new TQMYSQLResult( this ) ); +} + +TQStringList TQMYSQLDriver::tables( const TQString& typeName ) const +{ + TQStringList tl; + if ( !isOpen() ) + return tl; + if ( !typeName.isEmpty() && !(typeName.toInt() & (int)TQSql::Tables) ) + return tl; + + MYSQL_RES* tableRes = mysql_list_tables( d->mysql, NULL ); + MYSQL_ROW row; + int i = 0; + while ( tableRes && TRUE ) { + mysql_data_seek( tableRes, i ); + row = mysql_fetch_row( tableRes ); + if ( !row ) + break; + tl.append( TQString(row[0]) ); + i++; + } + mysql_free_result( tableRes ); + return tl; +} + +TQSqlIndex TQMYSQLDriver::primaryIndex( const TQString& tablename ) const +{ + TQSqlIndex idx; + if ( !isOpen() ) + return idx; + TQSqlQuery i = createQuery(); + TQString stmt( "show index from %1;" ); + TQSqlRecord fil = record( tablename ); + i.exec( stmt.arg( tablename ) ); + while ( i.isActive() && i.next() ) { + if ( i.value(2).toString() == "PRIMARY" ) { + idx.append( *fil.field( i.value(4).toString() ) ); + idx.setCursorName( i.value(0).toString() ); + idx.setName( i.value(2).toString() ); + } + } + return idx; +} + +TQSqlRecord TQMYSQLDriver::record( const TQString& tablename ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0); + if ( !r ) { + return fil; + } + MYSQL_FIELD* field; + while ( (field = mysql_fetch_field( r ))) { + TQSqlField f ( TQString( field->name ) , qDecodeMYSQLType( (int)field->type, field->flags ) ); + fil.append ( f ); + } + mysql_free_result( r ); + return fil; +} + +TQSqlRecord TQMYSQLDriver::record( const TQSqlQuery& query ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + if ( query.isActive() && query.isSelect() && query.driver() == this ) { + TQMYSQLResult* result = (TQMYSQLResult*)query.result(); + TQMYSQLResultPrivate* p = result->d; + if ( !mysql_errno( p->mysql ) ) { + for ( ;; ) { + MYSQL_FIELD* f = mysql_fetch_field( p->result ); + if ( f ) { + TQSqlField fi( TQString((const char*)f->name), qDecodeMYSQLType( f->type, f->flags ) ); + fil.append( fi ); + } else + break; + } + } + mysql_field_seek( p->result, 0 ); + } + return fil; +} + +TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQString& tablename ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0); + if ( !r ) { + return info; + } + MYSQL_FIELD* field; + while ( (field = mysql_fetch_field( r ))) { + info.append ( TQSqlFieldInfo( TQString( field->name ), + qDecodeMYSQLType( (int)field->type, field->flags ), + IS_NOT_NULL( field->flags ), + (int)field->length, + (int)field->decimals, + TQString( field->def ), + (int)field->type ) ); + } + mysql_free_result( r ); + return info; +} + +TQSqlRecordInfo TQMYSQLDriver::recordInfo( const TQSqlQuery& query ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + if ( query.isActive() && query.isSelect() && query.driver() == this ) { + TQMYSQLResult* result = (TQMYSQLResult*)query.result(); + TQMYSQLResultPrivate* p = result->d; + if ( !mysql_errno( p->mysql ) ) { + for ( ;; ) { + MYSQL_FIELD* field = mysql_fetch_field( p->result ); + if ( field ) { + info.append ( TQSqlFieldInfo( TQString( field->name ), + qDecodeMYSQLType( (int)field->type, field->flags ), + IS_NOT_NULL( field->flags ), + (int)field->length, + (int)field->decimals, + TQVariant(), + (int)field->type ) ); + + } else + break; + } + } + mysql_field_seek( p->result, 0 ); + } + return info; +} + +MYSQL* TQMYSQLDriver::mysql() +{ + return d->mysql; +} + +bool TQMYSQLDriver::beginTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQMYSQLDriver::beginTransaction: Database not open" ); +#endif + return FALSE; + } + if ( mysql_query( d->mysql, "BEGIN WORK" ) ) { + setLastError( qMakeError("Unable to begin transaction", TQSqlError::Statement, d ) ); + return FALSE; + } + return TRUE; +} + +bool TQMYSQLDriver::commitTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQMYSQLDriver::commitTransaction: Database not open" ); +#endif + return FALSE; + } + if ( mysql_query( d->mysql, "COMMIT" ) ) { + setLastError( qMakeError("Unable to commit transaction", TQSqlError::Statement, d ) ); + return FALSE; + } + return TRUE; +} + +bool TQMYSQLDriver::rollbackTransaction() +{ +#ifndef CLIENT_TRANSACTIONS + return FALSE; +#endif + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQMYSQLDriver::rollbackTransaction: Database not open" ); +#endif + return FALSE; + } + if ( mysql_query( d->mysql, "ROLLBACK" ) ) { + setLastError( qMakeError("Unable to rollback transaction", TQSqlError::Statement, d ) ); + return FALSE; + } + return TRUE; +} + +TQString TQMYSQLDriver::formatValue( const TQSqlField* field, bool trimStrings ) const +{ + TQString r; + if ( field->isNull() ) { + r = nullText(); + } else { + switch( field->type() ) { + case TQVariant::ByteArray: { + + const TQByteArray ba = field->value().toByteArray(); + // buffer has to be at least length*2+1 bytes + char* buffer = new char[ ba.size() * 2 + 1 ]; + /*uint escapedSize =*/ mysql_escape_string( buffer, ba.data(), ba.size() ); + r.append("'").append(buffer).append("'"); + delete[] buffer; + } + break; + case TQVariant::String: + case TQVariant::CString: { + // Escape '\' characters + r = TQSqlDriver::formatValue( field ); + r.replace( "\\", "\\\\" ); + break; + } + default: + r = TQSqlDriver::formatValue( field, trimStrings ); + } + } + return r; +} diff --git a/src/sql/drivers/mysql/qsql_mysql.h b/src/sql/drivers/mysql/qsql_mysql.h new file mode 100644 index 000000000..d47aa6ea9 --- /dev/null +++ b/src/sql/drivers/mysql/qsql_mysql.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Definition of MySQL driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQL_MYSQL_H +#define TQSQL_MYSQL_H + +#include <qsqldriver.h> +#include <qsqlresult.h> +#include <qsqlfield.h> +#include <qsqlindex.h> + +#if defined (Q_OS_WIN32) +#include <qt_windows.h> +#endif + +#include <mysql.h> + +#ifdef QT_PLUGIN +#define Q_EXPORT_SQLDRIVER_MYSQL +#else +#define Q_EXPORT_SQLDRIVER_MYSQL Q_EXPORT +#endif + +class TQMYSQLDriverPrivate; +class TQMYSQLResultPrivate; +class TQMYSQLDriver; +class TQSqlRecordInfo; + +class TQMYSQLResult : public TQSqlResult +{ + friend class TQMYSQLDriver; +public: + TQMYSQLResult( const TQMYSQLDriver* db ); + ~TQMYSQLResult(); + + MYSQL_RES* result(); +protected: + void cleanup(); + bool fetch( int i ); + bool fetchNext(); + bool fetchLast(); + bool fetchFirst(); + TQVariant data( int field ); + bool isNull( int field ); + bool reset ( const TQString& query ); + int size(); + int numRowsAffected(); +private: + TQMYSQLResultPrivate* d; +}; + +class Q_EXPORT_SQLDRIVER_MYSQL TQMYSQLDriver : public TQSqlDriver +{ + friend class TQMYSQLResult; +public: + TQMYSQLDriver( TQObject * parent=0, const char * name=0 ); + TQMYSQLDriver( MYSQL * con, TQObject * parent=0, const char * name=0 ); + ~TQMYSQLDriver(); + bool hasFeature( DriverFeature f ) const; + bool open( const TQString & db, + const TQString & user = TQString::null, + const TQString & password = TQString::null, + const TQString & host = TQString::null, + int port = -1 ); + void close(); + TQSqlQuery createQuery() const; + TQStringList tables( const TQString& user ) const; + TQSqlIndex primaryIndex( const TQString& tablename ) const; + TQSqlRecord record( const TQString& tablename ) const; + TQSqlRecord record( const TQSqlQuery& query ) const; + TQSqlRecordInfo recordInfo( const TQString& tablename ) const; + TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const; + TQString formatValue( const TQSqlField* field, + bool trimStrings ) const; + MYSQL* mysql(); + // ### remove me for 4.0 + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); + +protected: + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); +private: + void init(); + TQMYSQLDriverPrivate* d; +}; + + +#endif diff --git a/src/sql/drivers/odbc/debian_qsql_odbc.h b/src/sql/drivers/odbc/debian_qsql_odbc.h new file mode 100644 index 000000000..4b91f475c --- /dev/null +++ b/src/sql/drivers/odbc/debian_qsql_odbc.h @@ -0,0 +1,10 @@ +#ifdef UNICODE +typedef SQLWCHAR SQLTCHAR; +#else +typedef SQLCHAR SQLTCHAR; +#endif + +#define SQL_WCHAR (-8) +#define SQL_WVARCHAR (-9) +#define SQL_WLONGVARCHAR (-10) +#define SQL_C_WCHAR SQL_WCHAR diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp new file mode 100644 index 000000000..4d0f7969f --- /dev/null +++ b/src/sql/drivers/odbc/qsql_odbc.cpp @@ -0,0 +1,2035 @@ +/**************************************************************************** +** +** Implementation of ODBC driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsql_odbc.h" +#include <qsqlrecord.h> + +#if defined (Q_OS_WIN32) +#include <qt_windows.h> +#include <qapplication.h> +#endif +#include <qdatetime.h> +#include <private/qsqlextension_p.h> +#include <private/qinternal_p.h> +#include <stdlib.h> + +// undefine this to prevent initial check of the ODBC driver +#define ODBC_CHECK_DRIVER + +#if defined(Q_ODBC_VERSION_2) +//crude hack to get non-unicode capable driver managers to work +# undef UNICODE +# define SQLTCHAR SQLCHAR +# define SQL_C_WCHAR SQL_C_CHAR +#endif + +// newer platform SDKs use SQLLEN instead of SQLINTEGER +#ifdef SQLLEN +# define TQSQLLEN SQLLEN +#else +# define TQSQLLEN SQLINTEGER +#endif + +#ifdef SQLULEN +# define TQSQLULEN SQLULEN +#else +# define TQSQLULEN SQLUINTEGER +#endif + + +static const TQSQLLEN COLNAMESIZE = 256; +//Map TQt parameter types to ODBC types +static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT }; + +class TQODBCPrivate +{ +public: + TQODBCPrivate() + : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE) + { + sql_char_type = sql_varchar_type = sql_longvarchar_type = TQVariant::CString; + unicode = FALSE; + } + + SQLHANDLE hEnv; + SQLHANDLE hDbc; + SQLHANDLE hStmt; + + bool unicode; + bool useSchema; + TQVariant::Type sql_char_type; + TQVariant::Type sql_varchar_type; + TQVariant::Type sql_longvarchar_type; + + TQSqlRecordInfo rInf; + + bool checkDriver() const; + void checkUnicode(); + void checkSchemaUsage(); + bool setConnectionOptions( const TQString& connOpts ); + void splitTableQualifier(const TQString &qualifier, TQString &catalog, + TQString &schema, TQString &table); +}; + +class TQODBCPreparedExtension : public TQSqlExtension +{ +public: + TQODBCPreparedExtension( TQODBCResult * r ) + : result( r ) {} + + bool prepare( const TQString& query ) + { + return result->prepare( query ); + } + + bool exec() + { + return result->exec(); + } + + TQODBCResult * result; +}; + +TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict(); + +class TQODBCOpenExtension : public TQSqlOpenExtension +{ +public: + TQODBCOpenExtension( TQODBCDriver *dri ) + : TQSqlOpenExtension(), driver(dri) {} + ~TQODBCOpenExtension() {} + + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); +private: + TQODBCDriver *driver; +}; + +bool TQODBCOpenExtension::open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) +{ + return driver->open( db, user, password, host, port, connOpts ); +} + +static TQString qWarnODBCHandle(int handleType, SQLHANDLE handle) +{ + SQLINTEGER nativeCode_; + SQLSMALLINT msgLen; + SQLRETURN r = SQL_ERROR; + SQLTCHAR state_[SQL_SQLSTATE_SIZE+1]; + SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH]; + r = SQLGetDiagRec( handleType, + handle, + 1, + (SQLTCHAR*)state_, + &nativeCode_, + (SQLTCHAR*)description_, + SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */ + &msgLen); + if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) +#ifdef UNICODE + return TQString( (const TQChar*)description_, (uint)msgLen ); +#else + return TQString::fromLocal8Bit( (const char*)description_ ); +#endif + return TQString::null; +} + +static TQString qODBCWarn( const TQODBCPrivate* odbc) +{ + return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " " + + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " " + + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) ); +} + +static void qSqlWarning( const TQString& message, const TQODBCPrivate* odbc ) +{ +#ifdef QT_CHECK_RANGE + qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() ); +#endif +} + +static TQSqlError qMakeError( const TQString& err, int type, const TQODBCPrivate* p ) +{ + return TQSqlError( "TQODBC3: " + err, qODBCWarn(p), type ); +} + +static TQVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const TQODBCPrivate* p ) +{ + TQVariant::Type type = TQVariant::Invalid; + switch ( sqltype ) { + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: + type = TQVariant::Double; + break; + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_BIT: + case SQL_TINYINT: + type = TQVariant::Int; + break; + case SQL_BIGINT: + type = TQVariant::LongLong; + break; + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + type = TQVariant::ByteArray; + break; + case SQL_DATE: + case SQL_TYPE_DATE: + type = TQVariant::Date; + break; + case SQL_TIME: + case SQL_TYPE_TIME: + type = TQVariant::Time; + break; + case SQL_TIMESTAMP: + case SQL_TYPE_TIMESTAMP: + type = TQVariant::DateTime; + break; +#ifndef Q_ODBC_VERSION_2 + case SQL_WCHAR: + case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: + type = TQVariant::String; + break; +#endif + case SQL_CHAR: + type = p->sql_char_type; + break; + case SQL_VARCHAR: + type = p->sql_varchar_type; + break; + case SQL_LONGVARCHAR: + type = p->sql_longvarchar_type; + break; + default: + type = TQVariant::CString; + break; + } + return type; +} + +static TQString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE ) +{ + TQString fieldVal; + SQLRETURN r = SQL_ERROR; + TQSQLLEN lengthIndicator = 0; + + if ( colSize <= 0 ) { + colSize = 256; + } else if ( colSize > 65536 ) { // limit buffer size to 64 KB + colSize = 65536; + } else { + colSize++; // make sure there is room for more than the 0 termination + if ( unicode ) { + colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call + } + } + char* buf = new char[ colSize ]; + while ( TRUE ) { + r = SQLGetData( hStmt, + column+1, + unicode ? SQL_C_WCHAR : SQL_C_CHAR, + (SQLPOINTER)buf, + (TQSQLLEN)colSize, + &lengthIndicator ); + if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) { + if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) { + fieldVal = TQString::null; + isNull = TRUE; + break; + } + // if SQL_SUCCESS_WITH_INFO is returned, indicating that + // more data can be fetched, the length indicator does NOT + // contain the number of bytes returned - it contains the + // total number of bytes that CAN be fetched + // colSize-1: remove 0 termination when there is more data to fetch + int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator; + if ( unicode ) { + fieldVal += TQString( (TQChar*) buf, rSize / 2 ); + } else { + buf[ rSize ] = 0; + fieldVal += buf; + } + if ( lengthIndicator < colSize ) { + // workaround for Drivermanagers that don't return SQL_NO_DATA + break; + } + } else if ( r == SQL_NO_DATA ) { + break; + } else { +#ifdef QT_CHECK_RANGE + qWarning( "qGetStringData: Error while fetching data (%d)", r ); +#endif + fieldVal = TQString::null; + break; + } + } + delete[] buf; + return fieldVal; +} + +static TQByteArray qGetBinaryData( SQLHANDLE hStmt, int column, TQSQLLEN& lengthIndicator, bool& isNull ) +{ + TQByteArray fieldVal; + SQLSMALLINT colNameLen; + SQLSMALLINT colType; + TQSQLULEN colSize; + SQLSMALLINT colScale; + SQLSMALLINT nullable; + SQLRETURN r = SQL_ERROR; + + SQLTCHAR colName[COLNAMESIZE]; + r = SQLDescribeCol( hStmt, + column+1, + colName, + COLNAMESIZE, + &colNameLen, + &colType, + &colSize, + &colScale, + &nullable ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qWarning( "qGetBinaryData: Unable to describe column %d", column ); +#endif + // SQLDescribeCol may return 0 if size cannot be determined + if (!colSize) { + colSize = 256; + } + if ( colSize > 65536 ) { // read the field in 64 KB chunks + colSize = 65536; + } + char * buf = new char[ colSize ]; + while ( TRUE ) { + r = SQLGetData( hStmt, + column+1, + SQL_C_BINARY, + (SQLPOINTER) buf, + (TQSQLLEN)colSize, + &lengthIndicator ); + if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) { + if ( lengthIndicator == SQL_NULL_DATA ) { + isNull = TRUE; + break; + } else { + int rSize; + r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize; + if ( lengthIndicator == SQL_NO_TOTAL ) { // size cannot be determined + rSize = colSize; + } + // NB! This is not a memleak - the mem will be deleted by TQByteArray when + // no longer ref'd + char * tmp = (char *) malloc( rSize + fieldVal.size() ); + if ( fieldVal.size() ) { + memcpy( tmp, fieldVal.data(), fieldVal.size() ); + } + memcpy( tmp + fieldVal.size(), buf, rSize ); + fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize ); + + if ( r == SQL_SUCCESS ) { // the whole field was read in one chunk + break; + } + } + } else { + break; + } + } + delete [] buf; + return fieldVal; +} + +static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull ) +{ + TQSQLLEN intbuf = 0; + isNull = FALSE; + TQSQLLEN lengthIndicator = 0; + SQLRETURN r = SQLGetData( hStmt, + column+1, + SQL_C_SLONG, + (SQLPOINTER)&intbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) { + isNull = TRUE; + return 0; + } + return (int)intbuf; +} + +static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull ) +{ + SQLDOUBLE dblbuf; + TQSQLLEN lengthIndicator = 0; + isNull = FALSE; + SQLRETURN r = SQLGetData( hStmt, + column+1, + SQL_C_DOUBLE, + (SQLPOINTER)&dblbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) { + isNull = TRUE; + return 0.0; + } + + return (double) dblbuf; +} + +static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull ) +{ + SQLBIGINT lngbuf = Q_INT64_C( 0 ); + isNull = FALSE; + TQSQLLEN lengthIndicator = 0; + SQLRETURN r = SQLGetData( hStmt, + column+1, + SQL_C_SBIGINT, + (SQLPOINTER) &lngbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) + isNull = TRUE; + + return lngbuf; +} + +// creates a TQSqlFieldInfo from a valid hStmt generated +// by SQLColumns. The hStmt has to point to a valid position. +static TQSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const TQODBCPrivate* p ) +{ + bool isNull; + TQString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode ); + int type = qGetIntData( hStmt, 4, isNull ); // column type + int retquired = qGetIntData( hStmt, 10, isNull ); // nullable-flag + // retquired can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN + if ( retquired == SQL_NO_NULLS ) { + retquired = 1; + } else if ( retquired == SQL_NULLABLE ) { + retquired = 0; + } else { + retquired = -1; + } + int size = qGetIntData( hStmt, 6, isNull ); // column size + int prec = qGetIntData( hStmt, 8, isNull ); // precision + return TQSqlFieldInfo( fname, qDecodeODBCType( type, p ), retquired, size, prec, TQVariant(), type ); +} + +static TQSqlFieldInfo qMakeFieldInfo( const TQODBCPrivate* p, int i ) +{ + SQLSMALLINT colNameLen; + SQLSMALLINT colType; + TQSQLULEN colSize; + SQLSMALLINT colScale; + SQLSMALLINT nullable; + SQLRETURN r = SQL_ERROR; + SQLTCHAR colName[ COLNAMESIZE ]; + r = SQLDescribeCol( p->hStmt, + i+1, + colName, + (TQSQLULEN)COLNAMESIZE, + &colNameLen, + &colType, + &colSize, + &colScale, + &nullable); + + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( TQString("qMakeField: Unable to describe column %1").arg(i), p ); +#endif + return TQSqlFieldInfo(); + } +#ifdef UNICODE + TQString qColName( (const TQChar*)colName, (uint)colNameLen ); +#else + TQString qColName = TQString::fromLocal8Bit( (const char*)colName ); +#endif + // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN + int retquired = -1; + if ( nullable == SQL_NO_NULLS ) { + retquired = 1; + } else if ( nullable == SQL_NULLABLE ) { + retquired = 0; + } + TQVariant::Type type = qDecodeODBCType( colType, p ); + return TQSqlFieldInfo( qColName, + type, + retquired, + (int)colSize == 0 ? -1 : (int)colSize, + (int)colScale == 0 ? -1 : (int)colScale, + TQVariant(), + (int)colType ); +} + +bool TQODBCPrivate::setConnectionOptions( const TQString& connOpts ) +{ + // Set any connection attributes + TQStringList raw = TQStringList::split( ';', connOpts ); + TQStringList opts; + SQLRETURN r = SQL_SUCCESS; + TQMap<TQString, TQString> connMap; + for ( TQStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) { + TQString tmp( *it ); + int idx; + if ( (idx = tmp.find( '=' )) != -1 ) + connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace(); + else + qWarning( "TQODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() ); + } + if ( connMap.count() ) { + TQMap<TQString, TQString>::ConstIterator it; + TQString opt, val; + SQLUINTEGER v = 0; + for ( it = connMap.begin(); it != connMap.end(); ++it ) { + opt = it.key().upper(); + val = it.data().upper(); + r = SQL_SUCCESS; + if ( opt == "SQL_ATTR_ACCESS_MODE" ) { + if ( val == "SQL_MODE_READ_ONLY" ) { + v = SQL_MODE_READ_ONLY; + } else if ( val == "SQL_MODE_READ_WRITE" ) { + v = SQL_MODE_READ_WRITE; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = SQLSetConnectAttr( hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0 ); + } else if ( opt == "SQL_ATTR_CONNECTION_TIMEOUT" ) { + v = val.toUInt(); + r = SQLSetConnectAttr( hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0 ); + } else if ( opt == "SQL_ATTR_LOGIN_TIMEOUT" ) { + v = val.toUInt(); + r = SQLSetConnectAttr( hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0 ); + } else if ( opt == "SQL_ATTR_CURRENT_CATALOG" ) { + val.ucs2(); // 0 terminate + r = SQLSetConnectAttr( hDbc, SQL_ATTR_CURRENT_CATALOG, +#ifdef UNICODE + (SQLWCHAR*) val.unicode(), +#else + (SQLCHAR*) val.latin1(), +#endif + SQL_NTS ); + } else if ( opt == "SQL_ATTR_METADATA_ID" ) { + if ( val == "SQL_TRUE" ) { + v = SQL_TRUE; + } else if ( val == "SQL_FALSE" ) { + v = SQL_FALSE; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = SQLSetConnectAttr( hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0 ); + } else if ( opt == "SQL_ATTR_PACKET_SIZE" ) { + v = val.toUInt(); + r = SQLSetConnectAttr( hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0 ); + } else if ( opt == "SQL_ATTR_TRACEFILE" ) { + val.ucs2(); // 0 terminate + r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACEFILE, +#ifdef UNICODE + (SQLWCHAR*) val.unicode(), +#else + (SQLCHAR*) val.latin1(), +#endif + SQL_NTS ); + } else if ( opt == "SQL_ATTR_TRACE" ) { + if ( val == "SQL_OPT_TRACE_OFF" ) { + v = SQL_OPT_TRACE_OFF; + } else if ( val == "SQL_OPT_TRACE_ON" ) { + v = SQL_OPT_TRACE_ON; + } else { + qWarning( TQString( "TQODBCDriver::open: Unknown option value '%1'" ).arg( *it ) ); + break; + } + r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 ); + } +#ifdef QT_CHECK_RANGE + else { + qWarning( TQString("TQODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) ); + } +#endif + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( TQString("TQODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this ); +#endif + return FALSE; + } + } + } + return TRUE; +} + +void TQODBCPrivate::splitTableQualifier(const TQString & qualifier, TQString &catalog, + TQString &schema, TQString &table) +{ + if (!useSchema) { + table = qualifier; + return; + } + TQStringList l = TQStringList::split( ".", qualifier, TRUE ); + if ( l.count() > 3 ) + return; // can't possibly be a valid table qualifier + int i = 0, n = l.count(); + if ( n == 1 ) { + table = qualifier; + } else { + for ( TQStringList::Iterator it = l.begin(); it != l.end(); ++it ) { + if ( n == 3 ) { + if ( i == 0 ) { + catalog = *it; + } else if ( i == 1 ) { + schema = *it; + } else if ( i == 2 ) { + table = *it; + } + } else if ( n == 2 ) { + if ( i == 0 ) { + schema = *it; + } else if ( i == 1 ) { + table = *it; + } + } + i++; + } + } +} + +//////////////////////////////////////////////////////////////////////////// + +TQODBCResult::TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p ) +: TQSqlResult(db) +{ + d = new TQODBCPrivate(); + (*d) = (*p); + setExtension( new TQODBCPreparedExtension( this ) ); +} + +TQODBCResult::~TQODBCResult() +{ + if ( d->hStmt && driver()->isOpen() ) { + SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d ); +#endif + } + + delete d; +} + +bool TQODBCResult::reset ( const TQString& query ) +{ + setActive( FALSE ); + setAt( TQSql::BeforeFirst ); + SQLRETURN r; + + d->rInf.clear(); + // Always reallocate the statement handle - the statement attributes + // are not reset if SQLFreeStmt() is called which causes some problems. + if ( d->hStmt ) { + r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to free statement handle", d ); +#endif + return FALSE; + } + } + r = SQLAllocHandle( SQL_HANDLE_STMT, + d->hDbc, + &d->hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to allocate statement handle", d ); +#endif + return FALSE; + } + + if ( isForwardOnly() ) { + r = SQLSetStmtAttr( d->hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER ); + } else { + r = SQLSetStmtAttr( d->hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_STATIC, + SQL_IS_UINTEGER ); + } + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d ); +#endif + return FALSE; + } + +#ifdef UNICODE + r = SQLExecDirect( d->hStmt, + (SQLWCHAR*) query.unicode(), + (SQLINTEGER) query.length() ); +#else + TQCString query8 = query.local8Bit(); + r = SQLExecDirect( d->hStmt, + (SQLCHAR*) query8.data(), + (SQLINTEGER) query8.length() ); +#endif + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { + setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) ); + return FALSE; + } + SQLSMALLINT count; + r = SQLNumResultCols( d->hStmt, &count ); + if ( count ) { + setSelect( TRUE ); + for ( int i = 0; i < count; ++i ) { + d->rInf.append( qMakeFieldInfo( d, i ) ); + } + } else { + setSelect( FALSE ); + } + setActive( TRUE ); + return TRUE; +} + +bool TQODBCResult::fetch(int i) +{ + if ( isForwardOnly() && i < at() ) + return FALSE; + if ( i == at() ) + return TRUE; + fieldCache.clear(); + nullCache.clear(); + int actualIdx = i + 1; + if ( actualIdx <= 0 ) { + setAt( TQSql::BeforeFirst ); + return FALSE; + } + SQLRETURN r; + if ( isForwardOnly() ) { + bool ok = TRUE; + while ( ok && i > at() ) + ok = fetchNext(); + return ok; + } else { + r = SQLFetchScroll( d->hStmt, + SQL_FETCH_ABSOLUTE, + actualIdx ); + } + if ( r != SQL_SUCCESS ){ + return FALSE; + } + setAt( i ); + return TRUE; +} + +bool TQODBCResult::fetchNext() +{ + SQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + r = SQLFetchScroll( d->hStmt, + SQL_FETCH_NEXT, + 0 ); + if ( r != SQL_SUCCESS ) + return FALSE; + setAt( at() + 1 ); + return TRUE; +} + +bool TQODBCResult::fetchFirst() +{ + if ( isForwardOnly() && at() != TQSql::BeforeFirst ) + return FALSE; + SQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + if ( isForwardOnly() ) { + return fetchNext(); + } + r = SQLFetchScroll( d->hStmt, + SQL_FETCH_FIRST, + 0 ); + if ( r != SQL_SUCCESS ) + return FALSE; + setAt( 0 ); + return TRUE; +} + +bool TQODBCResult::fetchPrior() +{ + if ( isForwardOnly() ) + return FALSE; + SQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + r = SQLFetchScroll( d->hStmt, + SQL_FETCH_PRIOR, + 0 ); + if ( r != SQL_SUCCESS ) + return FALSE; + setAt( at() - 1 ); + return TRUE; +} + +bool TQODBCResult::fetchLast() +{ + SQLRETURN r; + fieldCache.clear(); + nullCache.clear(); + + if ( isForwardOnly() ) { + // cannot seek to last row in forwardOnly mode, so we have to use brute force + int i = at(); + if ( i == TQSql::AfterLast ) + return FALSE; + if ( i == TQSql::BeforeFirst ) + i = 0; + while ( fetchNext() ) + ++i; + setAt( i ); + return TRUE; + } + + r = SQLFetchScroll( d->hStmt, + SQL_FETCH_LAST, + 0 ); + if ( r != SQL_SUCCESS ) { + return FALSE; + } + SQLINTEGER currRow; + r = SQLGetStmtAttr( d->hStmt, + SQL_ROW_NUMBER, + &currRow, + SQL_IS_INTEGER, + 0 ); + if ( r != SQL_SUCCESS ) + return FALSE; + setAt( currRow-1 ); + return TRUE; +} + +TQVariant TQODBCResult::data( int field ) +{ + if ( field >= (int) d->rInf.count() ) { + qWarning( "TQODBCResult::data: column %d out of range", field ); + return TQVariant(); + } + if ( fieldCache.contains( field ) ) + return fieldCache[ field ]; + SQLRETURN r(0); + TQSQLLEN lengthIndicator = 0; + bool isNull = FALSE; + int current = fieldCache.count(); + for ( ; current < (field + 1); ++current ) { + const TQSqlFieldInfo info = d->rInf[ current ]; + switch ( info.type() ) { + case TQVariant::LongLong: + fieldCache[ current ] = TQVariant( (Q_LLONG) qGetBigIntData( d->hStmt, current, isNull ) ); + nullCache[ current ] = isNull; + break; + case TQVariant::Int: + fieldCache[ current ] = TQVariant( qGetIntData( d->hStmt, current, isNull ) ); + nullCache[ current ] = isNull; + break; + case TQVariant::Date: + DATE_STRUCT dbuf; + r = SQLGetData( d->hStmt, + current+1, + SQL_C_DATE, + (SQLPOINTER)&dbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) { + fieldCache[ current ] = TQVariant( TQDate( dbuf.year, dbuf.month, dbuf.day ) ); + nullCache[ current ] = FALSE; + } else { + fieldCache[ current ] = TQVariant( TQDate() ); + nullCache[ current ] = TRUE; + } + break; + case TQVariant::Time: + TIME_STRUCT tbuf; + r = SQLGetData( d->hStmt, + current+1, + SQL_C_TIME, + (SQLPOINTER)&tbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) { + fieldCache[ current ] = TQVariant( TQTime( tbuf.hour, tbuf.minute, tbuf.second ) ); + nullCache[ current ] = FALSE; + } else { + fieldCache[ current ] = TQVariant( TQTime() ); + nullCache[ current ] = TRUE; + } + break; + case TQVariant::DateTime: + TIMESTAMP_STRUCT dtbuf; + r = SQLGetData( d->hStmt, + current+1, + SQL_C_TIMESTAMP, + (SQLPOINTER)&dtbuf, + (TQSQLLEN)0, + &lengthIndicator ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) { + fieldCache[ current ] = TQVariant( TQDateTime( TQDate( dtbuf.year, dtbuf.month, dtbuf.day ), TQTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) ); + nullCache[ current ] = FALSE; + } else { + fieldCache[ current ] = TQVariant( TQDateTime() ); + nullCache[ current ] = TRUE; + } + break; + case TQVariant::ByteArray: { + isNull = FALSE; + TQByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull ); + fieldCache[ current ] = TQVariant( val ); + nullCache[ current ] = isNull; + break; } + case TQVariant::String: + isNull = FALSE; + fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current, + info.length(), isNull, TRUE ) ); + nullCache[ current ] = isNull; + break; + case TQVariant::Double: + if ( info.typeID() == SQL_DECIMAL || info.typeID() == SQL_NUMERIC ) + // bind Double values as string to prevent loss of precision + fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current, + info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma + else + fieldCache[ current ] = TQVariant( qGetDoubleData( d->hStmt, current, isNull ) ); + nullCache[ current ] = isNull; + break; + case TQVariant::CString: + default: + isNull = FALSE; + fieldCache[ current ] = TQVariant( qGetStringData( d->hStmt, current, + info.length(), isNull, FALSE ) ); + nullCache[ current ] = isNull; + break; + } + } + return fieldCache[ --current ]; +} + +bool TQODBCResult::isNull( int field ) +{ + if ( !fieldCache.contains( field ) ) { + // since there is no good way to find out whether the value is NULL + // without fetching the field we'll fetch it here. + // (data() also sets the NULL flag) + data( field ); + } + return nullCache[ field ]; +} + +int TQODBCResult::size() +{ + return -1; +} + +int TQODBCResult::numRowsAffected() +{ + TQSQLLEN affectedRowCount(0); + SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount ); + if ( r == SQL_SUCCESS ) + return affectedRowCount; +#ifdef QT_CHECK_RANGE + else + qSqlWarning( "TQODBCResult::numRowsAffected: Unable to count affected rows", d ); +#endif + return -1; +} + +bool TQODBCResult::prepare( const TQString& query ) +{ + setActive( FALSE ); + setAt( TQSql::BeforeFirst ); + SQLRETURN r; + + d->rInf.clear(); + if ( d->hStmt ) { + r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to close statement", d ); +#endif + return FALSE; + } + } + r = SQLAllocHandle( SQL_HANDLE_STMT, + d->hDbc, + &d->hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to allocate statement handle", d ); +#endif + return FALSE; + } + + if ( isForwardOnly() ) { + r = SQLSetStmtAttr( d->hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER ); + } else { + r = SQLSetStmtAttr( d->hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_STATIC, + SQL_IS_UINTEGER ); + } + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d ); +#endif + return FALSE; + } + +#ifdef UNICODE + r = SQLPrepare( d->hStmt, + (SQLWCHAR*) query.unicode(), + (SQLINTEGER) query.length() ); +#else + TQCString query8 = query.local8Bit(); + r = SQLPrepare( d->hStmt, + (SQLCHAR*) query8.data(), + (SQLINTEGER) query8.length() ); +#endif + + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::prepare: Unable to prepare statement", d ); +#endif + return FALSE; + } + return TRUE; +} + +bool TQODBCResult::exec() +{ + SQLRETURN r; + TQPtrList<TQVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit + tmpStorage.setAutoDelete( TRUE ); + + setActive( FALSE ); + setAt( TQSql::BeforeFirst ); + d->rInf.clear(); + + if ( !d->hStmt ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCResult::exec: No statement handle available", d ); +#endif + return FALSE; + } else { + r = SQLFreeStmt( d->hStmt, SQL_CLOSE ); + if ( r != SQL_SUCCESS ) { + qSqlWarning( "TQODBCResult::exec: Unable to close statement handle", d ); + return FALSE; + } + } + + // bind parameters - only positional binding allowed + if ( extension()->index.count() > 0 ) { + TQMap<int, TQString>::Iterator it; + int para = 1; + TQVariant val; + for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) { + val = extension()->values[ it.data() ].value; + TQSQLLEN *ind = new TQSQLLEN( SQL_NTS ); + tmpStorage.append( qAutoDeleter(ind) ); + if ( val.isNull() ) { + *ind = SQL_NULL_DATA; + } + switch ( val.type() ) { + case TQVariant::Date: { + DATE_STRUCT * dt = new DATE_STRUCT; + tmpStorage.append( qAutoDeleter(dt) ); + TQDate qdt = val.toDate(); + dt->year = qdt.year(); + dt->month = qdt.month(); + dt->day = qdt.day(); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_DATE, + SQL_DATE, + 0, + 0, + (void *) dt, + (TQSQLLEN)0, + *ind == SQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::Time: { + TIME_STRUCT * dt = new TIME_STRUCT; + tmpStorage.append( qAutoDeleter(dt) ); + TQTime qdt = val.toTime(); + dt->hour = qdt.hour(); + dt->minute = qdt.minute(); + dt->second = qdt.second(); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_TIME, + SQL_TIME, + 0, + 0, + (void *) dt, + (TQSQLLEN)0, + *ind == SQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::DateTime: { + TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT; + tmpStorage.append( qAutoDeleter(dt) ); + TQDateTime qdt = val.toDateTime(); + dt->year = qdt.date().year(); + dt->month = qdt.date().month(); + dt->day = qdt.date().day(); + dt->hour = qdt.time().hour(); + dt->minute = qdt.time().minute(); + dt->second = qdt.time().second(); + dt->fraction = 0; + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_TIMESTAMP, + SQL_TIMESTAMP, + 0, + 0, + (void *) dt, + (TQSQLLEN)0, + *ind == SQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::Int: { + int * v = new int( val.toInt() ); + tmpStorage.append( qAutoDeleter(v) ); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_SLONG, + SQL_INTEGER, + 0, + 0, + (void *) v, + (TQSQLLEN)0, + *ind == SQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::Double: { + double * v = new double( val.toDouble() ); + tmpStorage.append( qAutoDeleter(v) ); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_DOUBLE, + SQL_DOUBLE, + 0, + 0, + (void *) v, + (TQSQLLEN)0, + *ind == SQL_NULL_DATA ? ind : NULL ); + break; } + case TQVariant::ByteArray: { + if ( *ind != SQL_NULL_DATA ) { + *ind = val.asByteArray().size(); + } + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_BINARY, + SQL_LONGVARBINARY, + val.asByteArray().size(), + 0, + (void *) val.asByteArray().data(), + (TQSQLLEN)val.asByteArray().size(), + ind ); + break; } +#ifndef Q_ODBC_VERSION_2 + case TQVariant::String: + if ( d->unicode ) { + TQString * str = new TQString( val.asString() ); + str->ucs2(); + int len = str->length()*2; + tmpStorage.append( qAutoDeleter(str) ); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_WCHAR, + len > 8000 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, + len > 8000 ? len : 0, + 0, + (void *) str->unicode(), + (TQSQLLEN) len, + ind ); + break; + } +#endif + // fall through + default: { + TQCString * str = new TQCString( val.asString().local8Bit() ); + tmpStorage.append( qAutoDeleter(str) ); + r = SQLBindParameter( d->hStmt, + para, + qParamType[ (int)extension()->values[ it.data() ].typ ], + SQL_C_CHAR, + str->length() > 4000 ? SQL_LONGVARCHAR : SQL_VARCHAR, + str->length() + 1, + 0, + (void *) str->data(), + (TQSQLLEN)(str->length() + 1), + ind ); + break; } + } + para++; + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() ); +#endif + setLastError( qMakeError( "Unable to bind variable", TQSqlError::Statement, d ) ); + return FALSE; + } + } + } + r = SQLExecute( d->hStmt ); + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() ); +#endif + setLastError( qMakeError( "Unable to execute statement", TQSqlError::Statement, d ) ); + return FALSE; + } + SQLSMALLINT count; + r = SQLNumResultCols( d->hStmt, &count ); + if ( count ) { + setSelect( TRUE ); + for ( int i = 0; i < count; ++i ) { + d->rInf.append( qMakeFieldInfo( d, i ) ); + } + } else { + setSelect( FALSE ); + } + setActive( TRUE ); + + //get out parameters + if ( extension()->index.count() > 0 ) { + TQMap<int, TQString>::Iterator it; + for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) { + + SQLINTEGER* indPtr = qAutoDeleterData( (TQAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() ); + if ( !indPtr ) + return FALSE; + bool isNull = (*indPtr == SQL_NULL_DATA); + tmpStorage.removeFirst(); + + TQVariant::Type type = extension()->values[ it.data() ].value.type(); + if ( isNull ) { + TQVariant v; + v.cast(type); + extension()->values[ it.data() ].value = v; + if (type != TQVariant::ByteArray) + tmpStorage.removeFirst(); + continue; + } + + switch (type) { + case TQVariant::Date: { + DATE_STRUCT * ds = qAutoDeleterData( (TQAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( TQDate( ds->year, ds->month, ds->day ) ); + break; } + case TQVariant::Time: { + TIME_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( TQTime( dt->hour, dt->minute, dt->second ) ); + break; } + case TQVariant::DateTime: { + TIMESTAMP_STRUCT * dt = qAutoDeleterData( (TQAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( TQDateTime( TQDate( dt->year, dt->month, dt->day ), + TQTime( dt->hour, dt->minute, dt->second ) ) ); + break; } + case TQVariant::Int: { + int * v = qAutoDeleterData( (TQAutoDeleter<int>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( *v ); + break; } + case TQVariant::Double: { + double * v = qAutoDeleterData( (TQAutoDeleter<double>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( *v ); + break; } + case TQVariant::ByteArray: + break; + case TQVariant::String: + if ( d->unicode ) { + TQString * str = qAutoDeleterData( (TQAutoDeleter<TQString>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( *str ); + break; + } + // fall through + default: { + TQCString * str = qAutoDeleterData( (TQAutoDeleter<TQCString>*)tmpStorage.getFirst() ); + extension()->values[ it.data() ].value = TQVariant( *str ); + break; } + } + if (type != TQVariant::ByteArray) + tmpStorage.removeFirst(); + } + } + + return TRUE; +} + +//////////////////////////////////////// + + +TQODBCDriver::TQODBCDriver( TQObject * parent, const char * name ) + : TQSqlDriver(parent,name ? name : "TQODBC") +{ + init(); +} + +TQODBCDriver::TQODBCDriver( SQLHANDLE env, SQLHANDLE con, TQObject * parent, const char * name ) + : TQSqlDriver(parent,name ? name : "TQODBC") +{ + init(); + d->hEnv = env; + d->hDbc = con; + if ( env && con ) { + setOpen( TRUE ); + setOpenError( FALSE ); + } +} + +void TQODBCDriver::init() +{ + qSqlOpenExtDict()->insert( this, new TQODBCOpenExtension(this) ); + d = new TQODBCPrivate(); +} + +TQODBCDriver::~TQODBCDriver() +{ + cleanup(); + delete d; + if ( !qSqlOpenExtDict()->isEmpty() ) { + TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this ); + delete ext; + } +} + +bool TQODBCDriver::hasFeature( DriverFeature f ) const +{ + switch ( f ) { + case Transactions: { + if ( !d->hDbc ) + return FALSE; + SQLUSMALLINT txn; + SQLSMALLINT t; + int r = SQLGetInfo( d->hDbc, + (SQLUSMALLINT)SQL_TXN_CAPABLE, + &txn, + sizeof(txn), + &t); + if ( r != SQL_SUCCESS || txn == SQL_TC_NONE ) + return FALSE; + else + return TRUE; + } + case QuerySize: + return FALSE; + case BLOB: + return TRUE; + case Unicode: + return d->unicode; + case PreparedQueries: + return TRUE; + case PositionalPlaceholders: + return TRUE; + default: + return FALSE; + } +} + +bool TQODBCDriver::open( const TQString&, + const TQString&, + const TQString&, + const TQString&, + int ) +{ + qWarning("TQODBCDriver::open(): This version of open() is no longer supported." ); + return FALSE; +} + +bool TQODBCDriver::open( const TQString & db, + const TQString & user, + const TQString & password, + const TQString &, + int, + const TQString& connOpts ) +{ + if ( isOpen() ) + close(); + SQLRETURN r; + r = SQLAllocHandle( SQL_HANDLE_ENV, + SQL_NULL_HANDLE, + &d->hEnv); + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::open: Unable to allocate environment", d ); +#endif + setOpenError( TRUE ); + return FALSE; + } + r = SQLSetEnvAttr( d->hEnv, + SQL_ATTR_ODBC_VERSION, + (SQLPOINTER)SQL_OV_ODBC2, + SQL_IS_UINTEGER ); + r = SQLAllocHandle( SQL_HANDLE_DBC, + d->hEnv, + &d->hDbc); + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::open: Unable to allocate connection", d ); +#endif + setOpenError( TRUE ); + return FALSE; + } + + if ( !d->setConnectionOptions( connOpts ) ) + return FALSE; + + // Create the connection string + TQString connTQStr; + // support the "DRIVER={SQL SERVER};SERVER=blah" syntax + if ( db.contains(".dsn") ) + connTQStr = "FILEDSN=" + db; + else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) ) + connTQStr = db; + else + connTQStr = "DSN=" + db; + connTQStr += ";UID=" + user + ";PWD=" + password; + SQLSMALLINT cb; + SQLTCHAR connOut[1024]; + r = SQLDriverConnect( d->hDbc, + NULL, +#ifdef UNICODE + (SQLWCHAR*)connTQStr.unicode(), +#else + (SQLCHAR*)connTQStr.latin1(), +#endif + (SQLSMALLINT)connTQStr.length(), + connOut, + 1024, + &cb, + SQL_DRIVER_NOPROMPT ); + if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { + setLastError( qMakeError( "Unable to connect", TQSqlError::Connection, d ) ); + setOpenError( TRUE ); + return FALSE; + } + + if ( !d->checkDriver() ) { + setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", TQSqlError::Connection, d ) ); + setOpenError( TRUE ); + return FALSE; + } + + d->checkUnicode(); + d->checkSchemaUsage(); + + setOpen( TRUE ); + setOpenError( FALSE ); + return TRUE; +} + +void TQODBCDriver::close() +{ + cleanup(); + setOpen( FALSE ); + setOpenError( FALSE ); +} + +void TQODBCDriver::cleanup() +{ + SQLRETURN r; + if ( !d ) + return; + + if( d->hDbc ) { + // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect + if ( isOpen() ) { + r = SQLDisconnect( d->hDbc ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::disconnect: Unable to disconnect datasource", d ); +#endif + } + + r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::cleanup: Unable to free connection handle", d ); +#endif + d->hDbc = 0; + } + + if ( d->hEnv ) { + r = SQLFreeHandle( SQL_HANDLE_ENV, d->hEnv ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::cleanup: Unable to free environment handle", d ); +#endif + d->hEnv = 0; + } +} + +// checks whether the server can return char, varchar and longvarchar +// as two byte unicode characters +void TQODBCPrivate::checkUnicode() +{ +#if defined(Q_WS_WIN) + if ( !qt_winunicode ) { + unicode = FALSE; + return; + } +#endif + SQLRETURN r; + SQLUINTEGER fFunc; + + unicode = FALSE; + r = SQLGetInfo( hDbc, + SQL_CONVERT_CHAR, + (SQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WCHAR ) ) { + sql_char_type = TQVariant::String; + unicode = TRUE; + } + + r = SQLGetInfo( hDbc, + SQL_CONVERT_VARCHAR, + (SQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WVARCHAR ) ) { + sql_varchar_type = TQVariant::String; + unicode = TRUE; + } + + r = SQLGetInfo( hDbc, + SQL_CONVERT_LONGVARCHAR, + (SQLPOINTER)&fFunc, + sizeof(fFunc), + NULL ); + if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WLONGVARCHAR ) ) { + sql_longvarchar_type = TQVariant::String; + unicode = TRUE; + } +} + +bool TQODBCPrivate::checkDriver() const +{ +#ifdef ODBC_CHECK_DRIVER + // do not query for SQL_API_SQLFETCHSCROLL because it can't be used at this time + static const SQLUSMALLINT reqFunc[] = { + SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS, + SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT, + SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0 + }; + + // these functions are optional + static const SQLUSMALLINT optFunc[] = { + SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0 + }; + + SQLRETURN r; + SQLUSMALLINT sup; + + + int i; + // check the retquired functions + for ( i = 0; reqFunc[ i ] != 0; ++i ) { + + r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup ); + +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) { + qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this ); + return FALSE; + } +#endif + if ( sup == SQL_FALSE ) { +#ifdef QT_CHECK_RANGE + qWarning ( "TQODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). " + "Please look at the TQt SQL Module Driver documentation for more information.", reqFunc[ i ] ); +#endif + return FALSE; + } + } + + // these functions are optional and just generate a warning + for ( i = 0; optFunc[ i ] != 0; ++i ) { + + r = SQLGetFunctions( hDbc, optFunc[ i ], &sup ); + +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) { + qSqlWarning( "TQODBCDriver::checkDriver: Cannot get list of supported functions", this ); + return FALSE; + } +#endif + if ( sup == SQL_FALSE ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] ); +#endif + return TRUE; + } + } +#endif //ODBC_CHECK_DRIVER + + return TRUE; +} + +void TQODBCPrivate::checkSchemaUsage() +{ + SQLRETURN r; + SQLUINTEGER val; + + r = SQLGetInfo(hDbc, + SQL_SCHEMA_USAGE, + (SQLPOINTER) &val, + sizeof(val), + NULL); + if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) + useSchema = (val != 0); +} + +TQSqlQuery TQODBCDriver::createQuery() const +{ + return TQSqlQuery( new TQODBCResult( this, d ) ); +} + +bool TQODBCDriver::beginTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning(" TQODBCDriver::beginTransaction: Database not open" ); +#endif + return FALSE; + } + SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF); + SQLRETURN r = SQLSetConnectAttr( d->hDbc, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)ac, + sizeof(ac) ); + if ( r != SQL_SUCCESS ) { + setLastError( qMakeError( "Unable to disable autocommit", TQSqlError::Transaction, d ) ); + return FALSE; + } + return TRUE; +} + +bool TQODBCDriver::commitTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning(" TQODBCDriver::commitTransaction: Database not open" ); +#endif + return FALSE; + } + SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC, + d->hDbc, + SQL_COMMIT ); + if ( r != SQL_SUCCESS ) { + setLastError( qMakeError("Unable to commit transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + return endTrans(); +} + +bool TQODBCDriver::rollbackTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning(" TQODBCDriver::rollbackTransaction: Database not open" ); +#endif + return FALSE; + } + SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC, + d->hDbc, + SQL_ROLLBACK ); + if ( r != SQL_SUCCESS ) { + setLastError( qMakeError( "Unable to rollback transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + return endTrans(); +} + +bool TQODBCDriver::endTrans() +{ + SQLUINTEGER ac(SQL_AUTOCOMMIT_ON); + SQLRETURN r = SQLSetConnectAttr( d->hDbc, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)ac, + sizeof(ac)); + if ( r != SQL_SUCCESS ) { + setLastError( qMakeError( "Unable to enable autocommit", TQSqlError::Transaction, d ) ); + return FALSE; + } + return TRUE; +} + +TQStringList TQODBCDriver::tables( const TQString& typeName ) const +{ + TQStringList tl; + if ( !isOpen() ) + return tl; + int type = typeName.toInt(); + SQLHANDLE hStmt; + + SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::tables: Unable to allocate handle", d ); +#endif + return tl; + } + r = SQLSetStmtAttr( hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER ); + TQString tableType; + if ( typeName.isEmpty() || ((type & (int)TQSql::Tables) == (int)TQSql::Tables) ) + tableType += "TABLE,"; + if ( (type & (int)TQSql::Views) == (int)TQSql::Views ) + tableType += "VIEW,"; + if ( (type & (int)TQSql::SystemTables) == (int)TQSql::SystemTables ) + tableType += "SYSTEM TABLE,"; + if ( tableType.isEmpty() ) + return tl; + tableType.truncate( tableType.length() - 1 ); + + r = SQLTables( hStmt, + NULL, + 0, + NULL, + 0, + NULL, + 0, +#ifdef UNICODE + (SQLWCHAR*)tableType.unicode(), +#else + (SQLCHAR*)tableType.latin1(), +#endif + tableType.length() /* characters, not bytes */ ); + +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::tables Unable to execute table list", d ); +#endif + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0); + while ( r == SQL_SUCCESS ) { + bool isNull; + TQString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->unicode ); + tl.append( fieldVal ); + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0); + } + + r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); + if ( r!= SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d ); + return tl; +} + +TQSqlIndex TQODBCDriver::primaryIndex( const TQString& tablename ) const +{ + TQSqlIndex index( tablename ); + if ( !isOpen() ) + return index; + bool usingSpecialColumns = FALSE; + TQSqlRecord rec = record( tablename ); + + SQLHANDLE hStmt; + SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::primaryIndex: Unable to list primary key", d ); +#endif + return index; + } + TQString catalog, schema, table; + d->splitTableQualifier( tablename, catalog, schema, table ); + r = SQLSetStmtAttr( hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER ); + r = SQLPrimaryKeys( hStmt, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(), +#else + catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(), +#else + schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (SQLWCHAR*)table.unicode(), +#else + (SQLCHAR*)table.latin1(), +#endif + table.length() /* in characters, not in bytes */); + + // if the SQLPrimaryKeys() call does not succeed (e.g the driver + // does not support it) - try an alternative method to get hold of + // the primary index (e.g MS Access and FoxPro) + if ( r != SQL_SUCCESS ) { + r = SQLSpecialColumns( hStmt, + SQL_BEST_ROWID, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(), +#else + catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(), +#else + schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (SQLWCHAR*)table.unicode(), +#else + (SQLCHAR*)table.latin1(), +#endif + + table.length(), + SQL_SCOPE_CURROW, + SQL_NULLABLE ); + + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::primaryIndex: Unable to execute primary key list", d ); +#endif + } else { + usingSpecialColumns = TRUE; + } + } + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0 ); + bool isNull; + int fakeId = 0; + TQString cName, idxName; + // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop + while ( r == SQL_SUCCESS ) { + if ( usingSpecialColumns ) { + cName = qGetStringData( hStmt, 1, -1, isNull, d->unicode ); // column name + idxName = TQString::number( fakeId++ ); // invent a fake index name + } else { + cName = qGetStringData( hStmt, 3, -1, isNull, d->unicode ); // column name + idxName = qGetStringData( hStmt, 5, -1, isNull, d->unicode ); // pk index name + } + TQSqlField *fld = rec.field(cName); + if (fld) + index.append(*fld); + index.setName( idxName ); + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0 ); + } + r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); + if ( r!= SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver: Unable to free statement handle" + TQString::number(r), d ); + return index; +} + +TQSqlRecord TQODBCDriver::record( const TQString& tablename ) const +{ + return recordInfo( tablename ).toRecord(); +} + +TQSqlRecord TQODBCDriver::record( const TQSqlQuery& query ) const +{ + return recordInfo( query ).toRecord(); +} + +TQSqlRecordInfo TQODBCDriver::recordInfo( const TQString& tablename ) const +{ + TQSqlRecordInfo fil; + if ( !isOpen() ) + return fil; + + SQLHANDLE hStmt; + TQString catalog, schema, table; + d->splitTableQualifier( tablename, catalog, schema, table ); + SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT, + d->hDbc, + &hStmt ); + if ( r != SQL_SUCCESS ) { +#ifdef QT_CHECK_RANGE + qSqlWarning( "TQODBCDriver::record: Unable to allocate handle", d ); +#endif + return fil; + } + r = SQLSetStmtAttr( hStmt, + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER ); + r = SQLColumns( hStmt, +#ifdef UNICODE + catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(), +#else + catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(), +#endif + catalog.length(), +#ifdef UNICODE + schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(), +#else + schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(), +#endif + schema.length(), +#ifdef UNICODE + (SQLWCHAR*)table.unicode(), +#else + (SQLCHAR*)table.latin1(), +#endif + table.length(), + NULL, + 0 ); +#ifdef QT_CHECK_RANGE + if ( r != SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver::record: Unable to execute column list", d ); +#endif + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0); + // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop + while ( r == SQL_SUCCESS ) { + + fil.append( qMakeFieldInfo( hStmt, d ) ); + + r = SQLFetchScroll( hStmt, + SQL_FETCH_NEXT, + 0); + } + + r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); + if ( r!= SQL_SUCCESS ) + qSqlWarning( "TQODBCDriver: Unable to free statement handle " + TQString::number(r), d ); + + return fil; +} + +TQSqlRecordInfo TQODBCDriver::recordInfo( const TQSqlQuery& query ) const +{ + TQSqlRecordInfo fil; + if ( !isOpen() ) + return fil; + if ( query.isActive() && query.driver() == this ) { + TQODBCResult* result = (TQODBCResult*)query.result(); + fil = result->d->rInf; + } + return fil; +} + +SQLHANDLE TQODBCDriver::environment() +{ + return d->hEnv; +} + +SQLHANDLE TQODBCDriver::connection() +{ + return d->hDbc; +} + +TQString TQODBCDriver::formatValue( const TQSqlField* field, + bool trimStrings ) const +{ + TQString r; + if ( field->isNull() ) { + r = nullText(); + } else if ( field->type() == TQVariant::DateTime ) { + // Use an escape sequence for the datetime fields + if ( field->value().toDateTime().isValid() ){ + TQDate dt = field->value().toDateTime().date(); + TQTime tm = field->value().toDateTime().time(); + // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10 + r = "{ ts '" + + TQString::number(dt.year()) + "-" + + TQString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" + + TQString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " + + tm.toString() + + "' }"; + } else + r = nullText(); + } else if ( field->type() == TQVariant::ByteArray ) { + TQByteArray ba = field->value().toByteArray(); + TQString res; + static const char hexchars[] = "0123456789abcdef"; + for ( uint i = 0; i < ba.size(); ++i ) { + uchar s = (uchar) ba[(int)i]; + res += hexchars[s >> 4]; + res += hexchars[s & 0x0f]; + } + r = "0x" + res; + } else { + r = TQSqlDriver::formatValue( field, trimStrings ); + } + return r; +} diff --git a/src/sql/drivers/odbc/qsql_odbc.h b/src/sql/drivers/odbc/qsql_odbc.h new file mode 100644 index 000000000..b65acf6ca --- /dev/null +++ b/src/sql/drivers/odbc/qsql_odbc.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Definition of ODBC driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQL_ODBC_H +#define TQSQL_ODBC_H + +#include <qmap.h> +#include <qstring.h> +#include <qsqldriver.h> +#include <qsqlfield.h> +#include <qsqlresult.h> +#include <qsqlindex.h> + +#if defined (Q_OS_WIN32) +#include <qt_windows.h> +#endif + +#if defined (Q_OS_MAC) +// assume we use iodbc on MAC +// comment next line out if you use a +// unicode compatible manager +# define Q_ODBC_VERSION_2 +#endif + +#ifdef QT_PLUGIN +#define Q_EXPORT_SQLDRIVER_ODBC +#else +#define Q_EXPORT_SQLDRIVER_ODBC Q_EXPORT +#endif + +#ifdef Q_OS_UNIX +#define HAVE_LONG_LONG 1 // force UnixODBC NOT to fall back to a struct for BIGINTs +#endif + +#if defined(Q_CC_BOR) +// workaround for Borland to make sure that SQLBIGINT is defined +# define _MSC_VER 900 +#endif +#include <sql.h> +#if defined(Q_CC_BOR) +# undef _MSC_VER +#endif + +#include <sqlext.h> +#include "debian_qsql_odbc.h" + +class TQODBCPrivate; +class TQODBCDriver; +class TQSqlRecordInfo; + +class TQODBCResult : public TQSqlResult +{ + friend class TQODBCDriver; +public: + TQODBCResult( const TQODBCDriver * db, TQODBCPrivate* p ); + ~TQODBCResult(); + + SQLHANDLE statement(); + bool prepare( const TQString& query ); + bool exec(); + +protected: + bool fetchNext(); + bool fetchFirst(); + bool fetchLast(); + bool fetchPrior(); + bool fetch(int i); + bool reset ( const TQString& query ); + TQVariant data( int field ); + bool isNull( int field ); + int size(); + int numRowsAffected(); +private: + TQODBCPrivate* d; + typedef TQMap<int,TQVariant> FieldCache; + FieldCache fieldCache; + typedef TQMap<int,bool> NullCache; + NullCache nullCache; +}; + +class Q_EXPORT_SQLDRIVER_ODBC TQODBCDriver : public TQSqlDriver +{ +public: + TQODBCDriver( TQObject * parent=0, const char * name=0 ); + TQODBCDriver( SQLHANDLE env, SQLHANDLE con, TQObject * parent=0, const char * name=0 ); + ~TQODBCDriver(); + bool hasFeature( DriverFeature f ) const; + bool open( const TQString & db, + const TQString & user = TQString::null, + const TQString & password = TQString::null, + const TQString & host = TQString::null, + int port = -1 ); + void close(); + TQSqlQuery createQuery() const; + TQStringList tables( const TQString& user ) const; + TQSqlRecord record( const TQString& tablename ) const; + TQSqlRecord record( const TQSqlQuery& query ) const; + TQSqlRecordInfo recordInfo( const TQString& tablename ) const; + TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const; + TQSqlIndex primaryIndex( const TQString& tablename ) const; + SQLHANDLE environment(); + SQLHANDLE connection(); + + TQString formatValue( const TQSqlField* field, + bool trimStrings ) const; + // ### remove me for 4.0 + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); + +protected: + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); +private: + void init(); + bool endTrans(); + void cleanup(); + TQODBCPrivate* d; +}; + +#endif diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp new file mode 100644 index 000000000..99df0fdfb --- /dev/null +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -0,0 +1,1117 @@ +/**************************************************************************** +** +** Implementation of PostgreSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsql_psql.h" +#include <private/qsqlextension_p.h> + +#include <math.h> + +#include <qpointarray.h> +#include <qsqlrecord.h> +#include <qregexp.h> +#include <qdatetime.h> +// PostgreSQL header <utils/elog.h> included by <postgres.h> redefines DEBUG. +#if defined(DEBUG) +# undef DEBUG +#endif +#include <postgres.h> +#include <libpq/libpq-fs.h> +// PostgreSQL header <catalog/pg_type.h> redefines errno erroneously. +#if defined(errno) +# undef errno +#endif +#define errno qt_psql_errno +#include <catalog/pg_type.h> +#undef errno +#ifdef open +# undef open +#endif + +TQPtrDict<TQSqlDriverExtension> *qSqlDriverExtDict(); +TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict(); + +class TQPSQLPrivate +{ +public: + TQPSQLPrivate():connection(0), result(0), isUtf8(FALSE) {} + PGconn *connection; + PGresult *result; + bool isUtf8; +}; + +class TQPSQLDriverExtension : public TQSqlDriverExtension +{ +public: + TQPSQLDriverExtension( TQPSQLDriver *dri ) + : TQSqlDriverExtension(), driver(dri) { } + ~TQPSQLDriverExtension() {} + + bool isOpen() const; +private: + TQPSQLDriver *driver; +}; + +bool TQPSQLDriverExtension::isOpen() const +{ + return PQstatus( driver->connection() ) == CONNECTION_OK; +} + +class TQPSQLOpenExtension : public TQSqlOpenExtension +{ +public: + TQPSQLOpenExtension( TQPSQLDriver *dri ) + : TQSqlOpenExtension(), driver(dri) { } + ~TQPSQLOpenExtension() {} + + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); +private: + TQPSQLDriver *driver; +}; + +bool TQPSQLOpenExtension::open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) +{ + return driver->open( db, user, password, host, port, connOpts ); +} + +static TQSqlError qMakeError( const TQString& err, int type, const TQPSQLPrivate* p ) +{ + const char *s = PQerrorMessage(p->connection); + TQString msg = p->isUtf8 ? TQString::fromUtf8(s) : TQString::fromLocal8Bit(s); + return TQSqlError("TQPSQL: " + err, msg, type); +} + +static TQVariant::Type qDecodePSQLType( int t ) +{ + TQVariant::Type type = TQVariant::Invalid; + switch ( t ) { + case BOOLOID : + type = TQVariant::Bool; + break; + case INT8OID : + type = TQVariant::LongLong; + break; + case INT2OID : + // case INT2VECTOROID : // 7.x + case INT4OID : + type = TQVariant::Int; + break; + case NUMERICOID : + case FLOAT4OID : + case FLOAT8OID : + type = TQVariant::Double; + break; + case ABSTIMEOID : + case RELTIMEOID : + case DATEOID : + type = TQVariant::Date; + break; + case TIMEOID : +#ifdef TIMETZOID // 7.x + case TIMETZOID : +#endif + type = TQVariant::Time; + break; + case TIMESTAMPOID : +#ifdef DATETIMEOID + // Postgres 6.x datetime workaround. + // DATETIMEOID == TIMESTAMPOID (only the names have changed) + case DATETIMEOID : +#endif +#ifdef TIMESTAMPTZOID + // Postgres 7.2 workaround + // TIMESTAMPTZOID == TIMESTAMPOID == DATETIMEOID + case TIMESTAMPTZOID : +#endif + type = TQVariant::DateTime; + break; + // case ZPBITOID : // 7.x + // case VARBITOID : // 7.x + case OIDOID : + case BYTEAOID : + type = TQVariant::ByteArray; + break; + case REGPROCOID : + case TIDOID : + case XIDOID : + case CIDOID : + // case OIDVECTOROID : // 7.x + case UNKNOWNOID : + // case TINTERVALOID : // 7.x + type = TQVariant::Invalid; + break; + default: + case CHAROID : + case BPCHAROID : + // case LZTEXTOID : // 7.x + case VARCHAROID : + case TEXTOID : + case NAMEOID : + case CASHOID : + case INETOID : + case CIDROID : + case CIRCLEOID : + type = TQVariant::String; + break; + } + return type; +} + +TQPSQLResult::TQPSQLResult( const TQPSQLDriver* db, const TQPSQLPrivate* p ) +: TQSqlResult( db ), + currentSize( 0 ) +{ + d = new TQPSQLPrivate(); + (*d) = (*p); +} + +TQPSQLResult::~TQPSQLResult() +{ + cleanup(); + delete d; +} + +PGresult* TQPSQLResult::result() +{ + return d->result; +} + +void TQPSQLResult::cleanup() +{ + if ( d->result ) + PQclear( d->result ); + d->result = 0; + setAt( -1 ); + currentSize = 0; + setActive( FALSE ); +} + +bool TQPSQLResult::fetch( int i ) +{ + if ( !isActive() ) + return FALSE; + if ( i < 0 ) + return FALSE; + if ( i >= currentSize ) + return FALSE; + if ( at() == i ) + return TRUE; + setAt( i ); + return TRUE; +} + +bool TQPSQLResult::fetchFirst() +{ + return fetch( 0 ); +} + +bool TQPSQLResult::fetchLast() +{ + return fetch( PQntuples( d->result ) - 1 ); +} + +// some Postgres conversions +static TQPoint pointFromString( const TQString& s) +{ + // format '(x,y)' + int pivot = s.find( ',' ); + if ( pivot != -1 ) { + int x = s.mid( 1, pivot-1 ).toInt(); + int y = s.mid( pivot+1, s.length()-pivot-2 ).toInt(); + return TQPoint( x, y ) ; + } else + return TQPoint(); +} + +TQVariant TQPSQLResult::data( int i ) +{ + if ( i >= PQnfields( d->result ) ) { + qWarning( "TQPSQLResult::data: column %d out of range", i ); + return TQVariant(); + } + int ptype = PQftype( d->result, i ); + TQVariant::Type type = qDecodePSQLType( ptype ); + const TQString val = ( d->isUtf8 && ptype != BYTEAOID ) ? + TQString::fromUtf8( PQgetvalue( d->result, at(), i ) ) : + TQString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) ); + if ( PQgetisnull( d->result, at(), i ) ) { + TQVariant v; + v.cast( type ); + return v; + } + switch ( type ) { + case TQVariant::Bool: + { + TQVariant b ( (bool)(val == "t"), 0 ); + return ( b ); + } + case TQVariant::String: + return TQVariant( val ); + case TQVariant::LongLong: + if ( val[0] == '-' ) + return TQVariant( val.toLongLong() ); + else + return TQVariant( val.toULongLong() ); + case TQVariant::Int: + return TQVariant( val.toInt() ); + case TQVariant::Double: + if ( ptype == NUMERICOID ) + return TQVariant( val ); + return TQVariant( val.toDouble() ); + case TQVariant::Date: + if ( val.isEmpty() ) { + return TQVariant( TQDate() ); + } else { + return TQVariant( TQDate::fromString( val, TQt::ISODate ) ); + } + case TQVariant::Time: + if ( val.isEmpty() ) + return TQVariant( TQTime() ); + if ( val.at( val.length() - 3 ) == '+' ) + // strip the timezone + return TQVariant( TQTime::fromString( val.left( val.length() - 3 ), TQt::ISODate ) ); + return TQVariant( TQTime::fromString( val, TQt::ISODate ) ); + case TQVariant::DateTime: { + if ( val.length() < 10 ) + return TQVariant( TQDateTime() ); + // remove the timezone + TQString dtval = val; + if ( dtval.at( dtval.length() - 3 ) == '+' ) + dtval.truncate( dtval.length() - 3 ); + // milliseconds are sometimes returned with 2 digits only + if ( dtval.at( dtval.length() - 3 ).isPunct() ) + dtval += '0'; + if ( dtval.isEmpty() ) + return TQVariant( TQDateTime() ); + else + return TQVariant( TQDateTime::fromString( dtval, TQt::ISODate ) ); + } + case TQVariant::Point: + return TQVariant( pointFromString( val ) ); + case TQVariant::Rect: // format '(x,y),(x',y')' + { + int pivot = val.find( "),(" ); + if ( pivot != -1 ) + return TQVariant( TQRect( pointFromString( val.mid(pivot+2,val.length()) ), pointFromString( val.mid(0,pivot+1) ) ) ); + return TQVariant( TQRect() ); + } + case TQVariant::PointArray: // format '((x,y),(x1,y1),...,(xn,yn))' + { + TQRegExp pointPattern("\\([0-9-]*,[0-9-]*\\)"); + int points = val.contains( pointPattern ); + TQPointArray parray( points ); + int idx = 1; + for ( int i = 0; i < points; i++ ){ + int start = val.find( pointPattern, idx ); + int end = -1; + if ( start != -1 ) { + end = val.find( ')', start+1 ); + if ( end != -1 ) { + parray.setPoint( i, pointFromString( val.mid(idx, end-idx+1) ) ); + } + else + parray.setPoint( i, TQPoint() ); + } else { + parray.setPoint( i, TQPoint() ); + break; + } + idx = end+2; + } + return TQVariant( parray ); + } + case TQVariant::ByteArray: { + if ( ptype == BYTEAOID ) { + uint i = 0; + int index = 0; + uint len = val.length(); + static const TQChar backslash( '\\' ); + TQByteArray ba( (int)len ); + while ( i < len ) { + if ( val.at( i ) == backslash ) { + if ( val.at( i + 1 ).isDigit() ) { + ba[ index++ ] = (char)(val.mid( i + 1, 3 ).toInt( 0, 8 )); + i += 4; + } else { + ba[ index++ ] = val.at( i + 1 ); + i += 2; + } + } else { + ba[ index++ ] = val.at( i++ ).unicode(); + } + } + ba.resize( index ); + return TQVariant( ba ); + } + + TQByteArray ba; + ((TQSqlDriver*)driver())->beginTransaction(); + Oid oid = val.toInt(); + int fd = lo_open( d->connection, oid, INV_READ ); +#ifdef QT_CHECK_RANGE + if ( fd < 0) { + qWarning( "TQPSQLResult::data: unable to open large object for read" ); + ((TQSqlDriver*)driver())->commitTransaction(); + return TQVariant( ba ); + } +#endif + int size = 0; + int retval = lo_lseek( d->connection, fd, 0L, SEEK_END ); + if ( retval >= 0 ) { + size = lo_tell( d->connection, fd ); + lo_lseek( d->connection, fd, 0L, SEEK_SET ); + } + if ( size == 0 ) { + lo_close( d->connection, fd ); + ((TQSqlDriver*)driver())->commitTransaction(); + return TQVariant( ba ); + } + char * buf = new char[ size ]; + +#ifdef Q_OS_WIN32 + // ### For some reason lo_read() fails if we try to read more than + // ### 32760 bytes + char * p = buf; + int nread = 0; + + while( size < nread ){ + retval = lo_read( d->connection, fd, p, 32760 ); + nread += retval; + p += retval; + } +#else + retval = lo_read( d->connection, fd, buf, size ); +#endif + + if (retval < 0) { + qWarning( "TQPSQLResult::data: unable to read large object" ); + } else { + ba.duplicate( buf, size ); + } + delete [] buf; + lo_close( d->connection, fd ); + ((TQSqlDriver*)driver())->commitTransaction(); + return TQVariant( ba ); + } + default: + case TQVariant::Invalid: +#ifdef QT_CHECK_RANGE + qWarning("TQPSQLResult::data: unknown data type"); +#endif + ; + } + return TQVariant(); +} + +bool TQPSQLResult::isNull( int field ) +{ + PQgetvalue( d->result, at(), field ); + return PQgetisnull( d->result, at(), field ); +} + +bool TQPSQLResult::reset ( const TQString& query ) +{ + cleanup(); + if ( !driver() ) + return FALSE; + if ( !driver()->isOpen() || driver()->isOpenError() ) + return FALSE; + setActive( FALSE ); + setAt( TQSql::BeforeFirst ); + if ( d->result ) + PQclear( d->result ); + if ( d->isUtf8 ) { + d->result = PQexec( d->connection, query.utf8().data() ); + } else { + d->result = PQexec( d->connection, query.local8Bit().data() ); + } + int status = PQresultStatus( d->result ); + if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) { + if ( status == PGRES_TUPLES_OK ) { + setSelect( TRUE ); + currentSize = PQntuples( d->result ); + } else { + setSelect( FALSE ); + currentSize = -1; + } + setActive( TRUE ); + return TRUE; + } + setLastError( qMakeError( "Unable to create query", TQSqlError::Statement, d ) ); + return FALSE; +} + +int TQPSQLResult::size() +{ + return currentSize; +} + +int TQPSQLResult::numRowsAffected() +{ + return TQString( PQcmdTuples( d->result ) ).toInt(); +} + +/////////////////////////////////////////////////////////////////// + +static bool setEncodingUtf8( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "SET CLIENT_ENCODING TO 'UNICODE'" ); + int status = PQresultStatus( result ); + PQclear( result ); + return status == PGRES_COMMAND_OK; +} + +static void setDatestyle( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "SET DATESTYLE TO 'ISO'" ); +#ifdef QT_CHECK_RANGE + int status = PQresultStatus( result ); + if ( status != PGRES_COMMAND_OK ) + qWarning( "%s", PQerrorMessage( connection ) ); +#endif + PQclear( result ); +} + +static TQPSQLDriver::Protocol getPSQLVersion( PGconn* connection ) +{ + PGresult* result = PQexec( connection, "select version()" ); + int status = PQresultStatus( result ); + if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) { + TQString val( PQgetvalue( result, 0, 0 ) ); + PQclear( result ); + TQRegExp rx( "(\\d+)\\.(\\d+)" ); + rx.setMinimal ( TRUE ); // enforce non-greedy RegExp + if ( rx.search( val ) != -1 ) { + int vMaj = rx.cap( 1 ).toInt(); + int vMin = rx.cap( 2 ).toInt(); + if ( vMaj < 6 ) { +#ifdef QT_CHECK_RANGE + qWarning( "This version of PostgreSQL is not supported and may not work." ); +#endif + return TQPSQLDriver::Version6; + } + if ( vMaj == 6 ) { + return TQPSQLDriver::Version6; + } else if ( vMaj == 7 ) { + if ( vMin < 1 ) + return TQPSQLDriver::Version7; + else if ( vMin < 3 ) + return TQPSQLDriver::Version71; + } + return TQPSQLDriver::Version73; + } + } else { +#ifdef QT_CHECK_RANGE + qWarning( "This version of PostgreSQL is not supported and may not work." ); +#endif + } + + return TQPSQLDriver::Version6; +} + +TQPSQLDriver::TQPSQLDriver( TQObject * parent, const char * name ) + : TQSqlDriver(parent,name ? name : "TQPSQL"), pro( TQPSQLDriver::Version6 ) +{ + init(); +} + +TQPSQLDriver::TQPSQLDriver( PGconn * conn, TQObject * parent, const char * name ) + : TQSqlDriver(parent,name ? name : "TQPSQL"), pro( TQPSQLDriver::Version6 ) +{ + init(); + d->connection = conn; + if ( conn ) { + pro = getPSQLVersion( d->connection ); + setOpen( TRUE ); + setOpenError( FALSE ); + } +} + +void TQPSQLDriver::init() +{ + qSqlDriverExtDict()->insert( this, new TQPSQLDriverExtension(this) ); + qSqlOpenExtDict()->insert( this, new TQPSQLOpenExtension(this) ); + + d = new TQPSQLPrivate(); +} + +TQPSQLDriver::~TQPSQLDriver() +{ + if ( d->connection ) + PQfinish( d->connection ); + delete d; + if ( !qSqlDriverExtDict()->isEmpty() ) { + TQSqlDriverExtension *ext = qSqlDriverExtDict()->take( this ); + delete ext; + } + if ( !qSqlOpenExtDict()->isEmpty() ) { + TQSqlOpenExtension *ext = qSqlOpenExtDict()->take( this ); + delete ext; + } +} + +PGconn* TQPSQLDriver::connection() +{ + return d->connection; +} + + +bool TQPSQLDriver::hasFeature( DriverFeature f ) const +{ + switch ( f ) { + case Transactions: + return TRUE; + case QuerySize: + return TRUE; + case BLOB: + return pro >= TQPSQLDriver::Version71; + case Unicode: + return d->isUtf8; + default: + return FALSE; + } +} + +bool TQPSQLDriver::open( const TQString&, + const TQString&, + const TQString&, + const TQString&, + int ) +{ + qWarning("TQPSQLDriver::open(): This version of open() is no longer supported." ); + return FALSE; +} + +bool TQPSQLDriver::open( const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port, + const TQString& connOpts ) +{ + if ( isOpen() ) + close(); + TQString connectString; + if ( host.length() ) + connectString.append( "host=" ).append( host ); + if ( db.length() ) + connectString.append( " dbname=" ).append( db ); + if ( user.length() ) + connectString.append( " user=" ).append( user ); + if ( password.length() ) + connectString.append( " password=" ).append( password ); + if ( port > -1 ) + connectString.append( " port=" ).append( TQString::number( port ) ); + + // add any connect options - the server will handle error detection + if ( !connOpts.isEmpty() ) + connectString += " " + TQStringList::split( ';', connOpts ).join( " " ); + + d->connection = PQconnectdb( connectString.local8Bit().data() ); + if ( PQstatus( d->connection ) == CONNECTION_BAD ) { + setLastError( qMakeError("Unable to connect", TQSqlError::Connection, d ) ); + setOpenError( TRUE ); + return FALSE; + } + + pro = getPSQLVersion( d->connection ); + d->isUtf8 = setEncodingUtf8( d->connection ); + setDatestyle( d->connection ); + + setOpen( TRUE ); + setOpenError( FALSE ); + return TRUE; +} + +void TQPSQLDriver::close() +{ + if ( isOpen() ) { + if (d->connection) + PQfinish( d->connection ); + d->connection = 0; + setOpen( FALSE ); + setOpenError( FALSE ); + } +} + +TQSqlQuery TQPSQLDriver::createQuery() const +{ + return TQSqlQuery( new TQPSQLResult( this, d ) ); +} + +bool TQPSQLDriver::beginTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQPSQLDriver::beginTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "BEGIN" ); + if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) { + PQclear( res ); + setLastError( qMakeError( "Could not begin transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +bool TQPSQLDriver::commitTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQPSQLDriver::commitTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "COMMIT" ); + if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) { + PQclear( res ); + setLastError( qMakeError( "Could not commit transaction", TQSqlError::Transaction, d ) ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +bool TQPSQLDriver::rollbackTransaction() +{ + if ( !isOpen() ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQPSQLDriver::rollbackTransaction: Database not open" ); +#endif + return FALSE; + } + PGresult* res = PQexec( d->connection, "ROLLBACK" ); + if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) { + setLastError( qMakeError( "Could not rollback transaction", TQSqlError::Transaction, d ) ); + PQclear( res ); + return FALSE; + } + PQclear( res ); + return TRUE; +} + +TQStringList TQPSQLDriver::tables( const TQString& typeName ) const +{ + TQStringList tl; + if ( !isOpen() ) + return tl; + int type = typeName.toInt(); + TQSqlQuery t = createQuery(); + t.setForwardOnly( TRUE ); + + if ( typeName.isEmpty() || ((type & (int)TQSql::Tables) == (int)TQSql::Tables) ) { + + TQString query("select relname from pg_class where (relkind = 'r') " + "and (relname !~ '^Inv') " + "and (relname !~ '^pg_') "); + if (pro >= TQPSQLDriver::Version73) + query.append("and (relnamespace not in " + "(select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_table_is_visible(pg_class.oid) "); + t.exec(query); + while ( t.next() ) + tl.append( t.value(0).toString() ); + } + if ( (type & (int)TQSql::Views) == (int)TQSql::Views ) { + TQString query("select relname from pg_class where ( relkind = 'v' ) " + "and ( relname !~ '^Inv' ) " + "and ( relname !~ '^pg_' ) "); + if (pro >= TQPSQLDriver::Version73) + query.append("and (relnamespace not in " + "(select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_table_is_visible(pg_class.oid) "); + t.exec(query); + while ( t.next() ) + tl.append( t.value(0).toString() ); + } + if ( (type & (int)TQSql::SystemTables) == (int)TQSql::SystemTables ) { + TQString query( "select relname from pg_class where ( relkind = 'r' ) " + "and ( relname like 'pg_%' ) " ); + if (pro >= TQPSQLDriver::Version73) + query.append( "and pg_table_is_visible(pg_class.oid) " ); + t.exec(query); + while ( t.next() ) + tl.append( t.value(0).toString() ); + } + + return tl; +} + +TQSqlIndex TQPSQLDriver::primaryIndex( const TQString& tablename ) const +{ + TQSqlIndex idx( tablename ); + if ( !isOpen() ) + return idx; + TQSqlQuery i = createQuery(); + TQString stmt; + + switch( pro ) { + case TQPSQLDriver::Version6: + stmt = "select pg_att1.attname, int(pg_att1.atttypid), pg_att2.attnum, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where lower(pg_cl.relname) = '%1_pkey' "; + break; + case TQPSQLDriver::Version7: + case TQPSQLDriver::Version71: + stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where lower(pg_cl.relname) = '%1_pkey' "; + break; + case TQPSQLDriver::Version73: + stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where lower(pg_cl.relname) = '%1_pkey' " + "and pg_table_is_visible(pg_cl.oid) " + "and pg_att1.attisdropped = false "; + break; + } + stmt += "and pg_cl.oid = pg_ind.indexrelid " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + + i.exec( stmt.arg( tablename.lower() ) ); + while ( i.isActive() && i.next() ) { + TQSqlField f( i.value(0).toString(), qDecodePSQLType( i.value(1).toInt() ) ); + idx.append( f ); + idx.setName( i.value(2).toString() ); + } + return idx; +} + +TQSqlRecord TQPSQLDriver::record( const TQString& tablename ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + TQString stmt; + switch( pro ) { + case TQPSQLDriver::Version6: + stmt = "select pg_attribute.attname, int(pg_attribute.atttypid) " + "from pg_class, pg_attribute " + "where lower(pg_class.relname) = '%1' " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid "; + break; + case TQPSQLDriver::Version7: + case TQPSQLDriver::Version71: + stmt = "select pg_attribute.attname, pg_attribute.atttypid::int " + "from pg_class, pg_attribute " + "where lower(pg_class.relname) = '%1' " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid "; + break; + case TQPSQLDriver::Version73: + stmt = "select pg_attribute.attname, pg_attribute.atttypid::int " + "from pg_class, pg_attribute " + "where lower(pg_class.relname) = '%1' " + "and pg_table_is_visible(pg_class.oid) " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attisdropped = false " + "and pg_attribute.attrelid = pg_class.oid "; + break; + } + + TQSqlQuery fi = createQuery(); + fi.exec( stmt.arg( tablename.lower() ) ); + while ( fi.next() ) { + TQSqlField f( fi.value(0).toString(), qDecodePSQLType( fi.value(1).toInt() ) ); + fil.append( f ); + } + return fil; +} + +TQSqlRecord TQPSQLDriver::record( const TQSqlQuery& query ) const +{ + TQSqlRecord fil; + if ( !isOpen() ) + return fil; + if ( query.isActive() && query.driver() == this ) { + TQPSQLResult* result = (TQPSQLResult*)query.result(); + int count = PQnfields( result->d->result ); + for ( int i = 0; i < count; ++i ) { + TQString name = PQfname( result->d->result, i ); + TQVariant::Type type = qDecodePSQLType( PQftype( result->d->result, i ) ); + TQSqlField rf( name, type ); + fil.append( rf ); + } + } + return fil; +} + +TQSqlRecordInfo TQPSQLDriver::recordInfo( const TQString& tablename ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + + TQString stmt; + switch( pro ) { + case TQPSQLDriver::Version6: + stmt = "select pg_attribute.attname, int(pg_attribute.atttypid), pg_attribute.attnotnull, " + "pg_attribute.attlen, pg_attribute.atttypmod, int(pg_attribute.attrelid), pg_attribute.attnum " + "from pg_class, pg_attribute " + "where lower(pg_class.relname) = '%1' " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid "; + break; + case TQPSQLDriver::Version7: + stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, " + "pg_attribute.attlen, pg_attribute.atttypmod, pg_attribute.attrelid::int, pg_attribute.attnum " + "from pg_class, pg_attribute " + "where lower(pg_class.relname) = '%1' " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid "; + break; + case TQPSQLDriver::Version71: + stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, " + "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "where lower(pg_class.relname) = '%1' " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid " + "order by pg_attribute.attnum "; + break; + case TQPSQLDriver::Version73: + stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, " + "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "where lower(pg_class.relname) = '%1' " + "and pg_table_is_visible(pg_class.oid) " + "and pg_attribute.attnum > 0 " + "and pg_attribute.attrelid = pg_class.oid " + "and pg_attribute.attisdropped = false " + "order by pg_attribute.attnum "; + break; + } + + TQSqlQuery query = createQuery(); + query.exec( stmt.arg( tablename.lower() ) ); + if ( pro >= TQPSQLDriver::Version71 ) { + while ( query.next() ) { + int len = query.value( 3 ).toInt(); + int precision = query.value( 4 ).toInt(); + // swap length and precision if length == -1 + if ( len == -1 && precision > -1 ) { + len = precision - 4; + precision = -1; + } + TQString defVal = query.value( 5 ).toString(); + if ( !defVal.isEmpty() && defVal.startsWith( "'" ) ) + defVal = defVal.mid( 1, defVal.length() - 2 ); + info.append( TQSqlFieldInfo( query.value( 0 ).toString(), + qDecodePSQLType( query.value( 1 ).toInt() ), + query.value( 2 ).toBool(), + len, + precision, + defVal, + query.value( 1 ).toInt() ) ); + } + } else { + // Postgres < 7.1 cannot handle outer joins + while ( query.next() ) { + TQString defVal; + TQString stmt2 = "select pg_attrdef.adsrc from pg_attrdef where " + "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 "; + TQSqlQuery query2 = createQuery(); + query2.exec( stmt2.arg( query.value( 5 ).toInt() ).arg( query.value( 6 ).toInt() ) ); + if ( query2.isActive() && query2.next() ) + defVal = query2.value( 0 ).toString(); + if ( !defVal.isEmpty() && defVal.startsWith( "'" ) ) + defVal = defVal.mid( 1, defVal.length() - 2 ); + int len = query.value( 3 ).toInt(); + int precision = query.value( 4 ).toInt(); + // swap length and precision if length == -1 + if ( len == -1 && precision > -1 ) { + len = precision - 4; + precision = -1; + } + info.append( TQSqlFieldInfo( query.value( 0 ).toString(), + qDecodePSQLType( query.value( 1 ).toInt() ), + query.value( 2 ).toBool(), + len, + precision, + defVal, + query.value( 1 ).toInt() ) ); + } + } + + return info; +} + +TQSqlRecordInfo TQPSQLDriver::recordInfo( const TQSqlQuery& query ) const +{ + TQSqlRecordInfo info; + if ( !isOpen() ) + return info; + if ( query.isActive() && query.driver() == this ) { + TQPSQLResult* result = (TQPSQLResult*)query.result(); + int count = PQnfields( result->d->result ); + for ( int i = 0; i < count; ++i ) { + TQString name = PQfname( result->d->result, i ); + int len = PQfsize( result->d->result, i ); + int precision = PQfmod( result->d->result, i ); + // swap length and precision if length == -1 + if ( len == -1 && precision > -1 ) { + len = precision - 4; + precision = -1; + } + info.append( TQSqlFieldInfo( name, + qDecodePSQLType( PQftype( result->d->result, i ) ), + -1, + len, + precision, + TQVariant(), + PQftype( result->d->result, i ) ) ); + } + } + return info; +} + +TQString TQPSQLDriver::formatValue( const TQSqlField* field, + bool ) const +{ + TQString r; + if ( field->isNull() ) { + r = nullText(); + } else { + switch ( field->type() ) { + case TQVariant::DateTime: + if ( field->value().toDateTime().isValid() ) { + TQDate dt = field->value().toDateTime().date(); + TQTime tm = field->value().toDateTime().time(); + // msecs need to be right aligned otherwise psql + // interpretes them wrong + r = "'" + TQString::number( dt.year() ) + "-" + + TQString::number( dt.month() ) + "-" + + TQString::number( dt.day() ) + " " + + tm.toString() + "." + + TQString::number( tm.msec() ).rightJustify( 3, '0' ) + "'"; + } else { + r = nullText(); + } + break; + case TQVariant::Time: + if ( field->value().toTime().isValid() ) { + r = field->value().toTime().toString( TQt::ISODate ); + } else { + r = nullText(); + } + case TQVariant::String: + case TQVariant::CString: { + switch ( field->value().type() ) { + case TQVariant::Rect: { + TQRect rec = field->value().toRect(); + // upper right corner then lower left according to psql docs + r = "'(" + TQString::number( rec.right() ) + + "," + TQString::number( rec.bottom() ) + + "),(" + TQString::number( rec.left() ) + + "," + TQString::number( rec.top() ) + ")'"; + break; + } + case TQVariant::Point: { + TQPoint p = field->value().toPoint(); + r = "'(" + TQString::number( p.x() ) + + "," + TQString::number( p.y() ) + ")'"; + break; + } + case TQVariant::PointArray: { + TQPointArray pa = field->value().toPointArray(); + r = "' "; + for ( int i = 0; i < (int)pa.size(); ++i ) { + r += "(" + TQString::number( pa[i].x() ) + + "," + TQString::number( pa[i].y() ) + "),"; + } + r.truncate( r.length() - 1 ); + r += "'"; + break; + } + default: + // Escape '\' characters + r = TQSqlDriver::formatValue( field ); + r.replace( "\\", "\\\\" ); + break; + } + break; + } + case TQVariant::Bool: + if ( field->value().toBool() ) + r = "TRUE"; + else + r = "FALSE"; + break; + case TQVariant::ByteArray: { + TQByteArray ba = field->value().asByteArray(); + TQString res; + r = "'"; + unsigned char uc; + for ( int i = 0; i < (int)ba.size(); ++i ) { + uc = (unsigned char) ba[ i ]; + if ( uc > 40 && uc < 92 ) { + r += uc; + } else { + r += "\\\\"; + r += TQString::number( (unsigned char) ba[ i ], 8 ).rightJustify( 3, '0', TRUE ); + } + } + r += "'"; + break; + } + default: + r = TQSqlDriver::formatValue( field ); + break; + } + } + return r; +} diff --git a/src/sql/drivers/psql/qsql_psql.h b/src/sql/drivers/psql/qsql_psql.h new file mode 100644 index 000000000..217cfe45e --- /dev/null +++ b/src/sql/drivers/psql/qsql_psql.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Definition of PostgreSQL driver classes +** +** Created : 001103 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQL_PSQL_H +#define TQSQL_PSQL_H + +#include <qsqlresult.h> +#include <qsqlfield.h> +#include <qsqldriver.h> +#include <libpq-fe.h> + +#ifdef QT_PLUGIN +#define Q_EXPORT_SQLDRIVER_PSQL +#else +#define Q_EXPORT_SQLDRIVER_PSQL Q_EXPORT +#endif + +class TQPSQLPrivate; +class TQPSQLDriver; +class TQSqlRecordInfo; + +class TQPSQLResult : public TQSqlResult +{ + friend class TQPSQLDriver; +public: + TQPSQLResult( const TQPSQLDriver* db, const TQPSQLPrivate* p ); + ~TQPSQLResult(); + PGresult* result(); +protected: + void cleanup(); + bool fetch( int i ); + bool fetchFirst(); + bool fetchLast(); + TQVariant data( int i ); + bool isNull( int field ); + bool reset ( const TQString& query ); + int size(); + int numRowsAffected(); +private: + int currentSize; + TQPSQLPrivate* d; +}; + +class Q_EXPORT_SQLDRIVER_PSQL TQPSQLDriver : public TQSqlDriver +{ +public: + enum Protocol { + Version6 = 6, + Version7 = 7, + Version71 = 8, + Version73 = 9 + }; + + TQPSQLDriver( TQObject * parent=0, const char * name=0 ); + TQPSQLDriver( PGconn * conn, TQObject * parent=0, const char * name=0 ); + ~TQPSQLDriver(); + bool hasFeature( DriverFeature f ) const; + bool open( const TQString & db, + const TQString & user = TQString::null, + const TQString & password = TQString::null, + const TQString & host = TQString::null, + int port = -1 ); + void close(); + TQSqlQuery createQuery() const; + TQStringList tables( const TQString& user ) const; + TQSqlIndex primaryIndex( const TQString& tablename ) const; + TQSqlRecord record( const TQString& tablename ) const; + TQSqlRecord record( const TQSqlQuery& query ) const; + TQSqlRecordInfo recordInfo( const TQString& tablename ) const; + TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const; + + Protocol protocol() const { return pro; } + PGconn* connection(); + TQString formatValue( const TQSqlField* field, + bool trimStrings ) const; + + // ### remove me for 4.0 + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); +protected: + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); +private: + void init(); + Protocol pro; + TQPSQLPrivate* d; +}; + +#endif diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp new file mode 100644 index 000000000..ecdda1c0c --- /dev/null +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Implementation of SQLite driver classes. +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qsql_sqlite.h" + +#include <qdatetime.h> +#include <qregexp.h> +#include <qfile.h> + +#if (QT_VERSION-0 < 0x030000) +# include <qvector.h> +# if !defined Q_WS_WIN32 +# include <unistd.h> +# endif +# include "../../../3rdparty/libraries/sqlite/sqlite.h" +#else +# include <qptrvector.h> +# if !defined Q_WS_WIN32 +# include <unistd.h> +# endif +# include <sqlite.h> +#endif + +typedef struct sqlite_vm sqlite_vm; + +#define TQSQLITE_DRIVER_NAME "TQSQLITE" + +static TQSqlVariant::Type nameToType(const TQString& typeName) +{ + TQString tName = typeName.upper(); + if (tName.startsWith("INT")) + return TQSqlVariant::Int; + if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC")) + return TQSqlVariant::Double; + if (tName.startsWith("BOOL")) + return TQSqlVariant::Bool; + // SQLite is typeless - consider everything else as string + return TQSqlVariant::String; +} + +class TQSQLiteDriverPrivate +{ +public: + TQSQLiteDriverPrivate(); + sqlite *access; + bool utf8; +}; + +TQSQLiteDriverPrivate::TQSQLiteDriverPrivate() : access(0) +{ + utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0); +} + +class TQSQLiteResultPrivate +{ +public: + TQSQLiteResultPrivate(TQSQLiteResult *res); + void cleanup(); + bool fetchNext(TQtSqlCachedResult::RowCache *row); + bool isSelect(); + // initializes the recordInfo and the cache + void init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row = 0); + void finalize(); + + TQSQLiteResult* q; + sqlite *access; + + // and we have too keep our own struct for the data (sqlite works via + // callback. + const char *currentTail; + sqlite_vm *currentMachine; + + uint skippedStatus: 1; // the status of the fetchNext() that's skipped + TQtSqlCachedResult::RowCache *skipRow; + + uint utf8: 1; + TQSqlRecordInfo rInf; +}; + +static const uint initial_cache_size = 128; + +TQSQLiteResultPrivate::TQSQLiteResultPrivate(TQSQLiteResult* res) : q(res), access(0), currentTail(0), + currentMachine(0), skippedStatus(FALSE), skipRow(0), utf8(FALSE) +{ +} + +void TQSQLiteResultPrivate::cleanup() +{ + finalize(); + rInf.clear(); + currentTail = 0; + currentMachine = 0; + skippedStatus = FALSE; + delete skipRow; + skipRow = 0; + q->setAt(TQSql::BeforeFirst); + q->setActive(FALSE); + q->cleanup(); +} + +void TQSQLiteResultPrivate::finalize() +{ + if (!currentMachine) + return; + + char* err = 0; + int res = sqlite_finalize(currentMachine, &err); + if (err) { + q->setLastError(TQSqlError("Unable to fetch results", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + currentMachine = 0; +} + +// called on first fetch +void TQSQLiteResultPrivate::init(const char **cnames, int numCols, TQtSqlCachedResult::RowCache **row) +{ + if (!cnames) + return; + + rInf.clear(); + if (numCols <= 0) + return; + + for (int i = 0; i < numCols; ++i) { + const char* lastDot = strrchr(cnames[i], '.'); + const char* fieldName = lastDot ? lastDot + 1 : cnames[i]; + rInf.append(TQSqlFieldInfo(fieldName, nameToType(cnames[i+numCols]))); + } + // skip the first fetch + if (row && !*row) { + *row = new TQtSqlCachedResult::RowCache(numCols); + skipRow = *row; + } +} + +bool TQSQLiteResultPrivate::fetchNext(TQtSqlCachedResult::RowCache* row) +{ + // may be caching. + const char **fvals; + const char **cnames; + int colNum; + int res; + int i; + + if (skipRow) { + // already fetched + if (row) + *row = *skipRow; + delete skipRow; + skipRow = 0; + return skippedStatus; + } + + if (!currentMachine) + return FALSE; + + // keep trying while busy, wish I could implement this better. + while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == SQLITE_BUSY) { + // sleep instead requesting result again immidiately. +#if defined Q_WS_WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + + switch(res) { + case SQLITE_ROW: + // check to see if should fill out columns + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum, &row); + if (!fvals) + return FALSE; + if (!row) + return TRUE; + for (i = 0; i < colNum; ++i) + (*row)[i] = utf8 ? TQString::fromUtf8(fvals[i]) : TQString(fvals[i]); + return TRUE; + case SQLITE_DONE: + if (rInf.isEmpty()) + // must be first call. + init(cnames, colNum); + q->setAt(TQSql::AfterLast); + return FALSE; + case SQLITE_ERROR: + case SQLITE_MISUSE: + default: + // something wrong, don't get col info, but still return false + finalize(); // finalize to get the error message. + q->setAt(TQSql::AfterLast); + return FALSE; + } + return FALSE; +} + +TQSQLiteResult::TQSQLiteResult(const TQSQLiteDriver* db) +: TQtSqlCachedResult(db) +{ + d = new TQSQLiteResultPrivate(this); + d->access = db->d->access; + d->utf8 = db->d->utf8; +} + +TQSQLiteResult::~TQSQLiteResult() +{ + d->cleanup(); + delete d; +} + +/* + Execute \a query. +*/ +bool TQSQLiteResult::reset (const TQString& query) +{ + // this is where we build a query. + if (!driver()) + return FALSE; + if (!driver()-> isOpen() || driver()->isOpenError()) + return FALSE; + + d->cleanup(); + + // Um, ok. callback based so.... pass private static function for this. + setSelect(FALSE); + char *err = 0; + int res = sqlite_compile(d->access, + d->utf8 ? (const char*)query.utf8().data() : query.ascii(), + &(d->currentTail), + &(d->currentMachine), + &err); + if (res != SQLITE_OK || err) { + setLastError(TQSqlError("Unable to execute statement", err, TQSqlError::Statement, res)); + sqlite_freemem(err); + } + //if (*d->currentTail != '\000' then there is more sql to eval + if (!d->currentMachine) { + setActive(FALSE); + return FALSE; + } + // we have to fetch one row to find out about + // the structure of the result set + d->skippedStatus = d->fetchNext(0); + setSelect(!d->rInf.isEmpty()); + if (isSelect()) + init(d->rInf.count()); + setActive(TRUE); + return TRUE; +} + +bool TQSQLiteResult::gotoNext(TQtSqlCachedResult::RowCache* row) +{ + return d->fetchNext(row); +} + +int TQSQLiteResult::size() +{ + return -1; +} + +int TQSQLiteResult::numRowsAffected() +{ + return sqlite_changes(d->access); +} + +///////////////////////////////////////////////////////// + +TQSQLiteDriver::TQSQLiteDriver(TQObject * parent, const char * name) + : TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSQLiteDriverPrivate(); +} + +TQSQLiteDriver::TQSQLiteDriver(sqlite *connection, TQObject *parent, const char *name) + : TQSqlDriver(parent, name ? name : TQSQLITE_DRIVER_NAME) +{ + d = new TQSQLiteDriverPrivate(); + d->access = connection; + setOpen(TRUE); + setOpenError(FALSE); +} + + +TQSQLiteDriver::~TQSQLiteDriver() +{ + delete d; +} + +bool TQSQLiteDriver::hasFeature(DriverFeature f) const +{ + switch (f) { + case Transactions: + return TRUE; +#if (QT_VERSION-0 >= 0x030000) + case Unicode: + return d->utf8; +#endif +// case BLOB: + default: + return FALSE; + } +} + +/* + SQLite dbs have no user name, passwords, hosts or ports. + just file names. +*/ +bool TQSQLiteDriver::open(const TQString & db, const TQString &, const TQString &, const TQString &, int, const TQString &) +{ + if (isOpen()) + close(); + + if (db.isEmpty()) + return FALSE; + + char* err = 0; + d->access = sqlite_open(TQFile::encodeName(db), 0, &err); + if (err) { + setLastError(TQSqlError("Error to open database", err, TQSqlError::Connection)); + sqlite_freemem(err); + err = 0; + } + + if (d->access) { + setOpen(TRUE); + setOpenError(FALSE); + return TRUE; + } + setOpenError(TRUE); + return FALSE; +} + +void TQSQLiteDriver::close() +{ + if (isOpen()) { + sqlite_close(d->access); + d->access = 0; + setOpen(FALSE); + setOpenError(FALSE); + } +} + +TQSqlQuery TQSQLiteDriver::createQuery() const +{ + return TQSqlQuery(new TQSQLiteResult(this)); +} + +bool TQSQLiteDriver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "BEGIN", 0, this, &err); + + if (res == SQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to begin transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSQLiteDriver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "COMMIT", 0, this, &err); + + if (res == SQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to commit transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +bool TQSQLiteDriver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return FALSE; + + char* err; + int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err); + + if (res == SQLITE_OK) + return TRUE; + + setLastError(TQSqlError("Unable to rollback Transaction", err, TQSqlError::Transaction, res)); + sqlite_freemem(err); + return FALSE; +} + +TQStringList TQSQLiteDriver::tables(const TQString &typeName) const +{ + TQStringList res; + if (!isOpen()) + return res; + int type = typeName.toInt(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); +#if (QT_VERSION-0 >= 0x030000) + if ((type & (int)TQSql::Tables) && (type & (int)TQSql::Views)) + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); + else if (typeName.isEmpty() || (type & (int)TQSql::Tables)) + q.exec("SELECT name FROM sqlite_master WHERE type='table'"); + else if (type & (int)TQSql::Views) + q.exec("SELECT name FROM sqlite_master WHERE type='view'"); +#else + q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'"); +#endif + + + if (q.isActive()) { + while(q.next()) + res.append(q.value(0).toString()); + } + +#if (QT_VERSION-0 >= 0x030000) + if (type & (int)TQSql::SystemTables) { + // there are no internal tables beside this one: + res.append("sqlite_master"); + } +#endif + + return res; +} + +TQSqlIndex TQSQLiteDriver::primaryIndex(const TQString &tblname) const +{ + TQSqlRecordInfo rec(recordInfo(tblname)); // expensive :( + + if (!isOpen()) + return TQSqlIndex(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + // finrst find a UNITQUE INDEX + q.exec("PRAGMA index_list('" + tblname + "');"); + TQString indexname; + while(q.next()) { + if (q.value(2).toInt()==1) { + indexname = q.value(1).toString(); + break; + } + } + if (indexname.isEmpty()) + return TQSqlIndex(); + + q.exec("PRAGMA index_info('" + indexname + "');"); + + TQSqlIndex index(tblname, indexname); + while(q.next()) { + TQString name = q.value(2).toString(); + TQSqlVariant::Type type = TQSqlVariant::Invalid; + if (rec.contains(name)) + type = rec.find(name).type(); + index.append(TQSqlField(name, type)); + } + return index; +} + +TQSqlRecordInfo TQSQLiteDriver::recordInfo(const TQString &tbl) const +{ + if (!isOpen()) + return TQSqlRecordInfo(); + + TQSqlQuery q = createQuery(); + q.setForwardOnly(TRUE); + q.exec("SELECT * FROM " + tbl + " LIMIT 1"); + return recordInfo(q); +} + +TQSqlRecord TQSQLiteDriver::record(const TQString &tblname) const +{ + if (!isOpen()) + return TQSqlRecord(); + + return recordInfo(tblname).toRecord(); +} + +TQSqlRecord TQSQLiteDriver::record(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSQLiteResult* result = (TQSQLiteResult*)query.result(); + return result->d->rInf.toRecord(); + } + return TQSqlRecord(); +} + +TQSqlRecordInfo TQSQLiteDriver::recordInfo(const TQSqlQuery& query) const +{ + if (query.isActive() && query.driver() == this) { + TQSQLiteResult* result = (TQSQLiteResult*)query.result(); + return result->d->rInf; + } + return TQSqlRecordInfo(); +} diff --git a/src/sql/drivers/sqlite/qsql_sqlite.h b/src/sql/drivers/sqlite/qsql_sqlite.h new file mode 100644 index 000000000..ccde9b865 --- /dev/null +++ b/src/sql/drivers/sqlite/qsql_sqlite.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Definition of SQLite driver classes. +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** EDITIONS: FREE, ENTERPRISE +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef TQSQL_SQLITE_H +#define TQSQL_SQLITE_H + +#include <qsqldriver.h> +#include <qsqlresult.h> +#include <qsqlrecord.h> +#include <qsqlindex.h> +#include "../cache/qsqlcachedresult.h" + +#if (QT_VERSION-0 >= 0x030000) +typedef TQVariant TQSqlVariant; +#endif + +#if defined (Q_OS_WIN32) +# include <qt_windows.h> +#endif + +class TQSQLiteDriverPrivate; +class TQSQLiteResultPrivate; +class TQSQLiteDriver; +struct sqlite; + +class TQSQLiteResult : public TQtSqlCachedResult +{ + friend class TQSQLiteDriver; + friend class TQSQLiteResultPrivate; +public: + TQSQLiteResult(const TQSQLiteDriver* db); + ~TQSQLiteResult(); + +protected: + bool gotoNext(TQtSqlCachedResult::RowCache* row); + bool reset (const TQString& query); + int size(); + int numRowsAffected(); + +private: + TQSQLiteResultPrivate* d; +}; + +class TQSQLiteDriver : public TQSqlDriver +{ + friend class TQSQLiteResult; +public: + TQSQLiteDriver(TQObject *parent = 0, const char *name = 0); + TQSQLiteDriver(sqlite *connection, TQObject *parent = 0, const char *name = 0); + ~TQSQLiteDriver(); + bool hasFeature(DriverFeature f) const; + bool open(const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port, + const TQString & connOpts); + bool open( const TQString & db, + const TQString & user, + const TQString & password, + const TQString & host, + int port ) { return open (db, user, password, host, port, TQString()); } + void close(); + TQSqlQuery createQuery() const; + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); + TQStringList tables(const TQString& user) const; + + TQSqlRecord record(const TQString& tablename) const; + TQSqlRecordInfo recordInfo(const TQString& tablename) const; + TQSqlIndex primaryIndex(const TQString &table) const; + TQSqlRecord record(const TQSqlQuery& query) const; + TQSqlRecordInfo recordInfo(const TQSqlQuery& query) const; + +private: + TQSQLiteDriverPrivate* d; +}; +#endif diff --git a/src/sql/qdatabrowser.cpp b/src/sql/qdatabrowser.cpp new file mode 100644 index 000000000..b1dc0bddd --- /dev/null +++ b/src/sql/qdatabrowser.cpp @@ -0,0 +1,1284 @@ +/**************************************************************************** +** +** Implementation of TQDataBrowser class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qdatabrowser.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "qsqlform.h" +#include "qsqlmanager_p.h" +#include "qsqlresult.h" + +class TQDataBrowserPrivate +{ +public: + TQDataBrowserPrivate() : boundaryCheck( TRUE ), readOnly( FALSE ) {} + TQSqlCursorManager cur; + TQSqlFormManager frm; + TQDataManager dat; + bool boundaryCheck; + bool readOnly; +}; + +/*! + \class TQDataBrowser qdatabrowser.h + \brief The TQDataBrowser class provides data manipulation and + navigation for data entry forms. + + \ingroup database + \mainclass + \module sql + + A high-level API is provided for navigating through data records + in a cursor, for inserting, updating and deleting records, and for + refreshing data in the display. + + If you want a read-only form to present database data use + TQDataView; if you want a table-based presentation of your data use + TQDataTable. + + A TQDataBrowser is used to associate a dataset with a form in much + the same way as a TQDataTable associates a dataset with a table. + Once the data browser has been constructed it can be associated + with a dataset with setSqlCursor(), and with a form with + setForm(). Boundary checking, sorting and filtering can be set + with setBoundaryChecking(), setSort() and setFilter(), + respectively. + + The insertCurrent() function reads the fields from the default + form into the default cursor and performs the insert. The + updateCurrent() and deleteCurrent() functions perform similarly to + update and delete the current record respectively. + + The user can be asked to confirm all edits with setConfirmEdits(). + For more precise control use setConfirmInsert(), + setConfirmUpdate(), setConfirmDelete() and setConfirmCancels(). + Use setAutoEdit() to control the behaviour of the form when the + user edits a record and then navigates. + + The record set is navigated using first(), next(), prev(), last() + and seek(). The form's display is updated with refresh(). When + navigation takes place the firstRecordAvailable(), + lastRecordAvailable(), nextRecordAvailable() and + prevRecordAvailable() signals are emitted. When the cursor record + is changed due to navigation the cursorChanged() signal is + emitted. + + If you want finer control of the insert, update and delete + processes then you can use the lower level functions to perform + these operations as described below. + + The form is populated with data from the database with + readFields(). If the user is allowed to edit, (see setReadOnly()), + write the form's data back to the cursor's edit buffer with + writeFields(). You can clear the values in the form with + clearValues(). Editing is performed as follows: + \list + \i \e insert When the data browser enters insertion mode it emits the + primeInsert() signal which you can connect to, for example to + pre-populate fields. Call writeFields() to write the user's edits to + the cursor's edit buffer then call insert() to insert the record + into the database. The beforeInsert() signal is emitted just before + the cursor's edit buffer is inserted into the database; connect to + this for example, to populate fields such as an auto-generated + primary key. + \i \e update For updates the primeUpdate() signal is emitted when + the data browser enters update mode. After calling writeFields() + call update() to update the record and connect to the beforeUpdate() + signal to manipulate the user's data before the update takes place. + \i \e delete For deletion the primeDelete() signal is emitted when + the data browser enters deletion mode. After calling writeFields() + call del() to delete the record and connect to the beforeDelete() + signal, for example to record an audit of the deleted record. + \endlist + +*/ + +/*! + \enum TQDataBrowser::Boundary + + This enum describes where the data browser is positioned. + + \value Unknown the boundary cannot be determined (usually because + there is no default cursor, or the default cursor is not active). + + \value None the browser is not positioned on a boundary, but it is + positioned on a record somewhere in the middle. + + \value BeforeBeginning the browser is positioned before the + first available record. + + \value Beginning the browser is positioned at the first record. + + \value End the browser is positioned at the last + record. + + \value AfterEnd the browser is positioned after the last + available record. +*/ + +/*! + Constructs a data browser which is a child of \a parent, with the + name \a name and widget flags set to \a fl. +*/ + +TQDataBrowser::TQDataBrowser( TQWidget *parent, const char *name, WFlags fl ) + : TQWidget( parent, name, fl ) +{ + d = new TQDataBrowserPrivate(); + d->dat.setMode( TQSql::Update ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDataBrowser::~TQDataBrowser() +{ + delete d; +} + + +/*! + Returns an enum indicating the boundary status of the browser. + + This is achieved by moving the default cursor and checking the + position, however the current default form values will not be + altered. After checking for the boundary, the cursor is moved back + to its former position. See \l TQDataBrowser::Boundary. + + \sa Boundary +*/ + +TQDataBrowser::Boundary TQDataBrowser::boundary() +{ + TQSqlCursor* cur = d->cur.cursor(); + if ( !cur || !cur->isActive() ) + return Unknown; + if ( !cur->isValid() ) { + if ( cur->at() == TQSql::BeforeFirst ) + return BeforeBeginning; + if ( cur->at() == TQSql::AfterLast ) + return AfterEnd; + return Unknown; + } + if ( cur->at() == 0 ) + return Beginning; + int currentAt = cur->at(); + + Boundary b = None; + if ( !cur->prev() ) + b = Beginning; + else + cur->seek( currentAt ); + if ( b == None && !cur->next() ) + b = End; + cur->seek( currentAt ); + return b; +} + + +/*! + \property TQDataBrowser::boundaryChecking + \brief whether boundary checking is active + + When boundary checking is active (the default), signals are + emitted indicating the current position of the default cursor. + + \sa boundary() +*/ + +void TQDataBrowser::setBoundaryChecking( bool active ) +{ + d->boundaryCheck = active; +} + +bool TQDataBrowser::boundaryChecking() const +{ + return d->boundaryCheck; +} + +/*! + \property TQDataBrowser::sort + \brief the data browser's sort + + The data browser's sort affects the order in which records are + viewed in the browser. Call refresh() to apply the new sort. + + When retrieving the sort property, a string list is returned in + the form 'fieldname order', e.g. 'id ASC', 'surname DESC'. + + There is no default sort. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDataBrowser.sort(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ + +void TQDataBrowser::setSort( const TQStringList& sort ) +{ + d->cur.setSort( sort ); +} + +/*! + \overload + + Sets the data browser's sort to the TQSqlIndex \a sort. To apply + the new sort, use refresh(). + +*/ +void TQDataBrowser::setSort( const TQSqlIndex& sort ) +{ + d->cur.setSort( sort ); +} + +TQStringList TQDataBrowser::sort() const +{ + return d->cur.sort(); +} + + +/*! + \property TQDataBrowser::filter + \brief the data browser's filter + + The filter applies to the data shown in the browser. Call + refresh() to apply the new filter. A filter is a string containing + a SQL WHERE clause without the WHERE keyword, e.g. "id>1000", + "name LIKE 'A%'", etc. + + There is no default filter. + + \sa sort() +*/ + +void TQDataBrowser::setFilter( const TQString& filter ) +{ + d->cur.setFilter( filter ); +} + + +TQString TQDataBrowser::filter() const +{ + return d->cur.filter(); +} + + +/*! + Sets the default cursor used by the data browser to \a cursor. If + \a autoDelete is TRUE (the default is FALSE), the data browser + takes ownership of the \a cursor pointer, which will be deleted + when the browser is destroyed, or when setSqlCursor() is called + again. To activate the \a cursor use refresh(). The cursor's edit + buffer is used in the default form to browse and edit records. + + \sa sqlCursor() form() setForm() +*/ + +void TQDataBrowser::setSqlCursor( TQSqlCursor* cursor, bool autoDelete ) +{ + if ( !cursor ) + return; + d->cur.setCursor( cursor, autoDelete ); + d->frm.setRecord( cursor->editBuffer() ); + if ( cursor->isReadOnly() ) + setReadOnly( TRUE ); +} + + +/*! + Returns the default cursor used for navigation, or 0 if there is + no default cursor. + + \sa setSqlCursor() +*/ + +TQSqlCursor* TQDataBrowser::sqlCursor() const +{ + return d->cur.cursor(); +} + + +/*! + Sets the browser's default form to \a form. The cursor and all + navigation and data manipulation functions that the browser + provides become available to the \a form. +*/ + +void TQDataBrowser::setForm( TQSqlForm* form ) +{ + d->frm.setForm( form ); +} + + +/*! + Returns the data browser's default form or 0 if no form has been + set. +*/ + +TQSqlForm* TQDataBrowser::form() +{ + return d->frm.form(); +} + +/*! + \property TQDataBrowser::readOnly + \brief whether the browser is read-only + + The default is FALSE, i.e. data can be edited. If the data browser + is read-only, no database edits will be allowed. +*/ + +void TQDataBrowser::setReadOnly( bool active ) +{ + d->readOnly = active; +} + +bool TQDataBrowser::isReadOnly() const +{ + return d->readOnly; +} + +void TQDataBrowser::setConfirmEdits( bool confirm ) +{ + d->dat.setConfirmEdits( confirm ); +} + +/*! + \property TQDataBrowser::confirmInsert + \brief whether the data browser confirms insertions + + If this property is TRUE, the browser confirms insertions, + otherwise insertions happen immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete() confirmEdit() +*/ + +void TQDataBrowser::setConfirmInsert( bool confirm ) +{ + d->dat.setConfirmInsert( confirm ); +} + +/*! + \property TQDataBrowser::confirmUpdate + \brief whether the browser confirms updates + + If this property is TRUE, the browser confirms updates, otherwise + updates happen immediately. + + \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete() confirmEdit() +*/ + +void TQDataBrowser::setConfirmUpdate( bool confirm ) +{ + d->dat.setConfirmUpdate( confirm ); +} + +/*! + \property TQDataBrowser::confirmDelete + \brief whether the browser confirms deletions + + If this property is TRUE, the browser confirms deletions, + otherwise deletions happen immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert() confirmEdit() +*/ + +void TQDataBrowser::setConfirmDelete( bool confirm ) +{ + d->dat.setConfirmDelete( confirm ); +} + +/*! + \property TQDataBrowser::confirmEdits + \brief whether the browser confirms edits + + If this property is TRUE, the browser confirms all edit operations + (insertions, updates and deletions), otherwise all edit operations + happen immediately. Confirmation is achieved by presenting the + user with a message box -- this behavior can be changed by + reimplementing the confirmEdit() function, + + \sa confirmEdit() confirmCancels() confirmInsert() confirmUpdate() confirmDelete() +*/ + +bool TQDataBrowser::confirmEdits() const +{ + return ( d->dat.confirmEdits() ); +} + +bool TQDataBrowser::confirmInsert() const +{ + return ( d->dat.confirmInsert() ); +} + +bool TQDataBrowser::confirmUpdate() const +{ + return ( d->dat.confirmUpdate() ); +} + +bool TQDataBrowser::confirmDelete() const +{ + return ( d->dat.confirmDelete() ); +} + +/*! + \property TQDataBrowser::confirmCancels + \brief whether the browser confirms cancel operations + + If this property is TRUE, all cancels must be confirmed by the + user through a message box (this behavior can be changed by + overriding the confirmCancel() function), otherwise all cancels + occur immediately. The default is FALSE. + + \sa confirmEdits() confirmCancel() +*/ + +void TQDataBrowser::setConfirmCancels( bool confirm ) +{ + d->dat.setConfirmCancels( confirm ); +} + +bool TQDataBrowser::confirmCancels() const +{ + return d->dat.confirmCancels(); +} + +/*! + \property TQDataBrowser::autoEdit + \brief whether the browser automatically applies edits + + The default value for this property is TRUE. When the user begins + an insertion or an update on a form there are two possible + outcomes when they navigate to another record: + + \list + \i the insert or update is is performed -- this occurs if autoEdit is TRUE + \i the insert or update is discarded -- this occurs if autoEdit is FALSE + \endlist +*/ + +void TQDataBrowser::setAutoEdit( bool autoEdit ) +{ + d->dat.setAutoEdit( autoEdit ); +} + +bool TQDataBrowser::autoEdit() const +{ + return d->dat.autoEdit(); +} + +/*! + \fn void TQDataBrowser::firstRecordAvailable( bool available ) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + first record in the default cursor is available. +*/ + +/*! + \fn void TQDataBrowser::lastRecordAvailable( bool available ) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + last record in the default cursor is available. +*/ + +/*! + \fn void TQDataBrowser::nextRecordAvailable( bool available ) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + next record in the default cursor is available. +*/ + + +/*! + \fn void TQDataBrowser::prevRecordAvailable( bool available ) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + previous record in the default cursor is available. +*/ + + +/*! + \fn void TQDataBrowser::currentChanged( const TQSqlRecord* record ) + + This signal is emitted whenever the current cursor position + changes. The \a record parameter points to the contents of the + current cursor's record. +*/ + + +/*! + \fn void TQDataBrowser::primeInsert( TQSqlRecord* buf ) + + This signal is emitted when the data browser enters insertion + mode. The \a buf parameter points to the record buffer that is to + be inserted. Connect to this signal to, for example, prime the + record buffer with default data values, auto-numbered fields etc. + (Note that TQSqlCursor::primeInsert() is \e not called on the + default cursor, as this would corrupt values in the form.) + + \sa insert() +*/ + + +/*! + \fn void TQDataBrowser::primeUpdate( TQSqlRecord* buf ) + + This signal is emitted when the data browser enters update mode. + Note that during navigation (first(), last(), next(), prev()), + each record that is shown in the default form is primed for + update. The \a buf parameter points to the record buffer being + updated. (Note that TQSqlCursor::primeUpdate() is \e not called on + the default cursor, as this would corrupt values in the form.) + Connect to this signal in order to, for example, keep track of + which records have been updated, perhaps for auditing purposes. + + \sa update() +*/ + +/*! + \fn void TQDataBrowser::primeDelete( TQSqlRecord* buf ) + + This signal is emitted when the data browser enters deletion mode. + The \a buf parameter points to the record buffer being deleted. + (Note that TQSqlCursor::primeDelete() is \e not called on the + default cursor, as this would corrupt values in the form.) + Connect to this signal in order to, for example, save a copy of + the deleted record for auditing purposes. + + \sa del() +*/ + + +/*! + \fn void TQDataBrowser::cursorChanged( TQSqlCursor::Mode mode ) + + This signal is emitted whenever the cursor record was changed due + to navigation. The \a mode parameter is the edit that just took + place, e.g. Insert, Update or Delete. See \l TQSqlCursor::Mode. +*/ + + +/*! + Refreshes the data browser's data using the default cursor. The + browser's current filter and sort are applied if they have been + set. + + \sa setFilter() setSort() +*/ + +void TQDataBrowser::refresh() +{ + d->cur.refresh(); +} + + +/*! + Performs an insert operation on the data browser's cursor. If + there is no default cursor or no default form, nothing happens. + + If auto-editing is on (see setAutoEdit()), the following happens: + + \list + \i If the browser is already actively inserting a record, + the current form's data is inserted into the database. + \i If the browser is not inserting a record, but the current record + was changed by the user, the record is updated in the database with + the current form's data (i.e. with the changes). + \endlist + + If there is an error handling any of the above auto-edit actions, + handleError() is called and no insert or update is performed. + + If no error occurred, or auto-editing is not enabled, the data browser + begins actively inserting a record into the database by performing the + following actions: + + \list + \i The default cursor is primed for insert using TQSqlCursor::primeInsert(). + \i The primeInsert() signal is emitted. + \i The form is updated with the values in the default cursor's. + edit buffer so that the user can fill in the values to be inserted. + \endlist + +*/ + +void TQDataBrowser::insert() +{ + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return; + bool doIns = TRUE; + TQSql::Confirm conf = TQSql::Yes; + switch ( d->dat.mode() ) { + case TQSql::Insert: + if ( autoEdit() ) { + if ( confirmInsert() ) + conf = confirmEdit( TQSql::Insert ); + switch ( conf ) { + case TQSql::Yes: + insertCurrent(); + break; + case TQSql::No: + break; + case TQSql::Cancel: + doIns = FALSE; + break; + } + } + break; + default: + if ( autoEdit() && currentEdited() ) { + if ( confirmUpdate() ) + conf = confirmEdit( TQSql::Update ); + switch ( conf ) { + case TQSql::Yes: + updateCurrent(); + break; + case TQSql::No: + break; + case TQSql::Cancel: + doIns = FALSE; + break; + } + } + break; + } + if ( doIns ) { + d->dat.setMode( TQSql::Insert ); + sqlCursor()->primeInsert(); + emit primeInsert( d->frm.record() ); + readFields(); + } +} + + +/*! + Performs an update operation on the data browser's cursor. + + If there is no default cursor or no default form, nothing happens. + Otherwise, the following happens: + + If the data browser is actively inserting a record (see insert()), + that record is inserted into the database using insertCurrent(). + Otherwise, the database is updated with the current form's data + using updateCurrent(). If there is an error handling either + action, handleError() is called. +*/ + +void TQDataBrowser::update() +{ + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return; + TQSql::Confirm conf = TQSql::Yes; + switch ( d->dat.mode() ){ + case TQSql::Insert: + if ( confirmInsert() ) + conf = confirmEdit( TQSql::Insert ); + switch ( conf ) { + case TQSql::Yes: + if ( insertCurrent() ) + d->dat.setMode( TQSql::Update ); + break; + case TQSql::No: + d->dat.setMode( TQSql::Update ); + cur->editBuffer( TRUE ); + readFields(); + break; + case TQSql::Cancel: + break; + } + break; + default: + d->dat.setMode( TQSql::Update ); + if ( confirmUpdate() ) + conf = confirmEdit( TQSql::Update ); + switch ( conf ) { + case TQSql::Yes: + updateCurrent(); + break; + case TQSql::No: + case TQSql::Cancel: + break; + } + break; + } +} + + +/*! + Performs a delete operation on the data browser's cursor. If there + is no default cursor or no default form, nothing happens. + + Otherwise, the following happens: + + The current form's record is deleted from the database, providing + that the data browser is not in insert mode. If the data browser + is actively inserting a record (see insert()), the insert action + is canceled, and the browser navigates to the last valid record + that was current. If there is an error, handleError() is called. +*/ + +void TQDataBrowser::del() +{ + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return; + TQSql::Confirm conf = TQSql::Yes; + switch ( d->dat.mode() ){ + case TQSql::Insert: + if ( confirmCancels() ) + conf = confirmCancel( TQSql::Insert ); + if ( conf == TQSql::Yes ) { + cur->editBuffer( TRUE ); /* restore from cursor */ + readFields(); + d->dat.setMode( TQSql::Update ); + } else + d->dat.setMode( TQSql::Insert ); + break; + default: + if ( confirmDelete() ) + conf = confirmEdit( TQSql::Delete ); + switch ( conf ) { + case TQSql::Yes: + emit primeDelete( buf ); + deleteCurrent(); + break; + case TQSql::No: + case TQSql::Cancel: + break; + } + d->dat.setMode( TQSql::Update ); + break; + } +} + +/*! + Moves the default cursor to the record specified by the index \a i + and refreshes the default form to display this record. If there is + no default form or no default cursor, nothing happens. If \a + relative is TRUE (the default is FALSE), the cursor is moved + relative to its current position. If the data browser successfully + navigated to the desired record, the default cursor is primed for + update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the desired record nothing + happens. +*/ + +bool TQDataBrowser::seek( int i, bool relative ) +{ + int b = 0; + TQSqlCursor* cur = d->cur.cursor(); + if ( !cur ) + return FALSE; + if ( preNav() ) + b = cur->seek( i, relative ); + postNav( b ); + return b; +} + +/*! + Moves the default cursor to the first record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the first record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the first record nothing + happens. + +*/ + +void TQDataBrowser::first() +{ + nav( &TQSqlCursor::first ); +} + + +/*! + Moves the default cursor to the last record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the last record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the last record nothing + happens. +*/ + +void TQDataBrowser::last() +{ + nav( &TQSqlCursor::last ); +} + + +/*! + Moves the default cursor to the next record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the next record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is positioned on the last record nothing happens. +*/ + +void TQDataBrowser::next() +{ + nav( &TQSqlCursor::next ); +} + + +/*! + Moves the default cursor to the previous record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the previous record, the default cursor + is primed for update and the primeUpdate() signal is emitted. + + If the browser is positioned on the first record nothing happens. +*/ + +void TQDataBrowser::prev() +{ + nav( &TQSqlCursor::prev ); +} + +/*! + Reads the fields from the default cursor's edit buffer and + displays them in the form. If there is no default cursor or no + default form, nothing happens. +*/ + +void TQDataBrowser::readFields() +{ + d->frm.readFields(); +} + + +/*! + Writes the form's data to the default cursor's edit buffer. If + there is no default cursor or no default form, nothing happens. +*/ + +void TQDataBrowser::writeFields() +{ + d->frm.writeFields(); +} + + +/*! + Clears all the values in the form. + + All the edit buffer field values are set to their 'zero state', + e.g. 0 for numeric fields and "" for string fields. Then the + widgets are updated using the property map. For example, a + combobox that is property-mapped to integers would scroll to the + first item. See the \l TQSqlPropertyMap constructor for the default + mappings of widgets to properties. +*/ + +void TQDataBrowser::clearValues() +{ + d->frm.clearValues(); +} + +/*! + Reads the fields from the default form into the default cursor and + performs an insert on the default cursor. If there is no default + form or no default cursor, nothing happens. If an error occurred + during the insert into the database, handleError() is called and + FALSE is returned. If the insert was successfull, the cursor is + refreshed and relocated to the newly inserted record, the + cursorChanged() signal is emitted, and TRUE is returned. + + \sa cursorChanged() sqlCursor() form() handleError() +*/ + +bool TQDataBrowser::insertCurrent() +{ + if ( isReadOnly() ) + return FALSE; + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return FALSE; + writeFields(); + emit beforeInsert( buf ); + int ar = cur->insert(); + if ( !ar || !cur->isActive() ) { + handleError( cur->lastError() ); + refresh(); + updateBoundary(); + } else { + refresh(); + d->cur.findBuffer( cur->primaryIndex() ); + updateBoundary(); + cursorChanged( TQSqlCursor::Insert ); + return TRUE; + } + return FALSE; +} + + +/*! + Reads the fields from the default form into the default cursor and + performs an update on the default cursor. If there is no default + form or no default cursor, nothing happens. If an error occurred + during the update on the database, handleError() is called and + FALSE is returned. If the update was successfull, the cursor is + refreshed and relocated to the updated record, the cursorChanged() + signal is emitted, and TRUE is returned. + + \sa cursor() form() handleError() +*/ + +bool TQDataBrowser::updateCurrent() +{ + if ( isReadOnly() ) + return FALSE; + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return FALSE; + writeFields(); + emit beforeUpdate( buf ); + int ar = cur->update(); + if ( !ar || !cur->isActive() ) { + handleError( cur->lastError() ); + refresh(); + updateBoundary(); + } else { + refresh(); + d->cur.findBuffer( cur->primaryIndex() ); + updateBoundary(); + cur->editBuffer( TRUE ); + cursorChanged( TQSqlCursor::Update ); + readFields(); + return TRUE; + } + return FALSE; +} + + +/*! + Performs a delete on the default cursor using the values from the + default form and updates the default form. If there is no default + form or no default cursor, nothing happens. If the deletion was + successful, the cursor is repositioned to the nearest record and + TRUE is returned. The nearest record is the next record if there + is one otherwise the previous record if there is one. If an error + occurred during the deletion from the database, handleError() is + called and FALSE is returned. + + \sa cursor() form() handleError() +*/ + +bool TQDataBrowser::deleteCurrent() +{ + if ( isReadOnly() ) + return FALSE; + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return FALSE; + writeFields(); + int n = cur->at(); + emit beforeDelete( buf ); + int ar = cur->del(); + if ( ar ) { + refresh(); + updateBoundary(); + cursorChanged( TQSqlCursor::Delete ); + if ( !cur->seek( n ) ) + last(); + if ( cur->isValid() ) { + cur->editBuffer( TRUE ); + readFields(); + } else { + clearValues(); + } + return TRUE; + } else { + if ( !cur->isActive() ) { + handleError( cur->lastError() ); + refresh(); + updateBoundary(); + } + } + return FALSE; +} + + +/*! + Returns TRUE if the form's edit buffer differs from the current + cursor buffer; otherwise returns FALSE. +*/ + +bool TQDataBrowser::currentEdited() +{ + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return FALSE; + if ( !cur->isActive() || !cur->isValid() ) + return FALSE; + writeFields(); + for ( uint i = 0; i < cur->count(); ++i ) { + if ( cur->value(i) != buf->value(i) ) + return TRUE; + } + return FALSE; +} + +/*! \internal + + Pre-navigation checking. +*/ + +bool TQDataBrowser::preNav() +{ + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return FALSE; + + if ( !isReadOnly() && autoEdit() && currentEdited() ) { + bool ok = TRUE; + TQSql::Confirm conf = TQSql::Yes; + switch ( d->dat.mode() ){ + case TQSql::Insert: + if ( confirmInsert() ) + conf = confirmEdit( TQSql::Insert ); + switch ( conf ) { + case TQSql::Yes: + ok = insertCurrent(); + d->dat.setMode( TQSql::Update ); + break; + case TQSql::No: + d->dat.setMode( TQSql::Update ); + break; + case TQSql::Cancel: + return FALSE; + } + break; + default: + if ( confirmUpdate() ) + conf = confirmEdit( TQSql::Update ); + switch ( conf ) { + case TQSql::Yes: + ok = updateCurrent(); + break; + case TQSql::No: + break; + case TQSql::Cancel: + return FALSE; + } + } + return ok; + } + return TRUE; +} + +/*! \internal + + Handles post-navigation according to \a primeUpd. +*/ + +void TQDataBrowser::postNav( bool primeUpd ) +{ + if ( primeUpd ) { + TQSqlRecord* buf = d->frm.record(); + TQSqlCursor* cur = d->cur.cursor(); + if ( !buf || !cur ) + return; + currentChanged( cur ); + cur->primeUpdate(); + emit primeUpdate( buf ); + readFields(); + } + updateBoundary(); +} + +/*! \internal + + Navigate default cursor according to \a nav. Handles autoEdit. + +*/ +void TQDataBrowser::nav( Nav nav ) +{ + int b = 0; + TQSqlCursor* cur = d->cur.cursor(); + if ( !cur ) + return; + if ( preNav() ) + b = (cur->*nav)(); + postNav( b ); +} + +/*! + If boundaryChecking() is TRUE, checks the boundary of the current + default cursor and emits signals which indicate the position of + the cursor. +*/ + +void TQDataBrowser::updateBoundary() +{ + if ( d->boundaryCheck ) { + Boundary bound = boundary(); + switch ( bound ) { + case Unknown: + case None: + emit firstRecordAvailable( TRUE ); + emit prevRecordAvailable( TRUE ); + emit nextRecordAvailable( TRUE ); + emit lastRecordAvailable( TRUE ); + break; + + case BeforeBeginning: + emit firstRecordAvailable( FALSE ); + emit prevRecordAvailable( FALSE ); + emit nextRecordAvailable( TRUE ); + emit lastRecordAvailable( TRUE ); + break; + + case Beginning: + emit firstRecordAvailable( FALSE ); + emit prevRecordAvailable( FALSE ); + emit nextRecordAvailable( TRUE ); + emit lastRecordAvailable( TRUE ); + break; + + case End: + emit firstRecordAvailable( TRUE ); + emit prevRecordAvailable( TRUE ); + emit nextRecordAvailable( FALSE ); + emit lastRecordAvailable( FALSE ); + break; + + case AfterEnd: + emit firstRecordAvailable( TRUE ); + emit prevRecordAvailable( TRUE ); + emit nextRecordAvailable( FALSE ); + emit lastRecordAvailable( FALSE ); + break; + } + } +} + +/*! + Virtual function which handles the error \a error. The default + implementation warns the user with a message box. +*/ + +void TQDataBrowser::handleError( const TQSqlError& error ) +{ + d->dat.handleError( this, error ); +} + +/*! + Protected virtual function which returns a confirmation for an + edit of mode \a m. Derived classes can reimplement this function + and provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +TQSql::Confirm TQDataBrowser::confirmEdit( TQSql::Op m ) +{ + return d->dat.confirmEdit( this, m ); +} + +/*! + Protected virtual function which returns a confirmation for + cancelling an edit mode \a m. Derived classes can reimplement this + function and provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +TQSql::Confirm TQDataBrowser::confirmCancel( TQSql::Op m ) +{ + return d->dat.confirmCancel( this, m ); +} + +/*! + \fn void TQDataBrowser::beforeInsert( TQSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + inserted into the database. The \a buf parameter points to the + edit buffer being inserted. You might connect to this signal to + populate a generated primary key for example. +*/ + +/*! + \fn void TQDataBrowser::beforeUpdate( TQSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + updated in the database. The \a buf parameter points to the edit + buffer being updated. You might connect to this signal to capture + some auditing information about the update. +*/ + +/*! + \fn void TQDataBrowser::beforeDelete( TQSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + deleted from the database. The \a buf parameter points to the edit + buffer being deleted. You might connect to this signal to capture + some auditing information about the deletion. +*/ + +#endif diff --git a/src/sql/qdatabrowser.h b/src/sql/qdatabrowser.h new file mode 100644 index 000000000..b7aee2bf8 --- /dev/null +++ b/src/sql/qdatabrowser.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Definition of TQDataBrowser class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDATABROWSER_H +#define TQDATABROWSER_H + +#ifndef QT_H +#include "qwidget.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qsql.h" +#include "qsqlindex.h" +#include "qsqlcursor.h" +#include "qsqlerror.h" +#endif // QT_H + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class TQSqlForm; +class TQDataBrowserPrivate; + +class Q_EXPORT TQDataBrowser : public TQWidget +{ + Q_OBJECT + Q_PROPERTY( bool boundaryChecking READ boundaryChecking WRITE setBoundaryChecking ) + Q_PROPERTY( TQString filter READ filter WRITE setFilter ) + Q_PROPERTY( TQStringList sort READ sort WRITE setSort ) + Q_PROPERTY( bool confirmEdits READ confirmEdits WRITE setConfirmEdits ) + Q_PROPERTY( bool confirmInsert READ confirmInsert WRITE setConfirmInsert ) + Q_PROPERTY( bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate ) + Q_PROPERTY( bool confirmDelete READ confirmDelete WRITE setConfirmDelete ) + Q_PROPERTY( bool confirmCancels READ confirmCancels WRITE setConfirmCancels ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool autoEdit READ autoEdit WRITE setAutoEdit ) + +public: + TQDataBrowser( TQWidget* parent=0, const char* name=0, WFlags fl = 0 ); + ~TQDataBrowser(); + + enum Boundary { + Unknown, + None, + BeforeBeginning, + Beginning, + End, + AfterEnd + }; + + Boundary boundary(); + void setBoundaryChecking( bool active ); + bool boundaryChecking() const; + + void setSort( const TQSqlIndex& sort ); + void setSort( const TQStringList& sort ); + TQStringList sort() const; + void setFilter( const TQString& filter ); + TQString filter() const; + virtual void setSqlCursor( TQSqlCursor* cursor, bool autoDelete = FALSE ); + TQSqlCursor* sqlCursor() const; + virtual void setForm( TQSqlForm* form ); + TQSqlForm* form(); + + virtual void setConfirmEdits( bool confirm ); + virtual void setConfirmInsert( bool confirm ); + virtual void setConfirmUpdate( bool confirm ); + virtual void setConfirmDelete( bool confirm ); + virtual void setConfirmCancels( bool confirm ); + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + + virtual void setReadOnly( bool active ); + bool isReadOnly() const; + virtual void setAutoEdit( bool autoEdit ); + bool autoEdit() const; + + virtual bool seek( int i, bool relative = FALSE ); + +signals: + void firstRecordAvailable( bool available ); + void lastRecordAvailable( bool available ); + void nextRecordAvailable( bool available ); + void prevRecordAvailable( bool available ); + + void currentChanged( const TQSqlRecord* record ); + void primeInsert( TQSqlRecord* buf ); + void primeUpdate( TQSqlRecord* buf ); + void primeDelete( TQSqlRecord* buf ); + void beforeInsert( TQSqlRecord* buf ); + void beforeUpdate( TQSqlRecord* buf ); + void beforeDelete( TQSqlRecord* buf ); + void cursorChanged( TQSqlCursor::Mode mode ); + +public slots: + virtual void refresh(); + + virtual void insert(); + virtual void update(); + virtual void del(); + + virtual void first(); + virtual void last(); + virtual void next(); + virtual void prev(); + + virtual void readFields(); + virtual void writeFields(); + virtual void clearValues(); + + void updateBoundary(); + +protected: + virtual bool insertCurrent(); + virtual bool updateCurrent(); + virtual bool deleteCurrent(); + virtual bool currentEdited(); + + virtual TQSql::Confirm confirmEdit( TQSql::Op m ); + virtual TQSql::Confirm confirmCancel( TQSql::Op m ); + + virtual void handleError( const TQSqlError& error ); + +private: + typedef bool (TQSqlCursor::*Nav)(); + bool preNav(); + void postNav( bool primeUpd ); + void nav( Nav nav ); + TQDataBrowserPrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQDataBrowser( const TQDataBrowser & ); + TQDataBrowser &operator=( const TQDataBrowser & ); +#endif +}; + + +#endif +#endif diff --git a/src/sql/qdatatable.cpp b/src/sql/qdatatable.cpp new file mode 100644 index 000000000..8edc7f230 --- /dev/null +++ b/src/sql/qdatatable.cpp @@ -0,0 +1,2322 @@ +/**************************************************************************** +** +** Implementation of TQDataTable class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qdatatable.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "qsqldriver.h" +#include "qsqleditorfactory.h" +#include "qsqlpropertymap.h" +#include "qapplication.h" +#include "qlayout.h" +#include "qpainter.h" +#include "qpopupmenu.h" +#include "qvaluelist.h" +#include "qsqlmanager_p.h" +#include "qdatetime.h" +#include "qcursor.h" +#include "qtimer.h" + +//#define QT_DEBUG_DATATABLE + +class TQDataTablePrivate +{ +public: + TQDataTablePrivate() + : nullTxtChanged( FALSE ), + haveAllRows( FALSE ), + continuousEdit( FALSE ), + editorFactory( 0 ), + propertyMap( 0 ), + editRow( -1 ), + editCol( -1 ), + insertRowLast( -1 ), + insertPreRows( -1 ), + editBuffer( 0 ), + cancelMode( FALSE ), + cancelInsert( FALSE ), + cancelUpdate( FALSE ) + {} + ~TQDataTablePrivate() { if ( propertyMap ) delete propertyMap; } + + TQString nullTxt; + bool nullTxtChanged; + typedef TQValueList< uint > ColIndex; + ColIndex colIndex; + bool haveAllRows; + bool continuousEdit; + TQSqlEditorFactory* editorFactory; + TQSqlPropertyMap* propertyMap; + TQString trueTxt; + TQt::DateFormat datefmt; + TQString falseTxt; + int editRow; + int editCol; + int insertRowLast; + TQString insertHeaderLabelLast; + int insertPreRows; + TQSqlRecord* editBuffer; + bool cancelMode; + bool cancelInsert; + bool cancelUpdate; + int lastAt; + TQString ftr; + TQStringList srt; + TQStringList fld; + TQStringList fldLabel; + TQValueList<int> fldWidth; + TQValueList<TQIconSet> fldIcon; + TQValueList<bool> fldHidden; + TQSqlCursorManager cur; + TQDataManager dat; +}; + +#ifdef QT_DEBUG_DATATABLE +void qt_debug_buffer( const TQString& msg, TQSqlRecord* cursor ) +{ + qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + qDebug(msg); + for ( uint j = 0; j < cursor->count(); ++j ) { + qDebug(cursor->field(j)->name() + " type:" + TQString(cursor->field(j)->value().typeName()) + " value:" + cursor->field(j)->value().toString() ); + } +} +#endif + +/*! + \enum TQDataTable::Refresh + + This enum describes the refresh options. + + \value RefreshData refresh the data, i.e. read it from the database + \value RefreshColumns refresh the list of fields, e.g. the column headings + \value RefreshAll refresh both the data and the list of fields +*/ + + +/*! + \class TQDataTable qdatatable.h + \brief The TQDataTable class provides a flexible SQL table widget that supports browsing and editing. + + \ingroup database + \mainclass + \module sql + + TQDataTable supports various functions for presenting and editing + SQL data from a \l TQSqlCursor in a table. + + If you want a to present your data in a form use TQDataBrowser, or + for read-only forms, TQDataView. + + When displaying data, TQDataTable only retrieves data for visible + rows. If the driver supports the 'query size' property the + TQDataTable will have the correct number of rows and the vertical + scrollbar will accurately reflect the number of rows displayed in + proportion to the number of rows in the dataset. If the driver + does not support the 'query size' property, rows are dynamically + fetched from the database on an as-needed basis with the scrollbar + becoming more accurate as the user scrolls down through the + records. This allows extremely large queries to be displayed as + tquickly as possible, with minimum memory usage. + + TQDataTable inherits TQTable's API and extends it with functions to + sort and filter the data and sort columns. See setSqlCursor(), + setFilter(), setSort(), setSorting(), sortColumn() and refresh(). + + When displaying editable cursors, cell editing will be enabled. + (For more information on editable cursors, see \l TQSqlCursor). + TQDataTable can be used to modify existing data and to add new + records. When a user makes changes to a field in the table, the + cursor's edit buffer is used. The table will not send changes in + the edit buffer to the database until the user moves to a + different record in the grid or presses Enter. Cell editing is + initiated by pressing F2 (or right clicking and then clicking the + appropriate popup menu item) and canceled by pressing Esc. If + there is a problem updating or adding data, errors are handled + automatically (see handleError() to change this behavior). Note + that if autoEdit() is FALSE navigating to another record will + cancel the insert or update. + + The user can be asked to confirm all edits with setConfirmEdits(). + For more precise control use setConfirmInsert(), + setConfirmUpdate(), setConfirmDelete() and setConfirmCancels(). + Use setAutoEdit() to control the behaviour of the table when the + user edits a record and then navigates. (Note that setAutoDelete() + is unrelated; it is used to set whether the TQSqlCursor is deleted + when the table is deleted.) + + Since the data table can perform edits, it must be able to + uniquely identify every record so that edits are correctly + applied. Because of this the underlying cursor must have a valid + primary index to ensure that a unique record is inserted, updated + or deleted within the database otherwise the database may be + changed to an inconsistent state. + + TQDataTable creates editors using the default \l TQSqlEditorFactory. + Different editor factories can be used by calling + installEditorFactory(). A property map is used to map between the + cell's value and the editor. You can use your own property map + with installPropertyMap(). + + The contents of a cell is available as a TQString with text() or as + a TQVariant with value(). The current record is returned by + currentRecord(). Use the find() function to search for a string in + the table. + + Editing actions can be applied programatically. For example, the + insertCurrent() function reads the fields from the current record + into the cursor and performs the insert. The updateCurrent() and + deleteCurrent() functions perform similarly to update and delete + the current record respectively. + + Columns in the table can be created automatically based on the + cursor (see setSqlCursor()). Columns can be manipulated manually + using addColumn(), removeColumn() and setColumn(). + + The table automatically copies many of the properties of the + cursor to format the display of data within cells (alignment, + visibility, etc.). The cursor can be changed with setSqlCursor(). + The filter (see setFilter()) and sort defined within the table are + used instead of the filter and sort set on the cursor. For sorting + options see setSort(), sortColumn(), sortAscending() and + sortDescending(). Note that sorting operations will not behave as + expected if you are using a TQSqlSelectCursor because it uses + user-defined SQL queries to obtain data. + + The text used to represent NULL, TRUE and FALSE values can be + changed with setNullText(), setTrueText() and setFalseText() + respectively. You can change the appearance of cells by + reimplementing paintField(). + + Whenever a new row is selected in the table the currentChanged() + signal is emitted. The primeInsert() signal is emitted when an + insert is initiated. The primeUpdate() and primeDelete() signals + are emitted when update and deletion are initiated respectively. + Just before the database is updated a signal is emitted; + beforeInsert(), beforeUpdate() or beforeDelete() as appropriate. + +*/ + +/*! + Constructs a data table which is a child of \a parent, called + name \a name. +*/ + +TQDataTable::TQDataTable ( TQWidget * parent, const char * name ) + : TQTable( parent, name ) +{ + init(); +} + +/*! + Constructs a data table which is a child of \a parent, called name + \a name using the cursor \a cursor. + + If \a autoPopulate is TRUE (the default is FALSE), columns are + automatically created based upon the fields in the \a cursor + record. Note that \a autoPopulate only governs the creation of + columns; to load the cursor's data into the table use refresh(). + + If the \a cursor is read-only, the table also becomes read-only. + In addition, the table adopts the cursor's driver's definition for + representing NULL values as strings. +*/ + +TQDataTable::TQDataTable ( TQSqlCursor* cursor, bool autoPopulate, TQWidget * parent, const char * name ) + : TQTable( parent, name ) +{ + init(); + setSqlCursor( cursor, autoPopulate ); +} + +/*! \internal +*/ + + +void TQDataTable::init() +{ + d = new TQDataTablePrivate(); + setAutoEdit( TRUE ); + setSelectionMode( SingleRow ); + setFocusStyle( FollowStyle ); + d->trueTxt = tr( "True" ); + d->falseTxt = tr( "False" ); + d->datefmt = TQt::LocalDate; + reset(); + connect( this, SIGNAL( selectionChanged() ), + SLOT( updateCurrentSelection())); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDataTable::~TQDataTable() +{ + delete d; +} + + +/*! + Adds the next column to be displayed using the field \a fieldName, + column label \a label, width \a width and iconset \a iconset. + + If \a label is specified, it is used as the column's header label, + otherwise the field's display label is used when setSqlCursor() is + called. The \a iconset is used to set the icon used by the column + header; by default there is no icon. + + \sa setSqlCursor() refresh() +*/ + +void TQDataTable::addColumn( const TQString& fieldName, + const TQString& label, + int width, + const TQIconSet& iconset ) +{ + d->fld += fieldName; + d->fldLabel += label; + d->fldIcon += iconset; + d->fldWidth += width; + d->fldHidden += FALSE; +} + +/*! + Sets the \a col column to display using the field \a fieldName, + column label \a label, width \a width and iconset \a iconset. + + If \a label is specified, it is used as the column's header label, + otherwise the field's display label is used when setSqlCursor() is + called. The \a iconset is used to set the icon used by the column + header; by default there is no icon. + + \sa setSqlCursor() refresh() +*/ + +void TQDataTable::setColumn( uint col, const TQString& fieldName, + const TQString& label, + int width, + const TQIconSet& iconset ) +{ + d->fld[col]= fieldName; + d->fldLabel[col] = label; + d->fldIcon[col] = iconset; + d->fldWidth[col] = width; + d->fldHidden[col] = FALSE; +} + +/*! + Removes column \a col from the list of columns to be displayed. If + \a col does not exist, nothing happens. + + \sa TQSqlField +*/ + +void TQDataTable::removeColumn( uint col ) +{ + if ( d->fld.at( col ) != d->fld.end() ) { + d->fld.remove( d->fld.at( col ) ); + d->fldLabel.remove( d->fldLabel.at( col ) ); + d->fldIcon.remove( d->fldIcon.at( col ) ); + d->fldWidth.remove( d->fldWidth.at( col ) ); + d->fldHidden.remove( d->fldHidden.at( col ) ); + } +} + +/*! + Sets the column \a col to the width \a w. Note that unlike TQTable + the TQDataTable is not immediately redrawn, you must call + refresh(TQDataTable::RefreshColumns) + yourself. + + \sa refresh() +*/ +void TQDataTable::setColumnWidth( int col, int w ) +{ + if ( d->fldWidth.at( col ) != d->fldWidth.end() ) { + d->fldWidth[col] = w; + } +} + +/*! + Resizes column \a col so that the column width is wide enough to + display the widest item the column contains (including the column + label). If the table's TQSqlCursor is not currently active, the + cursor will be refreshed before the column width is calculated. Be + aware that this function may be slow on tables that contain large + result sets. +*/ +void TQDataTable::adjustColumn( int col ) +{ + TQSqlCursor * cur = sqlCursor(); + if ( !cur || cur->count() <= (uint)col ) + return; + if ( !cur->isActive() ) { + d->cur.refresh(); + } + int oldRow = currentRow(); + int w = fontMetrics().width( horizontalHeader()->label( col ) + "W" ); + cur->seek( TQSql::BeforeFirst ); + while ( cur->next() ) { + w = TQMAX( w, fontMetrics().width( fieldToString( cur->field( indexOf( col ) ) ) ) + 10 ); + } + setColumnWidth( col, w ); + cur->seek( oldRow ); + refresh( RefreshColumns ); +} + +/*! \reimp +*/ +void TQDataTable::setColumnStretchable( int col, bool s ) +{ + if ( numCols() == 0 ) { + refresh( RefreshColumns ); + } + if ( numCols() > col ) { + TQTable::setColumnStretchable( col, s ); + } +} + +TQString TQDataTable::filter() const +{ + return d->cur.filter(); +} + +/*! + \property TQDataTable::filter + \brief the data filter for the data table + + The filter applies to the data shown in the table. To view data + with a new filter, use refresh(). A filter string is an SQL WHERE + clause without the WHERE keyword. + + There is no default filter. + + \sa sort() + +*/ + +void TQDataTable::setFilter( const TQString& filter ) +{ + d->cur.setFilter( filter ); +} + + +/*! + \property TQDataTable::sort + \brief the data table's sort + + The table's sort affects the order in which data records are + displayed in the table. To apply a sort, use refresh(). + + When examining the sort property, a string list is returned with + each item having the form 'fieldname order' (e.g., 'id ASC', + 'surname DESC'). + + There is no default sort. + + Note that if you want to iterate over the sort list, you should + iterate over a copy, e.g. + \code + TQStringList list = myDataTable.sort(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + + \sa filter() refresh() +*/ + +void TQDataTable::setSort( const TQStringList& sort ) +{ + d->cur.setSort( sort ); +} + +/*! + \overload + + Sets the sort to be applied to the displayed data to \a sort. If + there is no current cursor, nothing happens. A TQSqlIndex contains + field names and their ordering (ASC or DESC); these are used to + compose the ORDER BY clause. + + \sa sort() +*/ + +void TQDataTable::setSort( const TQSqlIndex& sort ) +{ + d->cur.setSort( sort ); +} + +TQStringList TQDataTable::sort() const +{ + return d->cur.sort(); +} + +/*! + Returns the cursor used by the data table. +*/ + +TQSqlCursor* TQDataTable::sqlCursor() const +{ + return d->cur.cursor(); +} + +void TQDataTable::setConfirmEdits( bool confirm ) +{ + d->dat.setConfirmEdits( confirm ); +} + +void TQDataTable::setConfirmInsert( bool confirm ) +{ + d->dat.setConfirmInsert( confirm ); +} + +void TQDataTable::setConfirmUpdate( bool confirm ) +{ + d->dat.setConfirmUpdate( confirm ); +} + +void TQDataTable::setConfirmDelete( bool confirm ) +{ + d->dat.setConfirmDelete( confirm ); +} + +/*! + \property TQDataTable::confirmEdits + \brief whether the data table confirms edit operations + + If the confirmEdits property is TRUE, the data table confirms all + edit operations (inserts, updates and deletes). Finer control of + edit confirmation can be achieved using \l confirmCancels, \l + confirmInsert, \l confirmUpdate and \l confirmDelete. + + \sa confirmCancels() confirmInsert() confirmUpdate() confirmDelete() +*/ + +bool TQDataTable::confirmEdits() const +{ + return ( d->dat.confirmEdits() ); +} + +/*! + \property TQDataTable::confirmInsert + \brief whether the data table confirms insert operations + + If the confirmInsert property is TRUE, all insertions must be + confirmed by the user through a message box (this behaviour can be + changed by overriding the confirmEdit() function), otherwise all + insert operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete() +*/ + +bool TQDataTable::confirmInsert() const +{ + return ( d->dat.confirmInsert() ); +} + +/*! + \property TQDataTable::confirmUpdate + \brief whether the data table confirms update operations + + If the confirmUpdate property is TRUE, all updates must be + confirmed by the user through a message box (this behaviour can be + changed by overriding the confirmEdit() function), otherwise all + update operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete() +*/ + +bool TQDataTable::confirmUpdate() const +{ + return ( d->dat.confirmUpdate() ); +} + +/*! + \property TQDataTable::confirmDelete + \brief whether the data table confirms delete operations + + If the confirmDelete property is TRUE, all deletions must be + confirmed by the user through a message box (this behaviour can be + changed by overriding the confirmEdit() function), otherwise all + delete operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert() +*/ + +bool TQDataTable::confirmDelete() const +{ + return ( d->dat.confirmDelete() ); +} + +/*! + \property TQDataTable::confirmCancels + \brief whether the data table confirms cancel operations + + If the confirmCancel property is TRUE, all cancels must be + confirmed by the user through a message box (this behavior can be + changed by overriding the confirmCancel() function), otherwise all + cancels occur immediately. The default is FALSE. + + \sa confirmEdits() confirmCancel() +*/ + +void TQDataTable::setConfirmCancels( bool confirm ) +{ + d->dat.setConfirmCancels( confirm ); +} + +bool TQDataTable::confirmCancels() const +{ + return d->dat.confirmCancels(); +} + +/*! + \reimp + + For an editable table, creates an editor suitable for the field in + column \a col. The editor is created using the default editor + factory, unless a different editor factory was installed with + installEditorFactory(). The editor is primed with the value of the + field in \a col using a property map. The property map used is the + default property map, unless a new property map was installed with + installPropertMap(). If \a initFromCell is TRUE then the editor is + primed with the value in the TQDataTable cell. +*/ + +TQWidget * TQDataTable::createEditor( int , int col, bool initFromCell ) const +{ + if ( d->dat.mode() == TQSql::None ) + return 0; + + TQSqlEditorFactory * f = (d->editorFactory == 0) ? + TQSqlEditorFactory::defaultFactory() : d->editorFactory; + + TQSqlPropertyMap * m = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + + TQWidget * w = 0; + if( initFromCell && d->editBuffer ){ + w = f->createEditor( viewport(), d->editBuffer->field( indexOf( col ) ) ); + if ( w ) + m->setProperty( w, d->editBuffer->value( indexOf( col ) ) ); + } + return w; +} + +/*! \reimp */ +bool TQDataTable::eventFilter( TQObject *o, TQEvent *e ) +{ + if ( d->cancelMode ) + return TRUE; + + int r = currentRow(); + int c = currentColumn(); + + if ( d->dat.mode() != TQSql::None ) { + r = d->editRow; + c = d->editCol; + } + + d->cancelInsert = FALSE; + d->cancelUpdate = FALSE; + switch ( e->type() ) { + case TQEvent::KeyPress: { + int conf = TQSql::Yes; + TQKeyEvent *ke = (TQKeyEvent*)e; + if ( ( ke->key() == Key_Tab || ke->key() == TQt::Key_BackTab ) + && ke->state() & TQt::ControlButton ) + return FALSE; + + if ( ke->key() == Key_Escape && d->dat.mode() == TQSql::Insert ){ + if ( confirmCancels() && !d->cancelMode ) { + d->cancelMode = TRUE; + conf = confirmCancel( TQSql::Insert ); + d->cancelMode = FALSE; + } + if ( conf == TQSql::Yes ) { + d->cancelInsert = TRUE; + } else { + TQWidget *editorWidget = cellWidget( r, c ); + if ( editorWidget ) { + editorWidget->setActiveWindow(); + editorWidget->setFocus(); + } + return TRUE; + } + } + if ( ke->key() == Key_Escape && d->dat.mode() == TQSql::Update ) { + if ( confirmCancels() && !d->cancelMode ) { + d->cancelMode = TRUE; + conf = confirmCancel( TQSql::Update ); + d->cancelMode = FALSE; + } + if ( conf == TQSql::Yes ){ + d->cancelUpdate = TRUE; + } else { + TQWidget *editorWidget = cellWidget( r, c ); + if ( editorWidget ) { + editorWidget->setActiveWindow(); + editorWidget->setFocus(); + } + return TRUE; + } + } + if ( ke->key() == Key_Insert && d->dat.mode() == TQSql::None ) { + beginInsert(); + return TRUE; + } + if ( ke->key() == Key_Delete && d->dat.mode() == TQSql::None ) { + deleteCurrent(); + return TRUE; + } + if ( d->dat.mode() != TQSql::None ) { + if ( (ke->key() == Key_Tab) && (c < numCols() - 1) && (!isColumnReadOnly( c+1 ) || d->dat.mode() == TQSql::Insert) ) + d->continuousEdit = TRUE; + else if ( (ke->key() == Key_BackTab) && (c > 0) && (!isColumnReadOnly( c-1 ) || d->dat.mode() == TQSql::Insert) ) + d->continuousEdit = TRUE; + else + d->continuousEdit = FALSE; + } + TQSqlCursor * sql = sqlCursor(); + if ( sql && sql->driver() && + !sql->driver()->hasFeature( TQSqlDriver::QuerySize ) && + ke->key() == Key_End && d->dat.mode() == TQSql::None ) { +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::WaitCursor ); +#endif + int i = sql->at(); + if ( i < 0 ) { + i = 0; + sql->seek(0); + } + while ( sql->next() ) + i++; + setNumRows( i+1 ); + setCurrentCell( i+1, currentColumn() ); +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + return TRUE; + } + break; + } + case TQEvent::FocusOut: { + TQWidget *editorWidget = cellWidget( r, c ); + repaintCell( currentRow(), currentColumn() ); + if ( !d->cancelMode && editorWidget && o == editorWidget && + ( d->dat.mode() == TQSql::Insert) && !d->continuousEdit) { + setCurrentCell( r, c ); + d->cancelInsert = TRUE; + } + d->continuousEdit = FALSE; + break; + } + case TQEvent::FocusIn: + repaintCell( currentRow(), currentColumn() ); + break; + default: + break; + } + return TQTable::eventFilter( o, e ); +} + +/*! \reimp */ +void TQDataTable::resizeEvent ( TQResizeEvent * e ) +{ + if ( sqlCursor() && + sqlCursor()->driver() && + !sqlCursor()->driver()->hasFeature( TQSqlDriver::QuerySize ) ) + loadNextPage(); + TQTable::resizeEvent( e ); +} + +/*! \reimp */ +void TQDataTable::contentsContextMenuEvent( TQContextMenuEvent* e ) +{ + TQTable::contentsContextMenuEvent( e ); + if ( isEditing() && d->dat.mode() != TQSql::None ) + endEdit( d->editRow, d->editCol, autoEdit(), FALSE ); + if ( !sqlCursor() ) + return; + if ( d->dat.mode() == TQSql::None ) { + if ( isReadOnly() ) + return; + enum { + IdInsert, + IdUpdate, + IdDelete + }; + TQGuardedPtr<TQPopupMenu> popup = new TQPopupMenu( this, "qt_datatable_menu" ); + int id[ 3 ]; + id[ IdInsert ] = popup->insertItem( tr( "Insert" ) ); + id[ IdUpdate ] = popup->insertItem( tr( "Update" ) ); + id[ IdDelete ] = popup->insertItem( tr( "Delete" ) ); + bool enableInsert = sqlCursor()->canInsert(); + popup->setItemEnabled( id[ IdInsert ], enableInsert ); + bool enableUpdate = currentRow() > -1 && sqlCursor()->canUpdate() && !isColumnReadOnly( currentColumn() ); + popup->setItemEnabled( id[ IdUpdate ], enableUpdate ); + bool enableDelete = currentRow() > -1 && sqlCursor()->canDelete(); + popup->setItemEnabled( id[ IdDelete ], enableDelete ); + int r = popup->exec( e->globalPos() ); + delete (TQPopupMenu*) popup; + if ( r == id[ IdInsert ] ) + beginInsert(); + else if ( r == id[ IdUpdate ] ) { + if ( beginEdit( currentRow(), currentColumn(), FALSE ) ) + setEditMode( Editing, currentRow(), currentColumn() ); + else + endUpdate(); + } + else if ( r == id[ IdDelete ] ) + deleteCurrent(); + e->accept(); + } +} + +/*! \reimp */ +void TQDataTable::contentsMousePressEvent( TQMouseEvent* e ) +{ + TQTable::contentsMousePressEvent( e ); +} + +/*! \reimp */ +TQWidget* TQDataTable::beginEdit ( int row, int col, bool replace ) +{ + d->editRow = -1; + d->editCol = -1; + if ( !sqlCursor() ) + return 0; + if ( d->dat.mode() == TQSql::Insert && !sqlCursor()->canInsert() ) + return 0; + if ( d->dat.mode() == TQSql::Update && !sqlCursor()->canUpdate() ) + return 0; + d->editRow = row; + d->editCol = col; + if ( d->continuousEdit ) { + // see comment in beginInsert() + bool fakeReadOnly = isColumnReadOnly( col ); + setColumnReadOnly( col, FALSE ); + TQWidget* w = TQTable::beginEdit( row, col, replace ); + setColumnReadOnly( col, fakeReadOnly ); + return w; + } + if ( d->dat.mode() == TQSql::None && sqlCursor()->canUpdate() && sqlCursor()->primaryIndex().count() > 0 ) + return beginUpdate( row, col, replace ); + return 0; +} + +/*! \reimp */ +void TQDataTable::endEdit( int row, int col, bool, bool ) +{ + bool accept = autoEdit() && !d->cancelInsert && !d->cancelUpdate; + + TQWidget *editor = cellWidget( row, col ); + if ( !editor ) + return; + if ( d->cancelMode ) + return; + if ( d->dat.mode() != TQSql::None && d->editBuffer ) { + TQSqlPropertyMap * m = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + d->editBuffer->setValue( indexOf( col ), m->property( editor ) ); + clearCellWidget( row, col ); + if ( !d->continuousEdit ) { + switch ( d->dat.mode() ) { + case TQSql::Insert: + if ( accept ) + TQTimer::singleShot( 0, this, SLOT( doInsertCurrent() ) ); + else + endInsert(); + break; + case TQSql::Update: + if ( accept ) + TQTimer::singleShot( 0, this, SLOT( doUpdateCurrent() ) ); + else + endUpdate(); + break; + default: + break; + } + } + } else { + setEditMode( NotEditing, -1, -1 ); + } + if ( d->dat.mode() == TQSql::None ) + viewport()->setFocus(); + updateCell( row, col ); + emit valueChanged( row, col ); +} + +/*! \internal */ +void TQDataTable::doInsertCurrent() +{ + insertCurrent(); +} + +/*! \internal */ +void TQDataTable::doUpdateCurrent() +{ + updateCurrent(); + if ( d->dat.mode() == TQSql::None ) { + viewport()->setFocus(); + } +} + +/*! \reimp */ +void TQDataTable::activateNextCell() +{ +// if ( d->dat.mode() == TQSql::None ) +// TQTable::activateNextCell(); +} + +/*! \internal +*/ + +void TQDataTable::endInsert() +{ + if ( d->dat.mode() != TQSql::Insert ) + return; + d->dat.setMode( TQSql::None ); + d->editBuffer = 0; + verticalHeader()->setLabel( d->editRow, TQString::number( d->editRow +1 ) ); + d->editRow = -1; + d->editCol = -1; + d->insertRowLast = -1; + d->insertHeaderLabelLast = TQString::null; + setEditMode( NotEditing, -1, -1 ); + setNumRows( d->insertPreRows ); + d->insertPreRows = -1; + viewport()->setFocus(); +} + +/*! \internal +*/ + +void TQDataTable::endUpdate() +{ + d->dat.setMode( TQSql::None ); + d->editBuffer = 0; + updateRow( d->editRow ); + d->editRow = -1; + d->editCol = -1; + setEditMode( NotEditing, -1, -1 ); +} + +/*! + Protected virtual function called when editing is about to begin + on a new record. If the table is read-only, or if there's no + cursor or the cursor does not allow inserts, nothing happens. + + Editing takes place using the cursor's edit buffer(see + TQSqlCursor::editBuffer()). + + When editing begins, a new row is created in the table marked with + an asterisk '*' in the row's vertical header column, i.e. at the + left of the row. +*/ + +bool TQDataTable::beginInsert() +{ + if ( !sqlCursor() || isReadOnly() || !numCols() ) + return FALSE; + if ( !sqlCursor()->canInsert() ) + return FALSE; + int i = 0; + int row = currentRow(); + + d->insertPreRows = numRows(); + if ( row < 0 || numRows() < 1 ) + row = 0; + setNumRows( d->insertPreRows + 1 ); + setCurrentCell( row, 0 ); + d->editBuffer = sqlCursor()->primeInsert(); + emit primeInsert( d->editBuffer ); + d->dat.setMode( TQSql::Insert ); + int lastRow = row; + int lastY = contentsY() + visibleHeight(); + for ( i = row; i < numRows() ; ++i ) { + TQRect cg = cellGeometry( i, 0 ); + if ( (cg.y()+cg.height()) > lastY ) { + lastRow = i; + break; + } + } + if ( lastRow == row && ( numRows()-1 > row ) ) + lastRow = numRows() - 1; + d->insertRowLast = lastRow; + d->insertHeaderLabelLast = verticalHeader()->label( d->insertRowLast ); + verticalHeader()->setLabel( row, "*" ); + d->editRow = row; + // in the db world it's common to allow inserting new records + // into a table that has read-only columns - temporarily + // switch off read-only mode for such columns + bool fakeReadOnly = isColumnReadOnly( 0 ); + setColumnReadOnly( 0, FALSE ); + if ( TQTable::beginEdit( row, 0, FALSE ) ) + setEditMode( Editing, row, 0 ); + setColumnReadOnly( 0, fakeReadOnly ); + return TRUE; +} + +/*! + Protected virtual function called when editing is about to begin + on an existing row. If the table is read-only, or if there's no + cursor, nothing happens. + + Editing takes place using the cursor's edit buffer (see + TQSqlCursor::editBuffer()). + + \a row and \a col refer to the row and column in the TQDataTable. + + (\a replace is provided for reimplementors and reflects the API of + TQTable::beginEdit().) +*/ + +TQWidget* TQDataTable::beginUpdate ( int row, int col, bool replace ) +{ + if ( !sqlCursor() || isReadOnly() || isColumnReadOnly( col ) ) + return 0; + setCurrentCell( row, col ); + d->dat.setMode( TQSql::Update ); + if ( sqlCursor()->seek( row ) ) { + d->editBuffer = sqlCursor()->primeUpdate(); + sqlCursor()->seek( currentRow() ); + emit primeUpdate( d->editBuffer ); + return TQTable::beginEdit( row, col, replace ); + } + return 0; +} + +/*! + For an editable table, issues an insert on the current cursor + using the values in the cursor's edit buffer. If there is no + current cursor or there is no current "insert" row, nothing + happens. If confirmEdits() or confirmInsert() is TRUE, + confirmEdit() is called to confirm the insert. Returns TRUE if the + insert succeeded; otherwise returns FALSE. + + The underlying cursor must have a valid primary index to ensure + that a unique record is inserted within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool TQDataTable::insertCurrent() +{ + if ( d->dat.mode() != TQSql::Insert || ! numCols() ) + return FALSE; + if ( !sqlCursor()->canInsert() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQDataTable::insertCurrent: insert not allowed for %s", + sqlCursor()->name().latin1() ); +#endif + endInsert(); + return FALSE; + } + int b = 0; + int conf = TQSql::Yes; + if ( confirmEdits() || confirmInsert() ) + conf = confirmEdit( TQSql::Insert ); + switch ( conf ) { + case TQSql::Yes: { +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::waitCursor ); +#endif + emit beforeInsert( d->editBuffer ); + b = sqlCursor()->insert(); +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) { + handleError( sqlCursor()->lastError() ); + endInsert(); // cancel the insert if anything goes wrong + refresh(); + } else { + endInsert(); + refresh(); + TQSqlIndex idx = sqlCursor()->primaryIndex(); + findBuffer( idx, d->lastAt ); + repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE ); + emit cursorChanged( TQSql::Insert ); + } + break; + } + case TQSql::No: + endInsert(); + break; + case TQSql::Cancel: + if ( TQTable::beginEdit( currentRow(), currentColumn(), FALSE ) ) + setEditMode( Editing, currentRow(), currentColumn() ); + break; + } + return ( b > 0 ); +} + +/*! \internal + + Updates the row \a row. +*/ + +void TQDataTable::updateRow( int row ) +{ + for ( int i = 0; i < numCols(); ++i ) + updateCell( row, i ); +} + +/*! + For an editable table, issues an update using the cursor's edit + buffer. If there is no current cursor or there is no current + selection, nothing happens. If confirmEdits() or confirmUpdate() + is TRUE, confirmEdit() is called to confirm the update. Returns + TRUE if the update succeeded; otherwise returns FALSE. + + The underlying cursor must have a valid primary index to ensure + that a unique record is updated within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool TQDataTable::updateCurrent() +{ + if ( d->dat.mode() != TQSql::Update ) + return FALSE; + if ( sqlCursor()->primaryIndex().count() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQDataTable::updateCurrent: no primary index for %s", + sqlCursor()->name().latin1() ); +#endif + endUpdate(); + return FALSE; + } + if ( !sqlCursor()->canUpdate() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQDataTable::updateCurrent: updates not allowed for %s", + sqlCursor()->name().latin1() ); +#endif + endUpdate(); + return FALSE; + } + int b = 0; + int conf = TQSql::Yes; + if ( confirmEdits() || confirmUpdate() ) + conf = confirmEdit( TQSql::Update ); + switch ( conf ) { + case TQSql::Yes: { +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::waitCursor ); +#endif + emit beforeUpdate( d->editBuffer ); + b = sqlCursor()->update(); +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) { + handleError( sqlCursor()->lastError() ); + endUpdate(); + refresh(); + setCurrentCell( d->editRow, d->editCol ); + if ( TQTable::beginEdit( d->editRow, d->editCol, FALSE ) ) + setEditMode( Editing, d->editRow, d->editCol ); + } else { + emit cursorChanged( TQSql::Update ); + refresh(); + endUpdate(); + } + break; + } + case TQSql::No: + endUpdate(); + setEditMode( NotEditing, -1, -1 ); + break; + case TQSql::Cancel: + setCurrentCell( d->editRow, d->editCol ); + if ( TQTable::beginEdit( d->editRow, d->editCol, FALSE ) ) + setEditMode( Editing, d->editRow, d->editCol ); + break; + } + return ( b > 0 ); +} + +/*! + For an editable table, issues a delete on the current cursor's + primary index using the values of the currently selected row. If + there is no current cursor or there is no current selection, + nothing happens. If confirmEdits() or confirmDelete() is TRUE, + confirmEdit() is called to confirm the delete. Returns TRUE if the + delete succeeded; otherwise FALSE. + + The underlying cursor must have a valid primary index to ensure + that a unique record is deleted within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool TQDataTable::deleteCurrent() +{ + if ( !sqlCursor() || isReadOnly() ) + return FALSE; + if ( sqlCursor()->primaryIndex().count() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQDataTable::deleteCurrent: no primary index %s", + sqlCursor()->name().latin1() ); +#endif + return FALSE; + } + if ( !sqlCursor()->canDelete() ) + return FALSE; + + int b = 0; + int conf = TQSql::Yes; + if ( confirmEdits() || confirmDelete() ) + conf = confirmEdit( TQSql::Delete ); + + // Have to have this here - the confirmEdit() might pop up a + // dialog that causes a repaint which the cursor to the + // record it has to repaint. + if ( !sqlCursor()->seek( currentRow() ) ) + return FALSE; + switch ( conf ) { + case TQSql::Yes:{ +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::waitCursor ); +#endif + sqlCursor()->primeDelete(); + emit primeDelete( sqlCursor()->editBuffer() ); + emit beforeDelete( sqlCursor()->editBuffer() ); + b = sqlCursor()->del(); +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + if ( !b ) + handleError( sqlCursor()->lastError() ); + refresh(); + emit cursorChanged( TQSql::Delete ); + setCurrentCell( currentRow(), currentColumn() ); + repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE ); + verticalHeader()->repaint(); // get rid of trailing garbage + } + break; + case TQSql::No: + setEditMode( NotEditing, -1, -1 ); + break; + } + return ( b > 0 ); +} + +/*! + Protected virtual function which returns a confirmation for an + edit of mode \a m. Derived classes can reimplement this function + to provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +TQSql::Confirm TQDataTable::confirmEdit( TQSql::Op m ) +{ + return d->dat.confirmEdit( this, m ); +} + +/*! + Protected virtual function which returns a confirmation for + cancelling an edit mode of \a m. Derived classes can reimplement + this function to provide their own cancel dialog. The default + implementation uses a message box which prompts the user to + confirm the cancel. +*/ + +TQSql::Confirm TQDataTable::confirmCancel( TQSql::Op m ) +{ + return d->dat.confirmCancel( this, m ); +} + + +/*! + Searches the current cursor for a cell containing the string \a + str starting at the current cell and working forwards (or + backwards if \a backwards is TRUE). If the string is found, the + cell containing the string is set as the current cell. If \a + caseSensitive is FALSE the case of \a str will be ignored. + + The search will wrap, i.e. if the first (or if backwards is TRUE, + last) cell is reached without finding \a str the search will + continue until it reaches the starting cell. If \a str is not + found the search will fail and the current cell will remain + unchanged. +*/ +void TQDataTable::find( const TQString & str, bool caseSensitive, bool backwards ) +{ + if ( !sqlCursor() ) + return; + + TQSqlCursor * r = sqlCursor(); + TQString tmp, text; + uint row = currentRow(), startRow = row, + col = backwards ? currentColumn() - 1 : currentColumn() + 1; + bool wrap = TRUE, found = FALSE; + + if( str.isEmpty() || str.isNull() ) + return; + + if( !caseSensitive ) + tmp = str.lower(); + else + tmp = str; + +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::waitCursor ); +#endif + while( wrap ){ + while( !found && r->seek( row ) ){ + for( int i = col; backwards ? (i >= 0) : (i < (int) numCols()); + backwards ? i-- : i++ ) + { + text = r->value( indexOf( i ) ).toString(); + if( !caseSensitive ){ + text = text.lower(); + } + if( text.contains( tmp ) ){ + setCurrentCell( row, i ); + col = i; + found = TRUE; + } + } + if( !backwards ){ + col = 0; + row++; + } else { + col = numCols() - 1; + row--; + } + } + if( !backwards ){ + if( startRow != 0 ){ + startRow = 0; + } else { + wrap = FALSE; + } + r->first(); + row = 0; + } else { + if( startRow != (uint) (numRows() - 1) ){ + startRow = numRows() - 1; + } else { + wrap = FALSE; + } + r->last(); + row = numRows() - 1; + } + } +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif +} + + +/*! + Resets the table so that it displays no data. + + \sa setSqlCursor() +*/ + +void TQDataTable::reset() +{ + clearCellWidget( currentRow(), currentColumn() ); + switch ( d->dat.mode() ) { + case TQSql::Insert: + endInsert(); + break; + case TQSql::Update: + endUpdate(); + break; + default: + break; + } + ensureVisible( 0, 0 ); + verticalScrollBar()->setValue(0); + setNumRows(0); + + d->haveAllRows = FALSE; + d->continuousEdit = FALSE; + d->dat.setMode( TQSql::None ); + d->editRow = -1; + d->editCol = -1; + d->insertRowLast = -1; + d->insertHeaderLabelLast = TQString::null; + d->cancelMode = FALSE; + d->lastAt = -1; + d->fld.clear(); + d->fldLabel.clear(); + d->fldWidth.clear(); + d->fldIcon.clear(); + d->fldHidden.clear(); + if ( sorting() ) + horizontalHeader()->setSortIndicator( -1 ); +} + +/*! + Returns the index of the field within the current SQL query that + is displayed in column \a i. +*/ + +int TQDataTable::indexOf( uint i ) const +{ + TQDataTablePrivate::ColIndex::ConstIterator it = d->colIndex.at( i ); + if ( it != d->colIndex.end() ) + return *it; + return -1; +} + +/*! + Returns TRUE if the table will automatically delete the cursor + specified by setSqlCursor(); otherwise returns FALSE. +*/ + +bool TQDataTable::autoDelete() const +{ + return d->cur.autoDelete(); +} + +/*! + Sets the cursor auto-delete flag to \a enable. If \a enable is + TRUE, the table will automatically delete the cursor specified by + setSqlCursor(). If \a enable is FALSE (the default), the cursor + will not be deleted. +*/ + +void TQDataTable::setAutoDelete( bool enable ) +{ + d->cur.setAutoDelete( enable ); +} + +/*! + \property TQDataTable::autoEdit + \brief whether the data table automatically applies edits + + The default value for this property is TRUE. When the user begins + an insert or update in the table there are two possible outcomes + when they navigate to another record: + + \list 1 + \i the insert or update is is performed -- this occurs if autoEdit is TRUE + \i the insert or update is abandoned -- this occurs if autoEdit is FALSE + \endlist +*/ + +void TQDataTable::setAutoEdit( bool autoEdit ) +{ + d->dat.setAutoEdit( autoEdit ); +} + +bool TQDataTable::autoEdit() const +{ + return d->dat.autoEdit(); +} + +/*! + \property TQDataTable::nullText + \brief the text used to represent NULL values + + The nullText property will be used to represent NULL values in the + table. The default value is provided by the cursor's driver. +*/ + +void TQDataTable::setNullText( const TQString& nullText ) +{ + d->nullTxt = nullText; + d->nullTxtChanged = TRUE; +} + +TQString TQDataTable::nullText() const +{ + return d->nullTxt; +} + +/*! + \property TQDataTable::trueText + \brief the text used to represent true values + + The trueText property will be used to represent NULL values in the + table. The default value is "True". +*/ + +void TQDataTable::setTrueText( const TQString& trueText ) +{ + d->trueTxt = trueText; +} + +TQString TQDataTable::trueText() const +{ + return d->trueTxt; +} + +/*! + \property TQDataTable::falseText + \brief the text used to represent false values + + The falseText property will be used to represent NULL values in + the table. The default value is "False". +*/ + +void TQDataTable::setFalseText( const TQString& falseText ) +{ + d->falseTxt = falseText; +} + +TQString TQDataTable::falseText() const +{ + return d->falseTxt; +} + +/*! + \property TQDataTable::dateFormat + \brief the format used for displaying date/time values + + The dateFormat property is used for displaying date/time values in + the table. The default value is \c TQt::LocalDate. +*/ + +void TQDataTable::setDateFormat( const DateFormat f ) +{ + d->datefmt = f; +} + +TQt::DateFormat TQDataTable::dateFormat() const +{ + return d->datefmt; +} + +/*! + \property TQDataTable::numRows + + \brief the number of rows in the table +*/ + +int TQDataTable::numRows() const +{ + return TQTable::numRows(); +} + +/*! + \reimp + + The number of rows in the table will be determined by the cursor + (see setSqlCursor()), so normally this function should never be + called. It is included for completeness. +*/ + +void TQDataTable::setNumRows ( int r ) +{ + TQTable::setNumRows( r ); +} + +/*! + \reimp + + The number of columns in the table will be determined + automatically (see addColumn()), so normally this function should + never be called. It is included for completeness. +*/ + +void TQDataTable::setNumCols ( int r ) +{ + TQTable::setNumCols( r ); +} + +/*! + \property TQDataTable::numCols + + \brief the number of columns in the table +*/ + +int TQDataTable::numCols() const +{ + return TQTable::numCols(); +} + +/*! + Returns the text in cell \a row, \a col, or an empty string if the + cell is empty. If the cell's value is NULL then nullText() will be + returned. If the cell does not exist then TQString::null is + returned. +*/ + +TQString TQDataTable::text ( int row, int col ) const +{ + if ( !sqlCursor() ) + return TQString::null; + + TQString s; + if ( sqlCursor()->seek( row ) ) + s = sqlCursor()->value( indexOf( col ) ).toString(); + sqlCursor()->seek( currentRow() ); + return s; +} + +/*! + Returns the value in cell \a row, \a col, or an invalid value if + the cell does not exist or has no value. +*/ + +TQVariant TQDataTable::value ( int row, int col ) const +{ + if ( !sqlCursor() ) + return TQVariant(); + + TQVariant v; + if ( sqlCursor()->seek( row ) ) + v = sqlCursor()->value( indexOf( col ) ); + sqlCursor()->seek( currentRow() ); + return v; +} + +/*! \internal + Used to update the table when the size of the result set cannot be + determined - divide the result set into pages and load the pages as + the user moves around in the table. +*/ +void TQDataTable::loadNextPage() +{ + if ( d->haveAllRows ) + return; + if ( !sqlCursor() ) + return; + int pageSize = 0; + int lookAhead = 0; + if ( height() ) { + pageSize = (int)( height() * 2 / 20 ); + lookAhead = pageSize / 2; + } + int startIdx = verticalScrollBar()->value() / 20; + int endIdx = startIdx + pageSize + lookAhead; + if ( endIdx < numRows() || endIdx < 0 ) + return; + + // check for empty result set + if ( sqlCursor()->at() == TQSql::BeforeFirst && !sqlCursor()->next() ) { + d->haveAllRows = TRUE; + return; + } + + while ( endIdx > 0 && !sqlCursor()->seek( endIdx ) ) + endIdx--; + if ( endIdx != ( startIdx + pageSize + lookAhead ) ) + d->haveAllRows = TRUE; + // small hack to prevent TQTable from moving the view when a row + // is selected and the contents is resized + SelectionMode m = selectionMode(); + clearSelection(); + setSelectionMode( NoSelection ); + setNumRows( endIdx + 1 ); + sqlCursor()->seek( currentRow() ); + setSelectionMode( m ); +} + +/*! \internal */ +void TQDataTable::sliderPressed() +{ + disconnect( verticalScrollBar(), SIGNAL( valueChanged(int) ), + this, SLOT( loadNextPage() ) ); +} + +/*! \internal */ +void TQDataTable::sliderReleased() +{ + loadNextPage(); + connect( verticalScrollBar(), SIGNAL( valueChanged(int) ), + this, SLOT( loadNextPage() ) ); +} + +/*! + Sorts column \a col in ascending order if \a ascending is TRUE + (the default); otherwise sorts in descending order. + + The \a wholeRows parameter is ignored; TQDataTable always sorts + whole rows by the specified column. +*/ + +void TQDataTable::sortColumn ( int col, bool ascending, + bool ) +{ + if ( sorting() ) { + if ( isEditing() && d->dat.mode() != TQSql::None ) + endEdit( d->editRow, d->editCol, autoEdit(), FALSE ); + if ( !sqlCursor() ) + return; + TQSqlIndex lastSort = sqlCursor()->sort(); + TQSqlIndex newSort( lastSort.cursorName(), "newSort" ); + TQSqlField *field = sqlCursor()->field( indexOf( col ) ); + if ( field ) + newSort.append( *field ); + newSort.setDescending( 0, !ascending ); + horizontalHeader()->setSortIndicator( col, ascending ); + setSort( newSort ); + refresh(); + } +} + +/*! \reimp */ +void TQDataTable::columnClicked ( int col ) +{ + if ( sorting() ) { + if ( !sqlCursor() ) + return; + TQSqlIndex lastSort = sqlCursor()->sort(); + bool asc = TRUE; + if ( lastSort.count() && lastSort.field( 0 )->name() == sqlCursor()->field( indexOf( col ) )->name() ) + asc = lastSort.isDescending( 0 ); + sortColumn( col, asc ); + emit currentChanged( sqlCursor() ); + } +} + +/*! + \reimp + + Repaints the cell at \a row, \a col. +*/ +void TQDataTable::repaintCell( int row, int col ) +{ + TQRect cg = cellGeometry( row, col ); + TQRect re( TQPoint( cg.x() - 2, cg.y() - 2 ), + TQSize( cg.width() + 4, cg.height() + 4 ) ); + repaintContents( re, FALSE ); +} + +/*! + \reimp + + This function renders the cell at \a row, \a col with the value of + the corresponding cursor field on the painter \a p. Depending on + the table's current edit mode, paintField() is called for the + appropriate cursor field. \a cr describes the cell coordinates in + the content coordinate system. If \a selected is TRUE the cell has + been selected and would normally be rendered differently than an + unselected cell. + + \sa TQSql::isNull() +*/ + +void TQDataTable::paintCell( TQPainter * p, int row, int col, const TQRect & cr, + bool selected, const TQColorGroup &cg ) +{ + TQTable::paintCell( p, row, col, cr, selected, cg ); // empty cell + + if ( !sqlCursor() ) + return; + + p->setPen( selected ? cg.highlightedText() : cg.text() ); + if ( d->dat.mode() != TQSql::None ) { + if ( row == d->editRow && d->editBuffer ) { + paintField( p, d->editBuffer->field( indexOf( col ) ), cr, + selected ); + } else if ( row > d->editRow && d->dat.mode() == TQSql::Insert ) { + if ( sqlCursor()->seek( row - 1 ) ) + paintField( p, sqlCursor()->field( indexOf( col ) ), cr, + selected ); + } else { + if ( sqlCursor()->seek( row ) ) + paintField( p, sqlCursor()->field( indexOf( col ) ), cr, + selected ); + } + } else { + if ( sqlCursor()->seek( row ) ) + paintField( p, sqlCursor()->field( indexOf( col ) ), cr, selected ); + + } +} + + +/*! + Paints the \a field on the painter \a p. The painter has already + been translated to the appropriate cell's origin where the \a + field is to be rendered. \a cr describes the cell coordinates in + the content coordinate system. The \a selected parameter is + ignored. + + If you want to draw custom field content you must reimplement + paintField() to do the custom drawing. The default implementation + renders the \a field value as text. If the field is NULL, + nullText() is displayed in the cell. If the field is Boolean, + trueText() or falseText() is displayed as appropriate. +*/ + +void TQDataTable::paintField( TQPainter * p, const TQSqlField* field, + const TQRect & cr, bool ) +{ + if ( !field ) + return; + p->drawText( 2,2, cr.width()-4, cr.height()-4, fieldAlignment( field ), fieldToString( field ) ); +} + +/*! + Returns the alignment for \a field. +*/ + +int TQDataTable::fieldAlignment( const TQSqlField* /*field*/ ) +{ + return TQt::AlignLeft | TQt::AlignVCenter; //## Reggie: add alignment to TQTable +} + + +/*! + If the cursor's \a sql driver supports query sizes, the number of + rows in the table is set to the size of the query. Otherwise, the + table dynamically resizes itself as it is scrolled. If \a sql is + not active, it is made active by issuing a select() on the cursor + using the \a sql cursor's current filter and current sort. +*/ + +void TQDataTable::setSize( TQSqlCursor* sql ) +{ + // ### what are the connect/disconnect calls doing here!? move to refresh() + if ( sql->driver() && sql->driver()->hasFeature( TQSqlDriver::QuerySize ) ) { + setVScrollBarMode( Auto ); + disconnect( verticalScrollBar(), SIGNAL( sliderPressed() ), + this, SLOT( sliderPressed() ) ); + disconnect( verticalScrollBar(), SIGNAL( sliderReleased() ), + this, SLOT( sliderReleased() ) ); + disconnect( verticalScrollBar(), SIGNAL( valueChanged(int) ), + this, SLOT( loadNextPage() ) ); + if ( numRows() != sql->size() ) + setNumRows( sql->size() ); + } else { + setVScrollBarMode( AlwaysOn ); + connect( verticalScrollBar(), SIGNAL( sliderPressed() ), + this, SLOT( sliderPressed() ) ); + connect( verticalScrollBar(), SIGNAL( sliderReleased() ), + this, SLOT( sliderReleased() ) ); + connect( verticalScrollBar(), SIGNAL( valueChanged(int) ), + this, SLOT( loadNextPage() ) ); + setNumRows(0); + loadNextPage(); + } +} + +/*! + Sets \a cursor as the data source for the table. To force the + display of the data from \a cursor, use refresh(). If \a + autoPopulate is TRUE, columns are automatically created based upon + the fields in the \a cursor record. If \a autoDelete is TRUE (the + default is FALSE), the table will take ownership of the \a cursor + and delete it when appropriate. If the \a cursor is read-only, the + table becomes read-only. The table adopts the cursor's driver's + definition for representing NULL values as strings. + + \sa refresh() setReadOnly() setAutoDelete() TQSqlDriver::nullText() +*/ + +void TQDataTable::setSqlCursor( TQSqlCursor* cursor, bool autoPopulate, bool autoDelete ) +{ + setUpdatesEnabled( FALSE ); + d->cur.setCursor( 0 ); + if ( cursor ) { + d->cur.setCursor( cursor, autoDelete ); + if ( autoPopulate ) { + d->fld.clear(); + d->fldLabel.clear(); + d->fldWidth.clear(); + d->fldIcon.clear(); + d->fldHidden.clear(); + for ( uint i = 0; i < sqlCursor()->count(); ++i ) { + addColumn( sqlCursor()->field( i )->name(), sqlCursor()->field( i )->name() ); + setColumnReadOnly( i, sqlCursor()->field( i )->isReadOnly() ); + } + } + setReadOnly( sqlCursor()->isReadOnly() ); + if ( sqlCursor()->driver() && !d->nullTxtChanged ) + setNullText(sqlCursor()->driver()->nullText() ); + setAutoDelete( autoDelete ); + } else { + setNumRows( 0 ); + setNumCols( 0 ); + } + setUpdatesEnabled( TRUE ); +} + + +/*! + Protected virtual function which is called when an error \a e has + occurred on the current cursor(). The default implementation + displays a warning message to the user with information about the + error. +*/ +void TQDataTable::handleError( const TQSqlError& e ) +{ + d->dat.handleError( this, e ); +} + +/*! \reimp + */ + +void TQDataTable::keyPressEvent( TQKeyEvent* e ) +{ + switch( e->key() ) { + case Key_Left: + case Key_Right: + case Key_Up: + case Key_Down: + case Key_Prior: + case Key_Next: + case Key_Home: + case Key_End: + case Key_F2: + case Key_Enter: case Key_Return: + case Key_Tab: case Key_BackTab: + TQTable::keyPressEvent( e ); + default: + return; + } +} + +/*! \reimp +*/ + +void TQDataTable::resizeData ( int ) +{ + +} + +/*! \reimp +*/ + +TQTableItem * TQDataTable::item ( int, int ) const +{ + return 0; +} + +/*! \reimp +*/ + +void TQDataTable::setItem ( int , int , TQTableItem * ) +{ + +} + +/*! \reimp +*/ + +void TQDataTable::clearCell ( int , int ) +{ + +} + +/*! \reimp +*/ + +void TQDataTable::setPixmap ( int , int , const TQPixmap & ) +{ + +} + +/*! \reimp */ +void TQDataTable::takeItem ( TQTableItem * ) +{ + +} + +/*! + Installs a new SQL editor factory \a f. This enables the user to + create and instantiate their own editors for use in cell editing. + Note that TQDataTable takes ownership of this pointer, and will + delete it when it is no longer needed or when + installEditorFactory() is called again. + + \sa TQSqlEditorFactory +*/ + +void TQDataTable::installEditorFactory( TQSqlEditorFactory * f ) +{ + if( f ) { + delete d->editorFactory; + d->editorFactory = f; + } +} + +/*! + Installs a new property map \a m. This enables the user to create + and instantiate their own property maps for use in cell editing. + Note that TQDataTable takes ownership of this pointer, and will + delete it when it is no longer needed or when installPropertMap() + is called again. + + \sa TQSqlPropertyMap +*/ + +void TQDataTable::installPropertyMap( TQSqlPropertyMap* m ) +{ + if ( m ) { + delete d->propertyMap; + d->propertyMap = m; + } +} + +/*! \internal + + Sets the current selection to \a row, \a col. +*/ + +void TQDataTable::setCurrentSelection( int row, int ) +{ + if ( !sqlCursor() ) + return; + if ( row == d->lastAt ) + return; + if ( !sqlCursor()->seek( row ) ) + return; + d->lastAt = row; + emit currentChanged( sqlCursor() ); +} + +void TQDataTable::updateCurrentSelection() +{ + setCurrentSelection( currentRow(), -1 ); +} + +/*! + Returns the currently selected record, or 0 if there is no current + selection. The table owns the pointer, so do \e not delete it or + otherwise modify it or the cursor it points to. +*/ + +TQSqlRecord* TQDataTable::currentRecord() const +{ + if ( !sqlCursor() || currentRow() < 0 ) + return 0; + if ( !sqlCursor()->seek( currentRow() ) ) + return 0; + return sqlCursor(); +} + +/*! + Sorts column \a col in ascending order. + + \sa setSorting() +*/ + +void TQDataTable::sortAscending( int col ) +{ + sortColumn( col, TRUE ); +} + +/*! + Sorts column \a col in descending order. + + \sa setSorting() +*/ + +void TQDataTable::sortDescending( int col ) +{ + sortColumn( col, FALSE ); +} + +/*! + \overload void TQDataTable::refresh( Refresh mode ) + + Refreshes the table. If there is no currently defined cursor (see + setSqlCursor()), nothing happens. The \a mode parameter determines + which type of refresh will take place. + + \sa Refresh setSqlCursor() addColumn() +*/ + +void TQDataTable::refresh( TQDataTable::Refresh mode ) +{ + TQSqlCursor* cur = sqlCursor(); + if ( !cur ) + return; + bool refreshData = ( (mode & RefreshData) == RefreshData ); + bool refreshCol = ( (mode & RefreshColumns) == RefreshColumns ); + if ( ( (mode & RefreshAll) == RefreshAll ) ) { + refreshData = TRUE; + refreshCol = TRUE; + } + if ( !refreshCol && d->fld.count() && numCols() == 0 ) + refreshCol = TRUE; + viewport()->setUpdatesEnabled( FALSE ); + d->haveAllRows = FALSE; + if ( refreshData ) { + if ( !d->cur.refresh() && d->cur.cursor() ) { + handleError( d->cur.cursor()->lastError() ); + } + d->lastAt = -1; + } + if ( refreshCol ) { + setNumCols( 0 ); + d->colIndex.clear(); + if ( d->fld.count() ) { + TQSqlField* field = 0; + int i; + int fpos = -1; + for ( i = 0; i < (int)d->fld.count(); ++i ) { + if ( cur->field( i ) && cur->field( i )->name() == d->fld[ i ] ) + // if there is a field with the desired name on the desired position + // then we take that + fpos = i; + else + // otherwise we take the first field that matches the desired name + fpos = cur->position( d->fld[ i ] ); + field = cur->field( fpos ); + if ( field && ( cur->isGenerated( fpos ) || + cur->isCalculated( field->name() ) ) ) + { + setNumCols( numCols() + 1 ); + d->colIndex.append( fpos ); + setColumnReadOnly( numCols()-1, field->isReadOnly() || isColumnReadOnly( numCols()-1 ) ); + horizontalHeader()->setLabel( numCols()-1, d->fldIcon[ i ], d->fldLabel[ i ] ); + if ( d->fldHidden[ i ] ) { + TQTable::showColumn( i ); // ugly but necessary + TQTable::hideColumn( i ); + } else { + TQTable::showColumn( i ); + } + if ( d->fldWidth[ i ] > -1 ) + TQTable::setColumnWidth( i, d->fldWidth[i] ); + } + } + } + } + viewport()->setUpdatesEnabled( TRUE ); + viewport()->repaint( FALSE ); + horizontalHeader()->repaint(); + verticalHeader()->repaint(); + setSize( cur ); + // keep others aware + if ( d->lastAt == -1 ) + setCurrentSelection( -1, -1 ); + else if ( d->lastAt != currentRow() ) + setCurrentSelection( currentRow(), currentColumn() ); + if ( cur->isValid() ) + emit currentChanged( sqlCursor() ); +} + +/*! + Refreshes the table. The cursor is refreshed using the current + filter, the current sort, and the currently defined columns. + Equivalent to calling refresh( TQDataTable::RefreshData ). +*/ + +void TQDataTable::refresh() +{ + refresh( RefreshData ); +} + +/*! + \reimp + + Selects the record in the table using the current cursor edit + buffer and the fields specified by the index \a idx. If \a atHint + is specified, it will be used as a hint about where to begin + searching. +*/ + +bool TQDataTable::findBuffer( const TQSqlIndex& idx, int atHint ) +{ + TQSqlCursor* cur = sqlCursor(); + if ( !cur ) + return FALSE; + bool found = d->cur.findBuffer( idx, atHint ); + if ( found ) + setCurrentCell( cur->at(), currentColumn() ); + return found; +} + +/*! \internal + Returns the string representation of a database field. +*/ +TQString TQDataTable::fieldToString( const TQSqlField * field ) +{ + TQString text; + if ( field->isNull() ) { + text = nullText(); + } else { + TQVariant val = field->value(); + switch ( val.type() ) { + case TQVariant::Bool: + text = val.toBool() ? d->trueTxt : d->falseTxt; + break; + case TQVariant::Date: + text = val.toDate().toString( d->datefmt ); + break; + case TQVariant::Time: + text = val.toTime().toString( d->datefmt ); + break; + case TQVariant::DateTime: + text = val.toDateTime().toString( d->datefmt ); + break; + default: + text = val.toString(); + break; + } + } + return text; +} + +/*! + \reimp +*/ + +void TQDataTable::swapColumns( int col1, int col2, bool ) +{ + TQString fld = d->fld[ col1 ]; + TQString fldLabel = d->fldLabel[ col1 ]; + TQIconSet fldIcon = d->fldIcon[ col1 ]; + int fldWidth = d->fldWidth[ col1 ]; + + d->fld[ col1 ] = d->fld[ col2 ]; + d->fldLabel[ col1 ] = d->fldLabel[ col2 ]; + d->fldIcon[ col1 ] = d->fldIcon[ col2 ]; + d->fldWidth[ col1 ] = d->fldWidth[ col2 ]; + + d->fld[ col2 ] = fld; + d->fldLabel[ col2 ] = fldLabel; + d->fldIcon[ col2 ] = fldIcon; + d->fldWidth[ col2 ] = fldWidth; + + int colIndex = d->colIndex[ col1 ]; + d->colIndex[ col1 ] = d->colIndex[ col2 ]; + d->colIndex[ col2 ] = colIndex; +} + +/*! + \reimp +*/ + +void TQDataTable::drawContents( TQPainter * p, int cx, int cy, int cw, int ch ) +{ + TQTable::drawContents( p, cx, cy, cw, ch ); + if ( sqlCursor() && currentRow() >= 0 ) + sqlCursor()->seek( currentRow() ); +} + +/*! + \reimp +*/ + +void TQDataTable::hideColumn( int col ) +{ + d->fldHidden[col] = TRUE; + refresh( RefreshColumns ); +} + +/*! + \reimp +*/ + +void TQDataTable::showColumn( int col ) +{ + d->fldHidden[col] = FALSE; + refresh( RefreshColumns ); +} + +/*! + \fn void TQDataTable::currentChanged( TQSqlRecord* record ) + + This signal is emitted whenever a new row is selected in the + table. The \a record parameter points to the contents of the newly + selected record. +*/ + +/*! + \fn void TQDataTable::primeInsert( TQSqlRecord* buf ) + + This signal is emitted after the cursor is primed for insert by + the table, when an insert action is beginning on the table. The \a + buf parameter points to the edit buffer being inserted. Connect to + this signal in order to, for example, prime the record buffer with + default data values. +*/ + +/*! + \fn void TQDataTable::primeUpdate( TQSqlRecord* buf ) + + This signal is emitted after the cursor is primed for update by + the table, when an update action is beginning on the table. The \a + buf parameter points to the edit buffer being updated. Connect to + this signal in order to, for example, provide some visual feedback + that the user is in 'edit mode'. +*/ + +/*! + \fn void TQDataTable::primeDelete( TQSqlRecord* buf ) + + This signal is emitted after the cursor is primed for delete by + the table, when a delete action is beginning on the table. The \a + buf parameter points to the edit buffer being deleted. Connect to + this signal in order to, for example, record auditing information + on deletions. +*/ + +/*! + \fn void TQDataTable::beforeInsert( TQSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + inserted into the database. The \a buf parameter points to the + edit buffer being inserted. Connect to this signal to, for + example, populate a key field with a unique sequence number. +*/ + +/*! + \fn void TQDataTable::beforeUpdate( TQSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + updated in the database. The \a buf parameter points to the edit + buffer being updated. Connect to this signal when you want to + transform the user's data behind-the-scenes. +*/ + +/*! + \fn void TQDataTable::beforeDelete( TQSqlRecord* buf ) + + This signal is emitted just before the currently selected record + is deleted from the database. The \a buf parameter points to the + edit buffer being deleted. Connect to this signal to, for example, + copy some of the fields for later use. +*/ + +/*! + \fn void TQDataTable::cursorChanged( TQSql::Op mode ) + + This signal is emitted whenever the cursor record was changed due + to an edit. The \a mode parameter is the type of edit that just + took place. +*/ + +#endif diff --git a/src/sql/qdatatable.h b/src/sql/qdatatable.h new file mode 100644 index 000000000..7b789ca78 --- /dev/null +++ b/src/sql/qdatatable.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Definition of TQDataTable class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDATATABLE_H +#define TQDATATABLE_H + +#ifndef QT_H +#include "qstring.h" +#include "qvariant.h" +#include "qtable.h" +#include "qsql.h" +#include "qsqlcursor.h" +#include "qsqlindex.h" +#include "qsqleditorfactory.h" +#include "qiconset.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class TQPainter; +class TQSqlField; +class TQSqlPropertyMap; +class TQDataTablePrivate; + +class TQM_EXPORT_SQL TQDataTable : public TQTable +{ + Q_OBJECT + + Q_PROPERTY( TQString nullText READ nullText WRITE setNullText ) + Q_PROPERTY( TQString trueText READ trueText WRITE setTrueText ) + Q_PROPERTY( TQString falseText READ falseText WRITE setFalseText ) + Q_PROPERTY( DateFormat dateFormat READ dateFormat WRITE setDateFormat ) + Q_PROPERTY( bool confirmEdits READ confirmEdits WRITE setConfirmEdits ) + Q_PROPERTY( bool confirmInsert READ confirmInsert WRITE setConfirmInsert ) + Q_PROPERTY( bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate ) + Q_PROPERTY( bool confirmDelete READ confirmDelete WRITE setConfirmDelete ) + Q_PROPERTY( bool confirmCancels READ confirmCancels WRITE setConfirmCancels ) + Q_PROPERTY( bool autoEdit READ autoEdit WRITE setAutoEdit ) + Q_PROPERTY( TQString filter READ filter WRITE setFilter ) + Q_PROPERTY( TQStringList sort READ sort WRITE setSort ) + Q_PROPERTY( int numCols READ numCols ) + Q_PROPERTY( int numRows READ numRows ) + +public: + TQDataTable ( TQWidget* parent=0, const char* name=0 ); + TQDataTable ( TQSqlCursor* cursor, bool autoPopulate = FALSE, TQWidget* parent=0, const char* name=0 ); + ~TQDataTable(); + + virtual void addColumn( const TQString& fieldName, + const TQString& label = TQString::null, + int width = -1, + const TQIconSet& iconset = TQIconSet() ); + virtual void removeColumn( uint col ); + virtual void setColumn( uint col, const TQString& fieldName, + const TQString& label = TQString::null, + int width = -1, + const TQIconSet& iconset = TQIconSet() ); + + TQString nullText() const; + TQString trueText() const; + TQString falseText() const; + DateFormat dateFormat() const; + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + bool autoDelete() const; + bool autoEdit() const; + TQString filter() const; + TQStringList sort() const; + + virtual void setSqlCursor( TQSqlCursor* cursor = 0, + bool autoPopulate = FALSE, bool autoDelete = FALSE ); + TQSqlCursor* sqlCursor() const; + + virtual void setNullText( const TQString& nullText ); + virtual void setTrueText( const TQString& trueText ); + virtual void setFalseText( const TQString& falseText ); + virtual void setDateFormat( const DateFormat f ); + virtual void setConfirmEdits( bool confirm ); + virtual void setConfirmInsert( bool confirm ); + virtual void setConfirmUpdate( bool confirm ); + virtual void setConfirmDelete( bool confirm ); + virtual void setConfirmCancels( bool confirm ); + virtual void setAutoDelete( bool enable ); + virtual void setAutoEdit( bool autoEdit ); + virtual void setFilter( const TQString& filter ); + virtual void setSort( const TQStringList& sort ); + virtual void setSort( const TQSqlIndex& sort ); + + enum Refresh { + RefreshData = 1, + RefreshColumns = 2, + RefreshAll = 3 + }; + void refresh( Refresh mode ); + void sortColumn ( int col, bool ascending = TRUE, + bool wholeRows = FALSE ); + TQString text ( int row, int col ) const; + TQVariant value ( int row, int col ) const; + TQSqlRecord* currentRecord() const; + + void installEditorFactory( TQSqlEditorFactory * f ); + void installPropertyMap( TQSqlPropertyMap* m ); + + int numCols() const; + int numRows() const; + void setNumCols( int c ); + void setNumRows ( int r ); + bool findBuffer( const TQSqlIndex& idx, int atHint = 0 ); + + void hideColumn( int col ); + void showColumn( int col ); +signals: + void currentChanged( TQSqlRecord* record ); + void primeInsert( TQSqlRecord* buf ); + void primeUpdate( TQSqlRecord* buf ); + void primeDelete( TQSqlRecord* buf ); + void beforeInsert( TQSqlRecord* buf ); + void beforeUpdate( TQSqlRecord* buf ); + void beforeDelete( TQSqlRecord* buf ); + void cursorChanged( TQSql::Op mode ); + +public slots: + virtual void find( const TQString & str, bool caseSensitive, + bool backwards ); + virtual void sortAscending( int col ); + virtual void sortDescending( int col ); + virtual void refresh(); + void setColumnWidth( int col, int w ); + void adjustColumn( int col ); + void setColumnStretchable( int col, bool stretch ); + void swapColumns( int col1, int col2, bool swapHeaders = FALSE ); + +protected: + virtual bool insertCurrent(); + virtual bool updateCurrent(); + virtual bool deleteCurrent(); + + virtual TQSql::Confirm confirmEdit( TQSql::Op m ); + virtual TQSql::Confirm confirmCancel( TQSql::Op m ); + + virtual void handleError( const TQSqlError& e ); + + virtual bool beginInsert(); + virtual TQWidget* beginUpdate ( int row, int col, bool replace ); + + bool eventFilter( TQObject *o, TQEvent *e ); + void keyPressEvent( TQKeyEvent* ); + void resizeEvent ( TQResizeEvent * ); + void contentsMousePressEvent( TQMouseEvent* e ); + void contentsContextMenuEvent( TQContextMenuEvent* e ); + void endEdit( int row, int col, bool accept, bool replace ); + TQWidget * createEditor( int row, int col, bool initFromCell ) const; + void activateNextCell(); + int indexOf( uint i ) const; // ### make this public in 4.0 + void reset(); + void setSize( TQSqlCursor* sql ); + void repaintCell( int row, int col ); + void paintCell ( TQPainter * p, int row, int col, const TQRect & cr, + bool selected, const TQColorGroup &cg ); + virtual void paintField( TQPainter * p, const TQSqlField* field, const TQRect & cr, + bool selected ); + void drawContents( TQPainter * p, int cx, int cy, int cw, int ch ); + virtual int fieldAlignment( const TQSqlField* field ); + void columnClicked ( int col ); + void resizeData ( int len ); + + TQTableItem * item ( int row, int col ) const; + void setItem ( int row, int col, TQTableItem * item ); + void clearCell ( int row, int col ) ; + void setPixmap ( int row, int col, const TQPixmap & pix ); + void takeItem ( TQTableItem * i ); + +private slots: + void loadNextPage(); + void setCurrentSelection( int row, int col ); + void updateCurrentSelection(); + void sliderPressed(); + void sliderReleased(); + void doInsertCurrent(); + void doUpdateCurrent(); + +private: + TQString fieldToString( const TQSqlField * field ); + void init(); + TQWidget* beginEdit ( int row, int col, bool replace ); + void updateRow( int row ); + void endInsert(); + void endUpdate(); + TQDataTablePrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQDataTable( const TQDataTable & ); + TQDataTable &operator=( const TQDataTable & ); +#endif +}; + +#endif +#endif diff --git a/src/sql/qdataview.cpp b/src/sql/qdataview.cpp new file mode 100644 index 000000000..1a73dc6f5 --- /dev/null +++ b/src/sql/qdataview.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Implementation of TQDataView class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qdataview.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "qsqlmanager_p.h" + +class TQDataViewPrivate +{ +public: + TQDataViewPrivate() {} + TQSqlFormManager frm; +}; + + +/*! + \class TQDataView qdataview.h + \brief The TQDataView class provides read-only SQL forms. + + \ingroup database + \mainclass + \module sql + + This class provides a form which displays SQL field data from a + record buffer. Because TQDataView does not support editing it uses + less resources than a TQDataBrowser. This class is well suited for + displaying read-only data from a SQL database. + + If you want a to present your data in an editable form use + TQDataBrowser; if you want a table-based presentation of your data + use TQDataTable. + + The form is associated with the data view with setForm() and the + record is associated with setRecord(). You can also pass a + TQSqlRecord to the refresh() function which will set the record to + the given record and read the record's fields into the form. +*/ + +/*! + Constructs a data view which is a child of \a parent, called \a + name, and with widget flags \a fl. +*/ + +TQDataView::TQDataView( TQWidget *parent, const char *name, WFlags fl ) + : TQWidget( parent, name, fl ) +{ + d = new TQDataViewPrivate(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDataView::~TQDataView() +{ + delete d; +} + +/*! + Clears the default form's values. If there is no default form, + nothing happens. All the values are set to their 'zero state', + e.g. 0 for numeric fields, "" for string fields. +*/ + +void TQDataView::clearValues() +{ + d->frm.clearValues(); +} + +/*! + Sets the form used by the data view to \a form. If a record has + already been assigned to the data view, the form will display that + record's data. + + \sa form() +*/ + +void TQDataView::setForm( TQSqlForm* form ) +{ + d->frm.setForm( form ); +} + + +/*! + Returns the default form used by the data view, or 0 if there is + none. + + \sa setForm() +*/ + +TQSqlForm* TQDataView::form() +{ + return d->frm.form(); +} + + +/*! + Sets the record used by the data view to \a record. If a form has + already been assigned to the data view, the form will display the + data from \a record in that form. + + \sa record() +*/ + +void TQDataView::setRecord( TQSqlRecord* record ) +{ + d->frm.setRecord( record ); +} + + +/*! + Returns the default record used by the data view, or 0 if there is + none. + + \sa setRecord() +*/ + +TQSqlRecord* TQDataView::record() +{ + return d->frm.record(); +} + + +/*! + Causes the default form to read its fields from the record buffer. + If there is no default form, or no record, nothing happens. + + \sa setForm() +*/ + +void TQDataView::readFields() +{ + d->frm.readFields(); +} + +/*! + Causes the default form to write its fields to the record buffer. + If there is no default form, or no record, nothing happens. + + \sa setForm() +*/ + +void TQDataView::writeFields() +{ + d->frm.writeFields(); +} + +/*! + Causes the default form to display the contents of \a buf. If + there is no default form, nothing happens.The \a buf also becomes + the default record for all subsequent calls to readFields() and + writefields(). This slot is equivalant to calling: + + \code + myView.setRecord( record ); + myView.readFields(); + \endcode + + \sa setRecord() readFields() +*/ + +void TQDataView::refresh( TQSqlRecord* buf ) +{ + if ( buf && buf != record() ) + setRecord( buf ); + readFields(); +} + +#endif diff --git a/src/sql/qdataview.h b/src/sql/qdataview.h new file mode 100644 index 000000000..e45227b2f --- /dev/null +++ b/src/sql/qdataview.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Definition of TQDataView class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDATAVIEW_H +#define TQDATAVIEW_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class TQSqlForm; +class TQSqlRecord; +class TQDataViewPrivate; + +class TQM_EXPORT_SQL TQDataView : public TQWidget +{ + Q_OBJECT + +public: + TQDataView( TQWidget* parent=0, const char* name=0, WFlags fl = 0 ); + ~TQDataView(); + + virtual void setForm( TQSqlForm* form ); + TQSqlForm* form(); + virtual void setRecord( TQSqlRecord* record ); + TQSqlRecord* record(); + +public slots: + virtual void refresh( TQSqlRecord* buf ); + virtual void readFields(); + virtual void writeFields(); + virtual void clearValues(); + +private: + TQDataViewPrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQDataView( const TQDataView & ); + TQDataView &operator=( const TQDataView & ); +#endif +}; + + +#endif +#endif diff --git a/src/sql/qeditorfactory.cpp b/src/sql/qeditorfactory.cpp new file mode 100644 index 000000000..dc2c4f62f --- /dev/null +++ b/src/sql/qeditorfactory.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Implementation of TQEditorFactory class +** +** Created : 2000-11-17 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qcleanuphandler.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qspinbox.h" +#include "qcombobox.h" + +#include "qeditorfactory.h" +#include "qdatetimeedit.h" + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +/*! + \class TQEditorFactory qeditorfactory.h + \brief The TQEditorFactory class is used to create editor widgets + for TQVariant data types. + + \ingroup database + \module sql + + Each editor factory provides the createEditor() function which + given a TQVariant will create and return a TQWidget that can edit + that TQVariant. For example if you have a TQVariant::String type, a + TQLineEdit would be the default editor returned, whereas a + TQVariant::Int's default editor would be a TQSpinBox. + + If you want to create different editors for fields with the same + data type, subclass TQEditorFactory and reimplement the + createEditor() function. +*/ + +/*! + Constructs an editor factory with parent \a parent, called \a name. +*/ + +TQEditorFactory::TQEditorFactory ( TQObject * parent, const char * name ) + : TQObject( parent, name ) +{ + +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQEditorFactory::~TQEditorFactory() +{ + +} + +static TQEditorFactory * defaultfactory = 0; +static TQCleanupHandler< TQEditorFactory > q_cleanup_editor_factory; + +/*! + Returns an instance of a default editor factory. +*/ + +TQEditorFactory * TQEditorFactory::defaultFactory() +{ + if( defaultfactory == 0 ){ + defaultfactory = new TQEditorFactory(); + q_cleanup_editor_factory.add( &defaultfactory ); + } + + return defaultfactory; +} + +/*! + Replaces the default editor factory with \a factory. + \e{TQEditorFactory takes ownership of factory, and destroys it + when it is no longer needed.} +*/ + +void TQEditorFactory::installDefaultFactory( TQEditorFactory * factory ) +{ + if( factory == 0 || factory == defaultfactory ) return; + + if( defaultfactory != 0 ){ + q_cleanup_editor_factory.remove( &defaultfactory ); + delete defaultfactory; + } + defaultfactory = factory; + q_cleanup_editor_factory.add( &defaultfactory ); +} + +/*! + Creates and returns the appropriate editor for the TQVariant \a v. + If the TQVariant is invalid, 0 is returned. The \a parent is passed + to the appropriate editor's constructor. +*/ + +TQWidget * TQEditorFactory::createEditor( TQWidget * parent, const TQVariant & v ) +{ + TQWidget * w = 0; + switch( v.type() ){ + case TQVariant::Invalid: + w = 0; + break; + case TQVariant::Bool: + w = new TQComboBox( parent, "qt_editor_bool" ); + ((TQComboBox *) w)->insertItem( "False" ); + ((TQComboBox *) w)->insertItem( "True" ); + break; + case TQVariant::UInt: + w = new TQSpinBox( 0, 999999, 1, parent, "qt_editor_spinbox" ); + break; + case TQVariant::Int: + w = new TQSpinBox( -999999, 999999, 1, parent, "qt_editor_int" ); + break; + case TQVariant::String: + case TQVariant::CString: + case TQVariant::Double: + w = new TQLineEdit( parent, "qt_editor_double" ); + ((TQLineEdit*)w)->setFrame( FALSE ); + break; + case TQVariant::Date: + w = new TQDateEdit( parent, "qt_editor_date" ); + break; + case TQVariant::Time: + w = new TQTimeEdit( parent, "qt_editor_time" ); + break; + case TQVariant::DateTime: + w = new TQDateTimeEdit( parent, "qt_editor_datetime" ); + break; +#ifndef QT_NO_LABEL + case TQVariant::Pixmap: + w = new TQLabel( parent, "qt_editor_pixmap" ); + break; +#endif + case TQVariant::Palette: + case TQVariant::ColorGroup: + case TQVariant::Color: + case TQVariant::Font: + case TQVariant::Brush: + case TQVariant::Bitmap: + case TQVariant::Cursor: + case TQVariant::Map: + case TQVariant::StringList: + case TQVariant::Rect: + case TQVariant::Size: + case TQVariant::IconSet: + case TQVariant::Point: + case TQVariant::PointArray: + case TQVariant::Region: + case TQVariant::SizePolicy: + case TQVariant::ByteArray: + default: + w = new TQWidget( parent, "qt_editor_default" ); + break; + } + return w; +} +#endif // QT_NO_SQL diff --git a/src/sql/qeditorfactory.h b/src/sql/qeditorfactory.h new file mode 100644 index 000000000..7f3428f77 --- /dev/null +++ b/src/sql/qeditorfactory.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Definition of TQEditorFactory class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQEDITORFACTORY_H +#define TQEDITORFACTORY_H + +#ifndef QT_H +#include "qobject.h" +#include "qvariant.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +class TQM_EXPORT_SQL TQEditorFactory : public TQObject +{ +public: + TQEditorFactory ( TQObject * parent = 0, const char * name = 0 ); + ~TQEditorFactory(); + + virtual TQWidget * createEditor( TQWidget * parent, const TQVariant & v ); + + static TQEditorFactory * defaultFactory(); + static void installDefaultFactory( TQEditorFactory * factory); + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQEditorFactory( const TQEditorFactory & ); + TQEditorFactory &operator=( const TQEditorFactory & ); +#endif +}; + +#endif // QT_NO_SQL +#endif // TQEDITORFACTORY_H diff --git a/src/sql/qsql.cpp b/src/sql/qsql.cpp new file mode 100644 index 000000000..c2932fbe0 --- /dev/null +++ b/src/sql/qsql.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Implementation of TQSql class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +/*! + \class TQSql qsql.h + \brief The TQSql class is a namespace for TQt SQL identifiers that + need to be global-like. + + \ingroup database + \mainclass + \module sql + + Normally, you can ignore this class. Several TQt SQL classes + inherit it, so all the identifiers in the TQt SQL namespace are + visible without qualification. +*/ + +/*! + \enum TQSql::Confirm + + This enum type describes edit confirmations. + + \value Yes + \value No + \value Cancel +*/ + +/*! + \enum TQSql::Op + + This enum type describes edit operations. + + \value None + \value Insert + \value Update + \value Delete +*/ + + +/*! + \enum TQSql::Location + + This enum type describes SQL navigation locations. + + \value BeforeFirst + \value AfterLast +*/ + +/*! + \enum TQSql::ParameterType + + This enum is used to set the type of a bind parameter + + \value In the bind parameter is used to put data into the database + \value Out the bind parameter is used to receive data from the database + \value InOut the bind parameter is used to put data into the + database; it will be overwritten with output data on executing + a query. +*/ + +/*! + \enum TQSql::TableType + + This enum type describes types of tables + + \value Tables All the tables visible to the user + \value SystemTables Internal tables used by the DBMS + \value Views All the views visible to the user + \value AllTables All of the above +*/ + +/*! + \fn TQSql::TQSql() + + Constructs a TQt SQL namespace class +*/ diff --git a/src/sql/qsql.h b/src/sql/qsql.h new file mode 100644 index 000000000..3fcbfc7f4 --- /dev/null +++ b/src/sql/qsql.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Definition of TQSql class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQL_H +#define TQSQL_H + +#ifndef QT_H +#include "qglobal.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQM_EXPORT_SQL TQSql +{ +public: + TQSql() {} + enum Op { + None = -1, + Insert = 0, + Update = 1, + Delete = 2 + }; + + enum Location { + BeforeFirst = -1, + AfterLast = -2 + }; + + enum Confirm { + Cancel = -1, + No = 0, + Yes = 1 + }; + + enum ParameterType { + In = 1, + Out = 2, + InOut = 3 //InOut = In | Out + }; + + enum TableType { + Tables = 0x01, + SystemTables = 0x02, + Views = 0x04, + AllTables = 0xff + }; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSql( const TQSql & ); + TQSql &operator=( const TQSql & ); +#endif + +}; + +#endif +#endif diff --git a/src/sql/qsqlcursor.cpp b/src/sql/qsqlcursor.cpp new file mode 100644 index 000000000..5ec8b754a --- /dev/null +++ b/src/sql/qsqlcursor.cpp @@ -0,0 +1,1549 @@ +/**************************************************************************** +** +** Implementation of TQSqlCursor class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlcursor.h" + +#ifndef QT_NO_SQL + +#include "qsqldriver.h" +#include "qsqlresult.h" +#include "qdatetime.h" +#include "qsqldatabase.h" +#include "qsql.h" + +class TQSqlCursorPrivate +{ +public: + + TQSqlCursorPrivate( const TQString& name, TQSqlDatabase* sdb ) + : lastAt( TQSql::BeforeFirst ), nm( name ), srt( name ), md( 0 ), db( sdb ), q( 0 ) + {} + ~TQSqlCursorPrivate() + { + delete q; + } + + TQSqlQuery* query() + { + if ( !q ) + q = new TQSqlQuery( 0, db ); + return q; + } + + int lastAt; + TQString nm; //name + TQSqlIndex srt; //sort + TQString ftr; //filter + int md; //mode + TQSqlIndex priIndx; //primary index + TQSqlRecord editBuffer; + // the primary index as it was before the user changed the values in editBuffer + TQString editIndex; + TQSqlRecordInfo infoBuffer; + TQSqlDatabase* db; + TQSqlQuery* q; +}; + +TQString qOrderByClause( const TQSqlIndex & i, const TQString& prefix = TQString::null ) +{ + TQString str; + int k = i.count(); + if( k == 0 ) return TQString::null; + str = " order by " + i.toString( prefix ); + return str; +} + +TQString qWhereClause( const TQString& prefix, TQSqlField* field, const TQSqlDriver* driver ) +{ + TQString f; + if ( field && driver ) { + f = ( prefix.length() > 0 ? prefix + TQString(".") : TQString::null ) + field->name(); + if ( field->isNull() ) { + f += " IS NULL"; + } else { + f += " = " + driver->formatValue( field ); + } + } + return f; +} + +TQString qWhereClause( TQSqlRecord* rec, const TQString& prefix, const TQString& sep, + const TQSqlDriver* driver ) +{ + static TQString blank( " " ); + TQString filter; + bool separator = FALSE; + for ( uint j = 0; j < rec->count(); ++j ) { + TQSqlField* f = rec->field( j ); + if ( rec->isGenerated( j ) ) { + if ( separator ) + filter += sep + blank; + filter += qWhereClause( prefix, f, driver ); + filter += blank; + separator = TRUE; + } + } + return filter; +} + +/*! + \class TQSqlCursor qsqlcursor.h + \brief The TQSqlCursor class provides browsing and editing of SQL + tables and views. + + \ingroup database + \module sql + + A TQSqlCursor is a database record (see \l TQSqlRecord) that + corresponds to a table or view within an SQL database (see \l + TQSqlDatabase). There are two buffers in a cursor, one used for + browsing and one used for editing records. Each buffer contains a + list of fields which correspond to the fields in the table or + view. + + When positioned on a valid record, the browse buffer contains the + values of the current record's fields from the database. The edit + buffer is separate, and is used for editing existing records and + inserting new records. + + For browsing data, a cursor must first select() data from the + database. After a successful select() the cursor is active + (isActive() returns TRUE), but is initially not positioned on a + valid record (isValid() returns FALSE). To position the cursor on + a valid record, use one of the navigation functions, next(), + prev(), first(), last(), or seek(). Once positioned on a valid + record, data can be retrieved from the browse buffer using + value(). If a navigation function is not successful, it returns + FALSE, the cursor will no longer be positioned on a valid record + and the values returned by value() are undefined. + + For example: + + \quotefile sql/overview/retrieve2/main.cpp + \skipto TQSqlCursor + \printline TQSqlCursor + \printuntil } + + In the above example, a cursor is created specifying a table or + view name in the database. Then, select() is called, which can be + optionally parameterised to filter and order the records + retrieved. Each record in the cursor is retrieved using next(). + When next() returns FALSE, there are no more records to process, + and the loop terminates. + + For editing records (rows of data), a cursor contains a separate + edit buffer which is independent of the fields used when browsing. + The functions insert(), update() and del() operate on the edit + buffer. This allows the cursor to be repositioned to other + records while simultaneously maintaining a separate buffer for + edits. You can get a pointer to the edit buffer using + editBuffer(). The primeInsert(), primeUpdate() and primeDelete() + functions also return a pointer to the edit buffer and prepare it + for insert, update and delete respectively. Edit operations only + affect a single row at a time. Note that update() and del() + retquire that the table or view contain a primaryIndex() to ensure + that edit operations affect a unique record within the database. + + For example: + + \quotefile sql/overview/update/main.cpp + \skipto prices + \printline prices + \printuntil update + \printline + + To edit an existing database record, first move to the record you + wish to update. Call primeUpdate() to get the pointer to the + cursor's edit buffer. Then use this pointer to modify the values + in the edit buffer. Finally, call update() to save the changes to + the database. The values in the edit buffer will be used to + locate the appropriate record when updating the database (see + primaryIndex()). + + Similarly, when deleting an existing database record, first move + to the record you wish to delete. Then, call primeDelete() to get + the pointer to the edit buffer. Finally, call del() to delete the + record from the database. Again, the values in the edit buffer + will be used to locate and delete the appropriate record. + + To insert a new record, call primeInsert() to get the pointer to + the edit buffer. Use this pointer to populate the edit buffer + with new values and then insert() the record into the database. + + After calling insert(), update() or del(), the cursor is no longer + positioned on a valid record and can no longer be navigated + (isValid() return FALSE). The reason for this is that any changes + made to the database will not be visible until select() is called + to refresh the cursor. You can change this behavior by passing + FALSE to insert(), update() or del() which will prevent the cursor + from becoming invalid. The edits will still not be visible when + navigating the cursor until select() is called. + + TQSqlCursor contains virtual methods which allow editing behavior + to be customized by subclasses. This allows custom cursors to be + created that encapsulate the editing behavior of a database table + for an entire application. For example, a cursor can be customized + to always auto-number primary index fields, or provide fields with + suitable default values, when inserting new records. TQSqlCursor + generates SQL statements which are sent to the database engine; + you can control which fields are included in these statements + using setGenerated(). + + Note that TQSqlCursor does not inherit from TQObject. This means + that you are responsible for destroying instances of this class + yourself. However if you create a TQSqlCursor and use it in a + \l TQDataTable, \l TQDataBrowser or a \l TQDataView these classes will + usually take ownership of the cursor and destroy it when they + don't need it anymore. The documentation for TQDataTable, + TQDataBrowser and TQDataView explicitly states which calls take + ownership of the cursor. +*/ + +/*! + \enum TQSqlCursor::Mode + + This enum type describes how TQSqlCursor operates on records in the + database. + + \value ReadOnly the cursor can only SELECT records from the + database. + + \value Insert the cursor can INSERT records into the database. + + \value Update the cursor can UPDATE records in the database. + + \value Delete the cursor can DELETE records from the database. + + \value Writable the cursor can INSERT, UPDATE and DELETE records + in the database. +*/ + +/*! + Constructs a cursor on database \a db using table or view \a name. + + If \a autopopulate is TRUE (the default), the \a name of the + cursor must correspond to an existing table or view name in the + database so that field information can be automatically created. + If the table or view does not exist, the cursor will not be + functional. + + The cursor is created with an initial mode of TQSqlCursor::Writable + (meaning that records can be inserted, updated or deleted using + the cursor). If the cursor does not have a unique primary index, + update and deletes cannot be performed. + + Note that \a autopopulate refers to populating the cursor with + meta-data, e.g. the names of the table's fields, not with + retrieving data. The select() function is used to populate the + cursor with data. + + \sa setName() setMode() +*/ + +TQSqlCursor::TQSqlCursor( const TQString & name, bool autopopulate, TQSqlDatabase* db ) + : TQSqlRecord(), TQSqlQuery( TQString::null, db ) +{ + d = new TQSqlCursorPrivate( name, db ); + setMode( Writable ); + if ( !d->nm.isNull() ) + setName( d->nm, autopopulate ); +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlCursor::TQSqlCursor( const TQSqlCursor & other ) + : TQSqlRecord( other ), TQSqlQuery( other ) +{ + d = new TQSqlCursorPrivate( other.d->nm, other.d->db ); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode( other.mode() ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlCursor::~TQSqlCursor() +{ + delete d; +} + +/*! + Sets the cursor equal to \a other. +*/ + +TQSqlCursor& TQSqlCursor::operator=( const TQSqlCursor& other ) +{ + TQSqlRecord::operator=( other ); + TQSqlQuery::operator=( other ); + delete d; + d = new TQSqlCursorPrivate( other.d->nm, other.d->db ); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode( other.mode() ); + return *this; +} + +/*! + Sets the current sort to \a sort. Note that no new records are + selected. To select new records, use select(). The \a sort will + apply to any subsequent select() calls that do not explicitly + specify a sort. +*/ + +void TQSqlCursor::setSort( const TQSqlIndex& sort ) +{ + d->srt = sort; +} + +/*! + Returns the current sort, or an empty index if there is no current + sort. +*/ +TQSqlIndex TQSqlCursor::sort() const +{ + return d->srt; +} + +/*! + Sets the current filter to \a filter. Note that no new records are + selected. To select new records, use select(). The \a filter will + apply to any subsequent select() calls that do not explicitly + specify a filter. + + The filter is a SQL \c WHERE clause without the keyword 'WHERE', + e.g. \c{name='Dave'} which will be processed by the DBMS. +*/ +void TQSqlCursor::setFilter( const TQString& filter ) +{ + d->ftr = filter; +} + +/*! + Returns the current filter, or an empty string if there is no + current filter. +*/ +TQString TQSqlCursor::filter() const +{ + return d->ftr; +} + +/*! + Sets the name of the cursor to \a name. If \a autopopulate is TRUE + (the default), the \a name must correspond to a valid table or + view name in the database. Also, note that all references to the + cursor edit buffer become invalidated when fields are + auto-populated. See the TQSqlCursor constructor documentation for + more information. +*/ +void TQSqlCursor::setName( const TQString& name, bool autopopulate ) +{ + d->nm = name; + if ( autopopulate ) { + if ( driver() ) { + d->infoBuffer = driver()->recordInfo( name ); + *this = d->infoBuffer.toRecord(); + d->editBuffer = *this; + d->priIndx = driver()->primaryIndex( name ); + } +#ifdef QT_CHECK_RANGE + if ( isEmpty() ) + qWarning("TQSqlCursor::setName: unable to build record, does '%s' exist?", name.latin1() ); +#endif + } +} + +/*! + Returns the name of the cursor. +*/ + +TQString TQSqlCursor::name() const +{ + return d->nm; +} + +/*! \reimp +*/ + +TQString TQSqlCursor::toString( const TQString& prefix, const TQString& sep ) const +{ + TQString pflist; + TQString pfix = prefix.isEmpty() ? TQString::null : prefix + "."; + bool comma = FALSE; + + for ( uint i = 0; i < count(); ++i ) { + const TQString fname = fieldName( i ); + if ( isGenerated( i ) ) { + if( comma ) + pflist += sep + " "; + pflist += pfix + fname; + comma = TRUE; + } + } + return pflist; +} + +/*! + \internal + + Assigns the record \a list. + +*/ +TQSqlRecord & TQSqlCursor::operator=( const TQSqlRecord & list ) +{ + return TQSqlRecord::operator=( list ); +} + +/*! + Append a copy of field \a fieldInfo to the end of the cursor. Note + that all references to the cursor edit buffer become invalidated. +*/ + +void TQSqlCursor::append( const TQSqlFieldInfo& fieldInfo ) +{ + d->editBuffer.append( fieldInfo.toField() ); + d->editBuffer.setGenerated( d->editBuffer.count() - 1, fieldInfo.isGenerated() ); + d->infoBuffer.append( fieldInfo ); + TQSqlRecord::append( fieldInfo.toField() ); + TQSqlRecord::setGenerated( TQSqlRecord::count() - 1, fieldInfo.isGenerated() ); +} + +/*! + Removes all fields from the cursor. Note that all references to + the cursor edit buffer become invalidated. +*/ +void TQSqlCursor::clear() +{ + d->editBuffer.clear(); + d->infoBuffer.clear(); + TQSqlRecord::clear(); +} + + +/*! + Insert a copy of \a fieldInfo at position \a pos. If a field + already exists at \a pos, it is removed. Note that all references + to the cursor edit buffer become invalidated. +*/ + +void TQSqlCursor::insert( int pos, const TQSqlFieldInfo& fieldInfo ) +{ + d->editBuffer.insert( pos, fieldInfo.toField() ); + d->editBuffer.setGenerated( pos, fieldInfo.isGenerated() ); + d->infoBuffer[ pos ] = fieldInfo; + TQSqlRecord::insert( pos, fieldInfo.toField() ); + TQSqlRecord::setGenerated( pos, fieldInfo.isGenerated() ); +} + +/*! + Removes the field at \a pos. If \a pos does not exist, nothing + happens. Note that all references to the cursor edit buffer become + invalidated. +*/ + +void TQSqlCursor::remove( int pos ) +{ + d->editBuffer.remove( pos ); + d->infoBuffer[ pos ] = TQSqlFieldInfo(); + TQSqlRecord::remove( pos ); +} + +/*! + Sets the generated flag for the field \a name to \a generated. If + the field does not exist, nothing happens. Only fields that have + \a generated set to TRUE are included in the SQL that is + generated by insert(), update() or del(). + + \sa isGenerated() +*/ + +void TQSqlCursor::setGenerated( const TQString& name, bool generated ) +{ + int pos = position( name ); + if ( pos == -1 ) + return; + TQSqlRecord::setGenerated( name, generated ); + d->editBuffer.setGenerated( name, generated ); + d->infoBuffer[ pos ].setGenerated( generated ); +} + +/*! + \overload + + Sets the generated flag for the field \a i to \a generated. + + \sa isGenerated() +*/ +void TQSqlCursor::setGenerated( int i, bool generated ) +{ + if ( i < 0 || i >= (int)d->infoBuffer.count() ) + return; + TQSqlRecord::setGenerated( i, generated ); + d->editBuffer.setGenerated( i, generated ); + d->infoBuffer[i].setGenerated( generated ); +} + +/*! + Returns the primary index associated with the cursor as defined in + the database, or an empty index if there is no primary index. If + \a setFromCursor is TRUE (the default), the index fields are + populated with the corresponding values in the cursor's current + record. +*/ + +TQSqlIndex TQSqlCursor::primaryIndex( bool setFromCursor ) const +{ + if ( setFromCursor ) { + for ( uint i = 0; i < d->priIndx.count(); ++i ) { + const TQString fn = d->priIndx.fieldName( i ); + if ( contains( fn ) ) + d->priIndx.setValue( i, value( fn ) ); + } + } + return d->priIndx; +} + +/*! + Sets the primary index associated with the cursor to the index \a + idx. Note that this index must contain a field or set of fields + which identify a unique record within the underlying database + table or view so that update() and del() will execute as expected. + + \sa update() del() +*/ + +void TQSqlCursor::setPrimaryIndex( const TQSqlIndex& idx ) +{ + d->priIndx = idx; +} + + +/*! + Returns an index composed of \a fieldNames, all in ASCending + order. Note that all field names must exist in the cursor, + otherwise an empty index is returned. + + \sa TQSqlIndex +*/ + +TQSqlIndex TQSqlCursor::index( const TQStringList& fieldNames ) const +{ + TQSqlIndex idx; + for ( TQStringList::ConstIterator it = fieldNames.begin(); it != fieldNames.end(); ++it ) { + const TQSqlField* f = field( (*it) ); + if ( !f ) { /* all fields must exist */ + idx.clear(); + break; + } + idx.append( *f ); + } + return idx; +} + +/*! + \overload + + Returns an index based on \a fieldName. +*/ + +TQSqlIndex TQSqlCursor::index( const TQString& fieldName ) const +{ + TQStringList fl( fieldName ); + return index( fl ); +} + +/*! + \overload + + Returns an index based on \a fieldName. +*/ + +TQSqlIndex TQSqlCursor::index( const char* fieldName ) const +{ + return index( TQStringList( TQString( fieldName ) ) ); +} + +/*! + Selects all fields in the cursor from the database matching the + filter criteria \a filter. The data is returned in the order + specified by the index \a sort. Returns TRUE if the data was + successfully selected; otherwise returns FALSE. + + The \a filter is a string containing a SQL \c WHERE clause but + without the 'WHERE' keyword. The cursor is initially positioned at + an invalid row after this function is called. To move to a valid + row, use seek(), first(), last(), prev() or next(). + + Example: + \code + TQSqlCursor cur( "Employee" ); // Use the Employee table or view + cur.select( "deptno=10" ); // select all records in department 10 + while( cur.next() ) { + ... // process data + } + ... + // select records in other departments, ordered by department number + cur.select( "deptno>10", cur.index( "deptno" ) ); + ... + \endcode + + The filter will apply to any subsequent select() calls that do not + explicitly specify another filter. Similarly the sort will apply + to any subsequent select() calls that do not explicitly specify + another sort. + + \code + TQSqlCursor cur( "Employee" ); + cur.select( "deptno=10" ); // select all records in department 10 + while( cur.next() ) { + ... // process data + } + ... + cur.select(); // re-selects all records in department 10 + ... + \endcode + +*/ + +bool TQSqlCursor::select( const TQString & filter, const TQSqlIndex & sort ) +{ + TQString fieldList = toString( d->nm ); + if ( fieldList.isEmpty() ) + return FALSE; + TQString str= "select " + fieldList; + str += " from " + d->nm; + if ( !filter.isEmpty() ) { + d->ftr = filter; + str += " where " + filter; + } else + d->ftr = TQString::null; + if ( sort.count() > 0 ) + str += " order by " + sort.toString( d->nm ); + d->srt = sort; + return exec( str ); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The rows are + returned in the order specified by the last call to setSort() or + the last call to select() that specified a sort, whichever is the + most recent. If there is no current sort, the order in which the + rows are returned is undefined. The records are filtered according + to the filter specified by the last call to setFilter() or the + last call to select() that specified a filter, whichever is the + most recent. If there is no current filter, all records are + returned. The cursor is initially positioned at an invalid row. To + move to a valid row, use seek(), first(), last(), prev() or + next(). + + \sa setSort() setFilter() +*/ + +bool TQSqlCursor::select() +{ + return select( filter(), sort() ); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The data is + returned in the order specified by the index \a sort. The records + are filtered according to the filter specified by the last call to + setFilter() or the last call to select() that specified a filter, + whichever is the most recent. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), prev() or next(). +*/ + +bool TQSqlCursor::select( const TQSqlIndex& sort ) +{ + return select( filter(), sort ); +} + +/*! + \overload + + Selects all fields in the cursor matching the filter index \a + filter. The data is returned in the order specified by the index + \a sort. The \a filter index works by constructing a WHERE clause + using the names of the fields from the \a filter and their values + from the current cursor record. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), prev() or next(). This function is useful, for example, + for retrieving data based upon a table's primary index: + + \code + TQSqlCursor cur( "Employee" ); + TQSqlIndex pk = cur.primaryIndex(); + cur.setValue( "id", 10 ); + cur.select( pk, pk ); // generates "SELECT ... FROM Employee WHERE id=10 ORDER BY id" + ... + \endcode + + In this example the TQSqlIndex, pk, is used for two different + purposes. When used as the filter (first) argument, the field + names it contains are used to construct the WHERE clause, each set + to the current cursor value, \c{WHERE id=10}, in this case. When + used as the sort (second) argument the field names it contains are + used for the ORDER BY clause, \c{ORDER BY id} in this example. +*/ + +bool TQSqlCursor::select( const TQSqlIndex & filter, const TQSqlIndex & sort ) +{ + return select( toString( filter, this, d->nm, "=", "and" ), sort ); +} + +/*! + Sets the cursor mode to \a mode. This value can be an OR'ed + combination of \l TQSqlCursor::Mode values. The default mode for a + cursor is \c TQSqlCursor::Writable. + + \code + TQSqlCursor cur( "Employee" ); + cur.setMode( TQSqlCursor::Writable ); // allow insert/update/delete + ... + cur.setMode( TQSqlCursor::Insert | TQSqlCursor::Update ); // allow inserts and updates only + ... + cur.setMode( TQSqlCursor::ReadOnly ); // no inserts/updates/deletes allowed + + \endcode +*/ + +void TQSqlCursor::setMode( int mode ) +{ + d->md = mode; +} + +/*! + Returns the current cursor mode. + + \sa setMode() +*/ + +int TQSqlCursor::mode() const +{ + return d->md; +} + +/*! + Sets field \a name to \a calculated. If the field \a name does not + exist, nothing happens. The value of a calculated field is set by + the calculateField() virtual function which you must reimplement + (or the field value will be an invalid TQVariant). Calculated + fields do not appear in generated SQL statements sent to the + database. + + \sa calculateField() TQSqlRecord::setGenerated() +*/ + +void TQSqlCursor::setCalculated( const TQString& name, bool calculated ) +{ + int pos = position( name ); + if ( pos < 0 ) + return; + d->infoBuffer[ pos ].setCalculated( calculated ); + if ( calculated ) + setGenerated( pos, FALSE ); +} + +/*! + Returns TRUE if the field \a name exists and is calculated; + otherwise returns FALSE. + + \sa setCalculated() +*/ + +bool TQSqlCursor::isCalculated( const TQString& name ) const +{ + int pos = position( name ); + if ( pos < 0 ) + return FALSE; + return d->infoBuffer[ pos ].isCalculated(); +} + +/*! + Sets field \a{name}'s trimmed status to \a trim. If the field \a + name does not exist, nothing happens. + + When a trimmed field of type string or cstring is read from the + database any trailing (right-most) spaces are removed. + + \sa isTrimmed() TQVariant +*/ + +void TQSqlCursor::setTrimmed( const TQString& name, bool trim ) +{ + int pos = position( name ); + if ( pos < 0 ) + return; + d->infoBuffer[ pos ].setTrim( trim ); +} + +/*! + Returns TRUE if the field \a name exists and is trimmed; otherwise + returns FALSE. + + When a trimmed field of type string or cstring is read from the + database any trailing (right-most) spaces are removed. + + \sa setTrimmed() +*/ + +bool TQSqlCursor::isTrimmed( const TQString& name ) const +{ + int pos = position( name ); + if ( pos < 0 ) + return FALSE; + return d->infoBuffer[ pos ].isTrim(); +} + +/*! + Returns TRUE if the cursor is read-only; otherwise returns FALSE. + The default is FALSE. Read-only cursors cannot be edited using + insert(), update() or del(). + + \sa setMode() +*/ + +bool TQSqlCursor::isReadOnly() const +{ + return d->md == 0; +} + +/*! + Returns TRUE if the cursor will perform inserts; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canInsert() const +{ + return ( ( d->md & Insert ) == Insert ) ; +} + + +/*! + Returns TRUE if the cursor will perform updates; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canUpdate() const +{ + return ( ( d->md & Update ) == Update ) ; +} + +/*! + Returns TRUE if the cursor will perform deletes; otherwise returns + FALSE. + + \sa setMode() +*/ + +bool TQSqlCursor::canDelete() const +{ + return ( ( d->md & Delete ) == Delete ) ; +} + +/*! + \overload + + Returns a formatted string composed of the \a prefix (e.g. table + or view name), ".", the \a field name, the \a fieldSep and the + field value. If the \a prefix is empty then the string will begin + with the \a field name. This function is useful for generating SQL + statements. +*/ + +TQString TQSqlCursor::toString( const TQString& prefix, TQSqlField* field, const TQString& fieldSep ) const +{ + TQString f; + if ( field && driver() ) { + f = ( prefix.length() > 0 ? prefix + TQString(".") : TQString::null ) + field->name(); + f += " " + fieldSep + " "; + if ( field->isNull() ) { + f += "NULL"; + } else { + f += driver()->formatValue( field ); + } + } + return f; +} + +/*! + Returns a formatted string composed of all the fields in \a rec. + Each field is composed of the \a prefix (e.g. table or view name), + ".", the field name, the \a fieldSep and the field value. If the + \a prefix is empty then each field will begin with the field name. + The fields are then joined together separated by \a sep. Fields + where isGenerated() returns FALSE are not included. This function + is useful for generating SQL statements. +*/ + +TQString TQSqlCursor::toString( TQSqlRecord* rec, const TQString& prefix, const TQString& fieldSep, + const TQString& sep ) const +{ + static TQString blank( " " ); + TQString filter; + bool separator = FALSE; + for ( uint j = 0; j < count(); ++j ) { + TQSqlField* f = rec->field( j ); + if ( rec->isGenerated( j ) ) { + if ( separator ) + filter += sep + blank; + filter += toString( prefix, f, fieldSep ); + filter += blank; + separator = TRUE; + } + } + return filter; +} + +/*! + \overload + + Returns a formatted string composed of all the fields in the index + \a i. Each field is composed of the \a prefix (e.g. table or view + name), ".", the field name, the \a fieldSep and the field value. + If the \a prefix is empty then each field will begin with the field + name. The field values are taken from \a rec. The fields are then + joined together separated by \a sep. Fields where isGenerated() + returns FALSE are ignored. This function is useful for generating + SQL statements. +*/ + +TQString TQSqlCursor::toString( const TQSqlIndex& i, TQSqlRecord* rec, const TQString& prefix, + const TQString& fieldSep, const TQString& sep ) const +{ + TQString filter; + bool separator = FALSE; + for( uint j = 0; j < i.count(); ++j ){ + if ( rec->isGenerated( j ) ) { + if( separator ) { + filter += " " + sep + " " ; + } + TQString fn = i.fieldName( j ); + TQSqlField* f = rec->field( fn ); + filter += toString( prefix, f, fieldSep ); + separator = TRUE; + } + } + return filter; +} + +/*! + \overload + + Inserts the current contents of the cursor's edit record buffer + into the database, if the cursor allows inserts. Returns the + number of rows affected by the insert. For error information, use + lastError(). + + If \a invalidate is TRUE (the default), the cursor will no longer + be positioned on a valid record and can no longer be navigated. A + new select() call must be made before navigating to a valid + record. + + \quotefile sql/overview/insert2/main.cpp + \skipto prices + \printline prices + \printuntil insert + + In the above example, a cursor is created on the 'prices' table + and a pointer to the insert buffer is atquired using primeInsert(). + Each field's value is set to the desired value and then insert() + is called to insert the data into the database. Remember: all edit + operations (insert(), update() and delete()) operate on the + contents of the cursor edit buffer and not on the contents of the + cursor itself. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::insert( bool invalidate ) +{ + if ( ( d->md & Insert ) != Insert || !driver() ) + return FALSE; + int k = d->editBuffer.count(); + if ( k == 0 ) + return 0; + + TQString fList; + TQString vList; + bool comma = FALSE; + // use a prepared query if the driver supports it + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + int cnt = 0; + bool oraStyle = driver()->hasFeature( TQSqlDriver::NamedPlaceholders ); + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + vList += ","; + } + fList += f->name(); + vList += (oraStyle == TRUE) ? ":f" + TQString::number(cnt) : TQString("?"); + cnt++; + comma = TRUE; + } + } + if ( !comma ) { + return 0; + } + TQString str; + str.append( "insert into " ).append( name() ).append( "(" ).append( fList ).append( ") values (" ).append( vList ). append ( ")" ); + return applyPrepared( str, invalidate ); + } else { + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + vList += ","; + } + fList += f->name(); + vList += driver()->formatValue( f ); + comma = TRUE; + } + } + + if ( !comma ) { + // no valid fields found + return 0; + } + TQString str; + str.append( "insert into " ).append( name() ).append( "(" ).append( fList ).append( ") values (" ).append( vList ). append ( ")" ); + return apply( str, invalidate ); + } +} + +/*! + Returns the current internal edit buffer. If \a copy is TRUE (the + default is FALSE), the current cursor field values are first + copied into the edit buffer. The edit buffer is valid as long as + the cursor remains valid. The cursor retains ownership of the + returned pointer, so it must not be deleted or modified. + + \sa primeInsert(), primeUpdate() primeDelete() +*/ + +TQSqlRecord* TQSqlCursor::editBuffer( bool copy ) +{ + if ( copy ) { + for(uint i = 0; i < d->editBuffer.count(); i++) { + if ( TQSqlRecord::isNull( i ) ) { + d->editBuffer.setNull( i ); + } else { + d->editBuffer.setValue( i, value( i ) ); + } + } + } + return &d->editBuffer; +} + +/*! + This function primes the edit buffer's field values for update and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + TRUE ) ). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() update() +*/ + +TQSqlRecord* TQSqlCursor::primeUpdate() +{ + // memorize the primary keys as they were before the user changed the values in editBuffer + TQSqlRecord* buf = editBuffer( TRUE ); + TQSqlIndex idx = primaryIndex( FALSE ); + if ( !idx.isEmpty() ) + d->editIndex = toString( idx, buf, d->nm, "=", "and" ); + else + d->editIndex = qWhereClause( buf, d->nm, "and", driver() ); + return buf; +} + +/*! + This function primes the edit buffer's field values for delete and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + TRUE ) ). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() del() +*/ + +TQSqlRecord* TQSqlCursor::primeDelete() +{ + return editBuffer( TRUE ); +} + +/*! + This function primes the edit buffer's field values for insert and + returns the edit buffer. The default implementation clears all + field values in the edit buffer. The cursor retains ownership of + the returned pointer, so it must not be deleted or modified. + + \sa editBuffer() insert() +*/ + +TQSqlRecord* TQSqlCursor::primeInsert() +{ + d->editBuffer.clearValues(); + return &d->editBuffer; +} + + +/*! + Updates the database with the current contents of the edit buffer. + Returns the number of records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are updated. If the cursor does not contain + a primary index, no update is performed and 0 is returned. + + If \a invalidate is TRUE (the default), the current cursor can no + longer be navigated. A new select() call must be made before you + can move to a valid record. For example: + + \quotefile sql/overview/update/main.cpp + \skipto prices + \printline prices + \printuntil update + \printline + + In the above example, a cursor is created on the 'prices' table + and is positioned on the record to be updated. Then a pointer to + the cursor's edit buffer is actquired using primeUpdate(). A new + value is calculated and placed into the edit buffer with the + setValue() call. Finally, an update() call is made on the cursor + which uses the tables's primary index to update the record in the + database with the contents of the cursor's edit buffer. Remember: + all edit operations (insert(), update() and delete()) operate on + the contents of the cursor edit buffer and not on the contents of + the cursor itself. + + Note that if the primary index does not uniquely distinguish + records the database may be changed into an inconsistent state. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::update( bool invalidate ) +{ + if ( d->editIndex.isEmpty() ) + return 0; + return update( d->editIndex, invalidate ); +} + +/*! + \overload + + Updates the database with the current contents of the cursor edit + buffer using the specified \a filter. Returns the number of + records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria are updated, otherwise + all records in the table are updated. + + If \a invalidate is TRUE (the default), the cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. + + \sa primeUpdate() setMode() lastError() +*/ + +int TQSqlCursor::update( const TQString & filter, bool invalidate ) +{ + if ( ( d->md & Update ) != Update ) { + return FALSE; + } + int k = count(); + if ( k == 0 ) { + return 0; + } + + // use a prepared query if the driver supports it + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + TQString fList; + bool comma = FALSE; + int cnt = 0; + bool oraStyle = driver()->hasFeature( TQSqlDriver::NamedPlaceholders ); + for( int j = 0; j < k; ++j ) { + TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + if ( comma ) { + fList += ","; + } + fList += f->name() + " = " + (oraStyle == TRUE ? ":f" + TQString::number(cnt) : TQString("?")); + cnt++; + comma = TRUE; + } + } + if ( !comma ) { + return 0; + } + TQString str = "update " + name() + " set " + fList; + if ( filter.length() ) { + str+= " where " + filter; + } + return applyPrepared( str, invalidate ); + } else { + TQString str = "update " + name(); + str += " set " + toString( &d->editBuffer, TQString::null, "=", "," ); + if ( filter.length() ) { + str+= " where " + filter; + } + return apply( str, invalidate ); + } +} + +/*! + Deletes a record from the database using the cursor's primary + index and the contents of the cursor edit buffer. Returns the + number of records which were deleted. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are deleted. If the cursor does not contain + a primary index, no delete is performed and 0 is returned. If \a + invalidate is TRUE (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For example: + + \quotefile sql/overview/delete/main.cpp + \skipto prices + \printline prices + \printuntil } + + In the above example, a cursor is created on the 'prices' table + and positioned to the record to be deleted. First primeDelete() is + called to populate the edit buffer with the current cursor values, + e.g. with an id of 999, and then del() is called to actually + delete the record from the database. Remember: all edit operations + (insert(), update() and delete()) operate on the contents of the + cursor edit buffer and not on the contents of the cursor itself. + + \sa primeDelete() setMode() lastError() +*/ + +int TQSqlCursor::del( bool invalidate ) +{ + TQSqlIndex idx = primaryIndex( FALSE ); + if ( idx.isEmpty() ) + return del( qWhereClause( &d->editBuffer, d->nm, "and", driver() ), invalidate ); + else + return del( toString( primaryIndex(), &d->editBuffer, d->nm, + "=", "and" ), invalidate ); +} + +/*! + \overload + + Deletes the current cursor record from the database using the + filter \a filter. Only records which meet the filter criteria are + deleted. Returns the number of records which were deleted. If \a + invalidate is TRUE (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For error information, use lastError(). + + The \a filter is an SQL \c WHERE clause, e.g. \c{id=500}. + + \sa setMode() lastError() +*/ + +int TQSqlCursor::del( const TQString & filter, bool invalidate ) +{ + if ( ( d->md & Delete ) != Delete ) + return 0; + int k = count(); + if( k == 0 ) return 0; + TQString str = "delete from " + name(); + if ( filter.length() ) + str+= " where " + filter; + return apply( str, invalidate ); +} + +/* + \internal +*/ + +int TQSqlCursor::apply( const TQString& q, bool invalidate ) +{ + int ar = 0; + if ( invalidate ) { + if ( exec( q ) ) + ar = numRowsAffected(); + } else if ( driver() ) { + TQSqlQuery* sql = d->query(); + if ( sql && sql->exec( q ) ) + ar = sql->numRowsAffected(); + } + return ar; +} + +/* + \internal +*/ + +int TQSqlCursor::applyPrepared( const TQString& q, bool invalidate ) +{ + int ar = 0; + TQSqlQuery* sql = 0; + + if ( invalidate ) { + sql = (TQSqlQuery*)this; + d->lastAt = TQSql::BeforeFirst; + } else { + sql = d->query(); + } + if ( !sql ) + return 0; + + if ( invalidate || sql->lastQuery() != q ) { + if ( !sql->prepare( q ) ) + return 0; + } + + int cnt = 0; + int fieldCount = (int)count(); + for ( int j = 0; j < fieldCount; ++j ) { + const TQSqlField* f = d->editBuffer.field( j ); + if ( d->editBuffer.isGenerated( j ) ) { + sql->bindValue( cnt, f->value() ); + cnt++; + } + } + if ( sql->exec() ) { + ar = sql->numRowsAffected(); + } + return ar; +} + +/*! \reimp + + Executes the SQL query \a sql. Returns TRUE of the cursor is + active, otherwise returns FALSE. + +*/ +bool TQSqlCursor::exec( const TQString & sql ) +{ + d->lastAt = TQSql::BeforeFirst; + TQSqlQuery::exec( sql ); + return isActive(); +} + +/*! + Protected virtual function which is called whenever a field needs + to be calculated. If calculated fields are being used, derived + classes must reimplement this function and return the appropriate + value for field \a name. The default implementation returns an + invalid TQVariant. + + \sa setCalculated() +*/ + +TQVariant TQSqlCursor::calculateField( const TQString& ) +{ + return TQVariant(); +} + +/*! \internal + Ensure fieldlist is synced with query. + +*/ + +static TQString qTrim( const TQString& s ) +{ + TQString result = s; + int end = result.length() - 1; + while ( end >= 0 && result[end].isSpace() ) // skip white space from end + end--; + result.truncate( end + 1 ); + return result; +} + +/*! \internal + */ + +void TQSqlCursor::sync() +{ + if ( isActive() && isValid() && d->lastAt != at() ) { + d->lastAt = at(); + uint i = 0; + uint j = 0; + bool haveCalculatedFields = FALSE; + for ( ; i < count(); ++i ) { + if ( !haveCalculatedFields && d->infoBuffer[i].isCalculated() ) { + haveCalculatedFields = TRUE; + } + if ( TQSqlRecord::isGenerated( i ) ) { + TQVariant v = TQSqlQuery::value( j ); + if ( ( v.type() == TQVariant::String || v.type() == TQVariant::CString ) && + d->infoBuffer[ i ].isTrim() ) { + v = qTrim( v.toString() ); + } + TQSqlRecord::setValue( i, v ); + if ( TQSqlQuery::isNull( j ) ) + TQSqlRecord::field( i )->setNull(); + j++; + } + } + if ( haveCalculatedFields ) { + for ( i = 0; i < count(); ++i ) { + if ( d->infoBuffer[i].isCalculated() ) + TQSqlRecord::setValue( i, calculateField( fieldName( i ) ) ); + } + } + } +} + +/*! \reimp + +*/ + +void TQSqlCursor::afterSeek() +{ + sync(); +} + +/*! + \reimp + + Returns the value of field number \a i. +*/ + +TQVariant TQSqlCursor::value( int i ) const +{ + return TQSqlRecord::value( i ); +} + +/*! + \reimp + + Returns the value of the field called \a name. +*/ + +TQVariant TQSqlCursor::value( const TQString& name ) const +{ + return TQSqlRecord::value( name ); +} + +/*! \internal + cursors should be filled with TQSqlFieldInfos... +*/ +void TQSqlCursor::append( const TQSqlField& field ) +{ + append( TQSqlFieldInfo( field ) ); +} +/*! \internal + cursors should be filled with TQSqlFieldInfos... +*/ +void TQSqlCursor::insert( int pos, const TQSqlField& field ) +{ + insert( pos, TQSqlFieldInfo( field ) ); +} + +/*! + Returns TRUE if the field \a i is NULL or if there is no field at + position \a i; otherwise returns FALSE. + + This is the same as calling TQSqlRecord::isNull( \a i ) +*/ +bool TQSqlCursor::isNull( int i ) const +{ + return TQSqlRecord::isNull( i ); +} +/*! + \overload + + Returns TRUE if the field called \a name is NULL or if there is no + field called \a name; otherwise returns FALSE. + + This is the same as calling TQSqlRecord::isNull( \a name ) +*/ +bool TQSqlCursor::isNull( const TQString& name ) const +{ + return TQSqlRecord::isNull( name ); +} + +/*! \reimp */ +void TQSqlCursor::setValue( int i, const TQVariant& val ) +{ +#ifdef QT_DEBUG + qDebug("TQSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete()."); +#endif + TQSqlRecord::setValue( i, val ); +} + +/*! \reimp */ +void TQSqlCursor::setValue( const TQString& name, const TQVariant& val ) +{ +#ifdef QT_DEBUG + qDebug("TQSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete()."); +#endif + TQSqlRecord::setValue( name, val ); +} +#endif diff --git a/src/sql/qsqlcursor.h b/src/sql/qsqlcursor.h new file mode 100644 index 000000000..8c00f7132 --- /dev/null +++ b/src/sql/qsqlcursor.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Definition of TQSqlCursor class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLCURSOR_H +#define TQSQLCURSOR_H + +#ifndef QT_H +#include "qsqlrecord.h" +#include "qstringlist.h" +#include "qsqlquery.h" +#include "qsqlindex.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlDatabase; +class TQSqlCursorPrivate; + +class TQM_EXPORT_SQL TQSqlCursor : public TQSqlRecord, public TQSqlQuery +{ +public: + TQSqlCursor( const TQString & name = TQString::null, bool autopopulate = TRUE, TQSqlDatabase* db = 0 ); + TQSqlCursor( const TQSqlCursor & other ); + TQSqlCursor& operator=( const TQSqlCursor& other ); + ~TQSqlCursor(); + + enum Mode { + ReadOnly = 0, + Insert = 1, + Update = 2, + Delete = 4, + Writable = 7 + }; + + TQVariant value( int i ) const; + TQVariant value( const TQString& name ) const; + void setValue( int i, const TQVariant& val ); + void setValue( const TQString& name, const TQVariant& val ); + virtual TQSqlIndex primaryIndex( bool prime = TRUE ) const; + virtual TQSqlIndex index( const TQStringList& fieldNames ) const; + TQSqlIndex index( const TQString& fieldName ) const; + TQSqlIndex index( const char* fieldName ) const; + virtual void setPrimaryIndex( const TQSqlIndex& idx ); + + virtual void append( const TQSqlFieldInfo& fieldInfo ); + virtual void insert( int pos, const TQSqlFieldInfo& fieldInfo ); + void remove( int pos ); + void clear(); + void setGenerated( const TQString& name, bool generated ); + void setGenerated( int i, bool generated ); + + virtual TQSqlRecord* editBuffer( bool copy = FALSE ); + virtual TQSqlRecord* primeInsert(); + virtual TQSqlRecord* primeUpdate(); + virtual TQSqlRecord* primeDelete(); + virtual int insert( bool invalidate = TRUE ); + virtual int update( bool invalidate = TRUE ); + virtual int del( bool invalidate = TRUE ); + + virtual void setMode( int flags ); + int mode() const; + virtual void setCalculated( const TQString& name, bool calculated ); + bool isCalculated( const TQString& name ) const; + virtual void setTrimmed( const TQString& name, bool trim ); + bool isTrimmed( const TQString& name ) const; + + bool isReadOnly() const; + bool canInsert() const; + bool canUpdate() const; + bool canDelete() const; + + bool select(); + bool select( const TQSqlIndex& sort ); + bool select( const TQSqlIndex & filter, const TQSqlIndex & sort ); + virtual bool select( const TQString & filter, const TQSqlIndex & sort = TQSqlIndex() ); + + virtual void setSort( const TQSqlIndex& sort ); + TQSqlIndex sort() const; + virtual void setFilter( const TQString& filter ); + TQString filter() const; + virtual void setName( const TQString& name, bool autopopulate = TRUE ); + TQString name() const; + TQString toString( const TQString& prefix = TQString::null, + const TQString& sep = "," ) const; + bool isNull( int i ) const; + bool isNull( const TQString& name ) const; + +protected: + void afterSeek(); + bool exec( const TQString & sql ); + + virtual TQVariant calculateField( const TQString& name ); + virtual int update( const TQString & filter, bool invalidate = TRUE ); + virtual int del( const TQString & filter, bool invalidate = TRUE ); + + virtual TQString toString( const TQString& prefix, TQSqlField* field, const TQString& fieldSep ) const; + virtual TQString toString( TQSqlRecord* rec, const TQString& prefix, const TQString& fieldSep, + const TQString& sep ) const; + virtual TQString toString( const TQSqlIndex& i, TQSqlRecord* rec, const TQString& prefix, + const TQString& fieldSep, const TQString& sep ) const; + +private: + void sync(); + int apply( const TQString& q, bool invalidate ); + int applyPrepared( const TQString& q, bool invalidate ); + TQSqlRecord& operator=( const TQSqlRecord & list ); + void append( const TQSqlField& field ); + void insert( int pos, const TQSqlField& field ); + + TQSqlCursorPrivate* d; +}; + + + + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqldatabase.cpp b/src/sql/qsqldatabase.cpp new file mode 100644 index 000000000..e54fd124d --- /dev/null +++ b/src/sql/qsqldatabase.cpp @@ -0,0 +1,1332 @@ +/**************************************************************************** +** +** Implementation of TQSqlDatabase class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqldatabase.h" + +#ifndef QT_NO_SQL + +#ifdef Q_OS_WIN32 +// Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h +#define _WINSCARD_H_ +#endif + +#ifdef QT_SQL_POSTGRES +#include "drivers/psql/qsql_psql.h" +#endif +#ifdef QT_SQL_MYSQL +#include "drivers/mysql/qsql_mysql.h" +#endif +#ifdef QT_SQL_ODBC +#include "drivers/odbc/qsql_odbc.h" +#endif +#ifdef QT_SQL_OCI +#include "drivers/oci/qsql_oci.h" +#endif +#ifdef QT_SQL_TDS +#include "drivers/tds/qsql_tds.h" +#endif +#ifdef QT_SQL_DB2 +#include "drivers/db2/qsql_db2.h" +#endif +#ifdef QT_SQL_SQLITE +#include "drivers/sqlite/qsql_sqlite.h" +#endif +#ifdef QT_SQL_IBASE +#include "drivers/ibase/qsql_ibase.h" +#endif + +#include "qapplication.h" +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "qsqldriverinterface_p.h" +#include <private/qpluginmanager_p.h> +#include <private/qsqlextension_p.h> +#include "qobject.h" +#include "qguardedptr.h" +#include "qcleanuphandler.h" +#include "qdict.h" +#include <stdlib.h> + +QT_STATIC_CONST_IMPL char * const TQSqlDatabase::defaultConnection = "qt_sql_default_connection"; + +TQPtrDict<TQSqlDriverExtension> *qt_driver_extension_dict = 0; +TQPtrDict<TQSqlOpenExtension> *qt_open_extension_dict = 0; + +static TQSingleCleanupHandler< TQPtrDict<TQSqlDriverExtension> > qt_driver_ext_cleanup; +static TQSingleCleanupHandler< TQPtrDict<TQSqlOpenExtension> > qt_open_ext_cleanup; + +Q_EXPORT TQPtrDict<TQSqlDriverExtension> *qSqlDriverExtDict() +{ + if ( !qt_driver_extension_dict ) { + qt_driver_extension_dict = new TQPtrDict<TQSqlDriverExtension>; + qt_driver_ext_cleanup.set( &qt_driver_extension_dict ); + } + return qt_driver_extension_dict; +} + +Q_EXPORT TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict() +{ + if ( !qt_open_extension_dict ) { + qt_open_extension_dict = new TQPtrDict<TQSqlOpenExtension>; + qt_open_ext_cleanup.set( &qt_open_extension_dict ); + } + return qt_open_extension_dict; +} + +class TQNullResult : public TQSqlResult +{ +public: + TQNullResult(const TQSqlDriver* d): TQSqlResult(d){} + ~TQNullResult(){} +protected: + TQVariant data( int ) { return TQVariant(); } + bool reset ( const TQString& sqlquery ) { TQString s(sqlquery); return FALSE; } + bool fetch( int i ) { i = i; return FALSE; } + bool fetchFirst() { return FALSE; } + bool fetchLast() { return FALSE; } + bool isNull( int ) {return FALSE; } + TQSqlRecord record() {return TQSqlRecord();} + int size() {return 0;} + int numRowsAffected() {return 0;} +}; + +class TQNullDriver : public TQSqlDriver +{ +public: + TQNullDriver(): TQSqlDriver(){} + ~TQNullDriver(){} + bool hasFeature( DriverFeature /* f */ ) const { return FALSE; } ; + bool open( const TQString & , + const TQString & , + const TQString & , + const TQString &, + int ) { + return FALSE; + } + void close() {} + TQSqlQuery createQuery() const { return TQSqlQuery( new TQNullResult(this) ); } +}; + +typedef TQDict<TQSqlDriverCreatorBase> TQDriverDict; + +class TQSqlDatabaseManager : public TQObject +{ +public: + TQSqlDatabaseManager( TQObject * parent = 0, const char * name = 0 ); + ~TQSqlDatabaseManager(); + static TQSqlDatabase* database( const TQString& name, bool open ); + static TQSqlDatabase* addDatabase( TQSqlDatabase* db, const TQString & name ); + static void removeDatabase( const TQString& name ); + static void removeDatabase( TQSqlDatabase* db ); + static bool contains( const TQString& name ); + static TQDriverDict* driverDict(); + +protected: + static TQSqlDatabaseManager* instance(); + TQDict< TQSqlDatabase > dbDict; + TQDriverDict* drDict; +}; + +/*! + Constructs an SQL database manager. +*/ + +TQSqlDatabaseManager::TQSqlDatabaseManager( TQObject * parent, const char * name ) + : TQObject( parent, name ), dbDict( 1 ), drDict( 0 ) +{ +} + +/*! + Destroys the object and frees any allocated resources. All open + database connections are closed. All database connections are + deleted. +*/ + +TQSqlDatabaseManager::~TQSqlDatabaseManager() +{ + TQDictIterator< TQSqlDatabase > it( dbDict ); + while ( it.current() ) { + it.current()->close(); + delete it.current(); + ++it; + } + delete drDict; +} + +/*! + \internal +*/ +TQDriverDict* TQSqlDatabaseManager::driverDict() +{ + TQSqlDatabaseManager* sqlConnection = instance(); + if ( !sqlConnection->drDict ) { + sqlConnection->drDict = new TQDriverDict(); + sqlConnection->drDict->setAutoDelete( TRUE ); + } + return sqlConnection->drDict; +} + + +/*! + \internal +*/ +TQSqlDatabaseManager* TQSqlDatabaseManager::instance() +{ + static TQGuardedPtr<TQSqlDatabaseManager> sqlConnection = 0; + if ( !sqlConnection ) { + if( qApp == 0 ){ + qFatal( "TQSqlDatabaseManager: A TQApplication object has to be " + "instantiated in order to use the SQL module." ); + return 0; + } + sqlConnection = new TQSqlDatabaseManager( qApp, "database manager" ); + } + return (TQSqlDatabaseManager*)sqlConnection; +} + +/*! + Returns the database connection called \a name. If \a open is + TRUE, the database connection is opened. If \a name does not exist + in the list of managed databases, 0 is returned. +*/ + +TQSqlDatabase* TQSqlDatabaseManager::database( const TQString& name, bool open ) +{ + if ( !contains( name ) ) + return 0; + + TQSqlDatabaseManager* sqlConnection = instance(); + TQSqlDatabase* db = sqlConnection->dbDict.find( name ); + if ( db && !db->isOpen() && open ) { + db->open(); +#ifdef QT_CHECK_RANGE + if ( !db->isOpen() ) + qWarning("TQSqlDatabaseManager::database: unable to open database: %s: %s", + db->lastError().databaseText().latin1(), db->lastError().driverText().latin1() ); +#endif + } + return db; +} + +/*! + Returns TRUE if the list of database connections contains \a name; + otherwise returns FALSE. +*/ + +bool TQSqlDatabaseManager::contains( const TQString& name ) +{ + TQSqlDatabaseManager* sqlConnection = instance(); + TQSqlDatabase* db = sqlConnection->dbDict.find( name ); + if ( db ) + return TRUE; + return FALSE; +} + + +/*! + Adds a database to the SQL connection manager. The database + connection is referred to by \a name. The newly added database + connection is returned. This function will only return 0 if it is + called \e before a TQApplication object has been instantiated. Use + the output of drivers() to determine whether a particular driver + is available or not. + + The returned TQSqlDatabase object is owned by the framework and + must not be deleted. If you want to explicitly remove the connection, + use removeDatabase(). + + \sa TQSqlDatabase database() +*/ + +TQSqlDatabase* TQSqlDatabaseManager::addDatabase( TQSqlDatabase* db, const TQString & name ) +{ + TQSqlDatabaseManager* sqlConnection = instance(); + if( sqlConnection == 0 ) + return 0; + if ( contains( name ) ) + sqlConnection->removeDatabase( name ); + sqlConnection->dbDict.insert( name, db ); + return db; +} + +/*! + Removes the database connection \a name from the SQL connection + manager. + + \warning There should be no open queries on the database + connection when this function is called, otherwise a resource leak + will occur. +*/ + +void TQSqlDatabaseManager::removeDatabase( const TQString& name ) +{ + TQSqlDatabaseManager* sqlConnection = instance(); + sqlConnection->dbDict.setAutoDelete( TRUE ); + sqlConnection->dbDict.remove( name ); + sqlConnection->dbDict.setAutoDelete( FALSE ); +} + + +/*! + Removes the database connection \a db from the SQL connection + manager. The TQSqlDatabase object is destroyed when it is removed + from the manager. + + \warning The \a db pointer is not valid after this function has + been called. +*/ + +void TQSqlDatabaseManager::removeDatabase( TQSqlDatabase* db ) +{ + TQSqlDatabaseManager* sqlConnection = instance(); + if ( !sqlConnection ) + return; + TQDictIterator< TQSqlDatabase > it( sqlConnection->dbDict ); + while ( it.current() ) { + if ( it.current() == db ) { + sqlConnection->dbDict.remove( it.currentKey() ); + db->close(); + delete db; + break; + } + ++it; + } +} + +class TQSqlDatabasePrivate +{ +public: + TQSqlDatabasePrivate(): + driver(0), +#ifndef QT_NO_COMPONENT + plugIns(0), +#endif + port(-1) {} + ~TQSqlDatabasePrivate() + { + } + TQSqlDriver* driver; +#ifndef QT_NO_COMPONENT + TQPluginManager<TQSqlDriverFactoryInterface> *plugIns; +#endif + TQString dbname; + TQString uname; + TQString pword; + TQString hname; + TQString drvName; + int port; + TQString connOptions; +}; + +/*! + \class TQSqlDatabase qsqldatabase.h + \brief The TQSqlDatabase class is used to create SQL database + connections and to provide transaction handling. + + \ingroup database + \mainclass + \module sql + + Note that transaction handling is not supported by every SQL + database. You can find out whether transactions are supported + using TQSqlDriver::hasFeature(). + + The TQSqlDatabase class provides an abstract interface for + accessing many types of database backends. Database-specific + drivers are used internally to actually access and manipulate + data, (see TQSqlDriver). Result set objects provide the interface + for executing and manipulating SQL queries (see TQSqlQuery). +*/ + +/*! + Adds a database to the list of database connections using the + driver \a type and the connection name \a connectionName. + + The database connection is referred to by \a connectionName. The + newly added database connection is returned. This pointer is owned + by TQSqlDatabase and will be deleted on program exit or when + removeDatabase() is called. + + If \a connectionName is not specified, the newly added database + connection becomes the default database connection for the + application, and subsequent calls to database() (without a + database name parameter) will return a pointer to it. If \a + connectionName is given, use \link TQSqlDatabase::database() + database(connectionName)\endlink to retrieve a pointer to the + database connection. + + \warning If you add a database with the same name as an + existing database, the new database will replace the old one. + This will happen automatically if you call this function more + than once without specifying \a connectionName. + + \sa database() removeDatabase() +*/ +TQSqlDatabase* TQSqlDatabase::addDatabase( const TQString& type, const TQString& connectionName ) +{ + return TQSqlDatabaseManager::addDatabase( new TQSqlDatabase( type, connectionName ), connectionName ); +} + +/*! + Returns the database connection called \a connectionName. The + database connection must have been previously added with + addDatabase(). If \a open is TRUE (the default) and the database + connection is not already open it is opened now. If no \a + connectionName is specified the default connection is used. If \a + connectionName does not exist in the list of databases, 0 is + returned. The pointer returned is owned by TQSqlDatabase and should + \e not be deleted. + + \warning There are restrictions on the use of database connections + in threaded applications. Please see the \link threads.html#threads-sql + Thread Support in TQt\endlink document for more information about + threading and SQL databases. +*/ + +TQSqlDatabase* TQSqlDatabase::database( const TQString& connectionName, bool open ) +{ + return TQSqlDatabaseManager::database( connectionName, open ); +} + +/*! + Removes the database connection \a connectionName from the list of + database connections. + + \warning There should be no open queries on the database + connection when this function is called, otherwise a resource leak + will occur. +*/ + +void TQSqlDatabase::removeDatabase( const TQString& connectionName ) +{ + TQSqlDatabaseManager::removeDatabase( connectionName ); +} + +/*! + \overload + + Removes the database connection \a db from the list of database + connections. The TQSqlDatabase object is destroyed when it is removed + from the list. + + \warning The \a db pointer is not valid after this function has + been called. There should be no open queries on the database + connection when this function is called, otherwise a resource leak + will occur. +*/ + +void TQSqlDatabase::removeDatabase( TQSqlDatabase* db ) +{ + TQSqlDatabaseManager::removeDatabase( db ); +} + +/*! + Returns a list of all the available database drivers. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = TQSqlDatabase::drivers(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ + +TQStringList TQSqlDatabase::drivers() +{ + TQStringList l; + +#ifndef QT_NO_COMPONENT + TQPluginManager<TQSqlDriverFactoryInterface> *plugIns; + plugIns = new TQPluginManager<TQSqlDriverFactoryInterface>( IID_QSqlDriverFactory, TQApplication::libraryPaths(), "/sqldrivers" ); + + l = plugIns->featureList(); + delete plugIns; +#endif + + TQDictIterator<TQSqlDriverCreatorBase> itd( *TQSqlDatabaseManager::driverDict() ); + while ( itd.current() ) { + if ( !l.contains( itd.currentKey() ) ) + l << itd.currentKey(); + ++itd; + } + +#ifdef QT_SQL_POSTGRES + if ( !l.contains( "TQPSQL7" ) ) + l << "TQPSQL7"; +#endif +#ifdef QT_SQL_MYSQL + if ( !l.contains( "TQMYSQL3" ) ) + l << "TQMYSQL3"; +#endif +#ifdef QT_SQL_ODBC + if ( !l.contains( "TQODBC3" ) ) + l << "TQODBC3"; +#endif +#ifdef QT_SQL_OCI + if ( !l.contains( "TQOCI8" ) ) + l << "TQOCI8"; +#endif +#ifdef QT_SQL_TDS + if ( !l.contains( "TQTDS7" ) ) + l << "TQTDS7"; +#endif +#ifdef QT_SQL_DB2 + if ( !l.contains( "TQDB2" ) ) + l << "TQDB2"; +#endif +#ifdef QT_SQL_SQLITE + if ( !l.contains( "TQSQLITE" ) ) + l << "TQSQLITE"; +#endif +#ifdef QT_SQL_IBASE + if ( !l.contains( "TQIBASE" ) ) + l << "TQIBASE"; +#endif + + return l; +} + +/*! + This function registers a new SQL driver called \a name, within + the SQL framework. This is useful if you have a custom SQL driver + and don't want to compile it as a plugin. + + Example usage: + + \code + TQSqlDatabase::registerSqlDriver( "MYDRIVER", new TQSqlDriverCreator<MyDatabaseDriver> ); + TQSqlDatabase* db = TQSqlDatabase::addDatabase( "MYDRIVER" ); + ... + \endcode + + \warning The framework takes ownership of the \a creator pointer, + so it should not be deleted. +*/ +void TQSqlDatabase::registerSqlDriver( const TQString& name, const TQSqlDriverCreatorBase* creator ) +{ + TQSqlDatabaseManager::driverDict()->remove( name ); + if ( creator ) + TQSqlDatabaseManager::driverDict()->insert( name, creator ); +} + +/*! + Returns TRUE if the list of database connections contains \a + connectionName; otherwise returns FALSE. +*/ + +bool TQSqlDatabase::contains( const TQString& connectionName ) +{ + return TQSqlDatabaseManager::contains( connectionName ); +} + + +/*! + Creates a TQSqlDatabase connection called \a name that uses the + driver referred to by \a type, with the parent \a parent and the + object name \a objname. If the \a type is not recognized, the + database connection will have no functionality. + + The currently available drivers are: + + \table + \header \i Driver Type \i Description + \row \i TQODBC3 \i ODBC Driver (includes Microsoft SQL Server) + \row \i TQOCI8 \i Oracle Call Interface Driver + \row \i TQPSQL7 \i PostgreSQL v6.x and v7.x Driver + \row \i TQTDS7 \i Sybase Adaptive Server + \row \i TQMYSQL3 \i MySQL Driver + \row \i TQDB2 \i IBM DB2, v7.1 and higher + \row \i TQSQLITE \i SQLite Driver + \row \i TQIBASE \i Borland Interbase Driver + \endtable + + Additional third party drivers, including your own custom drivers, + can be loaded dynamically. + + \sa registerSqlDriver() +*/ + +TQSqlDatabase::TQSqlDatabase( const TQString& type, const TQString& name, TQObject * parent, const char * objname ) + : TQObject( parent, objname ) +{ + init( type, name ); +} + + +/*! + \overload + + Creates a database connection using the driver \a driver, with + the parent \a parent and the object name \a objname. + + \warning The framework takes ownership of the \a driver pointer, + so it should not be deleted. +*/ + +TQSqlDatabase::TQSqlDatabase( TQSqlDriver* driver, TQObject * parent, const char * objname ) + : TQObject( parent, objname ) +{ + d = new TQSqlDatabasePrivate(); + d->driver = driver; +} + +/*! + \internal + + Create the actual driver instance \a type. +*/ + +void TQSqlDatabase::init( const TQString& type, const TQString& ) +{ + d = new TQSqlDatabasePrivate(); + d->drvName = type; + + if ( !d->driver ) { + +#ifdef QT_SQL_POSTGRES + if ( type == "TQPSQL7" ) + d->driver = new TQPSQLDriver(); +#endif + +#ifdef QT_SQL_MYSQL + if ( type == "TQMYSQL3" ) + d->driver = new TQMYSQLDriver(); +#endif + +#ifdef QT_SQL_ODBC + if ( type == "TQODBC3" ) + d->driver = new TQODBCDriver(); +#endif + +#ifdef QT_SQL_OCI + if ( type == "TQOCI8" ) + d->driver = new TQOCIDriver(); +#endif + +#ifdef QT_SQL_TDS + if ( type == "TQTDS7" ) + d->driver = new TQTDSDriver(); +#endif + +#ifdef QT_SQL_DB2 + if ( type == "TQDB2" ) + d->driver = new TQDB2Driver(); +#endif + +#ifdef QT_SQL_SQLITE + if ( type == "TQSQLITE" ) + d->driver = new TQSQLiteDriver(); +#endif + +#ifdef QT_SQL_IBASE + if ( type == "TQIBASE" ) + d->driver = new TQIBaseDriver(); +#endif + + } + + if ( !d->driver ) { + TQDictIterator<TQSqlDriverCreatorBase> it( *TQSqlDatabaseManager::driverDict() ); + while ( it.current() && !d->driver ) { + if ( type == it.currentKey() ) { + d->driver = it.current()->createObject(); + } + ++it; + } + } + +#ifndef QT_NO_COMPONENT + if ( !d->driver ) { + d->plugIns = + new TQPluginManager<TQSqlDriverFactoryInterface>( IID_QSqlDriverFactory, TQApplication::libraryPaths(), "/sqldrivers" ); + + TQInterfacePtr<TQSqlDriverFactoryInterface> iface = 0; + d->plugIns->queryInterface( type, &iface ); + if( iface ) + d->driver = iface->create( type ); + } +#endif + + if ( !d->driver ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlDatabase: %s driver not loaded", type.latin1() ); + qWarning( "TQSqlDatabase: available drivers: %s", drivers().join(" ").latin1() ); +#endif + d->driver = new TQNullDriver(); + d->driver->setLastError( TQSqlError( "Driver not loaded", "Driver not loaded" ) ); + } +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlDatabase::~TQSqlDatabase() +{ + delete d->driver; +#ifndef QT_NO_COMPONENT + delete d->plugIns; +#endif + delete d; +} + +/*! + Executes a SQL statement (e.g. an \c INSERT, \c UPDATE or \c + DELETE statement) on the database, and returns a TQSqlQuery object. + Use lastError() to retrieve error information. If \a query is + TQString::null, an empty, invalid query is returned and lastError() + is not affected. + + \sa TQSqlQuery lastError() +*/ + +TQSqlQuery TQSqlDatabase::exec( const TQString & query ) const +{ + TQSqlQuery r = d->driver->createQuery(); + if ( !query.isNull() ) { + r.exec( query ); + d->driver->setLastError( r.lastError() ); + } + return r; +} + +/*! + Opens the database connection using the current connection values. + Returns TRUE on success; otherwise returns FALSE. Error + information can be retrieved using the lastError() function. + + \sa lastError() +*/ + +bool TQSqlDatabase::open() +{ + return d->driver->open( d->dbname, d->uname, d->pword, d->hname, + d->port, d->connOptions ); +} + +/*! + \overload + + Opens the database connection using the given \a user name and \a + password. Returns TRUE on success; otherwise returns FALSE. Error + information can be retrieved using the lastError() function. + + This function does not store the password it is given. Instead, + the password is passed directly to the driver for opening a + connection and is then discarded. + + \sa lastError() +*/ + +bool TQSqlDatabase::open( const TQString& user, const TQString& password ) +{ + setUserName( user ); + return d->driver->open( d->dbname, user, password, d->hname, + d->port, d->connOptions ); +} + +/*! + Closes the database connection, freeing any resources actquired. + + \sa removeDatabase() +*/ + +void TQSqlDatabase::close() +{ + d->driver->close(); +} + +/*! + Returns TRUE if the database connection is currently open; + otherwise returns FALSE. +*/ + +bool TQSqlDatabase::isOpen() const +{ + return d->driver->isOpen(); +} + +/*! + Returns TRUE if there was an error opening the database + connection; otherwise returns FALSE. Error information can be + retrieved using the lastError() function. +*/ + +bool TQSqlDatabase::isOpenError() const +{ + return d->driver->isOpenError(); +} + +/*! + Begins a transaction on the database if the driver supports + transactions. Returns TRUE if the operation succeeded; otherwise + returns FALSE. + + \sa TQSqlDriver::hasFeature() commit() rollback() +*/ + +bool TQSqlDatabase::transaction() +{ + if ( !d->driver->hasFeature( TQSqlDriver::Transactions ) ) + return FALSE; + return d->driver->beginTransaction(); +} + +/*! + Commits a transaction to the database if the driver supports + transactions. Returns TRUE if the operation succeeded; otherwise + returns FALSE. + + \sa TQSqlDriver::hasFeature() rollback() +*/ + +bool TQSqlDatabase::commit() +{ + if ( !d->driver->hasFeature( TQSqlDriver::Transactions ) ) + return FALSE; + return d->driver->commitTransaction(); +} + +/*! + Rolls a transaction back on the database if the driver supports + transactions. Returns TRUE if the operation succeeded; otherwise + returns FALSE. + + \sa TQSqlDriver::hasFeature() commit() transaction() +*/ + +bool TQSqlDatabase::rollback() +{ + if ( !d->driver->hasFeature( TQSqlDriver::Transactions ) ) + return FALSE; + return d->driver->rollbackTransaction(); +} + +/*! + \property TQSqlDatabase::databaseName + \brief the name of the database + + Note that the database name is the TNS Service Name for the TQOCI8 + (Oracle) driver. + + For the TQODBC3 driver it can either be a DSN, a DSN filename (the + file must have a \c .dsn extension), or a connection string. MS + Access users can for example use the following connection string + to open a \c .mdb file directly, instead of having to create a DSN + entry in the ODBC manager: + + \code + ... + db = TQSqlDatabase::addDatabase( "TQODBC3" ); + db->setDatabaseName( "DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=myaccessfile.mdb" ); + if ( db->open() ) { + // success! + } + ... + \endcode + ("FIL" is the retquired spelling in Microsoft's API.) + + There is no default value. +*/ + +void TQSqlDatabase::setDatabaseName( const TQString& name ) +{ + d->dbname = name; +} + +/*! + \property TQSqlDatabase::userName + \brief the user name connected to the database + + There is no default value. +*/ + +void TQSqlDatabase::setUserName( const TQString& name ) +{ + d->uname = name; +} + +/*! + \property TQSqlDatabase::password + \brief the password used to connect to the database + + There is no default value. + + \warning This function stores the password in plain text within + TQt. Use the open() call that takes a password as parameter to + avoid this behaviour. + + \sa open() +*/ + +void TQSqlDatabase::setPassword( const TQString& password ) +{ + d->pword = password; +} + +/*! + \property TQSqlDatabase::hostName + \brief the host name where the database resides + + There is no default value. +*/ + +void TQSqlDatabase::setHostName( const TQString& host ) +{ + d->hname = host; +} + +/*! + \property TQSqlDatabase::port + \brief the port used to connect to the database + + There is no default value. +*/ + +void TQSqlDatabase::setPort( int p ) +{ + d->port = p; +} + +TQString TQSqlDatabase::databaseName() const +{ + return d->dbname; +} + +TQString TQSqlDatabase::userName() const +{ + return d->uname; +} + +TQString TQSqlDatabase::password() const +{ + return d->pword; +} + +TQString TQSqlDatabase::hostName() const +{ + return d->hname; +} + +/*! + Returns the name of the driver used by the database connection. +*/ +TQString TQSqlDatabase::driverName() const +{ + return d->drvName; +} + +int TQSqlDatabase::port() const +{ + return d->port; +} + +/*! + Returns the database driver used to access the database + connection. +*/ + +TQSqlDriver* TQSqlDatabase::driver() const +{ + return d->driver; +} + +/*! + Returns information about the last error that occurred on the + database. See TQSqlError for more information. +*/ + +TQSqlError TQSqlDatabase::lastError() const +{ + return d->driver->lastError(); +} + + +/*! + \overload + + Returns a list of the database's tables that are visible to the + user. To include views or system tables, use the version of this + function that takes a table \c type parameter. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDatabase.tables(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ + +TQStringList TQSqlDatabase::tables() const +{ + return tables( TQSql::Tables ); +} + +/*! + Returns a list of the database's tables, system tables and views, + as specified by the parameter \a type. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDatabase.tables( TQSql::Tables | TQSql::Views ); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ + +TQStringList TQSqlDatabase::tables( TQSql::TableType type ) const +{ + return d->driver->tables( TQString::number( (int)type ) ); +} + +/*! + Returns the primary index for table \a tablename. If no primary + index exists an empty TQSqlIndex will be returned. +*/ + +TQSqlIndex TQSqlDatabase::primaryIndex( const TQString& tablename ) const +{ + return d->driver->primaryIndex( tablename ); +} + + +/*! + Returns a TQSqlRecord populated with the names of all the fields in + the table (or view) called \a tablename. The order in which the + fields appear in the record is undefined. If no such table (or + view) exists, an empty record is returned. + + \sa recordInfo() +*/ + +TQSqlRecord TQSqlDatabase::record( const TQString& tablename ) const +{ + return d->driver->record( tablename ); +} + + +/*! + \overload + + Returns a TQSqlRecord populated with the names of all the fields + used in the SQL \a query. If the query is a "SELECT *" the order + in which fields appear in the record is undefined. + + \sa recordInfo() +*/ + +TQSqlRecord TQSqlDatabase::record( const TQSqlQuery& query ) const +{ + return d->driver->record( query ); +} + +/*! + Returns a TQSqlRecordInfo populated with meta data about the table + or view \a tablename. If no such table (or view) exists, an empty + record is returned. + + \sa TQSqlRecordInfo, TQSqlFieldInfo, record() +*/ +TQSqlRecordInfo TQSqlDatabase::recordInfo( const TQString& tablename ) const +{ + return d->driver->recordInfo( tablename ); +} + +/*! + \overload + + Returns a TQSqlRecordInfo object with meta data for the TQSqlQuery + \a query. Note that this overloaded function may return less + information than the recordInfo() function which takes the name of + a table as parameter. + + \sa TQSqlRecordInfo, TQSqlFieldInfo, record() +*/ +TQSqlRecordInfo TQSqlDatabase::recordInfo( const TQSqlQuery& query ) const +{ + return d->driver->recordInfo( query ); +} + +/*! + \property TQSqlDatabase::connectOptions + \brief the database connect options + + The format of the options string is a semi-colon separated list of + option names or option = value pairs. The options depend on the + database client used: + + \table + \header \i ODBC \i MySQL \i PostgreSQL + \row + + \i + \list + \i SQL_ATTR_ACCESS_MODE + \i SQL_ATTR_LOGIN_TIMEOUT + \i SQL_ATTR_CONNECTION_TIMEOUT + \i SQL_ATTR_CURRENT_CATALOG + \i SQL_ATTR_METADATA_ID + \i SQL_ATTR_PACKET_SIZE + \i SQL_ATTR_TRACEFILE + \i SQL_ATTR_TRACE + \endlist + + \i + \list + \i CLIENT_COMPRESS + \i CLIENT_FOUND_ROWS + \i CLIENT_IGNORE_SPACE + \i CLIENT_SSL + \i CLIENT_ODBC + \i CLIENT_NO_SCHEMA + \i CLIENT_INTERACTIVE + \endlist + + \i + \list + \i connect_timeout + \i options + \i tty + \i retquiressl + \i service + \endlist + + \header \i DB2 \i OCI \i TDS + \row + + \i + \list + \i SQL_ATTR_ACCESS_MODE + \i SQL_ATTR_LOGIN_TIMEOUT + \endlist + + \i + \e none + + \i + \e none + + \endtable + + Example of usage: + \code + ... + // MySQL connection + db->setConnectOptions( "CLIENT_SSL;CLIENT_IGNORE_SPACE" ); // use an SSL connection to the server + if ( !db->open() ) { + db->setConnectOptions(); // clears the connect option string + ... + } + ... + // PostgreSQL connection + db->setConnectOptions( "retquiressl=1" ); // enable PostgreSQL SSL connections + if ( !db->open() ) { + db->setConnectOptions(); // clear options + ... + } + ... + // ODBC connection + db->setConnectOptions( "SQL_ATTR_ACCESS_MODE=SQL_MODE_READ_ONLY;SQL_ATTR_TRACE=SQL_OPT_TRACE_ON" ); // set ODBC options + if ( !db->open() ) { + db->setConnectOptions(); // don't try to set this option + ... + } + \endcode + + Please refer to the client library documentation for more + information about the different options. The options will be set + prior to opening the database connection. Setting new options + without re-opening the connection does nothing. + + \sa connectOptions() +*/ + +void TQSqlDatabase::setConnectOptions( const TQString& options ) +{ + d->connOptions = options; +} + +TQString TQSqlDatabase::connectOptions() const +{ + return d->connOptions; +} + +/*! + Returns TRUE if a driver called \a name is available; otherwise + returns FALSE. + + \sa drivers() +*/ + +bool TQSqlDatabase::isDriverAvailable( const TQString& name ) +{ + TQStringList l = drivers(); + TQStringList::ConstIterator it = l.begin(); + for ( ;it != l.end(); ++it ) { + if ( *it == name ) + return TRUE; + } + return FALSE; +} + +/*! \overload + + This function is useful if you need to set up the database + connection and instantiate the driver yourself. If you do this, it + is recommended that you include the driver code in your own + application. For example, setting up a custom PostgreSQL + connection and instantiating the TQPSQL7 driver can be done the + following way: + + \code + #include "qtdir/src/sql/drivers/psql/qsql_psql.cpp" + \endcode + (We assume that \c qtdir is the directory where TQt is installed.) + This will pull in the code that is needed to use the PostgreSQL + client library and to instantiate a TQPSQLDriver object, assuming + that you have the PostgreSQL headers somewhere in your include + search path. + + \code + PGconn* con = PQconnectdb( "host=server user=bart password=simpson dbname=springfield" ); + TQPSQLDriver* drv = new TQPSQLDriver( con ); + TQSqlDatabase* db = TQSqlDatabase::addDatabase( drv ); // becomes the new default connection + TQSqlQuery q; + q.exec( "SELECT * FROM people" ); + ... + \endcode + + The above code sets up a PostgreSQL connection and instantiates a + TQPSQLDriver object. Next, addDatabase() is called to add the + connection to the known connections so that it can be used by the + TQt SQL classes. When a driver is instantiated with a connection + handle (or set of handles), TQt assumes that you have already + opened the database connection. + + Remember that you must link your application against the database + client library as well. The simplest way to do this is to add + lines like those below to your \c .pro file: + + \code + unix:LIBS += -lpq + win32:LIBS += libpqdll.lib + \endcode + + You will need to have the client library in your linker's search + path. + + The method described above will work for all the drivers, the only + difference is the arguments the driver constructors take. Below is + an overview of the drivers and their constructor arguments. + + \table + \header \i Driver \i Class name \i Constructor arguments \i File to include + \row + \i TQPSQL7 + \i TQPSQLDriver + \i PGconn* connection + \i \c qsql_psql.cpp + \row + \i TQMYSQL3 + \i TQMYSQLDriver + \i MYSQL* connection + \i \c qsql_mysql.cpp + \row + \i TQOCI8 + \i TQOCIDriver + \i OCIEnv* environment, OCIError* error, OCISvcCtx* serviceContext + \i \c qsql_oci.cpp + \row + \i TQODBC3 + \i TQODBCDriver + \i SQLHANDLE environment, SQLHANDLE connection + \i \c qsql_odbc.cpp + \row + \i TQDB2 + \i TQDB2 + \i SQLHANDLE environment, SQLHANDLE connection + \i \c qsql_db2.cpp + \row + \i TQTDS7 + \i TQTDSDriver + \i LOGINREC* loginRecord, DBPROCESS* dbProcess, const TQString& hostName + \i \c qsql_tds.cpp + \row + \i TQSQLITE + \i TQSQLiteDriver + \i sqlite* connection + \i \c qsql_sqlite.cpp + \row + \i TQIBASE + \i TQIBaseDriver + \i isc_db_handle connection + \i \c qsql_ibase.cpp + \endtable + + Note: The host name (or service name) is needed when constructing + the TQTDSDriver for creating new connections for internal + queries. This is to prevent the simultaneous usage of several + TQSqlQuery/\l{TQSqlCursor} objects from blocking each other. + + \warning The SQL framework takes ownership of the \a driver pointer, + and it should not be deleted. The returned TQSqlDatabase object is + owned by the framework and must not be deleted. If you want to + explicitly remove the connection, use removeDatabase() + + \sa drivers() +*/ + +TQSqlDatabase* TQSqlDatabase::addDatabase( TQSqlDriver* driver, const TQString& connectionName ) +{ + return TQSqlDatabaseManager::addDatabase( new TQSqlDatabase( driver ), connectionName ); +} +#endif // QT_NO_SQL diff --git a/src/sql/qsqldatabase.h b/src/sql/qsqldatabase.h new file mode 100644 index 000000000..bd679278c --- /dev/null +++ b/src/sql/qsqldatabase.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Definition of TQSqlDatabase class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLDATABASE_H +#define TQSQLDATABASE_H + +#ifndef QT_H +#include "qobject.h" +#include "qstring.h" +#include "qsqlquery.h" +#include "qstringlist.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlError; +class TQSqlDriver; +class TQSqlIndex; +class TQSqlRecord; +class TQSqlRecordInfo; +class TQSqlDatabasePrivate; + +class TQM_EXPORT_SQL TQSqlDriverCreatorBase +{ +public: + virtual TQSqlDriver* createObject() = 0; +}; + +template <class type> +class TQM_EXPORT_SQL TQSqlDriverCreator: public TQSqlDriverCreatorBase +{ +public: + TQSqlDriver* createObject() { return new type; } +}; + +class TQM_EXPORT_SQL TQSqlDatabase : public TQObject +{ + Q_OBJECT + Q_PROPERTY( TQString databaseName READ databaseName WRITE setDatabaseName ) + Q_PROPERTY( TQString userName READ userName WRITE setUserName ) + Q_PROPERTY( TQString password READ password WRITE setPassword ) + Q_PROPERTY( TQString hostName READ hostName WRITE setHostName ) + Q_PROPERTY( int port READ port WRITE setPort ) + Q_PROPERTY( TQString connectOptions READ connectOptions WRITE setConnectOptions ) + +public: + ~TQSqlDatabase(); + + bool open(); + bool open( const TQString& user, const TQString& password ); + void close(); + bool isOpen() const; + bool isOpenError() const; + TQStringList tables() const; + TQStringList tables( TQSql::TableType type ) 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; + TQSqlQuery exec( const TQString& query = TQString::null ) const; + TQSqlError lastError() const; + + bool transaction(); + bool commit(); + bool rollback(); + + virtual void setDatabaseName( const TQString& name ); + virtual void setUserName( const TQString& name ); + virtual void setPassword( const TQString& password ); + virtual void setHostName( const TQString& host ); + virtual void setPort( int p ); + void setConnectOptions( const TQString& options = TQString::null ); + TQString databaseName() const; + TQString userName() const; + TQString password() const; + TQString hostName() const; + TQString driverName() const; + int port() const; + TQString connectOptions() const; + + TQSqlDriver* driver() const; + + // MOC_SKIP_BEGIN + QT_STATIC_CONST char * const defaultConnection; + // MOC_SKIP_END + + static TQSqlDatabase* addDatabase( const TQString& type, const TQString& connectionName = defaultConnection ); + static TQSqlDatabase* addDatabase( TQSqlDriver* driver, const TQString& connectionName = defaultConnection ); + static TQSqlDatabase* database( const TQString& connectionName = defaultConnection, bool open = TRUE ); + static void removeDatabase( const TQString& connectionName ); + static void removeDatabase( TQSqlDatabase* db ); + static bool contains( const TQString& connectionName = defaultConnection ); + static TQStringList drivers(); + static void registerSqlDriver( const TQString& name, const TQSqlDriverCreatorBase* creator ); // ### 4.0: creator should not be const + static bool isDriverAvailable( const TQString& name ); + +protected: + TQSqlDatabase( const TQString& type, const TQString& name, TQObject * parent=0, const char * objname=0 ); + TQSqlDatabase( TQSqlDriver* driver, TQObject * parent=0, const char * objname=0 ); +private: + void init( const TQString& type, const TQString& name ); + TQSqlDatabasePrivate* d; +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQSqlDatabase( const TQSqlDatabase & ); + TQSqlDatabase &operator=( const TQSqlDatabase & ); +#endif + +}; + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqldriver.cpp b/src/sql/qsqldriver.cpp new file mode 100644 index 000000000..522011e80 --- /dev/null +++ b/src/sql/qsqldriver.cpp @@ -0,0 +1,509 @@ +/**************************************************************************** +** +** Implementation of TQSqlDriver class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqldriver.h" + +#ifndef QT_NO_SQL + +#include "qdatetime.h" +#include "qsqlextension_p.h" + +// database states +#define DBState_Open 0x0001 +#define DBState_OpenError 0x0002 + +// ### This needs to go in 4.0! +TQPtrDict<TQSqlDriverExtension> *qSqlDriverExtDict(); +TQPtrDict<TQSqlOpenExtension> *qSqlOpenExtDict(); + +/*! + \class TQSqlDriver qsqldriver.h + \brief The TQSqlDriver class is an abstract base class for accessing + SQL databases. + + \ingroup database + \module sql + + This class should not be used directly. Use TQSqlDatabase instead. +*/ + +/*! + Default constructor. Creates a new driver with parent \a parent, + called \a name. + +*/ + +TQSqlDriver::TQSqlDriver( TQObject * parent, const char * name ) +: TQObject(parent, name), + dbState(0), + error() +{ +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlDriver::~TQSqlDriver() +{ +} + +/*! + \fn bool TQSqlDriver::open( const TQString& db, const TQString& user, + const TQString& password, const TQString& host, int port ) + + Derived classes must reimplement this abstract virtual function in + order to open a database connection on database \a db, using user + name \a user, password \a password, host \a host and port \a port. + + The function \e must return TRUE on success and FALSE on failure. + + \sa setOpen() + +*/ + +/*! + \fn bool TQSqlDriver::close() + + Derived classes must reimplement this abstract virtual function in + order to close the database connection. Return TRUE on success, + FALSE on failure. + + \sa setOpen() + +*/ + +/*! + \fn TQSqlQuery TQSqlDriver::createQuery() const + + Creates an empty SQL result on the database. Derived classes must + reimplement this function and return a TQSqlQuery object + appropriate for their database to the caller. + +*/ + +//void TQSqlDriver::destroyResult( TQSqlResult* r ) const +//{ +// if ( r ) +// delete r; +//} + +/*! + Returns TRUE if the database connection is open; otherwise returns + FALSE. +*/ + +bool TQSqlDriver::isOpen() const +{ + if ( !qSqlDriverExtDict()->isEmpty() ) { + TQSqlDriverExtension *ext = qSqlDriverExtDict()->find( (TQSqlDriver *) this ); + if ( ext ) + return ext->isOpen(); + } + + return ((dbState & DBState_Open) == DBState_Open); +} + +/*! + Returns TRUE if the there was an error opening the database + connection; otherwise returns FALSE. +*/ + +bool TQSqlDriver::isOpenError() const +{ + return ((dbState & DBState_OpenError) == DBState_OpenError); +} + +/*! + \enum TQSqlDriver::DriverFeature + + This enum contains a list of features a driver may support. Use + hasFeature() to query whether a feature is supported or not. + + \value Transactions whether the driver supports SQL transactions + \value QuerySize whether the database is capable of reporting the size + of a query. Note that some databases do not support returning the size + (i.e. number of rows returned) of a query, in which case + TQSqlQuery::size() will return -1 + \value BLOB whether the driver supports Binary Large Object fields + \value Unicode whether the driver supports Unicode strings if the + database server does + \value PreparedQueries whether the driver supports prepared query execution + \value NamedPlaceholders whether the driver supports usage of named placeholders + \value PositionalPlaceholders whether the driver supports usage of positional placeholders + + More information about supported features can be found in the + \link sql-driver.html TQt SQL driver\endlink documentation. + + \sa hasFeature() +*/ + +/*! + \fn bool TQSqlDriver::hasFeature( DriverFeature f ) const + + Returns TRUE if the driver supports feature \a f; otherwise + returns FALSE. + + Note that some databases need to be open() before this can be + determined. + + \sa DriverFeature +*/ + +/*! + Protected function which sets the open state of the database to \a + o. Derived classes can use this function to report the status of + open(). + + \sa open(), setOpenError() +*/ + +void TQSqlDriver::setOpen( bool o ) +{ + if ( o ) + dbState |= DBState_Open; + else + dbState &= ~DBState_Open; +} + +/*! + Protected function which sets the open error state of the database + to \a e. Derived classes can use this function to report the + status of open(). Note that if \a e is TRUE the open state of the + database is set to closed (i.e. isOpen() returns FALSE). + + \sa open(), setOpenError() +*/ + +void TQSqlDriver::setOpenError( bool e ) +{ + if ( e ) { + dbState |= DBState_OpenError; + dbState &= ~DBState_Open; + } + else + dbState &= ~DBState_OpenError; +} + +/*! + Protected function which derived classes can reimplement to begin + a transaction. If successful, return TRUE, otherwise return FALSE. + The default implementation returns FALSE. + + \sa commitTransaction(), rollbackTransaction() +*/ + +bool TQSqlDriver::beginTransaction() +{ + return FALSE; +} + +/*! + Protected function which derived classes can reimplement to commit + a transaction. If successful, return TRUE, otherwise return FALSE. + The default implementation returns FALSE. + + \sa beginTransaction(), rollbackTransaction() +*/ + +bool TQSqlDriver::commitTransaction() +{ + return FALSE; +} + +/*! + Protected function which derived classes can reimplement to + rollback a transaction. If successful, return TRUE, otherwise + return FALSE. The default implementation returns FALSE. + + \sa beginTransaction(), commitTransaction() +*/ + +bool TQSqlDriver::rollbackTransaction() +{ + return FALSE; +} + +/*! + Protected function which allows derived classes to set the value + of the last error, \a e, that occurred on the database. + + \sa lastError() +*/ + +void TQSqlDriver::setLastError( const TQSqlError& e ) +{ + error = e; +} + +/*! + Returns a TQSqlError object which contains information about the + last error that occurred on the database. +*/ + +TQSqlError TQSqlDriver::lastError() const +{ + return error; +} + +/*! + Returns a list of tables in the database. The default + implementation returns an empty list. + + The \a tableType argument describes what types of tables + should be returned. Due to binary compatibility, the string + contains the value of the enum TQSql::TableTypes as text. + An empty string should be treated as TQSql::Tables for + downward compatibility. + + \sa TQSql::TableType +*/ + +TQStringList TQSqlDriver::tables( const TQString& ) const +{ + return TQStringList(); +} + +/*! + Returns the primary index for table \a tableName. Returns an empty + TQSqlIndex if the table doesn't have a primary index. The default + implementation returns an empty index. +*/ + +TQSqlIndex TQSqlDriver::primaryIndex( const TQString& ) const +{ + return TQSqlIndex(); +} + + +/*! + Returns a TQSqlRecord populated with the names of the fields in + table \a tableName. If no such table exists, an empty record is + returned. The default implementation returns an empty record. +*/ + +TQSqlRecord TQSqlDriver::record( const TQString& ) const +{ + return TQSqlRecord(); +} + +/*! + \overload + + Returns a TQSqlRecord populated with the names of the fields in the + SQL \a query. The default implementation returns an empty record. +*/ + +TQSqlRecord TQSqlDriver::record( const TQSqlQuery& ) const +{ + return TQSqlRecord(); +} + +/*! + Returns a TQSqlRecordInfo object with meta data about the table \a + tablename. +*/ +TQSqlRecordInfo TQSqlDriver::recordInfo( const TQString& tablename ) const +{ + return TQSqlRecordInfo( record( tablename ) ); +} + +/*! + \overload + + Returns a TQSqlRecordInfo object with meta data for the TQSqlQuery + \a query. Note that this overloaded function may return less + information than the recordInfo() function which takes the name of + a table as parameter. +*/ +TQSqlRecordInfo TQSqlDriver::recordInfo( const TQSqlQuery& query ) const +{ + return TQSqlRecordInfo( record( query ) ); +} + + +/*! + Returns a string representation of the NULL value for the + database. This is used, for example, when constructing INSERT and + UPDATE statements. The default implementation returns the string + "NULL". +*/ + +TQString TQSqlDriver::nullText() const +{ + return "NULL"; +} + +/*! + Returns a string representation of the \a field value for the + database. This is used, for example, when constructing INSERT and + UPDATE statements. + + The default implementation returns the value formatted as a string + according to the following rules: + + \list + + \i If \a field is NULL, nullText() is returned. + + \i If \a field is character data, the value is returned enclosed + in single quotation marks, which is appropriate for many SQL + databases. Any embedded single-quote characters are escaped + (replaced with two single-quote characters). If \a trimStrings is + TRUE (the default is FALSE), all trailing whitespace is trimmed + from the field. + + \i If \a field is date/time data, the value is formatted in ISO + format and enclosed in single quotation marks. If the date/time + data is invalid, nullText() is returned. + + \i If \a field is bytearray data, and the driver can edit binary + fields, the value is formatted as a hexadecimal string. + + \i For any other field type toString() will be called on its value + and the result returned. + + \endlist + + \sa TQVariant::toString(). + +*/ +TQString TQSqlDriver::formatValue( const TQSqlField* field, bool trimStrings ) const +{ + TQString r; + if ( field->isNull() ) + r = nullText(); + else { + switch ( field->type() ) { + case TQVariant::Int: + case TQVariant::UInt: + if ( field->value().type() == TQVariant::Bool ) + r = field->value().toBool() ? "1" : "0"; + else + r = field->value().toString(); + break; + case TQVariant::Date: + if ( field->value().toDate().isValid() ) + r = "'" + field->value().toDate().toString( TQt::ISODate ) + "'"; + else + r = nullText(); + break; + case TQVariant::Time: + if ( field->value().toTime().isValid() ) + r = "'" + field->value().toTime().toString( TQt::ISODate ) + "'"; + else + r = nullText(); + break; + case TQVariant::DateTime: + if ( field->value().toDateTime().isValid() ) + r = "'" + + field->value().toDateTime().toString( TQt::ISODate ) + "'"; + else + r = nullText(); + break; + case TQVariant::String: + case TQVariant::CString: { + TQString result = field->value().toString(); + if ( trimStrings ) { + int end = result.length() - 1; + while ( end && result[end].isSpace() ) /* skip white space from end */ + end--; + result.truncate( end ); + } + /* escape the "'" character */ + result.replace( TQChar( '\'' ), "''" ); + r = "'" + result + "'"; + break; + } + case TQVariant::Bool: + if ( field->value().toBool() ) + r = "1"; + else + r = "0"; + break; + case TQVariant::ByteArray : { + if ( hasFeature( BLOB ) ) { + 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 = "'" + res + "'"; + break; + } + } + default: + r = field->value().toString(); + break; + } + } + return r; +} + +/*! + \overload + + Open a database connection on database \a db, using user name \a + user, password \a password, host \a host, port \a port and + connection options \a connOpts. + + Returns TRUE on success and FALSE on failure. + + \sa setOpen() +*/ +bool TQSqlDriver::open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) +{ + if ( !qSqlOpenExtDict()->isEmpty() ) { + TQSqlOpenExtension *ext = qSqlOpenExtDict()->find( (TQSqlDriver *) this ); + if ( ext ) + return ext->open( db, user, password, host, port, connOpts ); + } + return open( db, user, password, host, port ); +} + +#endif // QT_NO_SQL diff --git a/src/sql/qsqldriver.h b/src/sql/qsqldriver.h new file mode 100644 index 000000000..68ed94fc4 --- /dev/null +++ b/src/sql/qsqldriver.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Definition of TQSqlDriver class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLDRIVER_H +#define TQSQLDRIVER_H + +#ifndef QT_H +#include "qobject.h" +#include "qptrdict.h" +#include "qstring.h" +#include "qsqlerror.h" +#include "qsqlquery.h" +#include "qsqlfield.h" +#include "qsqlindex.h" +#include "qstringlist.h" +#include "qmap.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlDriverExtension; + +class TQSqlDatabase; + +class TQM_EXPORT_SQL TQSqlDriver : public TQObject +{ + friend class TQSqlDatabase; + Q_OBJECT +public: + enum DriverFeature { Transactions, QuerySize, BLOB, Unicode, PreparedQueries, + NamedPlaceholders, PositionalPlaceholders }; + + TQSqlDriver( TQObject * parent=0, const char * name=0 ); + ~TQSqlDriver(); + bool isOpen() const; + bool isOpenError() const; + + virtual bool beginTransaction(); + virtual bool commitTransaction(); + virtual bool rollbackTransaction(); + virtual TQStringList tables( const TQString& tableType ) const; + virtual TQSqlIndex primaryIndex( const TQString& tableName ) const; + virtual TQSqlRecord record( const TQString& tableName ) const; + virtual TQSqlRecord record( const TQSqlQuery& query ) const; + virtual TQSqlRecordInfo recordInfo( const TQString& tablename ) const; + virtual TQSqlRecordInfo recordInfo( const TQSqlQuery& query ) const; + virtual TQString nullText() const; + virtual TQString formatValue( const TQSqlField* field, bool trimStrings = FALSE ) const; + TQSqlError lastError() const; + + virtual bool hasFeature( DriverFeature f ) const = 0; + virtual bool open( const TQString & db, + const TQString & user = TQString::null, + const TQString & password = TQString::null, + const TQString & host = TQString::null, + int port = -1 ) = 0; + virtual void close() = 0; + virtual TQSqlQuery createQuery() const = 0; + + // ### remove for 4.0 + bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ); +protected: + virtual void setOpen( bool o ); + virtual void setOpenError( bool e ); + virtual void setLastError( const TQSqlError& e ); +private: + // ### This class needs a d-pointer in 4.0. + int dbState; + TQSqlError error; +#if defined(Q_DISABLE_COPY) + TQSqlDriver( const TQSqlDriver & ); + TQSqlDriver &operator=( const TQSqlDriver & ); +#endif +}; + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqldriverinterface_p.h b/src/sql/qsqldriverinterface_p.h new file mode 100644 index 000000000..dd2f539bb --- /dev/null +++ b/src/sql/qsqldriverinterface_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Definition of TQSqlDriverInterface class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLDRIVERINTERFACE_H +#define TQSQLDRIVERINTERFACE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include <private/qcom_p.h> +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +#ifndef QT_NO_COMPONENT + +// {EDDD5AD5-DF3C-400c-A711-163B72FE5F61} +#ifndef IID_QSqlDriverFactory +#define IID_QSqlDriverFactory TQUuid(0xeddd5ad5, 0xdf3c, 0x400c, 0xa7, 0x11, 0x16, 0x3b, 0x72, 0xfe, 0x5f, 0x61) +#endif + +class TQSqlDriver; + +struct TQM_EXPORT_SQL TQSqlDriverFactoryInterface : public TQFeatureListInterface +{ + virtual TQSqlDriver* create( const TQString& name ) = 0; +}; + +#endif //QT_NO_COMPONENT +#endif // QT_NO_SQL + +#endif // TQSQLDRIVERINTERFACE_P_H diff --git a/src/sql/qsqldriverplugin.cpp b/src/sql/qsqldriverplugin.cpp new file mode 100644 index 000000000..ff3620e5c --- /dev/null +++ b/src/sql/qsqldriverplugin.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Implementation of TQSqlDriverPlugin class +** +** Created : 2001-09-20 +** +** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqldriverplugin.h" + +#ifndef QT_NO_SQL +#ifndef QT_NO_COMPONENT + +#include "qsqldriverinterface_p.h" + +/*! + \class TQSqlDriverPlugin qsqldriverplugin.h + \brief The TQSqlDriverPlugin class provides an abstract base for custom TQSqlDriver plugins. + + \ingroup plugins + \mainclass + + The SQL driver plugin is a simple plugin interface that makes it + easy to create your own SQL driver plugins that can be loaded + dynamically by TQt. + + Writing a SQL plugin is achieved by subclassing this base class, + reimplementing the pure virtual functions keys() and create(), and + exporting the class with the \c Q_EXPORT_PLUGIN macro. See the SQL + plugins that come with TQt for example implementations (in the + \c{plugins/src/sqldrivers} subdirectory of the source + distribution). Read the \link plugins-howto.html plugins + documentation\endlink for more information on plugins. +*/ + +/*! + \fn TQStringList TQSqlDriverPlugin::keys() const + + Returns the list of drivers (keys) this plugin supports. + + These keys are usually the class names of the custom drivers that + are implemented in the plugin. + + \sa create() +*/ + +/*! + \fn TQSqlDriver* TQSqlDriverPlugin::create( const TQString& key ) + + Creates and returns a TQSqlDriver object for the driver key \a key. + The driver key is usually the class name of the retquired driver. + + \sa keys() +*/ + +class TQSqlDriverPluginPrivate : public TQSqlDriverFactoryInterface +{ +public: + TQSqlDriverPluginPrivate( TQSqlDriverPlugin *p ) + : plugin( p ) + { + } + virtual ~TQSqlDriverPluginPrivate(); + + TQRESULT queryInterface( const TQUuid &iid, TQUnknownInterface **iface ); + Q_REFCOUNT; + + TQStringList featureList() const; + TQSqlDriver *create( const TQString &key ); + +private: + TQSqlDriverPlugin *plugin; +}; + +TQSqlDriverPluginPrivate::~TQSqlDriverPluginPrivate() +{ + delete plugin; +} + +TQRESULT TQSqlDriverPluginPrivate::queryInterface( const TQUuid &iid, TQUnknownInterface **iface ) +{ + *iface = 0; + + if ( iid == IID_QUnknown ) + *iface = this; + else if ( iid == IID_QFeatureList ) + *iface = this; + else if ( iid == IID_QSqlDriverFactory ) + *iface = this; + else + return TQE_NOINTERFACE; + + (*iface)->addRef(); + return TQS_OK; +} + +TQStringList TQSqlDriverPluginPrivate::featureList() const +{ + return plugin->keys(); +} + +TQSqlDriver *TQSqlDriverPluginPrivate::create( const TQString &key ) +{ + return plugin->create( key ); +} + +/*! + Constructs a SQL driver plugin. This is invoked automatically by + the \c Q_EXPORT_PLUGIN macro. +*/ + +TQSqlDriverPlugin::TQSqlDriverPlugin() + : TQGPlugin( d = new TQSqlDriverPluginPrivate( this ) ) +{ +} + +/*! + Destroys the SQL driver plugin. + + You never have to call this explicitly. TQt destroys a plugin + automatically when it is no longer used. +*/ +TQSqlDriverPlugin::~TQSqlDriverPlugin() +{ + // don't delete d, as this is deleted by d +} + +#endif // QT_NO_COMPONENT +#endif // QT_NO_SQL diff --git a/src/sql/qsqldriverplugin.h b/src/sql/qsqldriverplugin.h new file mode 100644 index 000000000..591c384b1 --- /dev/null +++ b/src/sql/qsqldriverplugin.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Definition of TQSqlDriverPlugin class +** +** Created : 2001-09-20 +** +** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLDRIVERPLUGIN_H +#define TQSQLDRIVERPLUGIN_H + +#ifndef QT_H +#include "qgplugin.h" +#include "qstringlist.h" +#endif // QT_H + +#ifndef QT_NO_SQL +#ifndef QT_NO_COMPONENT + +class TQSqlDriver; +class TQSqlDriverPluginPrivate; + +class Q_EXPORT TQSqlDriverPlugin : public TQGPlugin +{ + Q_OBJECT +public: + TQSqlDriverPlugin(); + ~TQSqlDriverPlugin(); + + virtual TQStringList keys() const = 0; + virtual TQSqlDriver *create( const TQString &key ) = 0; + +private: + TQSqlDriverPluginPrivate *d; +}; + +#endif // QT_NO_COMPONENT +#endif // QT_NO_SQL + +#endif // TQSQLDRIVERPLUGIN_H diff --git a/src/sql/qsqleditorfactory.cpp b/src/sql/qsqleditorfactory.cpp new file mode 100644 index 000000000..72afcd188 --- /dev/null +++ b/src/sql/qsqleditorfactory.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Implementation of TQSqlEditorFactory class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqleditorfactory.h" + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +#include "qsqlfield.h" +#include "qcleanuphandler.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qspinbox.h" +#include "qcombobox.h" +#include "qdatetimeedit.h" + +/*! + \class TQSqlEditorFactory qsqleditorfactory.h + \brief The TQSqlEditorFactory class is used to create the editors + used by TQDataTable and TQSqlForm. + + \ingroup database + \module sql + + TQSqlEditorFactory is used by TQDataTable and TQSqlForm to + automatically create appropriate editors for a given TQSqlField. + For example if the field is a TQVariant::String a TQLineEdit would + be the default editor, whereas a TQVariant::Int's default editor + would be a TQSpinBox. + + If you want to create different editors for fields with the same + data type, subclass TQSqlEditorFactory and reimplement the + createEditor() function. + + \sa TQDataTable, TQSqlForm +*/ + + +/*! + Constructs a SQL editor factory with parent \a parent, called \a + name. +*/ + +TQSqlEditorFactory::TQSqlEditorFactory ( TQObject * parent, const char * name ) + : TQEditorFactory( parent, name ) +{ + +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlEditorFactory::~TQSqlEditorFactory() +{ + +} + +static TQSqlEditorFactory * defaultfactory = 0; +static TQCleanupHandler< TQSqlEditorFactory > qsql_cleanup_editor_factory; + +/*! + Returns an instance of a default editor factory. +*/ + +TQSqlEditorFactory * TQSqlEditorFactory::defaultFactory() +{ + if( defaultfactory == 0 ){ + defaultfactory = new TQSqlEditorFactory(); + qsql_cleanup_editor_factory.add( &defaultfactory ); + } + + return defaultfactory; +} + +/*! + Replaces the default editor factory with \a factory. All + TQDataTable and TQSqlForm instantiations will use this new factory + for creating field editors. \e{TQSqlEditorFactory takes ownership + of \a factory, and destroys it when it is no longer needed.} +*/ + +void TQSqlEditorFactory::installDefaultFactory( TQSqlEditorFactory * factory ) +{ + if( factory == 0 ) return; + + if( defaultfactory != 0 ){ + qsql_cleanup_editor_factory.remove( &defaultfactory ); + delete defaultfactory; + } + defaultfactory = factory; + qsql_cleanup_editor_factory.add( &defaultfactory ); +} + +/*! + Creates and returns the appropriate editor widget for the TQVariant + \a variant. + + The widget that is returned has the parent \a parent (which may be + zero). If \a variant is invalid, 0 is returned. +*/ + +TQWidget * TQSqlEditorFactory::createEditor( TQWidget * parent, + const TQVariant & variant ) +{ + return TQEditorFactory::createEditor( parent, variant ); +} + +/*! + \overload + + Creates and returns the appropriate editor for the TQSqlField \a + field. +*/ + +TQWidget * TQSqlEditorFactory::createEditor( TQWidget * parent, + const TQSqlField * field ) +{ + if ( !field ) { + return 0; + } + + TQWidget * w = 0; + switch( field->type() ){ + case TQVariant::Invalid: + w = 0; + break; + case TQVariant::Bool: + w = new TQComboBox( parent, "qt_editor_bool" ); + ((TQComboBox *) w)->insertItem( "False" ); + ((TQComboBox *) w)->insertItem( "True" ); + break; + case TQVariant::UInt: + w = new TQSpinBox( 0, 2147483647, 1, parent, "qt_editor_spinbox" ); + break; + case TQVariant::Int: + w = new TQSpinBox( -2147483647, 2147483647, 1, parent, "qt_editor_int" ); + break; + case TQVariant::LongLong: + case TQVariant::ULongLong: + case TQVariant::String: + case TQVariant::CString: + case TQVariant::Double: + w = new TQLineEdit( parent, "qt_editor_double" ); + ((TQLineEdit*)w)->setFrame( FALSE ); + break; + case TQVariant::Date: + w = new TQDateEdit( parent, "qt_editor_date" ); + break; + case TQVariant::Time: + w = new TQTimeEdit( parent, "qt_editor_time" ); + break; + case TQVariant::DateTime: + w = new TQDateTimeEdit( parent, "qt_editor_datetime" ); + break; +#ifndef QT_NO_LABEL + case TQVariant::Pixmap: + w = new TQLabel( parent, "qt_editor_pixmap" ); + break; +#endif + case TQVariant::Palette: + case TQVariant::ColorGroup: + case TQVariant::Color: + case TQVariant::Font: + case TQVariant::Brush: + case TQVariant::Bitmap: + case TQVariant::Cursor: + case TQVariant::Map: + case TQVariant::StringList: + case TQVariant::Rect: + case TQVariant::Size: + case TQVariant::IconSet: + case TQVariant::Point: + case TQVariant::PointArray: + case TQVariant::Region: + case TQVariant::SizePolicy: + case TQVariant::ByteArray: + default: + w = new TQWidget( parent, "qt_editor_default" ); + break; + } + return w; +} + +#endif // QT_NO_SQL diff --git a/src/sql/qsqleditorfactory.h b/src/sql/qsqleditorfactory.h new file mode 100644 index 000000000..f8d2c1339 --- /dev/null +++ b/src/sql/qsqleditorfactory.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Definition of TQSqlEditorFactory class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLEDITORFACTORY_H +#define TQSQLEDITORFACTORY_H + +#ifndef QT_H +#include "qeditorfactory.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +class TQSqlField; + +class TQM_EXPORT_SQL TQSqlEditorFactory : public TQEditorFactory +{ +public: + TQSqlEditorFactory ( TQObject * parent = 0, const char * name = 0 ); + ~TQSqlEditorFactory(); + virtual TQWidget * createEditor( TQWidget * parent, const TQVariant & variant ); + virtual TQWidget * createEditor( TQWidget * parent, const TQSqlField * field ); + + static TQSqlEditorFactory * defaultFactory(); + static void installDefaultFactory( TQSqlEditorFactory * factory ); + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQSqlEditorFactory( const TQSqlEditorFactory & ); + TQSqlEditorFactory &operator=( const TQSqlEditorFactory & ); +#endif +}; + +#endif // QT_NO_SQL +#endif // TQSQLEDITORFACTORY_H diff --git a/src/sql/qsqlerror.cpp b/src/sql/qsqlerror.cpp new file mode 100644 index 000000000..9dce821f9 --- /dev/null +++ b/src/sql/qsqlerror.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Implementation of TQSqlError class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlerror.h" +#include <qmessagebox.h> + +#ifndef QT_NO_SQL + +/*! + \class TQSqlError qsqlerror.h + \brief The TQSqlError class provides SQL database error information. + + \ingroup database + \module sql + + This class is used to report database-specific errors. An error + description and (if appropriate) a database-specific error number + can be obtained using this class. +*/ + +/*! + \enum TQSqlError::Type + + This enum type describes the type of SQL error that occurred. + + \value None no error occurred + \value Connection connection error + \value Statement SQL statement syntax error + \value Transaction transaction failed error + \value Unknown unknown error +*/ + +/*! + Constructs an error containing the driver error text \a + driverText, the database-specific error text \a databaseText, the + type \a type and the optional error number \a number. +*/ + +TQSqlError::TQSqlError( const TQString& driverText, + const TQString& databaseText, + int type, + int number ) +: driverError(driverText), + databaseError(databaseText), + errorType(type), + errorNumber(number) +{ +} + +/*! + Creates a copy of \a other. +*/ + +TQSqlError::TQSqlError( const TQSqlError& other ) +: driverError(other.driverError), + databaseError(other.databaseError), + errorType(other.errorType), + errorNumber(other.errorNumber) +{ +} + +/*! + Sets the error equal to \a other. +*/ + +TQSqlError& TQSqlError::operator=( const TQSqlError& other ) +{ + driverError = other.driverError; + databaseError = other.databaseError; + errorType = other.errorType; + errorNumber = other.errorNumber; + return *this; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlError::~TQSqlError() +{ +} + +/*! + Returns the text of the error as reported by the driver. This may + contain database-specific descriptions. +*/ +TQString TQSqlError::driverText() const +{ + return driverError; +} + +/*! + Sets the driver error text to the value of \a driverText. +*/ + +void TQSqlError::setDriverText( const TQString& driverText ) +{ + driverError = driverText; +} + +/*! + Returns the text of the error as reported by the database. This + may contain database-specific descriptions. +*/ + +TQString TQSqlError::databaseText() const +{ + return databaseError; +} + +/*! + Sets the database error text to the value of \a databaseText. +*/ + +void TQSqlError::setDatabaseText( const TQString& databaseText ) +{ + databaseError = databaseText; +} + +/*! + Returns the error type, or -1 if the type cannot be determined. + + \sa TQSqlError::Type. +*/ + +int TQSqlError::type() const +{ + return errorType; +} + +/*! + Sets the error type to the value of \a type. +*/ + +void TQSqlError::setType( int type ) +{ + errorType = type; +} + +/*! + Returns the database-specific error number, or -1 if it cannot be + determined. +*/ + +int TQSqlError::number() const +{ + return errorNumber; +} + +/*! + Sets the database-specific error number to \a number. +*/ + +void TQSqlError::setNumber( int number ) +{ + errorNumber = number; +} + +/*! + This is a convenience function that returns databaseText() and + driverText() concatenated into a single string. + + \sa showMessage(), driverText(), databaseText() +*/ + +TQString TQSqlError::text() const +{ + if ( databaseError.endsWith("\n") ) + return databaseError + driverError; + else + return databaseError + " " + driverError; +} + +/*! + \obsolete + + This is a convenience function that pops up a TQMessageBox + containing the message returned by text(). An additional string + can be passed in via the \a msg parameter, which will be + concatenated with the text() message. + + \sa text(), driverText(), databaseText() +*/ +void TQSqlError::showMessage( const TQString& msg ) const +{ +#ifndef QT_NO_MESSAGEBOX + TQMessageBox::warning( NULL, "SQL Error", msg + text(), + TQMessageBox::Ok, TQMessageBox::NoButton ); +#endif // QT_NO_MESSAGEBOX +} +#endif // QT_NO_SQL diff --git a/src/sql/qsqlerror.h b/src/sql/qsqlerror.h new file mode 100644 index 000000000..abe6f2fe7 --- /dev/null +++ b/src/sql/qsqlerror.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Definition of TQSqlError class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLERROR_H +#define TQSQLERROR_H + +#ifndef QT_H +#include "qstring.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQM_EXPORT_SQL TQSqlError +{ +public: + enum Type { + None, + Connection, + Statement, + Transaction, + Unknown + }; + TQSqlError( const TQString& driverText = TQString::null, + const TQString& databaseText = TQString::null, + int type = TQSqlError::None, + int number = -1 ); + TQSqlError( const TQSqlError& other ); + TQSqlError& operator=( const TQSqlError& other ); + virtual ~TQSqlError(); + + TQString driverText() const; + virtual void setDriverText( const TQString& driverText ); + TQString databaseText() const; + virtual void setDatabaseText( const TQString& databaseText ); + int type() const; + virtual void setType( int type ); + int number() const; + virtual void setNumber( int number ); + TQString text() const; + void showMessage( const TQString& msg = TQString::null ) const; + +private: + TQString driverError; + TQString databaseError; + int errorType; + int errorNumber; +}; + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlextension_p.cpp b/src/sql/qsqlextension_p.cpp new file mode 100644 index 000000000..21a6d2104 --- /dev/null +++ b/src/sql/qsqlextension_p.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Implementation of the TQSqlExtension class +** +** Created : 2002-06-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlextension_p.h" + +#ifndef QT_NO_SQL +TQSqlExtension::TQSqlExtension() + : bindm( BindByPosition ), bindCount( 0 ) +{ +} + +TQSqlExtension::~TQSqlExtension() +{ +} + +bool TQSqlExtension::prepare( const TQString& /*query*/ ) +{ + return FALSE; +} + +bool TQSqlExtension::exec() +{ + return FALSE; +} + +void TQSqlExtension::bindValue( const TQString& placeholder, const TQVariant& val, TQSql::ParameterType tp ) +{ + bindm = BindByName; + // if the index has already been set when doing emulated named + // bindings - don't reset it + if ( index.contains( (int)values.count() ) ) { + index[ (int)values.count() ] = placeholder; + } + values[ placeholder ] = Param( val, tp ); +} + +void TQSqlExtension::bindValue( int pos, const TQVariant& val, TQSql::ParameterType tp ) +{ + bindm = BindByPosition; + index[ pos ] = TQString::number( pos ); + TQString nm = TQString::number( pos ); + values[ nm ] = Param( val, tp ); +} + +void TQSqlExtension::addBindValue( const TQVariant& val, TQSql::ParameterType tp ) +{ + bindm = BindByPosition; + bindValue( bindCount++, val, tp ); +} + +void TQSqlExtension::clearValues() +{ + values.clear(); + bindCount = 0; +} + +void TQSqlExtension::resetBindCount() +{ + bindCount = 0; +} + +void TQSqlExtension::clearIndex() +{ + index.clear(); + holders.clear(); +} + +void TQSqlExtension::clear() +{ + clearValues(); + clearIndex(); +} + +TQVariant TQSqlExtension::parameterValue( const TQString& holder ) +{ + return values[ holder ].value; +} + +TQVariant TQSqlExtension::parameterValue( int pos ) +{ + return values[ index[ pos ] ].value; +} + +TQVariant TQSqlExtension::boundValue( const TQString& holder ) const +{ + return values[ holder ].value; +} + +TQVariant TQSqlExtension::boundValue( int pos ) const +{ + return values[ index[ pos ] ].value; +} + +TQMap<TQString, TQVariant> TQSqlExtension::boundValues() const +{ + TQMap<TQString, Param>::ConstIterator it; + TQMap<TQString, TQVariant> m; + if ( bindm == BindByName ) { + for ( it = values.begin(); it != values.end(); ++it ) + m.insert( it.key(), it.data().value ); + } else { + TQString key, tmp, fmt; + fmt.sprintf( "%%0%dd", TQString::number( values.count()-1 ).length() ); + for ( it = values.begin(); it != values.end(); ++it ) { + tmp.sprintf( fmt.ascii(), it.key().toInt() ); + m.insert( tmp, it.data().value ); + } + } + return m; +} + +TQSqlExtension::BindMethod TQSqlExtension::bindMethod() +{ + return bindm; +} + +TQSqlDriverExtension::TQSqlDriverExtension() +{ +} + +TQSqlDriverExtension::~TQSqlDriverExtension() +{ +} + +TQSqlOpenExtension::TQSqlOpenExtension() +{ +} + +TQSqlOpenExtension::~TQSqlOpenExtension() +{ +} +#endif diff --git a/src/sql/qsqlextension_p.h b/src/sql/qsqlextension_p.h new file mode 100644 index 000000000..e8b1a2c53 --- /dev/null +++ b/src/sql/qsqlextension_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Definition of the TQSqlExtension class +** +** Created : 2002-06-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLEXTENSION_P_H +#define TQSQLEXTENSION_P_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. +// +// + +#ifndef QT_H +#include "qmap.h" +#include "qvaluevector.h" +#include "qstring.h" +#include "qvariant.h" +#include "qsql.h" +#endif // QT_H + +#ifndef QT_NO_SQL + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#define TQM_TEMPLATE_EXTERN_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#define TQM_TEMPLATE_EXTERN_SQL Q_TEMPLATE_EXTERN +#endif + +struct Param { + Param( const TQVariant& v = TQVariant(), TQSql::ParameterType t = TQSql::In ): value( v ), typ( t ) {} + TQVariant value; + TQSql::ParameterType typ; + Q_DUMMY_COMPARISON_OPERATOR(Param) +}; + +struct Holder { + Holder( const TQString& hldr = TQString::null, int pos = -1 ): holderName( hldr ), holderPos( pos ) {} + bool operator==( const Holder& h ) const { return h.holderPos == holderPos && h.holderName == holderName; } + bool operator!=( const Holder& h ) const { return h.holderPos != holderPos || h.holderName != holderName; } + TQString holderName; + int holderPos; +}; + +#define Q_DEFINED_QSQLEXTENSION +#include "qwinexport.h" + +class TQM_EXPORT_SQL TQSqlExtension { +public: + TQSqlExtension(); + virtual ~TQSqlExtension(); + virtual bool prepare( const TQString& query ); + virtual bool exec(); + virtual void bindValue( const TQString& holder, const TQVariant& value, TQSql::ParameterType = TQSql::In ); + virtual void bindValue( int pos, const TQVariant& value, TQSql::ParameterType = TQSql::In ); + virtual void addBindValue( const TQVariant& value, TQSql::ParameterType = TQSql::In ); + virtual TQVariant parameterValue( const TQString& holder ); + virtual TQVariant parameterValue( int pos ); + TQVariant boundValue( const TQString& holder ) const; + TQVariant boundValue( int pos ) const; + TQMap<TQString, TQVariant> boundValues() const; + void clear(); + void clearValues(); + void clearIndex(); + void resetBindCount(); + + enum BindMethod { BindByPosition, BindByName }; + BindMethod bindMethod(); // ### 4.0: make this const + BindMethod bindm; + int bindCount; + + TQMap<int, TQString> index; + typedef TQMap<TQString, Param> ValueMap; + ValueMap values; + + // convenience container for TQSqlQuery + // to map holders <-> positions + typedef TQValueVector<Holder> HolderVector; + HolderVector holders; +}; + +class TQM_EXPORT_SQL TQSqlDriverExtension +{ +public: + TQSqlDriverExtension(); + virtual ~TQSqlDriverExtension(); + virtual bool isOpen() const = 0; +}; + +class TQM_EXPORT_SQL TQSqlOpenExtension +{ +public: + TQSqlOpenExtension(); + virtual ~TQSqlOpenExtension(); + virtual bool open( const TQString& db, + const TQString& user, + const TQString& password, + const TQString& host, + int port, + const TQString& connOpts ) = 0; +}; +#endif + +#endif diff --git a/src/sql/qsqlfield.cpp b/src/sql/qsqlfield.cpp new file mode 100644 index 000000000..b197b797e --- /dev/null +++ b/src/sql/qsqlfield.cpp @@ -0,0 +1,563 @@ +/**************************************************************************** +** +** Implementation of TQSqlField class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlfield.h" + +#ifndef QT_NO_SQL + + +/*! + \class TQSqlField qsqlfield.h + \brief The TQSqlField class manipulates the fields in SQL database tables + and views. + + \ingroup database + \module sql + + TQSqlField represents the characteristics of a single column in a + database table or view, such as the data type and column name. A + field also contains the value of the database column, which can be + viewed or changed. + + Field data values are stored as TQVariants. Using an incompatible + type is not permitted. For example: + + \code + TQSqlField f( "myfield", TQVariant::Int ); + f.setValue( TQPixmap() ); // will not work + \endcode + + However, the field will attempt to cast certain data types to the + field data type where possible: + + \code + TQSqlField f( "myfield", TQVariant::Int ); + f.setValue( TQString("123") ); // casts TQString to int + \endcode + + TQSqlField objects are rarely created explicitly in application + code. They are usually accessed indirectly through \l TQSqlRecord + or \l TQSqlCursor which already contain a list of fields. For + example: + + \code + TQSqlCursor cur( "Employee" ); // create cursor using the 'Employee' table + TQSqlField* f = cur.field( "name" ); // use the 'name' field + f->setValue( "Dave" ); // set field value + ... + \endcode + + In practice we rarely need to extract a pointer to a field at all. + The previous example would normally be written: + + \code + TQSqlCursor cur( "Employee" ); + cur.setValue( "name", "Dave" ); + ... + \endcode +*/ + +/*! + Constructs an empty field called \a fieldName of type \a type. +*/ + +TQSqlField::TQSqlField( const TQString& fieldName, TQVariant::Type type ) + : nm(fieldName), ro(FALSE), nul(FALSE) +{ + d = new TQSqlFieldPrivate(); + d->type = type; + val.cast( type ); +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlField::TQSqlField( const TQSqlField& other ) + : nm( other.nm ), val( other.val ), ro( other.ro ), nul( other.nul ) +{ + d = new TQSqlFieldPrivate(); + d->type = other.d->type; +} + +/*! + Sets the field equal to \a other. +*/ + +TQSqlField& TQSqlField::operator=( const TQSqlField& other ) +{ + nm = other.nm; + val = other.val; + ro = other.ro; + nul = other.nul; + d->type = other.d->type; + return *this; +} + +/*! + Returns TRUE if the field is equal to \a other; otherwise returns + FALSE. Fields are considered equal when the following field + properties are the same: + + \list + \i \c name() + \i \c isNull() + \i \c value() + \i \c isReadOnly() + \endlist + +*/ +bool TQSqlField::operator==(const TQSqlField& other) const +{ + return ( nm == other.nm && + val == other.val && + ro == other.ro && + nul == other.nul && + d->type == other.d->type ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlField::~TQSqlField() +{ + delete d; +} + + +/*! + \fn TQVariant TQSqlField::value() const + + Returns the value of the field as a TQVariant. +*/ + +/*! + Sets the value of the field to \a value. If the field is read-only + (isReadOnly() returns TRUE), nothing happens. If the data type of + \a value differs from the field's current data type, an attempt is + made to cast it to the proper type. This preserves the data type + of the field in the case of assignment, e.g. a TQString to an + integer data type. For example: + + \code + TQSqlCursor cur( "Employee" ); // 'Employee' table + TQSqlField* f = cur.field( "student_count" ); // an integer field + ... + f->setValue( myLineEdit->text() ); // cast the line edit text to an integer + \endcode + + \sa isReadOnly() +*/ + +void TQSqlField::setValue( const TQVariant& value ) +{ + if ( isReadOnly() ) + return; + if ( value.type() != d->type ) { + if ( !val.canCast( d->type ) ) + qWarning("TQSqlField::setValue: %s cannot cast from %s to %s", + nm.local8Bit().data(), value.typeName(), TQVariant::typeToName( d->type ) ); + } + val = value; + + if ( value.isNull() ) + nul = TRUE; + else + nul = val.type() == TQVariant::Invalid; +} + +/*! + Clears the value of the field. If the field is read-only, nothing + happens. If \a nullify is TRUE (the default), the field is set to + NULL. +*/ + +void TQSqlField::clear( bool nullify ) +{ + if ( isReadOnly() ) + return; + TQVariant v; + v.cast( type() ); + val = v; + if ( nullify ) + nul = TRUE; +} + +/*! + \fn void TQSqlField::setName( const TQString& name ) + + Sets the name of the field to \a name. +*/ + +void TQSqlField::setName( const TQString& name ) +{ + nm = name; +} + +/*! + \fn void TQSqlField::setNull() + + Sets the field to NULL and clears the value using clear(). If the + field is read-only, nothing happens. + + \sa isReadOnly() clear() +*/ + +void TQSqlField::setNull() +{ + clear( TRUE ); +} + +/*! + \fn void TQSqlField::setReadOnly( bool readOnly ) + + Sets the read only flag of the field's value to \a readOnly. + + \sa setValue() +*/ +void TQSqlField::setReadOnly( bool readOnly ) +{ + ro = readOnly; +} + +/*! + \fn TQString TQSqlField::name() const + + Returns the name of the field. +*/ + +/*! + \fn TQVariant::Type TQSqlField::type() const + + Returns the field's type as stored in the database. + Note that the actual value might have a different type, + Numerical values that are too large to store in a long + int or double are usually stored as strings to prevent + precision loss. +*/ + +/*! + \fn bool TQSqlField::isReadOnly() const + + Returns TRUE if the field's value is read only; otherwise returns + FALSE. +*/ + +/*! + \fn bool TQSqlField::isNull() const + + Returns TRUE if the field is currently NULL; otherwise returns + FALSE. +*/ + + +/******************************************/ +/******* TQSqlFieldInfo Impl ******/ +/******************************************/ + +struct TQSqlFieldInfoPrivate +{ + int retquired, len, prec, typeID; + uint generated: 1; + uint trim: 1; + uint calculated: 1; + TQString name; + TQString typeName; + TQVariant::Type typ; + TQVariant defValue; +}; + +/*! + \class TQSqlFieldInfo qsqlfield.h + \brief The TQSqlFieldInfo class stores meta data associated with a SQL field. + + \ingroup database + \module sql + + TQSqlFieldInfo objects only store meta data; field values are + stored in TQSqlField objects. + + All values must be set in the constructor, and may be retrieved + using isRetquired(), type(), length(), precision(), defaultValue(), + name(), isGenerated() and typeID(). +*/ + +/*! + Constructs a TQSqlFieldInfo with the following parameters: + \table + \row \i \a name \i the name of the field. + \row \i \a typ \i the field's type in a TQVariant. + \row \i \a retquired \i greater than 0 if the field is retquired, 0 + if its value can be NULL and less than 0 if it cannot be + determined whether the field is retquired or not. + \row \i \a len \i the length of the field. Note that for + non-character types some databases return either the length in + bytes or the number of digits. -1 signifies that the length cannot + be determined. + \row \i \a prec \i the precision of the field, or -1 if the field + has no precision or it cannot be determined. + \row \i \a defValue \i the default value that is inserted into + the table if none is specified by the user. TQVariant() if there is + no default value or it cannot be determined. + \row \i \a typeID \i the internal typeID of the database system + (only useful for low-level programming). 0 if unknown. + \row \i \a generated \i TRUE indicates that this field should be + included in auto-generated SQL statments, e.g. in TQSqlCursor. + \row \i \a trim \i TRUE indicates that widgets should remove + trailing whitespace from character fields. This does not affect + the field value but only its representation inside widgets. + \row \i \a calculated \i TRUE indicates that the value of this + field is calculated. The value of calculated fields can by + modified by subclassing TQSqlCursor and overriding + TQSqlCursor::calculateField(). + \endtable +*/ +TQSqlFieldInfo::TQSqlFieldInfo( const TQString& name, + TQVariant::Type typ, + int retquired, + int len, + int prec, + const TQVariant& defValue, + int typeID, + bool generated, + bool trim, + bool calculated ) +{ + d = new TQSqlFieldInfoPrivate(); + d->name = name; + d->typ = typ; + d->retquired = retquired; + d->len = len; + d->prec = prec; + d->defValue = defValue; + d->typeID = typeID; + d->generated = generated; + d->trim = trim; + d->calculated = calculated; +} + +/*! + Constructs a copy of \a other. +*/ +TQSqlFieldInfo::TQSqlFieldInfo( const TQSqlFieldInfo & other ) +{ + d = new TQSqlFieldInfoPrivate( *(other.d) ); +} + +/*! + Creates a TQSqlFieldInfo object with the type and the name of the + TQSqlField \a other. If \a generated is TRUE this field will be + included in auto-generated SQL statments, e.g. in TQSqlCursor. +*/ +TQSqlFieldInfo::TQSqlFieldInfo( const TQSqlField & other, bool generated ) +{ + d = new TQSqlFieldInfoPrivate(); + d->name = other.name(); + d->typ = other.type(); + d->retquired = -1; + d->len = -1; + d->prec = -1; + d->typeID = 0; + d->generated = generated; + d->trim = FALSE; + d->calculated = FALSE; +} + +/*! + Destroys the object and frees any allocated resources. +*/ +TQSqlFieldInfo::~TQSqlFieldInfo() +{ + delete d; +} + +/*! + Assigns \a other to this field info and returns a reference to it. +*/ +TQSqlFieldInfo& TQSqlFieldInfo::operator=( const TQSqlFieldInfo& other ) +{ + delete d; + d = new TQSqlFieldInfoPrivate( *(other.d) ); + return *this; +} + +/*! + Returns TRUE if this fieldinfo is equal to \a f; otherwise returns + FALSE. + + Two field infos are considered equal if all their attributes + match. +*/ +bool TQSqlFieldInfo::operator==( const TQSqlFieldInfo& f ) const +{ + return ( d->name == f.d->name && + d->typ == f.d->typ && + d->retquired == f.d->retquired && + d->len == f.d->len && + d->prec == f.d->prec && + d->defValue == f.d->defValue && + d->typeID == f.d->typeID && + d->generated == f.d->generated && + d->trim == f.d->trim && + d->calculated == f.d->calculated ); +} + +/*! + Returns an empty TQSqlField based on the information in this + TQSqlFieldInfo. +*/ +TQSqlField TQSqlFieldInfo::toField() const +{ return TQSqlField( d->name, d->typ ); } + +/*! + Returns a value greater than 0 if the field is retquired (NULL + values are not allowed), 0 if it isn't retquired (NULL values are + allowed) or less than 0 if it cannot be determined whether the + field is retquired or not. +*/ +int TQSqlFieldInfo::isRetquired() const +{ return d->retquired; } + +/*! + Returns the field's type or TQVariant::Invalid if the type is + unknown. +*/ +TQVariant::Type TQSqlFieldInfo::type() const +{ return d->typ; } + +/*! + Returns the field's length. For fields storing text the return + value is the maximum number of characters the field can hold. For + non-character fields some database systems return the number of + bytes needed or the number of digits allowed. If the length cannot + be determined -1 is returned. +*/ +int TQSqlFieldInfo::length() const +{ return d->len; } + +/*! + Returns the field's precision or -1 if the field has no precision + or it cannot be determined. +*/ +int TQSqlFieldInfo::precision() const +{ return d->prec; } + +/*! + Returns the field's default value or an empty TQVariant if the + field has no default value or the value couldn't be determined. + The default value is the value inserted in the database when it + is not explicitly specified by the user. +*/ +TQVariant TQSqlFieldInfo::defaultValue() const +{ return d->defValue; } + +/*! + Returns the name of the field in the SQL table. +*/ +TQString TQSqlFieldInfo::name() const +{ return d->name; } + +/*! + Returns the internal type identifier as returned from the database + system. The return value is 0 if the type is unknown. + + \warning This information is only useful for low-level database + programming and is \e not database independent. +*/ +int TQSqlFieldInfo::typeID() const +{ return d->typeID; } + +/*! + Returns TRUE if the field should be included in auto-generated + SQL statments, e.g. in TQSqlCursor; otherwise returns FALSE. + + \sa setGenerated() +*/ +bool TQSqlFieldInfo::isGenerated() const +{ return d->generated; } + +/*! + Returns TRUE if trailing whitespace should be removed from + character fields; otherwise returns FALSE. + + \sa setTrim() +*/ +bool TQSqlFieldInfo::isTrim() const +{ return d->trim; } + +/*! + Returns TRUE if the field is calculated; otherwise returns FALSE. + + \sa setCalculated() +*/ +bool TQSqlFieldInfo::isCalculated() const +{ return d->calculated; } + +/*! + If \a trim is TRUE widgets should remove trailing whitespace from + character fields. This does not affect the field value but only + its representation inside widgets. + + \sa isTrim() +*/ +void TQSqlFieldInfo::setTrim( bool trim ) +{ d->trim = trim; } + +/*! + \a gen set to FALSE indicates that this field should not appear + in auto-generated SQL statements (for example in TQSqlCursor). + + \sa isGenerated() +*/ +void TQSqlFieldInfo::setGenerated( bool gen ) +{ d->generated = gen; } + +/*! + \a calc set to TRUE indicates that this field is a calculated + field. The value of calculated fields can by modified by subclassing + TQSqlCursor and overriding TQSqlCursor::calculateField(). + + \sa isCalculated() +*/ +void TQSqlFieldInfo::setCalculated( bool calc ) +{ d->calculated = calc; } + +#endif diff --git a/src/sql/qsqlfield.h b/src/sql/qsqlfield.h new file mode 100644 index 000000000..4811fbad9 --- /dev/null +++ b/src/sql/qsqlfield.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Definition of TQSqlField class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLFIELD_H +#define TQSQLFIELD_H + +#ifndef QT_H +#include "qstring.h" +#include "qvariant.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlFieldPrivate +{ +public: + TQVariant::Type type; +}; + +class TQM_EXPORT_SQL TQSqlField +{ +public: + TQSqlField( const TQString& fieldName = TQString::null, TQVariant::Type type = TQVariant::Invalid ); + TQSqlField( const TQSqlField& other ); + TQSqlField& operator=( const TQSqlField& other ); + bool operator==(const TQSqlField& other) const; + virtual ~TQSqlField(); + + virtual void setValue( const TQVariant& value ); + virtual TQVariant value() const; + virtual void setName( const TQString& name ); + TQString name() const; + virtual void setNull(); + bool isNull() const; + virtual void setReadOnly( bool readOnly ); + bool isReadOnly() const; + void clear( bool nullify = TRUE ); + TQVariant::Type type() const; + +private: + TQString nm; + TQVariant val; + uint ro: 1; + uint nul: 1; + TQSqlFieldPrivate* d; +}; + +inline TQVariant TQSqlField::value() const +{ return val; } + +inline TQString TQSqlField::name() const +{ return nm; } + +inline bool TQSqlField::isNull() const +{ return nul; } + +inline bool TQSqlField::isReadOnly() const +{ return ro; } + +inline TQVariant::Type TQSqlField::type() const +{ return d->type; } + + +/******************************************/ +/******* TQSqlFieldInfo Class ******/ +/******************************************/ + +struct TQSqlFieldInfoPrivate; + +class TQM_EXPORT_SQL TQSqlFieldInfo +{ +public: + TQSqlFieldInfo( const TQString& name = TQString::null, + TQVariant::Type typ = TQVariant::Invalid, + int retquired = -1, + int len = -1, + int prec = -1, + const TQVariant& defValue = TQVariant(), + int sqlType = 0, + bool generated = TRUE, + bool trim = FALSE, + bool calculated = FALSE ); + TQSqlFieldInfo( const TQSqlFieldInfo & other ); + TQSqlFieldInfo( const TQSqlField & other, bool generated = TRUE ); + virtual ~TQSqlFieldInfo(); + TQSqlFieldInfo& operator=( const TQSqlFieldInfo& other ); + bool operator==( const TQSqlFieldInfo& f ) const; + + TQSqlField toField() const; + int isRetquired() const; + TQVariant::Type type() const; + int length() const; + int precision() const; + TQVariant defaultValue() const; + TQString name() const; + int typeID() const; + bool isGenerated() const; + bool isTrim() const; + bool isCalculated() const; + + virtual void setTrim( bool trim ); + virtual void setGenerated( bool gen ); + virtual void setCalculated( bool calc ); + +private: + TQSqlFieldInfoPrivate* d; +}; + + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlform.cpp b/src/sql/qsqlform.cpp new file mode 100644 index 000000000..726597442 --- /dev/null +++ b/src/sql/qsqlform.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Implementation of TQSqlForm class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlform.h" + +#ifndef QT_NO_SQL_FORM + +#include "qsqlfield.h" +#include "qsqlpropertymap.h" +#include "qsqlrecord.h" +#include "qstringlist.h" +#include "qwidget.h" +#include "qdict.h" + +class TQSqlFormPrivate +{ +public: + TQSqlFormPrivate() : propertyMap( 0 ), buf( 0 ), dirty( FALSE ) {} + ~TQSqlFormPrivate() { if ( propertyMap ) delete propertyMap; } + TQStringList fld; + TQDict<TQWidget> wgt; + TQMap< TQWidget *, TQSqlField * > map; + TQSqlPropertyMap * propertyMap; + TQSqlRecord* buf; + bool dirty; +}; + +/*! + \class TQSqlForm + \brief The TQSqlForm class creates and manages data entry forms + tied to SQL databases. + + \ingroup database + \mainclass + \module sql + + Typical use of a TQSqlForm consists of the following steps: + \list + \i Create the widgets you want to appear in the form. + \i Create a cursor and navigate to the record to be edited. + \i Create the TQSqlForm. + \i Set the form's record buffer to the cursor's update buffer. + \i Insert each widget and the field it is to edit into the form. + \i Use readFields() to update the editor widgets with values from + the database's fields. + \i Display the form and let the user edit values etc. + \i Use writeFields() to update the database's field values with + the values in the editor widgets. + \endlist + + Note that a TQSqlForm does not access the database directly, but + most often via TQSqlFields which are part of a TQSqlCursor. A + TQSqlCursor::insert(), TQSqlCursor::update() or TQSqlCursor::del() + call is needed to actually write values to the database. + + Some sample code to initialize a form successfully: + + \code + TQLineEdit myEditor( this ); + TQSqlForm myForm( this ); + TQSqlCursor myCursor( "mytable" ); + + // Execute a query to make the cursor valid + myCursor.select(); + // Move the cursor to a valid record (the first record) + myCursor.next(); + // Set the form's record pointer to the cursor's edit buffer (which + // contains the current record's values) + myForm.setRecord( myCursor.primeUpdate() ); + + // Insert a field into the form that uses myEditor to edit the + // field 'somefield' in 'mytable' + myForm.insert( &myEditor, "somefield" ); + + // Update myEditor with the value from the mapped database field + myForm.readFields(); + ... + // Let the user edit the form + ... + // Update the database + myForm.writeFields(); // Update the cursor's edit buffer from the form + myCursor.update(); // Update the database from the cursor's buffer + \endcode + + If you want to use custom editors for displaying and editing data + fields, you must install a custom TQSqlPropertyMap. The form + uses this object to get or set the value of a widget. + + Note that \link designer-manual.book TQt Designer\endlink provides + a visual means of creating data-aware forms. + + \sa installPropertyMap(), TQSqlPropertyMap +*/ + + +/*! + Constructs a TQSqlForm with parent \a parent and called \a name. +*/ +TQSqlForm::TQSqlForm( TQObject * parent, const char * name ) + : TQObject( parent, name ) +{ + d = new TQSqlFormPrivate(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ +TQSqlForm::~TQSqlForm() +{ + delete d; +} + +/*! + Installs a custom TQSqlPropertyMap. This is useful if you plan to + create your own custom editor widgets. + + TQSqlForm takes ownership of \a pmap, so \a pmap is deleted when + TQSqlForm goes out of scope. + + \sa TQDataTable::installEditorFactory() +*/ +void TQSqlForm::installPropertyMap( TQSqlPropertyMap * pmap ) +{ + if( d->propertyMap ) + delete d->propertyMap; + d->propertyMap = pmap; +} + +/*! + Sets \a buf as the record buffer for the form. To force the + display of the data from \a buf, use readFields(). + + \sa readFields() writeFields() +*/ + +void TQSqlForm::setRecord( TQSqlRecord* buf ) +{ + d->dirty = TRUE; + d->buf = buf; +} + +/*! + Inserts a \a widget, and the name of the \a field it is to be + mapped to, into the form. To actually associate inserted widgets + with an edit buffer, use setRecord(). + + \sa setRecord() +*/ + +void TQSqlForm::insert( TQWidget * widget, const TQString& field ) +{ + d->dirty = TRUE; + d->wgt.insert( field, widget ); + d->fld += field; +} + +/*! + \overload + + Removes \a field from the form. +*/ + +void TQSqlForm::remove( const TQString& field ) +{ + d->dirty = TRUE; + if ( d->fld.find( field ) != d->fld.end() ) + d->fld.remove( d->fld.find( field ) ); + d->wgt.remove( field ); +} + +/*! + \overload + + Inserts a \a widget, and the \a field it is to be mapped to, into + the form. +*/ + +void TQSqlForm::insert( TQWidget * widget, TQSqlField * field ) +{ + d->map[widget] = field; +} + +/*! + Removes a \a widget, and hence the field it's mapped to, from the + form. +*/ + +void TQSqlForm::remove( TQWidget * widget ) +{ + d->map.remove( widget ); +} + +/*! + Clears the values in all the widgets, and the fields they are + mapped to, in the form. If \a nullify is TRUE (the default is + FALSE), each field is also set to NULL. +*/ +void TQSqlForm::clearValues( bool nullify ) +{ + TQMap< TQWidget *, TQSqlField * >::Iterator it; + for( it = d->map.begin(); it != d->map.end(); ++it ){ + TQSqlField* f = (*it); + if ( f ) + f->clear( nullify ); + } + readFields(); +} + +/*! + Removes every widget, and the fields they're mapped to, from the form. +*/ +void TQSqlForm::clear() +{ + d->dirty = TRUE; + d->fld.clear(); + clearMap(); +} + +/*! + Returns the number of widgets in the form. +*/ +uint TQSqlForm::count() const +{ + return (uint)d->map.count(); +} + +/*! + Returns the \a{i}-th widget in the form. Useful for traversing + the widgets in the form. +*/ +TQWidget * TQSqlForm::widget( uint i ) const +{ + TQMap< TQWidget *, TQSqlField * >::ConstIterator it; + uint cnt = 0; + + if( i > d->map.count() ) return 0; + for( it = d->map.begin(); it != d->map.end(); ++it ){ + if( cnt++ == i ) + return it.key(); + } + return 0; +} + +/*! + Returns the widget that field \a field is mapped to. +*/ +TQWidget * TQSqlForm::fieldToWidget( TQSqlField * field ) const +{ + TQMap< TQWidget *, TQSqlField * >::ConstIterator it; + for( it = d->map.begin(); it != d->map.end(); ++it ){ + if( *it == field ) + return it.key(); + } + return 0; +} + +/*! + Returns the SQL field that widget \a widget is mapped to. +*/ +TQSqlField * TQSqlForm::widgetToField( TQWidget * widget ) const +{ + if( d->map.contains( widget ) ) + return d->map[widget]; + else + return 0; +} + +/*! + Updates the widgets in the form with current values from the SQL + fields they are mapped to. +*/ +void TQSqlForm::readFields() +{ + sync(); + TQSqlField * f; + TQMap< TQWidget *, TQSqlField * >::Iterator it; + TQSqlPropertyMap * pmap = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + for(it = d->map.begin() ; it != d->map.end(); ++it ){ + f = widgetToField( it.key() ); + if( !f ) + continue; + pmap->setProperty( it.key(), f->value() ); + } +} + +/*! + Updates the SQL fields with values from the widgets they are + mapped to. To actually update the database with the contents of + the record buffer, use TQSqlCursor::insert(), TQSqlCursor::update() + or TQSqlCursor::del() as appropriate. +*/ +void TQSqlForm::writeFields() +{ + sync(); + TQSqlField * f; + TQMap< TQWidget *, TQSqlField * >::Iterator it; + TQSqlPropertyMap * pmap = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + + for(it = d->map.begin() ; it != d->map.end(); ++it ){ + f = widgetToField( it.key() ); + if( !f ) + continue; + f->setValue( pmap->property( it.key() ) ); + } +} + +/*! + Updates the widget \a widget with the value from the SQL field it + is mapped to. Nothing happens if no SQL field is mapped to the \a + widget. +*/ +void TQSqlForm::readField( TQWidget * widget ) +{ + sync(); + TQSqlField * field = 0; + TQSqlPropertyMap * pmap = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + field = widgetToField( widget ); + if( field ) + pmap->setProperty( widget, field->value() ); +} + +/*! + Updates the SQL field with the value from the \a widget it is + mapped to. Nothing happens if no SQL field is mapped to the \a + widget. +*/ +void TQSqlForm::writeField( TQWidget * widget ) +{ + sync(); + TQSqlField * field = 0; + TQSqlPropertyMap * pmap = (d->propertyMap == 0) ? + TQSqlPropertyMap::defaultMap() : d->propertyMap; + field = widgetToField( widget ); + if( field ) + field->setValue( pmap->property( widget ) ); +} + +/*! \internal +*/ + +void TQSqlForm::sync() +{ + if ( d->dirty ) { + clearMap(); + if ( d->buf ) { + for ( uint i = 0; i < d->fld.count(); ++i ) + insert( d->wgt[ d->fld[ i ] ], d->buf->field( d->fld[ i ] ) ); + } + } + d->dirty = FALSE; +} + +/*! \internal + + Clears the internal map of widget/field associations +*/ + +void TQSqlForm::clearMap() +{ + d->map.clear(); +} + +#endif // QT_NO_SQL diff --git a/src/sql/qsqlform.h b/src/sql/qsqlform.h new file mode 100644 index 000000000..8b13816fe --- /dev/null +++ b/src/sql/qsqlform.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Definition of TQSqlForm class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLFORM_H +#define TQSQLFORM_H + +#ifndef QT_H +#include "qobject.h" +#include "qmap.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL_FORM + +class TQSqlField; +class TQSqlRecord; +class TQSqlEditorFactory; +class TQSqlPropertyMap; +class TQWidget; +class TQSqlFormPrivate; + +class TQM_EXPORT_SQL TQSqlForm : public TQObject +{ + Q_OBJECT +public: + TQSqlForm( TQObject * parent = 0, const char * name = 0 ); + ~TQSqlForm(); + + virtual void insert( TQWidget * widget, const TQString& field ); + virtual void remove( const TQString& field ); + uint count() const; + + TQWidget * widget( uint i ) const; + TQSqlField * widgetToField( TQWidget * widget ) const; + TQWidget * fieldToWidget( TQSqlField * field ) const; + + void installPropertyMap( TQSqlPropertyMap * map ); + + virtual void setRecord( TQSqlRecord* buf ); + +public slots: + virtual void readField( TQWidget * widget ); + virtual void writeField( TQWidget * widget ); + virtual void readFields(); + virtual void writeFields(); + + virtual void clear(); + virtual void clearValues( bool nullify = FALSE ); + +protected: + virtual void insert( TQWidget * widget, TQSqlField * field ); + virtual void remove( TQWidget * widget ); + void clearMap(); + +private: + virtual void sync(); + TQSqlFormPrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQSqlForm( const TQSqlForm & ); + TQSqlForm &operator=( const TQSqlForm & ); +#endif +}; + +#endif // QT_NO_SQL +#endif // TQSQLFORM_H diff --git a/src/sql/qsqlindex.cpp b/src/sql/qsqlindex.cpp new file mode 100644 index 000000000..251e9ef28 --- /dev/null +++ b/src/sql/qsqlindex.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Implementation of TQSqlIndex class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlindex.h" + +#ifndef QT_NO_SQL + +#include "qsqlcursor.h" + +/*! + \class TQSqlIndex qsqlindex.h + \brief The TQSqlIndex class provides functions to manipulate and + describe TQSqlCursor and TQSqlDatabase indexes. + + \ingroup database + \module sql + + This class is used to describe and manipulate TQSqlCursor and + TQSqlDatabase indexes. An index refers to a single table or view + in a database. Information about the fields that comprise the + index can be used to generate SQL statements, or to affect the + behavior of a \l TQSqlCursor object. + + Normally, TQSqlIndex objects are created by \l TQSqlDatabase or + TQSqlCursor. +*/ + +/*! + Constructs an empty index using the cursor name \a cursorname and + index name \a name. +*/ + +TQSqlIndex::TQSqlIndex( const TQString& cursorname, const TQString& name ) + : TQSqlRecord(), cursor(cursorname), nm(name) +{ + +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlIndex::TQSqlIndex( const TQSqlIndex& other ) + : TQSqlRecord(other), cursor(other.cursor), nm(other.nm), sorts(other.sorts) +{ +} + +/*! + Sets the index equal to \a other. +*/ + +TQSqlIndex& TQSqlIndex::operator=( const TQSqlIndex& other ) +{ + cursor = other.cursor; + nm = other.nm; + sorts = other.sorts; + TQSqlRecord::operator=( other ); + return *this; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlIndex::~TQSqlIndex() +{ + +} + +/*! + Sets the name of the index to \a name. +*/ + +void TQSqlIndex::setName( const TQString& name ) +{ + nm = name; +} + +/*! + \fn TQString TQSqlIndex::name() const + + Returns the name of the index. +*/ + +/*! + Appends the field \a field to the list of indexed fields. The + field is appended with an ascending sort order. +*/ + +void TQSqlIndex::append( const TQSqlField& field ) +{ + append( field, FALSE ); +} + +/*! + \overload + + Appends the field \a field to the list of indexed fields. The + field is appended with an ascending sort order, unless \a desc is + TRUE. +*/ + +void TQSqlIndex::append( const TQSqlField& field, bool desc ) +{ + sorts.append( desc ); + TQSqlRecord::append( field ); +} + + +/*! + Returns TRUE if field \a i in the index is sorted in descending + order; otherwise returns FALSE. +*/ + +bool TQSqlIndex::isDescending( int i ) const +{ + if ( sorts.at( i ) != sorts.end() ) + return sorts[i]; + return FALSE; +} + +/*! + If \a desc is TRUE, field \a i is sorted in descending order. + Otherwise, field \a i is sorted in ascending order (the default). + If the field does not exist, nothing happens. +*/ + +void TQSqlIndex::setDescending( int i, bool desc ) +{ + if ( sorts.at( i ) != sorts.end() ) + sorts[i] = desc; +} + +/*! + \reimp + + Returns a comma-separated list of all the index's field names as a + string. This string is suitable, for example, for generating a + SQL SELECT statement. Only generated fields are included in the + list (see \l{isGenerated()}). If a \a prefix is specified, e.g. a + table name, it is prepended before all field names in the form: + + "\a{prefix}.<fieldname>" + + If \a sep is specified, each field is separated by \a sep. If \a + verbose is TRUE (the default), each field contains a suffix + indicating an ASCending or DESCending sort order. +*/ + +TQString TQSqlIndex::toString( const TQString& prefix, const TQString& sep, bool verbose ) const +{ + TQString s; + bool comma = FALSE; + for ( uint i = 0; i < count(); ++i ) { + if( comma ) + s += sep + " "; + s += createField( i, prefix, verbose ); + comma = TRUE; + } + return s; +} + +/*! + \reimp + + Returns a list of all the index's field names. Only generated + fields are included in the list (see \l{isGenerated()}). If a \a + prefix is specified, e.g. a table name, all fields are prefixed in + the form: + + "\a{prefix}.<fieldname>" + + If \a verbose is TRUE (the default), each field contains a suffix + indicating an ASCending or DESCending sort order. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myIndex.toStringList(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + +*/ +TQStringList TQSqlIndex::toStringList( const TQString& prefix, bool verbose ) const +{ + TQStringList s; + for ( uint i = 0; i < count(); ++i ) + s += createField( i, prefix, verbose ); + return s; +} + +/*! \internal + + Creates a string representing the field number \a i using prefix \a + prefix. If \a verbose is TRUE, ASC or DESC is included in the field + description if the field is sorted in ASCending or DESCending order. +*/ + +TQString TQSqlIndex::createField( int i, const TQString& prefix, bool verbose ) const +{ + TQString f; + if ( !prefix.isEmpty() ) + f += prefix + "."; + f += field( i )->name(); + if ( verbose ) + f += " " + TQString( ( isDescending( i ) ? "DESC" : "ASC" ) ); + return f; +} + +/*! + Returns an index based on the field descriptions in \a l and the + cursor \a cursor. The field descriptions should be in the same + format that toStringList() produces, for example, a surname field + in the people table might be in one of these forms: "surname", + "surname DESC" or "people.surname ASC". + + \sa toStringList() +*/ + +TQSqlIndex TQSqlIndex::fromStringList( const TQStringList& l, const TQSqlCursor* cursor ) +{ + TQSqlIndex newSort; + for ( uint i = 0; i < l.count(); ++i ) { + TQString f = l[ i ]; + bool desc = FALSE; + if ( f.mid( f.length()-3 ) == "ASC" ) + f = f.mid( 0, f.length()-3 ); + if ( f.mid( f.length()-4 ) == "DESC" ) { + desc = TRUE; + f = f.mid( 0, f.length()-4 ); + } + int dot = f.findRev( '.' ); + if ( dot != -1 ) + f = f.mid( dot+1 ); + const TQSqlField* field = cursor->field( f.simplifyWhiteSpace() ); + if ( field ) + newSort.append( *field, desc ); + else + qWarning( "TQSqlIndex::fromStringList: unknown field: '%s'", f.latin1()); + } + return newSort; +} + +/*! + \fn TQString TQSqlIndex::cursorName() const + + Returns the name of the cursor which the index is associated with. +*/ + + +/*! + Sets the name of the cursor that the index is associated with to + \a cursorName. +*/ +void TQSqlIndex::setCursorName( const TQString& cursorName ) +{ + cursor = cursorName; +} + +#endif diff --git a/src/sql/qsqlindex.h b/src/sql/qsqlindex.h new file mode 100644 index 000000000..9985053bc --- /dev/null +++ b/src/sql/qsqlindex.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Definition of TQSqlIndex class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLINDEX_H +#define TQSQLINDEX_H + +#ifndef QT_H +#include "qstring.h" +#include "qstringlist.h" +#include "qsqlfield.h" +#include "qsqlrecord.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#define TQM_TEMPLATE_EXTERN_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#define TQM_TEMPLATE_EXTERN_SQL Q_TEMPLATE_EXTERN +#endif + +#ifndef QT_NO_SQL + +class TQSqlCursor; + +class TQM_EXPORT_SQL TQSqlIndex : public TQSqlRecord +{ +public: + TQSqlIndex( const TQString& cursorName = TQString::null, const TQString& name = TQString::null ); + TQSqlIndex( const TQSqlIndex& other ); + ~TQSqlIndex(); + TQSqlIndex& operator=( const TQSqlIndex& other ); + virtual void setCursorName( const TQString& cursorName ); + TQString cursorName() const { return cursor; } + virtual void setName( const TQString& name ); + TQString name() const { return nm; } + + void append( const TQSqlField& field ); + virtual void append( const TQSqlField& field, bool desc ); + + bool isDescending( int i ) const; + virtual void setDescending( int i, bool desc ); + + TQString toString( const TQString& prefix = TQString::null, + const TQString& sep = ",", + bool verbose = TRUE ) const; + TQStringList toStringList( const TQString& prefix = TQString::null, + bool verbose = TRUE ) const; + + static TQSqlIndex fromStringList( const TQStringList& l, const TQSqlCursor* cursor ); + +private: + TQString createField( int i, const TQString& prefix, bool verbose ) const; + TQString cursor; + TQString nm; + TQValueList<bool> sorts; +}; + +#define Q_DEFINED_QSQLINDEX +#include "qwinexport.h" +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlmanager_p.cpp b/src/sql/qsqlmanager_p.cpp new file mode 100644 index 000000000..100512623 --- /dev/null +++ b/src/sql/qsqlmanager_p.cpp @@ -0,0 +1,941 @@ +/**************************************************************************** +** +** Implementation of sql manager classes +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlmanager_p.h" + +#ifndef QT_NO_SQL + +#include "qapplication.h" +#include "qwidget.h" +#include "qsqlcursor.h" +#include "qsqlform.h" +#include "qsqldriver.h" +#include "qstring.h" +#include "qmessagebox.h" +#include "qbitarray.h" + +//#define QT_DEBUG_DATAMANAGER + +class TQSqlCursorManagerPrivate +{ +public: + TQSqlCursorManagerPrivate() + : cur( 0 ), autoDelete( FALSE ) + {} + + TQString ftr; + TQStringList srt; + TQSqlCursor* cur; + bool autoDelete; +}; + +/*! + \class TQSqlCursorManager qsqlmanager_p.h + \brief The TQSqlCursorManager class manages a database cursor. + + \module sql + + \internal + + This class provides common cursor management functionality. This + includes saving and applying sorts and filters, refreshing (i.e., + re-selecting) the cursor and searching for records within the + cursor. + +*/ + +/*! \internal + + Constructs a cursor manager. + +*/ + +TQSqlCursorManager::TQSqlCursorManager() +{ + d = new TQSqlCursorManagerPrivate; +} + + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +TQSqlCursorManager::~TQSqlCursorManager() +{ + if ( d->autoDelete ) + delete d->cur; + delete d; +} + +/*! \internal + + Sets the manager's sort to the index \a sort. To apply the new + sort, use refresh(). + + */ + +void TQSqlCursorManager::setSort( const TQSqlIndex& sort ) +{ + setSort( sort.toStringList() ); +} + +/*! \internal + + Sets the manager's sort to the stringlist \a sort. To apply the + new sort, use refresh(). + + */ + +void TQSqlCursorManager::setSort( const TQStringList& sort ) +{ + d->srt = sort; +} + +/*! \internal + + Returns the current sort, or an empty stringlist if there is none. + +*/ + +TQStringList TQSqlCursorManager::sort() const +{ + return d->srt; +} + +/*! \internal + + Sets the manager's filter to the string \a filter. To apply the + new filter, use refresh(). + +*/ + +void TQSqlCursorManager::setFilter( const TQString& filter ) +{ + d->ftr = filter; +} + +/*! \internal + + Returns the current filter, or an empty string if there is none. + +*/ + +TQString TQSqlCursorManager::filter() const +{ + return d->ftr; +} + +/*! \internal + + Sets auto-delete to \a enable. If TRUE, the default cursor will + be deleted when necessary. + + \sa autoDelete() +*/ + +void TQSqlCursorManager::setAutoDelete( bool enable ) +{ + d->autoDelete = enable; +} + + +/*! \internal + + Returns TRUE if auto-deletion is enabled, otherwise FALSE. + + \sa setAutoDelete() + +*/ + +bool TQSqlCursorManager::autoDelete() const +{ + return d->autoDelete; +} + +/*! \internal + + Sets the default cursor used by the manager to \a cursor. If \a + autoDelete is TRUE (the default is FALSE), the manager takes + ownership of the \a cursor pointer, which will be deleted when the + manager is destroyed, or when setCursor() is called again. To + activate the \a cursor use refresh(). + + \sa cursor() + +*/ + +void TQSqlCursorManager::setCursor( TQSqlCursor* cursor, bool autoDelete ) +{ + if ( d->autoDelete ) + delete d->cur; + d->cur = cursor; + d->autoDelete = autoDelete; +} + +/*! \internal + + Returns a pointer to the default cursor used for navigation, or 0 + if there is no default cursor. + + \sa setCursor() + +*/ + +TQSqlCursor* TQSqlCursorManager::cursor() const +{ + return d->cur; +} + + +/*! \internal + + Refreshes the manager using the default cursor. The manager's + filter and sort are applied. Returns TRUE on success, FALSE if an + error occurred or there is no current cursor. + + \sa setFilter() setSort() + +*/ + +bool TQSqlCursorManager::refresh() +{ + TQSqlCursor* cur = cursor(); + if ( !cur ) + return FALSE; + TQString currentFilter = d->ftr; + TQStringList currentSort = d->srt; + TQSqlIndex newSort = TQSqlIndex::fromStringList( currentSort, cur ); + return cur->select( currentFilter, newSort ); +} + +/* \internal + + Returns TRUE if the \a buf field values that correspond to \a idx + match the field values in \a cur that correspond to \a idx. +*/ + +static bool index_matches( const TQSqlCursor* cur, const TQSqlRecord* buf, + const TQSqlIndex& idx ) +{ + bool indexEquals = FALSE; + for ( uint i = 0; i < idx.count(); ++i ) { + const TQString fn( idx.field(i)->name() ); + if ( cur->value( fn ) == buf->value( fn ) ) + indexEquals = TRUE; + else { + indexEquals = FALSE; + break; + } + } + return indexEquals; +} + +/* + Return less than, equal to or greater than 0 if buf1 is less than, + equal to or greater than buf2 according to fields described in idx. + (### Currently only uses first field.) +*/ + +static int compare_recs( const TQSqlRecord* buf1, const TQSqlRecord* buf2, + const TQSqlIndex& idx ) +{ + int cmp = 0; + + int i = 0; + const TQString fn( idx.field(i)->name() ); + const TQSqlField* f1 = buf1->field( fn ); + + if ( f1 ) { + switch ( f1->type() ) { // ### more types? + case TQVariant::String: + case TQVariant::CString: + cmp = f1->value().toString().simplifyWhiteSpace().compare( + buf2->value(fn).toString().simplifyWhiteSpace() ); + break; + default: + if ( f1->value().toDouble() < buf2->value( fn ).toDouble() ) + cmp = -1; + else if ( f1->value().toDouble() > buf2->value( fn ).toDouble() ) + cmp = 1; + } + } + + if ( idx.isDescending(i) ) + cmp = -cmp; + return cmp; +} + +#ifdef QT_DEBUG_DATAMANAGER +static void debug_datamanager_buffer( const TQString& msg, TQSqlRecord* cursor ) +{ + qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + qDebug( "%s", msg.latin1() ); + for ( uint j = 0; j < cursor->count(); ++j ) { + qDebug( "%s", (cursor->field(j)->name() + " type:" + + TQString(cursor->field(j)->value().typeName()) + + " value:" + cursor->field(j)->value().toString()) + .latin1() ); + } +} +#endif + + +/*! \internal + + Relocates the default cursor to the record matching the cursor's +edit buffer. Only the field names specified by \a idx are used to +determine an exact match of the cursor to the edit buffer. However, +other fields in the edit buffer are also used during the search, +therefore all fields in the edit buffer should be primed with desired +values for the record being sought. This function is typically used +to relocate a cursor to the correct position after an insert or +update. For example: + +\code + TQSqlCursor* myCursor = myManager.cursor(); + ... + TQSqlRecord* buf = myCursor->primeUpdate(); + buf->setValue( "name", "Ola" ); + buf->setValue( "city", "Oslo" ); + ... + myCursor->update(); // update current record + myCursor->select(); // refresh the cursor + myManager.findBuffer( myCursor->primaryIndex() ); // go to the updated record +\endcode + +*/ + +//## possibly add sizeHint parameter +bool TQSqlCursorManager::findBuffer( const TQSqlIndex& idx, int atHint ) +{ +#ifdef QT_DEBUG_DATAMANAGER + qDebug("TQSqlCursorManager::findBuffer:"); +#endif + TQSqlCursor* cur = cursor(); + if ( !cur ) + return FALSE; + if ( !cur->isActive() ) + return FALSE; + if ( !idx.count() ) { + if ( cur->at() == TQSql::BeforeFirst ) + cur->next(); + return FALSE; + } + TQSqlRecord* buf = cur->editBuffer(); + bool indexEquals = FALSE; +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Checking hint..."); +#endif + /* check the hint */ + if ( cur->seek( atHint ) ) + indexEquals = index_matches( cur, buf, idx ); + + if ( !indexEquals ) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Checking current page..."); +#endif + /* check current page */ + int pageSize = 20; + int startIdx = TQMAX( atHint - pageSize, 0 ); + int endIdx = atHint + pageSize; + for ( int j = startIdx; j <= endIdx; ++j ) { + if ( cur->seek( j ) ) { + indexEquals = index_matches( cur, buf, idx ); + if ( indexEquals ) + break; + } + } + } + + if ( !indexEquals && cur->driver()->hasFeature( TQSqlDriver::QuerySize ) + && cur->sort().count() ) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Using binary search..."); +#endif + // binary search based on record buffer and current sort fields + int lo = 0; + int hi = cur->size(); + int mid; + if ( compare_recs( buf, cur, cur->sort() ) >= 0 ) + lo = cur->at(); + while ( lo != hi ) { + mid = lo + (hi - lo) / 2; + if ( !cur->seek( mid ) ) + break; + if ( index_matches( cur, buf, idx ) ) { + indexEquals = TRUE; + break; + } + int c = compare_recs( buf, cur, cur->sort() ); + if ( c < 0 ) { + hi = mid; + } else if ( c == 0 ) { + // found it, but there may be duplicates + int at = mid; + do { + mid--; + if ( !cur->seek( mid ) ) + break; + if ( index_matches( cur, buf, idx ) ) { + indexEquals = TRUE; + break; + } + } while ( compare_recs( buf, cur, cur->sort() ) == 0 ); + + if ( !indexEquals ) { + mid = at; + do { + mid++; + if ( !cur->seek( mid ) ) + break; + if ( index_matches( cur, buf, idx ) ) { + indexEquals = TRUE; + break; + } + } while ( compare_recs( buf, cur, cur->sort() ) == 0 ); + } + break; + } else if ( c > 0 ) { + lo = mid + 1; + } + } + } + + if ( !indexEquals ) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Using brute search..."); +#endif +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( TQt::waitCursor ); +#endif + /* give up, use brute force */ + int startIdx = 0; + if ( cur->at() != startIdx ) { + cur->seek( startIdx ); + } + for ( ;; ) { + indexEquals = FALSE; + indexEquals = index_matches( cur, buf, idx ); + if ( indexEquals ) + break; + if ( !cur->next() ) + break; + } +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + } +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Done, result:" + TQString::number( indexEquals ) ); +#endif + return indexEquals; +} + +#ifndef QT_NO_SQL_FORM + +class TQSqlFormManagerPrivate +{ +public: + TQSqlFormManagerPrivate() : frm(0), rcd(0) {} + TQSqlForm* frm; + TQSqlRecord* rcd; +}; + + +/*! \internal + + Creates a form manager. + +*/ + +TQSqlFormManager::TQSqlFormManager() +{ + d = new TQSqlFormManagerPrivate(); +} + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +TQSqlFormManager::~TQSqlFormManager() +{ + delete d; +} + +/*! \internal + + Clears the default form values. If there is no default form, + nothing happens, + +*/ + +void TQSqlFormManager::clearValues() +{ + if ( form() ) + form()->clearValues(); +} + +/*! \internal + + Sets the form used by the form manager to \a form. If a record has + already been assigned to the form manager, that record is also used by + the \a form to display data. + + \sa form() + +*/ + +void TQSqlFormManager::setForm( TQSqlForm* form ) +{ + d->frm = form; + if ( d->rcd && d->frm ) + d->frm->setRecord( d->rcd ); +} + + +/*! \internal + + Returns the default form used by the form manager, or 0 if there is + none. + + \sa setForm() + +*/ + +TQSqlForm* TQSqlFormManager::form() +{ + return d->frm; +} + + +/*! \internal + + Sets the record used by the form manager to \a record. If a form has + already been assigned to the form manager, \a record is also used by + the default form to display data. + + \sa record() + +*/ + +void TQSqlFormManager::setRecord( TQSqlRecord* record ) +{ + d->rcd = record; + if ( d->frm ) { + d->frm->setRecord( d->rcd ); + } +} + + +/*! \internal + + Returns the default record used by the form manager, or 0 if there is + none. + + \sa setRecord() +*/ + +TQSqlRecord* TQSqlFormManager::record() +{ + return d->rcd; +} + + +/*! \internal + + Causes the default form to read its fields . If there is no + default form, nothing happens. + + \sa setForm() + +*/ + +void TQSqlFormManager::readFields() +{ + if ( d->frm ) { + d->frm->readFields(); + } +} + +/*! \internal + + Causes the default form to write its fields . If there is no + default form, nothing happens. + + \sa setForm() + +*/ + +void TQSqlFormManager::writeFields() +{ + if ( d->frm ) { + d->frm->writeFields(); + } +} + +#endif // QT_NO_SQL_FORM + +class TQDataManagerPrivate +{ +public: + TQDataManagerPrivate() + : mode( TQSql::None ), autoEd( TRUE ), confEdits( 3 ), + confCancs( FALSE ) {} + TQSql::Op mode; + bool autoEd; + TQBitArray confEdits; + bool confCancs; + +}; + +/*! + \class TQDataManager qsqlmanager_p.h + \ingroup database + + \brief The TQDataManager class is an internal class for implementing + the data-aware widgets. + + \internal + + TQDataManager is a strictly internal class that acts as a base class + for other data-aware widgets. + +*/ + + +/*! \internal + + Constructs an empty data handler. + +*/ + +TQDataManager::TQDataManager() +{ + d = new TQDataManagerPrivate(); +} + + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +TQDataManager::~TQDataManager() +{ + delete d; +} + + +/*! \internal + + Virtual function which is called when an error has occurred The + default implementation displays a warning message to the user with + information about the error. + +*/ +void TQDataManager::handleError( TQWidget* parent, const TQSqlError& e ) +{ +#ifndef QT_NO_MESSAGEBOX + if (e.driverText().isEmpty() && e.databaseText().isEmpty()) { + TQMessageBox::warning ( parent, "Warning", "An error occurred while accessing the database"); + } else { + TQMessageBox::warning ( parent, "Warning", e.driverText() + "\n" + e.databaseText(), + 0, 0 ); + } +#endif // QT_NO_MESSAGEBOX +} + + +/*! \internal + + Sets the internal mode to \a m. + +*/ + +void TQDataManager::setMode( TQSql::Op m ) +{ + d->mode = m; +} + + +/*! \internal + + Returns the current mode. + +*/ + +TQSql::Op TQDataManager::mode() const +{ + return d->mode; +} + + +/*! \internal + + Sets the auto-edit mode to \a auto. + +*/ + +void TQDataManager::setAutoEdit( bool autoEdit ) +{ + d->autoEd = autoEdit; +} + + + +/*! \internal + + Returns TRUE if auto-edit mode is enabled; otherwise returns FALSE. + +*/ + +bool TQDataManager::autoEdit() const +{ + return d->autoEd; +} + +/*! \internal + + If \a confirm is TRUE, all edit operations (inserts, updates and + deletes) will be confirmed by the user. If \a confirm is FALSE (the + default), all edits are posted to the database immediately. + +*/ +void TQDataManager::setConfirmEdits( bool confirm ) +{ + d->confEdits.fill( confirm ); +} + +/*! \internal + + If \a confirm is TRUE, all inserts will be confirmed by the user. + If \a confirm is FALSE (the default), all edits are posted to the + database immediately. + +*/ + +void TQDataManager::setConfirmInsert( bool confirm ) +{ + d->confEdits[ TQSql::Insert ] = confirm; +} + +/*! \internal + + If \a confirm is TRUE, all updates will be confirmed by the user. + If \a confirm is FALSE (the default), all edits are posted to the + database immediately. + +*/ + +void TQDataManager::setConfirmUpdate( bool confirm ) +{ + d->confEdits[ TQSql::Update ] = confirm; +} + +/*! \internal + + If \a confirm is TRUE, all deletes will be confirmed by the user. + If \a confirm is FALSE (the default), all edits are posted to the + database immediately. + +*/ + +void TQDataManager::setConfirmDelete( bool confirm ) +{ + d->confEdits[ TQSql::Delete ] = confirm; +} + +/*! \internal + + Returns TRUE if the table confirms all edit operations (inserts, + updates and deletes), otherwise returns FALSE. +*/ + +bool TQDataManager::confirmEdits() const +{ + return ( confirmInsert() && confirmUpdate() && confirmDelete() ); +} + +/*! \internal + + Returns TRUE if the table confirms inserts, otherwise returns + FALSE. +*/ + +bool TQDataManager::confirmInsert() const +{ + return d->confEdits[ TQSql::Insert ]; +} + +/*! \internal + + Returns TRUE if the table confirms updates, otherwise returns + FALSE. +*/ + +bool TQDataManager::confirmUpdate() const +{ + return d->confEdits[ TQSql::Update ]; +} + +/*! \internal + + Returns TRUE if the table confirms deletes, otherwise returns + FALSE. +*/ + +bool TQDataManager::confirmDelete() const +{ + return d->confEdits[ TQSql::Delete ]; +} + +/*! \internal + + If \a confirm is TRUE, all cancels will be confirmed by the user + through a message box. If \a confirm is FALSE (the default), all + cancels occur immediately. +*/ + +void TQDataManager::setConfirmCancels( bool confirm ) +{ + d->confCancs = confirm; +} + +/*! \internal + + Returns TRUE if the table confirms cancels, otherwise returns FALSE. +*/ + +bool TQDataManager::confirmCancels() const +{ + return d->confCancs; +} + +/*! \internal + + Virtual function which returns a confirmation for an edit of mode \a + m. Derived classes can reimplement this function and provide their + own confirmation dialog. The default implementation uses a message + box which prompts the user to confirm the edit action. The dialog + is centered over \a parent. + +*/ + +TQSql::Confirm TQDataManager::confirmEdit( TQWidget* parent, TQSql::Op m ) +{ + int ans = 2; + if ( m == TQSql::Delete ) { +#ifndef QT_NO_MESSAGEBOX + ans = TQMessageBox::information( parent, + qApp->translate( "TQSql", "Delete" ), + qApp->translate( "TQSql", "Delete this record?" ), + qApp->translate( "TQSql", "Yes" ), + qApp->translate( "TQSql", "No" ), + TQString::null, 0, 1 ); +#else + ans = TQSql::No; +#endif // QT_NO_MESSAGEBOX + } else if ( m != TQSql::None ) { + TQString caption; + if ( m == TQSql::Insert ) { + caption = qApp->translate( "TQSql", "Insert" ); + } else { // TQSql::Update + caption = qApp->translate( "TQSql", "Update" ); + } +#ifndef QT_NO_MESSAGEBOX + ans = TQMessageBox::information( parent, caption, + qApp->translate( "TQSql", "Save edits?" ), + qApp->translate( "TQSql", "Yes" ), + qApp->translate( "TQSql", "No" ), + qApp->translate( "TQSql", "Cancel" ), + 0, 2 ); +#else + ans = TQSql::No; +#endif // QT_NO_MESSAGEBOX + } + + switch ( ans ) { + case 0: + return TQSql::Yes; + case 1: + return TQSql::No; + default: + return TQSql::Cancel; + } +} + +/*! \internal + + Virtual function which returns a confirmation for cancelling an edit + mode \a m. Derived classes can reimplement this function and + provide their own confirmation dialog. The default implementation + uses a message box which prompts the user to confirm the edit + action. The dialog is centered over \a parent. + + +*/ + +TQSql::Confirm TQDataManager::confirmCancel( TQWidget* parent, TQSql::Op ) +{ +#ifndef QT_NO_MESSAGEBOX + switch ( TQMessageBox::information( parent, + qApp->translate( "TQSql", "Confirm" ), + qApp->translate( "TQSql", "Cancel your edits?" ), + qApp->translate( "TQSql", "Yes" ), + qApp->translate( "TQSql", "No" ), + TQString::null, 0, 1 ) ) { + case 0: + return TQSql::Yes; + case 1: + return TQSql::No; + default: + return TQSql::Cancel; + } +#else + return TQSql::Yes; +#endif // QT_NO_MESSAGEBOX +} + +#endif diff --git a/src/sql/qsqlmanager_p.h b/src/sql/qsqlmanager_p.h new file mode 100644 index 000000000..660e4dded --- /dev/null +++ b/src/sql/qsqlmanager_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Definition of TQSqlManager class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLMANAGER_P_H +#define TQSQLMANAGER_P_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. +// +// + +#ifndef QT_H +#include "qglobal.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qsql.h" +#include "qsqlerror.h" +#include "qsqlindex.h" +#include "qsqlcursor.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlCursor; +class TQSqlForm; +class TQSqlCursorManagerPrivate; + +class TQM_EXPORT_SQL TQSqlCursorManager +{ +public: + TQSqlCursorManager(); + virtual ~TQSqlCursorManager(); + + virtual void setSort( const TQSqlIndex& sort ); + virtual void setSort( const TQStringList& sort ); + TQStringList sort() const; + virtual void setFilter( const TQString& filter ); + TQString filter() const; + virtual void setCursor( TQSqlCursor* cursor, bool autoDelete = FALSE ); + TQSqlCursor* cursor() const; + + virtual void setAutoDelete( bool enable ); + bool autoDelete() const; + + virtual bool refresh(); + virtual bool findBuffer( const TQSqlIndex& idx, int atHint = 0 ); + +private: + TQSqlCursorManagerPrivate* d; +}; + +#ifndef QT_NO_SQL_FORM + +class TQSqlFormManagerPrivate; + +class TQM_EXPORT_SQL TQSqlFormManager +{ +public: + TQSqlFormManager(); + virtual ~TQSqlFormManager(); + + virtual void setForm( TQSqlForm* form ); + TQSqlForm* form(); + virtual void setRecord( TQSqlRecord* record ); + TQSqlRecord* record(); + + virtual void clearValues(); + virtual void readFields(); + virtual void writeFields(); + +private: + TQSqlFormManagerPrivate* d; +}; + +#endif + +class TQWidget; +class TQDataManagerPrivate; + +class TQM_EXPORT_SQL TQDataManager +{ +public: + TQDataManager(); + virtual ~TQDataManager(); + + virtual void setMode( TQSql::Op m ); + TQSql::Op mode() const; + virtual void setAutoEdit( bool autoEdit ); + bool autoEdit() const; + + virtual void handleError( TQWidget* parent, const TQSqlError& error ); + virtual TQSql::Confirm confirmEdit( TQWidget* parent, TQSql::Op m ); + virtual TQSql::Confirm confirmCancel( TQWidget* parent, TQSql::Op m ); + + virtual void setConfirmEdits( bool confirm ); + virtual void setConfirmInsert( bool confirm ); + virtual void setConfirmUpdate( bool confirm ); + virtual void setConfirmDelete( bool confirm ); + virtual void setConfirmCancels( bool confirm ); + + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + +private: + TQDataManagerPrivate* d; +}; + + +#endif +#endif diff --git a/src/sql/qsqlpropertymap.cpp b/src/sql/qsqlpropertymap.cpp new file mode 100644 index 000000000..55699e569 --- /dev/null +++ b/src/sql/qsqlpropertymap.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Definition of TQSqlPropertyMap class +** +** Created : 2000-11-20 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlpropertymap.h" + +#ifndef QT_NO_SQL_FORM + +#include "qwidget.h" +#include "qcleanuphandler.h" +#include "qmetaobject.h" +#include "qmap.h" + +class TQSqlPropertyMapPrivate +{ +public: + TQSqlPropertyMapPrivate() {} + TQMap< TQString, TQString > propertyMap; +}; + +/*! + \class TQSqlPropertyMap qsqlpropertymap.h + \brief The TQSqlPropertyMap class is used to map widgets to SQL fields. + + \ingroup database + \module sql + + The SQL module uses TQt \link properties.html object + properties\endlink to insert and extract values from editor + widgets. + + This class is used to map editors to SQL fields. This works by + associating SQL editor class names to the properties used to + insert and extract values to/from the editor. + + For example, a TQLineEdit can be used to edit text strings and + other data types in TQDataTables or TQSqlForms. Several properties + are defined in TQLineEdit, but only the \e text property is used to + insert and extract text from a TQLineEdit. Both TQDataTable and + TQSqlForm use the global TQSqlPropertyMap for inserting and + extracting values to and from an editor widget. The global + property map defines several common widgets and properties that + are suitable for many applications. You can add and remove widget + properties to suit your specific needs. + + If you want to use custom editors with your TQDataTable or + TQSqlForm, you must install your own TQSqlPropertyMap for that table + or form. Example: + + \code + TQSqlPropertyMap *myMap = new TQSqlPropertyMap(); + TQSqlForm *myForm = new TQSqlForm( this ); + MyEditor myEditor( this ); + + // Set the TQSqlForm's record buffer to the update buffer of + // a pre-existing TQSqlCursor called 'cur'. + myForm->setRecord( cur->primeUpdate() ); + + // Install the customized map + myMap->insert( "MyEditor", "content" ); + myForm->installPropertyMap( myMap ); // myForm now owns myMap + ... + // Insert a field into the form that uses a myEditor to edit the + // field 'somefield' + myForm->insert( &myEditor, "somefield" ); + + // Update myEditor with the value from the mapped database field + myForm->readFields(); + ... + // Let the user edit the form + ... + // Update the database fields with the values in the form + myForm->writeFields(); + ... + \endcode + + You can also replace the global TQSqlPropertyMap that is used by + default. (Bear in mind that TQSqlPropertyMap takes ownership of the + new default map.) + + \code + TQSqlPropertyMap *myMap = new TQSqlPropertyMap; + + myMap->insert( "MyEditor", "content" ); + TQSqlPropertyMap::installDefaultMap( myMap ); + ... + \endcode + + \sa TQDataTable, TQSqlForm, TQSqlEditorFactory +*/ + +/*! + +Constructs a TQSqlPropertyMap. + +The default property mappings used by TQt widgets are: +\table +\header \i Widgets \i Property +\row \i \l TQCheckBox, + \l TQRadioButton + \i checked +\row \i \l TQComboBox, + \l TQListBox + \i currentItem +\row \i \l TQDateEdit + \i date +\row \i \l TQDateTimeEdit + \i dateTime +\row \i \l TQTextBrowser + \i source +\row \i \l TQButton, + \l TQDial, + \l TQLabel, + \l TQLineEdit, + \l TQMultiLineEdit, + \l TQPushButton, + \l TQTextEdit, + \i text +\row \i \l TQTimeEdit + \i time +\row \i \l TQLCDNumber, + \l TQScrollBar + \l TQSlider, + \l TQSpinBox + \i value +\endtable +*/ + +TQSqlPropertyMap::TQSqlPropertyMap() +{ + d = new TQSqlPropertyMapPrivate(); + const struct MapData { + const char *classname; + const char *property; + } mapData[] = { + { "TQButton", "text" }, + { "TQCheckBox", "checked" }, + { "TQRadioButton", "checked" }, + { "TQComboBox", "currentItem" }, + { "TQDateEdit", "date" }, + { "TQDateTimeEdit", "dateTime" }, + { "TQDial", "value" }, + { "TQLabel", "text" }, + { "TQLCDNumber", "value" }, + { "TQLineEdit", "text" }, + { "TQListBox", "currentItem" }, + { "TQMultiLineEdit", "text" }, + { "TQPushButton", "text" }, + { "TQScrollBar", "value" }, + { "TQSlider", "value" }, + { "TQSpinBox", "value" }, + { "TQTextBrowser", "source" }, + { "TQTextEdit", "text" }, + { "TQTextView", "text" }, + { "TQTimeEdit", "time" } + }; + + const MapData *m = mapData; + for ( uint i = 0; i < sizeof(mapData)/sizeof(MapData); i++, m++ ) + d->propertyMap.insert( m->classname, m->property ); +} + +/*! + Destroys the TQSqlPropertyMap. + + Note that if the TQSqlPropertyMap is installed with + installPropertyMap() the object it was installed into, e.g. the + TQSqlForm, takes ownership and will delete the TQSqlPropertyMap when + necessary. +*/ +TQSqlPropertyMap::~TQSqlPropertyMap() +{ + delete d; +} + +/*! + Returns the mapped property of \a widget as a TQVariant. +*/ +TQVariant TQSqlPropertyMap::property( TQWidget * widget ) +{ + if( !widget ) return TQVariant(); + const TQMetaObject* mo = widget->metaObject(); + while ( mo && !d->propertyMap.contains( TQString( mo->className() ) ) ) + mo = mo->superClass(); + + if ( !mo ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlPropertyMap::property: %s does not exist", widget->metaObject()->className() ); +#endif + return TQVariant(); + } + return widget->property( d->propertyMap[ mo->className() ] ); +} + +/*! + Sets the property of \a widget to \a value. +*/ +void TQSqlPropertyMap::setProperty( TQWidget * widget, const TQVariant & value ) +{ + if( !widget ) return; + + TQMetaObject* mo = widget->metaObject(); + while ( mo && !d->propertyMap.contains( TQString( mo->className() ) ) ) + mo = mo->superClass(); + if ( !mo ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlPropertyMap::setProperty: %s not handled by TQSqlPropertyMap", widget->metaObject()->className() ); +#endif + return; + } + + widget->setProperty( d->propertyMap[ mo->className() ], value ); +} + +/*! + Insert a new classname/property pair, which is used for custom SQL + field editors. There \e must be a \c Q_PROPERTY clause in the \a + classname class declaration for the \a property. +*/ +void TQSqlPropertyMap::insert( const TQString & classname, + const TQString & property ) +{ + d->propertyMap[ classname ] = property; +} + +/*! + Removes \a classname from the map. +*/ +void TQSqlPropertyMap::remove( const TQString & classname ) +{ + d->propertyMap.remove( classname ); +} + +static TQSqlPropertyMap * defaultmap = 0; +static TQCleanupHandler< TQSqlPropertyMap > qsql_cleanup_property_map; + +/*! + Returns the application global TQSqlPropertyMap. +*/ +TQSqlPropertyMap * TQSqlPropertyMap::defaultMap() +{ + if( defaultmap == 0 ){ + defaultmap = new TQSqlPropertyMap(); + qsql_cleanup_property_map.add( &defaultmap ); + } + return defaultmap; +} + +/*! + Replaces the global default property map with \a map. All + TQDataTable and TQSqlForm instantiations will use this new map for + inserting and extracting values to and from editors. + \e{TQSqlPropertyMap takes ownership of \a map, and destroys it + when it is no longer needed.} +*/ +void TQSqlPropertyMap::installDefaultMap( TQSqlPropertyMap * map ) +{ + if( map == 0 ) return; + + if( defaultmap != 0 ){ + qsql_cleanup_property_map.remove( &defaultmap ); + delete defaultmap; + } + defaultmap = map; + qsql_cleanup_property_map.add( &defaultmap ); +} + +#endif // QT_NO_SQL_FORM diff --git a/src/sql/qsqlpropertymap.h b/src/sql/qsqlpropertymap.h new file mode 100644 index 000000000..a326475d5 --- /dev/null +++ b/src/sql/qsqlpropertymap.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Definition of TQSqlPropertyMap class +** +** Created : 2000-11-20 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLPROPERTYMAP_H +#define TQSQLPROPERTYMAP_H + +#ifndef QT_H +#include "qvariant.h" +#include "qstring.h" +#endif // QT_H + +#ifndef QT_NO_SQL_FORM + +class TQWidget; +class TQSqlPropertyMapPrivate; + +class Q_EXPORT TQSqlPropertyMap { +public: + TQSqlPropertyMap(); + virtual ~TQSqlPropertyMap(); + + TQVariant property( TQWidget * widget ); + virtual void setProperty( TQWidget * widget, const TQVariant & value ); + + void insert( const TQString & classname, const TQString & property ); + void remove( const TQString & classname ); + + static TQSqlPropertyMap * defaultMap(); + static void installDefaultMap( TQSqlPropertyMap * map ); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSqlPropertyMap( const TQSqlPropertyMap & ); + TQSqlPropertyMap &operator=( const TQSqlPropertyMap & ); +#endif + TQSqlPropertyMapPrivate* d; + +}; + +#endif // QT_NO_SQL_FORM +#endif // TQSQLPROPERTYMAP_H diff --git a/src/sql/qsqlquery.cpp b/src/sql/qsqlquery.cpp new file mode 100644 index 000000000..c33eb8bf2 --- /dev/null +++ b/src/sql/qsqlquery.cpp @@ -0,0 +1,1215 @@ +/**************************************************************************** +** +** Implementation of TQSqlQuery class +** +** Created : 2000-11-03 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlquery.h" + +#ifndef QT_NO_SQL + +//#define QT_DEBUG_SQL + +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "qsqldatabase.h" +#include "qsql.h" +#include "qregexp.h" +#include "private/qsqlextension_p.h" + + +/*! +\internal +*/ +TQSqlResultShared::TQSqlResultShared( TQSqlResult* result ): sqlResult(result) +{ + if ( result ) + connect( result->driver(), SIGNAL(destroyed()), this, SLOT(slotResultDestroyed()) ); +} + +/*! +\internal +*/ +TQSqlResultShared::~TQSqlResultShared() +{ + delete sqlResult; +} + +/*! +\internal + +In case a plugin gets unloaded the pointer to the sqlResult gets invalid +*/ +void TQSqlResultShared::slotResultDestroyed() +{ + delete sqlResult; + sqlResult = 0; +} + +/*! + \class TQSqlQuery qsqlquery.h + \brief The TQSqlQuery class provides a means of executing and + manipulating SQL statements. + + \ingroup database + \mainclass + \module sql + + TQSqlQuery encapsulates the functionality involved in creating, + navigating and retrieving data from SQL queries which are executed + on a \l TQSqlDatabase. It can be used to execute DML (data + manipulation language) statements, e.g. \c SELECT, \c INSERT, \c + UPDATE and \c DELETE, and also DDL (data definition language) + statements, e.g. \c{CREATE TABLE}. It can also be used to + execute database-specific commands which are not standard SQL + (e.g. \c{SET DATESTYLE=ISO} for PostgreSQL). + + Successfully executed SQL statements set the query's state to + active (isActive() returns TRUE); otherwise the query's state is + set to inactive. In either case, when executing a new SQL + statement, the query is positioned on an invalid record; an active + query must be navigated to a valid record (so that isValid() + returns TRUE) before values can be retrieved. + + Navigating records is performed with the following functions: + + \list + \i \c next() + \i \c prev() + \i \c first() + \i \c last() + \i \c \link TQSqlQuery::seek() seek\endlink(int) + \endlist + + These functions allow the programmer to move forward, backward or + arbitrarily through the records returned by the query. If you only + need to move forward through the results, e.g. using next() or + using seek() with a positive offset, you can use setForwardOnly() + and save a significant amount of memory overhead. Once an active + query is positioned on a valid record, data can be retrieved using + value(). All data is transferred from the SQL backend using + TQVariants. + + For example: + + \code + TQSqlQuery query( "SELECT name FROM customer" ); + while ( query.next() ) { + TQString name = query.value(0).toString(); + doSomething( name ); + } + \endcode + + To access the data returned by a query, use the value() method. + Each field in the data returned by a SELECT statement is accessed + by passing the field's position in the statement, starting from 0. + Information about the fields can be obtained via TQSqlDatabase::record(). + For the sake of efficiency there are no functions to access a field + by name. (The \l TQSqlCursor class provides a higher-level interface + with field access by name and automatic SQL generation.) + + TQSqlQuery supports prepared query execution and the binding of + parameter values to placeholders. Some databases don't support + these features, so for them TQt emulates the retquired + functionality. For example, the Oracle and ODBC drivers have + proper prepared query support, and TQt makes use of it; but for + databases that don't have this support, TQt implements the feature + itself, e.g. by replacing placeholders with actual values when a + query is executed. The exception is positional binding using named + placeholders, which retquires that the database supports prepared + queries. + + Oracle databases identify placeholders by using a colon-name + syntax, e.g \c{:name}. ODBC simply uses \c ? characters. TQt + supports both syntaxes (although you can't mix them in the same + query). + + Below we present the same example using each of the four different + binding approaches. + + <b>Named binding using named placeholders</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (:id, :forename, :surname)" ); + query.bindValue( ":id", 1001 ); + query.bindValue( ":forename", "Bart" ); + query.bindValue( ":surname", "Simpson" ); + query.exec(); + \endcode + + <b>Positional binding using named placeholders</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (:id, :forename, :surname)" ); + query.bindValue( 0, 1001 ); + query.bindValue( 1, "Bart" ); + query.bindValue( 2, "Simpson" ); + query.exec(); + \endcode + <b>Note:</b> Using positional binding with named placeholders will + only work if the database supports prepared queries. This can be + checked with TQSqlDriver::hasFeature() using TQSqlDriver::PreparedQueries + as argument for driver feature. + + <b>Binding values using positional placeholders #1</b> + \code + TQSqlQuery query; + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (?, ?, ?)" ); + query.bindValue( 0, 1001 ); + query.bindValue( 1, "Bart" ); + query.bindValue( 2, "Simpson" ); + query.exec(); + \endcode + + <b>Binding values using positional placeholders #2</b> + \code + query.prepare( "INSERT INTO atable (id, forename, surname) " + "VALUES (?, ?, ?)" ); + query.addBindValue( 1001 ); + query.addBindValue( "Bart" ); + query.addBindValue( "Simpson" ); + query.exec(); + \endcode + + <b>Binding values to a stored procedure</b> + This code calls a stored procedure called \c AsciiToInt(), passing + it a character through its in parameter, and taking its result in + the out parameter. + \code + TQSqlQuery query; + query.prepare( "call AsciiToInt(?, ?)" ); + query.bindValue( 0, "A" ); + query.bindValue( 1, 0, TQSql::Out ); + query.exec(); + int i = query.boundValue( 1 ).toInt(); // i is 65. + \endcode + + \sa TQSqlDatabase TQSqlCursor TQVariant +*/ + +/*! + Creates a TQSqlQuery object which uses the TQSqlResult \a r to + communicate with a database. +*/ + +TQSqlQuery::TQSqlQuery( TQSqlResult * r ) +{ + d = new TQSqlResultShared( r ); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlQuery::~TQSqlQuery() +{ + if (d->deref()) { + delete d; + } +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlQuery::TQSqlQuery( const TQSqlQuery& other ) + : d(other.d) +{ + d->ref(); +} + +/*! + Creates a TQSqlQuery object using the SQL \a query and the database + \a db. If \a db is 0, (the default), the application's default + database is used. If \a query is not a null string, it will be + executed. + + \sa TQSqlDatabase +*/ +TQSqlQuery::TQSqlQuery( const TQString& query, TQSqlDatabase* db ) +{ + init( query, db ); +} + +/*! + Creates a TQSqlQuery object using the database \a db. If \a db is + 0, the application's default database is used. + + \sa TQSqlDatabase +*/ + +TQSqlQuery::TQSqlQuery( TQSqlDatabase* db ) +{ + init( TQString::null, db ); +} + +/*! \internal +*/ + +void TQSqlQuery::init( const TQString& query, TQSqlDatabase* db ) +{ + d = new TQSqlResultShared( 0 ); + TQSqlDatabase* database = db; + if ( !database ) + database = TQSqlDatabase::database( TQSqlDatabase::defaultConnection, FALSE ); + if ( database ) + *this = database->driver()->createQuery(); + if ( !query.isNull() ) + exec( query ); +} + +/*! + Assigns \a other to the query. +*/ + +TQSqlQuery& TQSqlQuery::operator=( const TQSqlQuery& other ) +{ + other.d->ref(); + deref(); + d = other.d; + return *this; +} + +/*! + Returns TRUE if the query is active and positioned on a valid + record and the \a field is NULL; otherwise returns FALSE. Note + that for some drivers isNull() will not return accurate + information until after an attempt is made to retrieve data. + + \sa isActive() isValid() value() +*/ + +bool TQSqlQuery::isNull( int field ) const +{ + if ( !d->sqlResult ) + return FALSE; + if ( d->sqlResult->isActive() && d->sqlResult->isValid() ) + return d->sqlResult->isNull( field ); + return FALSE; +} + +/*! + Executes the SQL in \a query. Returns TRUE and sets the query + state to active if the query was successful; otherwise returns + FALSE and sets the query state to inactive. The \a query string + must use syntax appropriate for the SQL database being queried, + for example, standard SQL. + + After the query is executed, the query is positioned on an \e + invalid record, and must be navigated to a valid record before + data values can be retrieved, e.g. using next(). + + Note that the last error for this query is reset when exec() is + called. + + \sa isActive() isValid() next() prev() first() last() seek() +*/ + +bool TQSqlQuery::exec ( const TQString& query ) +{ + if ( !d->sqlResult ) + return FALSE; + if ( d->sqlResult->extension() && driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) + d->sqlResult->extension()->clear(); + d->sqlResult->setActive( FALSE ); + d->sqlResult->setLastError( TQSqlError() ); + d->sqlResult->setAt( TQSql::BeforeFirst ); + if ( !driver() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: no driver" ); +#endif + return FALSE; + } + if ( d->count > 1 ) + *this = driver()->createQuery(); + d->sqlResult->setQuery( query.stripWhiteSpace() ); + d->executedQuery = d->sqlResult->lastQuery(); + if ( !driver()->isOpen() || driver()->isOpenError() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: database not open" ); +#endif + return FALSE; + } + if ( query.isNull() || query.length() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::exec: empty query" ); +#endif + return FALSE; + } +#ifdef QT_DEBUG_SQL + qDebug( "\n TQSqlQuery: " + query ); +#endif + return d->sqlResult->reset( query ); +} + +/*! + Returns the value of the \a{i}-th field in the query (zero based). + + The fields are numbered from left to right using the text of the + \c SELECT statement, e.g. in \c{SELECT forename, surname FROM people}, + field 0 is \c forename and field 1 is \c surname. Using \c{SELECT *} + is not recommended because the order of the fields in the query is + undefined. + + An invalid TQVariant is returned if field \a i does not exist, if + the query is inactive, or if the query is positioned on an invalid + record. + + \sa prev() next() first() last() seek() isActive() isValid() +*/ + +TQVariant TQSqlQuery::value( int i ) const +{ + if ( !d->sqlResult ) + return TQVariant(); + if ( isActive() && isValid() && ( i > TQSql::BeforeFirst ) ) { + return d->sqlResult->data( i ); + } else { +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlQuery::value: not positioned on a valid record" ); +#endif + } + return TQVariant(); +} + +/*! + Returns the current internal position of the query. The first + record is at position zero. If the position is invalid, a + TQSql::Location will be returned indicating the invalid position. + + \sa prev() next() first() last() seek() isActive() isValid() +*/ + +int TQSqlQuery::at() const +{ + if ( !d->sqlResult ) + return TQSql::BeforeFirst; + return d->sqlResult->at(); +} + +/*! + Returns the text of the current query being used, or TQString::null + if there is no current query text. + + \sa executedQuery() +*/ + +TQString TQSqlQuery::lastQuery() const +{ + if ( !d->sqlResult ) + return TQString::null; + return d->sqlResult->lastQuery(); +} + +/*! + Returns the database driver associated with the query. +*/ + +const TQSqlDriver* TQSqlQuery::driver() const +{ + if ( !d->sqlResult ) + return 0; + return d->sqlResult->driver(); +} + +/*! + Returns the result associated with the query. +*/ + +const TQSqlResult* TQSqlQuery::result() const +{ + return d->sqlResult; +} + +/*! + Retrieves the record at position (offset) \a i, if available, and + positions the query on the retrieved record. The first record is + at position 0. Note that the query must be in an active state and + isSelect() must return TRUE before calling this function. + + If \a relative is FALSE (the default), the following rules apply: + + \list + \i If \a i is negative, the result is positioned before the + first record and FALSE is returned. + \i Otherwise, an attempt is made to move to the record at position + \a i. If the record at position \a i could not be retrieved, the + result is positioned after the last record and FALSE is returned. If + the record is successfully retrieved, TRUE is returned. + \endlist + + If \a relative is TRUE, the following rules apply: + + \list + \i If the result is currently positioned before the first + record or on the first record, and \a i is negative, there is no + change, and FALSE is returned. + \i If the result is currently located after the last record, and + \a i is positive, there is no change, and FALSE is returned. + \i If the result is currently located somewhere in the middle, + and the relative offset \a i moves the result below zero, the + result is positioned before the first record and FALSE is + returned. + \i Otherwise, an attempt is made to move to the record \a i + records ahead of the current record (or \a i records behind the + current record if \a i is negative). If the record at offset \a i + could not be retrieved, the result is positioned after the last + record if \a i >= 0, (or before the first record if \a i is + negative), and FALSE is returned. If the record is successfully + retrieved, TRUE is returned. + \endlist + + \sa next() prev() first() last() at() isActive() isValid() +*/ +bool TQSqlQuery::seek( int i, bool relative ) +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + int actualIdx; + if ( !relative ) { // arbitrary seek + if ( i < 0 ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + actualIdx = i; + } else { + switch ( at() ) { // relative seek + case TQSql::BeforeFirst: + if ( i > 0 ) + actualIdx = i; + else { + afterSeek(); + return FALSE; + } + break; + case TQSql::AfterLast: + if ( i < 0 ) { + d->sqlResult->fetchLast(); + actualIdx = at() + i; + } else { + afterSeek(); + return FALSE; + } + break; + default: + if ( ( at() + i ) < 0 ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + actualIdx = at() + i; + break; + } + } + // let drivers optimize + if ( isForwardOnly() && actualIdx < at() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + afterSeek(); + return FALSE; + } + if ( actualIdx == ( at() + 1 ) && at() != TQSql::BeforeFirst ) { + if ( !d->sqlResult->fetchNext() ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } + if ( actualIdx == ( at() - 1 ) ) { + if ( !d->sqlResult->fetchPrev() ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } + if ( !d->sqlResult->fetch( actualIdx ) ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; +} + +/*! + Retrieves the next record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + + The following rules apply: + + \list + \i If the result is currently located before the first + record, e.g. immediately after a query is executed, an attempt is + made to retrieve the first record. + + \i If the result is currently located after the last record, + there is no change and FALSE is returned. + + \i If the result is located somewhere in the middle, an attempt + is made to retrieve the next record. + \endlist + + If the record could not be retrieved, the result is positioned after + the last record and FALSE is returned. If the record is successfully + retrieved, TRUE is returned. + + \sa prev() first() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::next() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + bool b = FALSE; + switch ( at() ) { + case TQSql::BeforeFirst: + b = d->sqlResult->fetchFirst(); + afterSeek(); + return b; + case TQSql::AfterLast: + afterSeek(); + return FALSE; + default: + if ( !d->sqlResult->fetchNext() ) { + d->sqlResult->setAt( TQSql::AfterLast ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } +} + +/*! + Retrieves the previous record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + + The following rules apply: + + \list + \i If the result is currently located before the first record, + there is no change and FALSE is returned. + + \i If the result is currently located after the last record, an + attempt is made to retrieve the last record. + + \i If the result is somewhere in the middle, an attempt is made + to retrieve the previous record. + \endlist + + If the record could not be retrieved, the result is positioned + before the first record and FALSE is returned. If the record is + successfully retrieved, TRUE is returned. + + \sa next() first() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::prev() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + if ( isForwardOnly() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + return FALSE; + } + + beforeSeek(); + checkDetach(); + bool b = FALSE; + switch ( at() ) { + case TQSql::BeforeFirst: + afterSeek(); + return FALSE; + case TQSql::AfterLast: + b = d->sqlResult->fetchLast(); + afterSeek(); + return b; + default: + if ( !d->sqlResult->fetchPrev() ) { + d->sqlResult->setAt( TQSql::BeforeFirst ); + afterSeek(); + return FALSE; + } + afterSeek(); + return TRUE; + } +} + +/*! + Retrieves the first record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + Returns TRUE if successful. If unsuccessful the query position is + set to an invalid position and FALSE is returned. + + \sa next() prev() last() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::first() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + if ( isForwardOnly() && at() > TQSql::BeforeFirst ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::seek: cannot seek backwards in a forward only query" ); +#endif + return FALSE; + } + beforeSeek(); + checkDetach(); + bool b = FALSE; + b = d->sqlResult->fetchFirst(); + afterSeek(); + return b; +} + +/*! + Retrieves the last record in the result, if available, and + positions the query on the retrieved record. Note that the result + must be in an active state and isSelect() must return TRUE before + calling this function or it will do nothing and return FALSE. + Returns TRUE if successful. If unsuccessful the query position is + set to an invalid position and FALSE is returned. + + \sa next() prev() first() seek() at() isActive() isValid() +*/ + +bool TQSqlQuery::last() +{ + if ( !isSelect() || !isActive() ) + return FALSE; + beforeSeek(); + checkDetach(); + bool b = FALSE; + b = d->sqlResult->fetchLast(); + afterSeek(); + return b; +} + +/*! + Returns the size of the result, (number of rows returned), or -1 + if the size cannot be determined or if the database does not + support reporting information about query sizes. Note that for + non-\c SELECT statements (isSelect() returns FALSE), size() will + return -1. If the query is not active (isActive() returns FALSE), + -1 is returned. + + To determine the number of rows affected by a non-SELECT + statement, use numRowsAffected(). + + \sa isActive() numRowsAffected() TQSqlDriver::hasFeature() +*/ +int TQSqlQuery::size() const +{ + if ( !d->sqlResult ) + return -1; + if ( isActive() && d->sqlResult->driver()->hasFeature( TQSqlDriver::QuerySize ) ) + return d->sqlResult->size(); + return -1; +} + +/*! + Returns the number of rows affected by the result's SQL statement, + or -1 if it cannot be determined. Note that for \c SELECT + statements, the value is undefined; see size() instead. If the + query is not active (isActive() returns FALSE), -1 is returned. + + \sa size() TQSqlDriver::hasFeature() +*/ + +int TQSqlQuery::numRowsAffected() const +{ + if ( !d->sqlResult ) + return -1; + if ( isActive() ) + return d->sqlResult->numRowsAffected(); + return -1; +} + +/*! + Returns error information about the last error (if any) that + occurred. + + \sa TQSqlError +*/ + +TQSqlError TQSqlQuery::lastError() const +{ + if ( !d->sqlResult ) + return TQSqlError(); + return d->sqlResult->lastError(); +} + +/*! + Returns TRUE if the query is currently positioned on a valid + record; otherwise returns FALSE. +*/ + +bool TQSqlQuery::isValid() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isValid(); +} + +/*! + Returns TRUE if the query is currently active; otherwise returns + FALSE. +*/ + +bool TQSqlQuery::isActive() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isActive(); +} + +/*! + Returns TRUE if the current query is a \c SELECT statement; + otherwise returns FALSE. +*/ + +bool TQSqlQuery::isSelect() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isSelect(); +} + +/*! + Returns TRUE if you can only scroll \e forward through a result + set; otherwise returns FALSE. + + \sa setForwardOnly() +*/ +bool TQSqlQuery::isForwardOnly() const +{ + if ( !d->sqlResult ) + return FALSE; + return d->sqlResult->isForwardOnly(); +} + +/*! + Sets forward only mode to \a forward. If forward is TRUE only + next(), and seek() with positive values, are allowed for + navigating the results. Forward only mode needs far less memory + since results do not need to be cached. + + Forward only mode is off by default. + + Forward only mode cannot be used with data aware widgets like + TQDataTable, since they must to be able to scroll backward as well + as forward. + + \sa isForwardOnly(), next(), seek() +*/ +void TQSqlQuery::setForwardOnly( bool forward ) +{ + if ( d->sqlResult ) + d->sqlResult->setForwardOnly( forward ); +} + +/*! + \internal +*/ + +void TQSqlQuery::deref() +{ + if ( d->deref() ) { + delete d; + d = 0; + } +} + +/*! + \internal +*/ + +bool TQSqlQuery::checkDetach() +{ + if ( d->count > 1 && d->sqlResult ) { + TQString sql = d->sqlResult->lastQuery(); + *this = driver()->createQuery(); + exec( sql ); + return TRUE; + } + return FALSE; +} + + +/*! + Protected virtual function called before the internal record + pointer is moved to a new record. The default implementation does + nothing. +*/ + +void TQSqlQuery::beforeSeek() +{ + +} + + +/*! + Protected virtual function called after the internal record + pointer is moved to a new record. The default implementation does + nothing. +*/ + +void TQSqlQuery::afterSeek() +{ + +} + +// XXX: Hack to keep BCI - remove in 4.0. TQSqlExtension should be +// removed, and the prepare(), exec() etc. fu's should be +// made virtual members of TQSqlQuery/TQSqlResult + +/*! + Prepares the SQL query \a query for execution. The query may + contain placeholders for binding values. Both Oracle style + colon-name (e.g. \c{:surname}), and ODBC style (e.g. \c{?}) + placeholders are supported; but they cannot be mixed in the same + query. See the \link #details Description\endlink for examples. + + \sa exec(), bindValue(), addBindValue() +*/ +bool TQSqlQuery::prepare( const TQString& query ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return FALSE; + d->sqlResult->setActive( FALSE ); + d->sqlResult->setLastError( TQSqlError() ); + d->sqlResult->setAt( TQSql::BeforeFirst ); + d->sqlResult->extension()->clear(); + if ( !driver() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: no driver" ); +#endif + return FALSE; + } + if ( d->count > 1 ) + *this = driver()->createQuery(); + d->sqlResult->setQuery( query.stripWhiteSpace() ); + if ( !driver()->isOpen() || driver()->isOpenError() ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: database not open" ); +#endif + return FALSE; + } + if ( query.isNull() || query.length() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("TQSqlQuery::prepare: empty query" ); +#endif + return FALSE; + } +#ifdef QT_DEBUG_SQL + qDebug( "\n TQSqlQuery: " + query ); +#endif + TQString q = query; + TQRegExp rx(TQString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)")); + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + // below we substitute Oracle placeholders with ODBC ones and + // vice versa to make this db independent + int i = 0, cnt = 0; + if ( driver()->hasFeature( TQSqlDriver::NamedPlaceholders ) ) { + TQRegExp rx(TQString::fromLatin1("'[^']*'|\\?")); + while ( (i = rx.search( q, i )) != -1 ) { + if ( rx.cap(0) == "?" ) { + q = q.replace( i, 1, ":f" + TQString::number(cnt) ); + cnt++; + } + i += rx.matchedLength(); + } + } else if ( driver()->hasFeature( TQSqlDriver::PositionalPlaceholders ) ) { + while ( (i = rx.search( q, i )) != -1 ) { + if ( rx.cap(1).isEmpty() ) { + i += rx.matchedLength(); + } else { + // record the index of the placeholder - needed + // for emulating named bindings with ODBC + d->sqlResult->extension()->index[ cnt ]= rx.cap(0); + q = q.replace( i, rx.matchedLength(), "?" ); + i++; + cnt++; + } + } + } + d->executedQuery = q; + return d->sqlResult->extension()->prepare( q ); + } else { + int i = 0; + while ( (i = rx.search( q, i )) != -1 ) { + if ( !rx.cap(1).isEmpty() ) + d->sqlResult->extension()->holders.append( Holder( rx.cap(0), i ) ); + i += rx.matchedLength(); + } + return TRUE; // fake prepares should always succeed + } +} + +/*! + \overload + + Executes a previously prepared SQL query. Returns TRUE if the + query executed successfully; otherwise returns FALSE. + + \sa prepare(), bindValue(), addBindValue() +*/ +bool TQSqlQuery::exec() +{ + bool ret; + if ( !d->sqlResult || !d->sqlResult->extension() ) + return FALSE; + if ( driver()->hasFeature( TQSqlDriver::PreparedQueries ) ) { + ret = d->sqlResult->extension()->exec(); + } else { + // fake preparation - just replace the placeholders.. + TQString query = d->sqlResult->lastQuery(); + if ( d->sqlResult->extension()->bindMethod() == TQSqlExtension::BindByName ) { + int i; + TQVariant val; + TQString holder; + for ( i = (int)d->sqlResult->extension()->holders.count() - 1; i >= 0; --i ) { + holder = d->sqlResult->extension()->holders[ (uint)i ].holderName; + val = d->sqlResult->extension()->values[ holder ].value; + TQSqlField f( "", val.type() ); + if ( val.isNull() ) + f.setNull(); + else + f.setValue( val ); + query = query.replace( (uint)d->sqlResult->extension()->holders[ (uint)i ].holderPos, + holder.length(), driver()->formatValue( &f ) ); + } + } else { + TQMap<int, TQString>::ConstIterator it; + TQString val; + int i = 0; + for ( it = d->sqlResult->extension()->index.begin(); + it != d->sqlResult->extension()->index.end(); ++it ) { + i = query.find( '?', i ); + if ( i > -1 ) { + TQSqlField f( "", d->sqlResult->extension()->values[ it.data() ].value.type() ); + if ( d->sqlResult->extension()->values[ it.data() ].value.isNull() ) + f.setNull(); + else + f.setValue( d->sqlResult->extension()->values[ it.data() ].value ); + val = driver()->formatValue( &f ); + query = query.replace( i, 1, driver()->formatValue( &f ) ); + i += val.length(); + } + } + } + // have to retain the original query w/placeholders.. + TQString orig = d->sqlResult->lastQuery(); + ret = exec( query ); + d->executedQuery = query; + d->sqlResult->setQuery( orig ); + } + d->sqlResult->extension()->resetBindCount(); + return ret; +} + +/*! + Set the placeholder \a placeholder to be bound to value \a val in + the prepared statement. Note that the placeholder mark (e.g \c{:}) + must be included when specifying the placeholder name. If \a type + is \c TQSql::Out or \c TQSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + \sa addBindValue(), prepare(), exec() +*/ +void TQSqlQuery::bindValue( const TQString& placeholder, const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->bindValue( placeholder, val, type ); +} + +/*! + \overload + + Set the placeholder in position \a pos to be bound to value \a val + in the prepared statement. Field numbering starts at 0. If \a type + is \c TQSql::Out or \c TQSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + \sa addBindValue(), prepare(), exec() +*/ +void TQSqlQuery::bindValue( int pos, const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->bindValue( pos, val, type ); +} + +/*! + Adds the value \a val to the list of values when using positional + value binding. The order of the addBindValue() calls determines + which placeholder a value will be bound to in the prepared query. + If \a type is \c TQSql::Out or \c TQSql::InOut, the placeholder will + be overwritten with data from the database after the exec() call. + + \sa bindValue(), prepare(), exec() +*/ +void TQSqlQuery::addBindValue( const TQVariant& val, TQSql::ParameterType type ) +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return; + d->sqlResult->extension()->addBindValue( val, type ); +} + + +/*! + \overload + + Binds the placeholder with type \c TQSql::In. +*/ +void TQSqlQuery::bindValue( const TQString& placeholder, const TQVariant& val ) +{ + bindValue( placeholder, val, TQSql::In ); +} + +/*! + \overload + + Binds the placeholder at position \a pos with type \c TQSql::In. +*/ +void TQSqlQuery::bindValue( int pos, const TQVariant& val ) +{ + bindValue( pos, val, TQSql::In ); +} + +/*! + \overload + + Binds the placeholder with type \c TQSql::In. +*/ +void TQSqlQuery::addBindValue( const TQVariant& val ) +{ + addBindValue( val, TQSql::In ); +} + +/*! + Returns the value for the \a placeholder. +*/ +TQVariant TQSqlQuery::boundValue( const TQString& placeholder ) const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQVariant(); + return d->sqlResult->extension()->boundValue( placeholder ); +} + +/*! + \overload + + Returns the value for the placeholder at position \a pos. +*/ +TQVariant TQSqlQuery::boundValue( int pos ) const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQVariant(); + return d->sqlResult->extension()->boundValue( pos ); +} + +/*! + Returns a map of the bound values. + + The bound values can be examined in the following way: + \code + TQSqlQuery query; + ... + // Examine the bound values - bound using named binding + TQMap<TQString, TQVariant>::ConstIterator it; + TQMap<TQString, TQVariant> vals = query.boundValues(); + for ( it = vals.begin(); it != vals.end(); ++it ) + qWarning( "Placeholder: " + it.key() + ", Value: " + (*it).toString() ); + ... + + // Examine the bound values - bound using positional binding + TQValueList<TQVariant>::ConstIterator it; + TQValueList<TQVariant> list = query.boundValues().values(); + int i = 0; + for ( it = list.begin(); it != list.end(); ++it ) + qWarning( "Placeholder pos: %d, Value: " + (*it).toString(), i++ ); + ... + + \endcode +*/ +TQMap<TQString,TQVariant> TQSqlQuery::boundValues() const +{ + if ( !d->sqlResult || !d->sqlResult->extension() ) + return TQMap<TQString,TQVariant>(); + return d->sqlResult->extension()->boundValues(); +} + +/*! + Returns the last query that was executed. + + In most cases this function returns the same as lastQuery(). If a + prepared query with placeholders is executed on a DBMS that does + not support it, the preparation of this query is emulated. The + placeholders in the original query are replaced with their bound + values to form a new query. This function returns the modified + query. Useful for debugging purposes. + + \sa lastQuery() +*/ +TQString TQSqlQuery::executedQuery() const +{ + return d->executedQuery; +} +#endif // QT_NO_SQL diff --git a/src/sql/qsqlquery.h b/src/sql/qsqlquery.h new file mode 100644 index 000000000..bc4efa7d9 --- /dev/null +++ b/src/sql/qsqlquery.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Definition of TQSqlQuery class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLTQUERY_H +#define TQSQLTQUERY_H + +#ifndef QT_H +#include "qobject.h" +#include "qstring.h" +#include "qvariant.h" +#include "qvaluelist.h" +#include "qsqlerror.h" +#include "qsqlfield.h" +#include "qsql.h" +#endif // QT_H + +#ifndef QT_NO_SQL + +class TQSqlDriver; +class TQSqlResult; +class TQSqlDatabase; + +class Q_EXPORT TQSqlResultShared : public TQObject, public TQShared +{ + Q_OBJECT +public: + TQSqlResultShared( TQSqlResult* result ); + virtual ~TQSqlResultShared(); + TQSqlResult* sqlResult; + TQString executedQuery; +private slots: + void slotResultDestroyed(); +}; + +class Q_EXPORT TQSqlQuery +{ +public: + TQSqlQuery( TQSqlResult * r ); + TQSqlQuery( const TQString& query = TQString::null, TQSqlDatabase* db = 0 ); + Q_EXPLICIT TQSqlQuery( TQSqlDatabase* db ); + TQSqlQuery( const TQSqlQuery& other ); + TQSqlQuery& operator=( const TQSqlQuery& other ); + virtual ~TQSqlQuery(); + + bool isValid() const; + bool isActive() const; + bool isNull( int field ) const; + int at() const; + TQString lastQuery() const; + int numRowsAffected() const; + TQSqlError lastError() const; + bool isSelect() const; + int size() const; + const TQSqlDriver* driver() const; + const TQSqlResult* result() const; + bool isForwardOnly() const; + void setForwardOnly( bool forward ); + + virtual bool exec ( const TQString& query ); + virtual TQVariant value( int i ) const; + + virtual bool seek( int i, bool relative = FALSE ); + virtual bool next(); + virtual bool prev(); + virtual bool first(); + virtual bool last(); + + // prepared query support + bool exec(); + bool prepare( const TQString& query ); + void bindValue( const TQString& placeholder, const TQVariant& val ); + void bindValue( int pos, const TQVariant& val ); + void addBindValue( const TQVariant& val ); + // remove these overloads in 4.0 + void bindValue( const TQString& placeholder, const TQVariant& val, TQSql::ParameterType type ); + void bindValue( int pos, const TQVariant& val, TQSql::ParameterType type ); + void addBindValue( const TQVariant& val, TQSql::ParameterType type ); + TQVariant boundValue( const TQString& placeholder ) const; + TQVariant boundValue( int pos ) const; + TQMap<TQString, TQVariant> boundValues() const; + TQString executedQuery() const; + +protected: + virtual void beforeSeek(); + virtual void afterSeek(); + +private: + void init( const TQString& query, TQSqlDatabase* db ); + void deref(); + bool checkDetach(); + TQSqlResultShared* d; +}; + + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlrecord.cpp b/src/sql/qsqlrecord.cpp new file mode 100644 index 000000000..3bbe2d045 --- /dev/null +++ b/src/sql/qsqlrecord.cpp @@ -0,0 +1,774 @@ +/**************************************************************************** +** +** Implementation of TQSqlRecord class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlrecord.h" + +#ifndef QT_NO_SQL + +#include "qregexp.h" +#include "qvaluevector.h" +#include "qshared.h" +#include "qnamespace.h" + +class TQSqlRecordPrivate +{ +public: + class info { + public: + info() : nogen(FALSE){} + ~info() {} + info( const info& other ) + : field( other.field ), nogen( other.nogen ) + { + } + info& operator=(const info& other) + { + field = other.field; + nogen = other.nogen; + return *this; + } + bool isValid() const + { + return !field.name().isNull(); + } + Q_DUMMY_COMPARISON_OPERATOR(info) + TQSqlField field; + bool nogen; + }; + + TQSqlRecordPrivate(): cnt(0) + { + } + TQSqlRecordPrivate( const TQSqlRecordPrivate& other ) + { + *this = other; + } + ~TQSqlRecordPrivate() {}; + TQSqlRecordPrivate& operator=( const TQSqlRecordPrivate& other ) + { + fi = other.fi; + cnt = other.cnt; + return *this; + } + void append( const TQSqlField& field ) + { + info i; + i.field = field; + fi.append( i ); + cnt++; + } + void insert( int pos, const TQSqlField& field ) + { + info i; + i.field = field; + if ( pos == (int)fi.size() ) + append( field ); + if ( pos > (int)fi.size() ) { + fi.resize( pos + 1 ); + cnt++; + } + fi[ pos ] = i; + } + void remove( int i ) + { + info inf; + if ( i >= (int)fi.count() ) + return; + if ( fi[ i ].isValid() ) + cnt--; + fi[ i ] = inf; + // clean up some memory + while ( fi.count() && !fi.back().isValid() ) + fi.pop_back(); + } + void clear() + { + fi.clear(); + cnt = 0; + } + bool isEmpty() + { + return cnt == 0; + } + info* fieldInfo( int i ) + { + if ( i < (int)fi.count() ) + return &fi[i]; + return 0; + } + uint count() const + { + return cnt; + } + bool contains( int i ) const + { + return i >= 0 && i < (int)fi.count() && fi[ i ].isValid(); + } +private: + TQValueVector< info > fi; + uint cnt; +}; + +TQSqlRecordShared::~TQSqlRecordShared() +{ + if ( d ) + delete d; +} + +/*! + \class TQSqlRecord qsqlfield.h + \brief The TQSqlRecord class encapsulates a database record, i.e. a + set of database fields. + + \ingroup database + \module sql + + The TQSqlRecord class encapsulates the functionality and + characteristics of a database record (usually a table or view within + the database). TQSqlRecords support adding and removing fields as + well as setting and retrieving field values. + + TQSqlRecord is implicitly shared. This means you can make copies of + the record in time O(1). If multiple TQSqlRecord instances share + the same data and one is modifying the record's data then this + modifying instance makes a copy and modifies its private copy - + thus it does not affect other instances. + + \sa TQSqlRecordInfo +*/ + + +/*! + Constructs an empty record. +*/ + +TQSqlRecord::TQSqlRecord() +{ + sh = new TQSqlRecordShared( new TQSqlRecordPrivate() ); +} + +/*! + Constructs a copy of \a other. +*/ + +TQSqlRecord::TQSqlRecord( const TQSqlRecord& other ) + : sh( other.sh ) +{ + sh->ref(); +} + +/*! + Sets the record equal to \a other. +*/ + +TQSqlRecord& TQSqlRecord::operator=( const TQSqlRecord& other ) +{ + other.sh->ref(); + deref(); + sh = other.sh; + return *this; +} + +/*! \internal +*/ + +void TQSqlRecord::deref() +{ + if ( sh->deref() ) { + delete sh; + sh = 0; + } +} + +/*! \internal +*/ + +bool TQSqlRecord::checkDetach() +{ + if ( sh->count > 1 ) { + sh->deref(); + sh = new TQSqlRecordShared( new TQSqlRecordPrivate( *sh->d ) ); + return TRUE; + } + return FALSE; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlRecord::~TQSqlRecord() +{ + deref(); +} + +/*! + Returns the value of the field located at position \a i in the + record. If field \a i does not exist the resultant behaviour is + undefined. + + This function should be used with \l{TQSqlQuery}s. When working + with a TQSqlCursor the \link TQSqlCursor::value() value(const + TQString&)\endlink overload which uses field names is more + appropriate. +*/ + +TQVariant TQSqlRecord::value( int i ) const +{ + const TQSqlField * f = field(i); + + if( f ) + return f->value(); + return TQVariant(); +} + +/*! + \overload + + Returns the value of the field called \a name in the record. If + field \a name does not exist the resultant behaviour is undefined. +*/ + +TQVariant TQSqlRecord::value( const TQString& name ) const +{ + const TQSqlField * f = field( name ); + + if( f ) + return f->value(); + return TQVariant(); +} + +/*! + Returns the name of the field at position \a i. If the field does + not exist, TQString::null is returned. +*/ + +TQString TQSqlRecord::fieldName( int i ) const +{ + const TQSqlField* f = field( i ); + if ( f ) + return f->name(); + return TQString::null; +} + +/*! + Returns the position of the field called \a name within the + record, or -1 if it cannot be found. Field names are not + case-sensitive. If more than one field matches, the first one is + returned. +*/ + +int TQSqlRecord::position( const TQString& name ) const +{ + for ( uint i = 0; i < count(); ++i ) { + if ( fieldName(i).upper() == name.upper() ) + return i; + } +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlRecord::position: unable to find field %s", name.latin1() ); +#endif + return -1; +} + +/*! + Returns the field at position \a i within the record, or 0 if it + cannot be found. +*/ + +TQSqlField* TQSqlRecord::field( int i ) +{ + checkDetach(); + if ( !sh->d->contains( i ) ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlRecord::field: index out of range: %d", i ); +#endif + return 0; + } + return &sh->d->fieldInfo( i )->field; +} + +/*! + \overload + + Returns the field called \a name within the record, or 0 if it + cannot be found. Field names are not case-sensitive. +*/ + +TQSqlField* TQSqlRecord::field( const TQString& name ) +{ + checkDetach(); + if ( !sh->d->contains( position( name ) ) ) + return 0; + return &sh->d->fieldInfo( position( name ) )->field; +} + + +/*! + \overload +*/ + +const TQSqlField* TQSqlRecord::field( int i ) const +{ + if ( !sh->d->contains( i ) ) { +#ifdef QT_CHECK_RANGE + qWarning( "TQSqlRecord::field: index out of range: %d", i ); +#endif // QT_CHECK_RANGE + return 0; + } + return &sh->d->fieldInfo( i )->field; +} + +/*! + \overload + + Returns the field called \a name within the record, or 0 if it + cannot be found. Field names are not case-sensitive. +*/ + +const TQSqlField* TQSqlRecord::field( const TQString& name ) const +{ + if( !sh->d->contains( position( name ) ) ) + return 0; + return &sh->d->fieldInfo( position( name ) )->field; +} + +/*! + Append a copy of field \a field to the end of the record. +*/ + +void TQSqlRecord::append( const TQSqlField& field ) +{ + checkDetach(); + sh->d->append( field ); +} + +/*! + Insert a copy of \a field at position \a pos. If a field already + exists at \a pos, it is removed. +*/ + +void TQSqlRecord::insert( int pos, const TQSqlField& field ) // ### 4.0: rename to ::replace +{ + checkDetach(); + sh->d->insert( pos, field ); +} + +/*! + Removes the field at \a pos. If \a pos does not exist, nothing + happens. +*/ + +void TQSqlRecord::remove( int pos ) +{ + checkDetach(); + sh->d->remove( pos ); +} + +/*! + Removes all the record's fields. + + \sa clearValues() +*/ + +void TQSqlRecord::clear() +{ + checkDetach(); + sh->d->clear(); +} + +/*! + Returns TRUE if there are no fields in the record; otherwise + returns FALSE. +*/ + +bool TQSqlRecord::isEmpty() const +{ + return sh->d->isEmpty(); +} + + +/*! + Returns TRUE if there is a field in the record called \a name; + otherwise returns FALSE. +*/ + +bool TQSqlRecord::contains( const TQString& name ) const +{ + for ( uint i = 0; i < count(); ++i ) { + if ( fieldName(i).upper() == name.upper() ) + return TRUE; + } + return FALSE; +} + +/*! + Clears the value of all fields in the record. If \a nullify is + TRUE, (the default is FALSE), each field is set to NULL. +*/ + +void TQSqlRecord::clearValues( bool nullify ) +{ + checkDetach(); + int cnt = (int)count(); + int i; + for ( i = 0; i < cnt; ++i ) { + field( i )->clear( nullify ); + } +} + +/*! + Sets the generated flag for the field called \a name to \a + generated. If the field does not exist, nothing happens. Only + fields that have \a generated set to TRUE are included in the SQL + that is generated, e.g. by TQSqlCursor. + + \sa isGenerated() +*/ + +void TQSqlRecord::setGenerated( const TQString& name, bool generated ) +{ + setGenerated( position( name ), generated ); +} + +/*! + \overload + + Sets the generated flag for the field \a i to \a generated. + + \sa isGenerated() +*/ + +void TQSqlRecord::setGenerated( int i, bool generated ) +{ + checkDetach(); + if ( !field( i ) ) + return; + sh->d->fieldInfo( i )->nogen = !generated; +} + +/*! + \internal + ### Remove in 4.0 +*/ +bool TQSqlRecord::isNull( int i ) +{ + checkDetach(); + TQSqlField* f = field( i ); + if ( f ) { + return f->isNull(); + } + return TRUE; +} + +/*! + \internal + ### Remove in 4.0 +*/ +bool TQSqlRecord::isNull( const TQString& name ) +{ + return isNull( position( name ) ); +} + +/*! + \overload + + Returns TRUE if the field \a i is NULL or if there is no field at + position \a i; otherwise returns FALSE. + + \sa fieldName() +*/ +bool TQSqlRecord::isNull( int i ) const +{ + const TQSqlField* f = field( i ); + if ( f ) { + return f->isNull(); + } + return TRUE; +} + +/*! + Returns TRUE if the field called \a name is NULL or if there is no + field called \a name; otherwise returns FALSE. + + \sa position() +*/ +bool TQSqlRecord::isNull( const TQString& name ) const +{ + return isNull( position( name ) ); +} + +/*! + Sets the value of field \a i to NULL. If the field does not exist, + nothing happens. +*/ +void TQSqlRecord::setNull( int i ) +{ + checkDetach(); + TQSqlField* f = field( i ); + if ( f ) { + f->setNull(); + } +} + +/*! + \overload + + Sets the value of the field called \a name to NULL. If the field + does not exist, nothing happens. +*/ +void TQSqlRecord::setNull( const TQString& name ) +{ + setNull( position( name ) ); +} + + +/*! + Returns TRUE if the record has a field called \a name and this + field is to be generated (the default); otherwise returns FALSE. + + \sa setGenerated() +*/ +bool TQSqlRecord::isGenerated( const TQString& name ) const +{ + return isGenerated( position( name ) ); +} + +/*! + \overload + + Returns TRUE if the record has a field at position \a i and this + field is to be generated (the default); otherwise returns FALSE. + + \sa setGenerated() +*/ +bool TQSqlRecord::isGenerated( int i ) const +{ + if ( !field( i ) ) + return FALSE; + return !sh->d->fieldInfo( i )->nogen; +} + + +/*! + Returns a list of all the record's field names as a string + separated by \a sep. + + Note that fields which are not generated are \e not included (see + \l{isGenerated()}). The returned string is suitable, for example, for + generating SQL SELECT statements. If a \a prefix is specified, + e.g. a table name, all fields are prefixed in the form: + + "\a{prefix}.\<fieldname\>" +*/ + +TQString TQSqlRecord::toString( const TQString& prefix, const TQString& sep ) const +{ + TQString pflist; + bool comma = FALSE; + for ( uint i = 0; i < count(); ++i ){ + if ( isGenerated( field(i)->name() ) ) { + if( comma ) + pflist += sep + " "; + pflist += createField( i, prefix ); + comma = TRUE; + } + } + return pflist; +} + +/*! + Returns a list of all the record's field names, each having the + prefix \a prefix. + + Note that fields which have generated set to FALSE are \e not + included. (See \l{isGenerated()}). If \a prefix is supplied, e.g. + a table name, all fields are prefixed in the form: + + "\a{prefix}.\<fieldname\>" +*/ + +TQStringList TQSqlRecord::toStringList( const TQString& prefix ) const +{ + TQStringList s; + for ( uint i = 0; i < count(); ++i ) { + if ( isGenerated( field(i)->name() ) ) + s += createField( i, prefix ); + } + return s; +} + +/*! \internal +*/ + +TQString TQSqlRecord::createField( int i, const TQString& prefix ) const +{ + TQString f; + if ( !prefix.isEmpty() ) + f = prefix + "."; + f += field( i )->name(); + return f; +} + +/*! + Returns the number of fields in the record. +*/ + +uint TQSqlRecord::count() const +{ + return sh->d->count(); +} + +/*! + Sets the value of the field at position \a i to \a val. If the + field does not exist, nothing happens. +*/ + +void TQSqlRecord::setValue( int i, const TQVariant& val ) +{ + checkDetach(); + TQSqlField* f = field( i ); + if ( f ) { + f->setValue( val ); + } +} + + +/*! + \overload + + Sets the value of the field called \a name to \a val. If the field + does not exist, nothing happens. +*/ + +void TQSqlRecord::setValue( const TQString& name, const TQVariant& val ) +{ + setValue( position( name ), val ); +} + + +/******************************************/ +/******* TQSqlRecordInfo Impl ******/ +/******************************************/ + +/*! + \class TQSqlRecordInfo qsqlrecord.h + \brief The TQSqlRecordInfo class encapsulates a set of database field meta data. + + \ingroup database + \module sql + + This class is a TQValueList that holds a set of database field meta + data. Use contains() to see if a given field name exists in the + record, and use find() to get a TQSqlFieldInfo record for a named + field. + + \sa TQValueList, TQSqlFieldInfo +*/ + + +/*! + Constructs a TQSqlRecordInfo object based on the fields in the + TQSqlRecord \a other. +*/ +TQSqlRecordInfo::TQSqlRecordInfo( const TQSqlRecord& other ) +{ + for ( uint i = 0; i < other.count(); ++i ) { + push_back( TQSqlFieldInfo( *(other.field( i )), other.isGenerated( i ) ) ); + } +} + +/*! + Returns the number of times a field called \a fieldName occurs in + the record. Returns 0 if no field by that name could be found. +*/ +TQSqlRecordInfo::size_type TQSqlRecordInfo::contains( const TQString& fieldName ) const +{ + size_type i = 0; + TQString fName = fieldName.upper(); + for( const_iterator it = begin(); it != end(); ++it ) { + if ( (*it).name().upper() == fName ) { + ++i; + } + } + return i; +} + +/*! + Returns a TQSqlFieldInfo object for the first field in the record + which has the field name \a fieldName. If no matching field is + found then an empty TQSqlFieldInfo object is returned. +*/ +TQSqlFieldInfo TQSqlRecordInfo::find( const TQString& fieldName ) const +{ + TQString fName = fieldName.upper(); + for( const_iterator it = begin(); it != end(); ++it ) { + if ( (*it).name().upper() == fName ) { + return *it; + } + } + return TQSqlFieldInfo(); +} + +/*! + Returns an empty TQSqlRecord based on the field information + in this TQSqlRecordInfo. +*/ +TQSqlRecord TQSqlRecordInfo::toRecord() const +{ + TQSqlRecord buf; + for( const_iterator it = begin(); it != end(); ++it ) { + buf.append( (*it).toField() ); + } + return buf; +} + +/*! + \fn TQSqlRecordInfo::TQSqlRecordInfo() + + Constructs an empty record info object +*/ + +/*! + \fn TQSqlRecordInfo::TQSqlRecordInfo( const TQSqlFieldInfoList& other ) + + Constructs a copy of \a other. +*/ + +#endif diff --git a/src/sql/qsqlrecord.h b/src/sql/qsqlrecord.h new file mode 100644 index 000000000..9fd499364 --- /dev/null +++ b/src/sql/qsqlrecord.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Definition of TQSqlRecord class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLRECORD_H +#define TQSQLRECORD_H + +#ifndef QT_H +#include "qstring.h" +#include "qstringlist.h" +#include "qvariant.h" +#include "qsqlfield.h" +#endif // QT_H + +#ifndef QT_NO_SQL + +class TQSqlRecordPrivate; + +class TQSqlRecordShared : public TQShared +{ +public: + TQSqlRecordShared( TQSqlRecordPrivate* sqlRecordPrivate ) + : d( sqlRecordPrivate ) + {} + virtual ~TQSqlRecordShared(); + TQSqlRecordPrivate* d; +}; + +class Q_EXPORT TQSqlRecord +{ +public: + TQSqlRecord(); + TQSqlRecord( const TQSqlRecord& other ); + TQSqlRecord& operator=( const TQSqlRecord& other ); + virtual ~TQSqlRecord(); + virtual TQVariant value( int i ) const; + virtual TQVariant value( const TQString& name ) const; + virtual void setValue( int i, const TQVariant& val ); + virtual void setValue( const TQString& name, const TQVariant& val ); + bool isGenerated( int i ) const; + bool isGenerated( const TQString& name ) const; + virtual void setGenerated( const TQString& name, bool generated ); + virtual void setGenerated( int i, bool generated ); + virtual void setNull( int i ); + virtual void setNull( const TQString& name ); + bool isNull( int i ); // remove in 4.0 + bool isNull( const TQString& name ); // remove in 4.0 + bool isNull( int i ) const; + bool isNull( const TQString& name ) const; + + int position( const TQString& name ) const; + TQString fieldName( int i ) const; + TQSqlField* field( int i ); + TQSqlField* field( const TQString& name ); + const TQSqlField* field( int i ) const; + const TQSqlField* field( const TQString& name ) const; + + virtual void append( const TQSqlField& field ); + virtual void insert( int pos, const TQSqlField& field ); + virtual void remove( int pos ); + + bool isEmpty() const; + bool contains( const TQString& name ) const; + virtual void clear(); + virtual void clearValues( bool nullify = FALSE ); + uint count() const; + virtual TQString toString( const TQString& prefix = TQString::null, + const TQString& sep = "," ) const; + virtual TQStringList toStringList( const TQString& prefix = TQString::null ) const; + +private: + TQString createField( int i, const TQString& prefix ) const; + void deref(); + bool checkDetach(); + TQSqlRecordShared* sh; +}; + +/******************************************/ +/******* TQSqlRecordInfo Class ******/ +/******************************************/ + +#if defined(Q_TEMPLATEDLL) +// MOC_SKIP_BEGIN +Q_TEMPLATE_EXTERN template class Q_EXPORT TQValueList<TQSqlFieldInfo>; +// MOC_SKIP_END +#endif + +typedef TQValueList<TQSqlFieldInfo> TQSqlFieldInfoList; + +class Q_EXPORT TQSqlRecordInfo: public TQSqlFieldInfoList +{ +public: + TQSqlRecordInfo(): TQSqlFieldInfoList() {} + TQSqlRecordInfo( const TQSqlFieldInfoList& other ): TQSqlFieldInfoList( other ) {} + TQSqlRecordInfo( const TQSqlRecord& other ); + + size_type contains( const TQString& fieldName ) const; + TQSqlFieldInfo find( const TQString& fieldName ) const; + TQSqlRecord toRecord() const; + +}; + + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlresult.cpp b/src/sql/qsqlresult.cpp new file mode 100644 index 000000000..6407064d4 --- /dev/null +++ b/src/sql/qsqlresult.cpp @@ -0,0 +1,368 @@ +/**************************************************************************** +** +** Implementation of TQSqlResult class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlresult.h" +#include "private/qsqlextension_p.h" + +#ifndef QT_NO_SQL + +class TQSqlResultPrivate +{ +public: + const TQSqlDriver* sqldriver; + int idx; + TQString sql; + bool active; + bool isSel; + TQSqlError error; + TQSqlExtension * ext; +}; + +/*! + \class TQSqlResult + \brief The TQSqlResult class provides an abstract interface for + accessing data from SQL databases. + + \ingroup database + \module sql + + Normally you would use TQSqlQuery instead of TQSqlResult since TQSqlQuery + provides a generic wrapper for database-specific implementations of + TQSqlResult. + + \sa TQSql +*/ + + +/*! + Protected constructor which creates a TQSqlResult using database \a + db. The object is initialized to an inactive state. +*/ + +TQSqlResult::TQSqlResult( const TQSqlDriver * db ): forwardOnly( FALSE ) +{ + d = new TQSqlResultPrivate(); + d->sqldriver = db; + d->idx = TQSql::BeforeFirst; + d->isSel = FALSE; + d->active = FALSE; + d->ext = new TQSqlExtension(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQSqlResult::~TQSqlResult() +{ + if ( d->ext ) + delete d->ext; + delete d; +} + +/*! + Sets the current query for the result to \a query. The result must + be reset() in order to execute the query on the database. +*/ + +void TQSqlResult::setQuery( const TQString& query ) +{ + d->sql = query; +} + +/*! + Returns the current SQL query text, or TQString::null if there is none. +*/ + +TQString TQSqlResult::lastQuery() const +{ + return d->sql; +} + +/*! + Returns the current (zero-based) position of the result. +*/ + +int TQSqlResult::at() const +{ + return d->idx; +} + + +/*! + Returns TRUE if the result is positioned on a valid record (that + is, the result is not positioned before the first or after the + last record); otherwise returns FALSE. +*/ + +bool TQSqlResult::isValid() const +{ + return ( d->idx != TQSql::BeforeFirst && \ + d->idx != TQSql::AfterLast ) ? TRUE : FALSE; +} + +/*! + \fn bool TQSqlResult::isNull( int i ) + + Returns TRUE if the field at position \a i is NULL; otherwise + returns FALSE. +*/ + + +/*! + Returns TRUE if the result has records to be retrieved; otherwise + returns FALSE. +*/ + +bool TQSqlResult::isActive() const +{ + return d->active; +} + +/*! + Protected function provided for derived classes to set the + internal (zero-based) result index to \a at. + + \sa at() +*/ + +void TQSqlResult::setAt( int at ) +{ + d->idx = at; +} + + +/*! + Protected function provided for derived classes to indicate + whether or not the current statement is a SQL SELECT statement. + The \a s parameter should be TRUE if the statement is a SELECT + statement, or FALSE otherwise. +*/ + +void TQSqlResult::setSelect( bool s ) +{ + d->isSel = s; +} + +/*! + Returns TRUE if the current result is from a SELECT statement; + otherwise returns FALSE. +*/ + +bool TQSqlResult::isSelect() const +{ + return d->isSel; +} + +/*! + Returns the driver associated with the result. +*/ + +const TQSqlDriver* TQSqlResult::driver() const +{ + return d->sqldriver; +} + + +/*! + Protected function provided for derived classes to set the + internal active state to the value of \a a. + + \sa isActive() +*/ + +void TQSqlResult::setActive( bool a ) +{ + d->active = a; +} + +/*! + Protected function provided for derived classes to set the last + error to the value of \a e. + + \sa lastError() +*/ + +void TQSqlResult::setLastError( const TQSqlError& e ) +{ + d->error = e; +} + + +/*! + Returns the last error associated with the result. +*/ + +TQSqlError TQSqlResult::lastError() const +{ + return d->error; +} + +/*! + \fn int TQSqlResult::size() + + Returns the size of the result or -1 if it cannot be determined. +*/ + +/*! + \fn int TQSqlResult::numRowsAffected() + + Returns the number of rows affected by the last query executed. +*/ + +/*! + \fn TQVariant TQSqlResult::data( int i ) + + Returns the data for field \a i (zero-based) as a TQVariant. This + function is only called if the result is in an active state and is + positioned on a valid record and \a i is non-negative. + Derived classes must reimplement this function and return the value + of field \a i, or TQVariant() if it cannot be determined. +*/ + +/*! + \fn bool TQSqlResult::reset( const TQString& query ) + + Sets the result to use the SQL statement \a query for subsequent + data retrieval. Derived classes must reimplement this function and + apply the \a query to the database. This function is called only + after the result is set to an inactive state and is positioned + before the first record of the new result. Derived classes should + return TRUE if the query was successful and ready to be used, + or FALSE otherwise. +*/ + +/*! + \fn bool TQSqlResult::fetch( int i ) + + Positions the result to an arbitrary (zero-based) index \a i. This + function is only called if the result is in an active state. Derived + classes must reimplement this function and position the result to the + index \a i, and call setAt() with an appropriate value. Return TRUE + to indicate success, or FALSE to signify failure. +*/ + +/*! + \fn bool TQSqlResult::fetchFirst() + + Positions the result to the first record in the result. This + function is only called if the result is in an active state. + Derived classes must reimplement this function and position the result + to the first record, and call setAt() with an appropriate value. + Return TRUE to indicate success, or FALSE to signify failure. +*/ + +/*! + \fn bool TQSqlResult::fetchLast() + + Positions the result to the last record in the result. This + function is only called if the result is in an active state. + Derived classes must reimplement this function and position the result + to the last record, and call setAt() with an appropriate value. + Return TRUE to indicate success, or FALSE to signify failure. +*/ + +/*! + Positions the result to the next available record in the result. + This function is only called if the result is in an active state. + The default implementation calls fetch() with the next index. + Derived classes can reimplement this function and position the result + to the next record in some other way, and call setAt() with an + appropriate value. Return TRUE to indicate success, or FALSE to + signify failure. +*/ + +bool TQSqlResult::fetchNext() +{ + return fetch( at() + 1 ); +} + +/*! + Positions the result to the previous available record in the + result. This function is only called if the result is in an active + state. The default implementation calls fetch() with the previous + index. Derived classes can reimplement this function and position the + result to the next record in some other way, and call setAt() with + an appropriate value. Return TRUE to indicate success, or FALSE to + signify failure. +*/ + +bool TQSqlResult::fetchPrev() +{ + return fetch( at() - 1 ); +} + +/*! + Returns TRUE if you can only scroll forward through a result set; + otherwise returns FALSE. +*/ +bool TQSqlResult::isForwardOnly() const +{ + return forwardOnly; +} + +/*! + Sets forward only mode to \a forward. If forward is TRUE only + fetchNext() is allowed for navigating the results. Forward only + mode needs far less memory since results do not have to be cached. + forward only mode is off by default. + + \sa fetchNext() +*/ +void TQSqlResult::setForwardOnly( bool forward ) +{ + forwardOnly = forward; +} + +// XXX BCI HACK - remove in 4.0 +/*! \internal */ +void TQSqlResult::setExtension( TQSqlExtension * ext ) +{ + if ( d->ext ) + delete d->ext; + d->ext = ext; +} + +/*! \internal */ +TQSqlExtension * TQSqlResult::extension() +{ + return d->ext; +} +#endif // QT_NO_SQL diff --git a/src/sql/qsqlresult.h b/src/sql/qsqlresult.h new file mode 100644 index 000000000..52874db56 --- /dev/null +++ b/src/sql/qsqlresult.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Definition of TQSqlResult class +** +** Created : 2000-11-03 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLRESULT_H +#define TQSQLRESULT_H + +#ifndef QT_H +#include "qstring.h" +#include "qvariant.h" +#include "qsqlerror.h" +#include "qsqlfield.h" +#include "qsql.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlDriver; +class TQSql; +class TQSqlResultPrivate; +class TQSqlExtension; + +class TQM_EXPORT_SQL TQSqlResult +{ +friend class TQSqlQuery; +friend class TQSqlResultShared; +public: + virtual ~TQSqlResult(); + + // BCI HACK - remove in 4.0 + void setExtension( TQSqlExtension * ext ); + TQSqlExtension * extension(); + +protected: + TQSqlResult(const TQSqlDriver * db ); + int at() const; + TQString lastQuery() const; + TQSqlError lastError() const; + bool isValid() const; + bool isActive() const; + bool isSelect() const; + bool isForwardOnly() const; + const TQSqlDriver* driver() const; + virtual void setAt( int at ); + virtual void setActive( bool a ); + virtual void setLastError( const TQSqlError& e ); + virtual void setQuery( const TQString& query ); + virtual void setSelect( bool s ); + virtual void setForwardOnly( bool forward ); + + virtual TQVariant data( int i ) = 0; + virtual bool isNull( int i ) = 0; + virtual bool reset ( const TQString& sqlquery ) = 0; + virtual bool fetch( int i ) = 0; + virtual bool fetchNext(); + virtual bool fetchPrev(); + virtual bool fetchFirst() = 0; + virtual bool fetchLast() = 0; + virtual int size() = 0; + virtual int numRowsAffected() = 0; +private: + TQSqlResultPrivate* d; + bool forwardOnly; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSqlResult( const TQSqlResult & ); + TQSqlResult &operator=( const TQSqlResult & ); +#endif +}; + +#endif // QT_NO_SQL +#endif diff --git a/src/sql/qsqlselectcursor.cpp b/src/sql/qsqlselectcursor.cpp new file mode 100644 index 000000000..037a79854 --- /dev/null +++ b/src/sql/qsqlselectcursor.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Definition of TQSqlSelectCursor class +** +** Created : 2002-11-13 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsqlselectcursor.h" +#include "qsqldriver.h" + +#ifndef QT_NO_SQL + +class TQSqlSelectCursorPrivate +{ +public: + TQSqlSelectCursorPrivate() : populated( FALSE ) {} + TQString query; + bool populated : 1; +}; + +/*! + \class TQSqlSelectCursor qsqlselectcursor.h + \brief The TQSqlSelectCursor class provides browsing of general SQL + SELECT statements. + + \ingroup database + \module sql + + TQSqlSelectCursor is a convenience class that makes it possible to + display result sets from general SQL \c SELECT statements in + data-aware TQt widgets. TQSqlSelectCursor is read-only and does not + support \c INSERT, \c UPDATE or \c DELETE operations. + + Pass the query in at construction time, or use the + TQSqlSelectCursor::exec() function. + + Example: + \code + ... + TQSqlSelectCursor* cur = new TQSqlSelectCursor( "SELECT id, firstname, lastname FROM author" ); + TQDataTable* table = new TQDataTable( this ); + table->setSqlCursor( cur, TRUE, TRUE ); + table->refresh(); + ... + cur->exec( "SELECT * FROM books" ); + table->refresh(); + ... + \endcode +*/ + +/*! + Constructs a read only cursor on database \a db using the query \a query. + */ +TQSqlSelectCursor::TQSqlSelectCursor( const TQString& query, TQSqlDatabase* db ) + : TQSqlCursor( TQString::null, FALSE, db ) +{ + d = new TQSqlSelectCursorPrivate; + d->query = query; + TQSqlCursor::setMode( ReadOnly ); + if ( !query.isNull() ) + exec( query ); +} + +/*! Constructs a copy of \a other */ +TQSqlSelectCursor::TQSqlSelectCursor( const TQSqlSelectCursor& other ) + : TQSqlCursor( other ) +{ + d = new TQSqlSelectCursorPrivate; + d->query = other.d->query; + d->populated = other.d->populated; +} + +/*! Destroys the object and frees any allocated resources */ +TQSqlSelectCursor::~TQSqlSelectCursor() +{ + delete d; +} + +/*! \reimp */ +bool TQSqlSelectCursor::exec( const TQString& query ) +{ + d->query = query; + bool ret = TQSqlCursor::exec( query ); + if ( ret ) { + TQSqlCursor::clear(); + populateCursor(); + } + return ret; +} + +/*! \fn bool TQSqlSelectCursor::select() + \reimp +*/ + +/*! \reimp */ +bool TQSqlSelectCursor::select( const TQString&, const TQSqlIndex& ) +{ + bool ret = TQSqlCursor::exec( d->query ); + if ( ret && !d->populated ) + populateCursor(); + return ret; +} + +/*! \internal */ +void TQSqlSelectCursor::populateCursor() +{ + TQSqlRecordInfo inf = driver()->recordInfo( *(TQSqlQuery*)this ); + for ( TQSqlRecordInfo::const_iterator it = inf.begin(); it != inf.end(); ++it ) + TQSqlCursor::append( *it ); + d->populated = TRUE; +} + +/*! \fn TQSqlIndex TQSqlSelectCursor::primaryIndex( bool ) const + \reimp +*/ + +/*! \fn TQSqlIndex TQSqlSelectCursor::index( const TQStringList& ) const + \reimp +*/ + +/*! \fn TQSqlIndex TQSqlSelectCursor::index( const TQString& ) const + \reimp +*/ + +/*! \fn TQSqlIndex TQSqlSelectCursor::index( const char* ) const + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setPrimaryIndex( const TQSqlIndex& ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::append( const TQSqlFieldInfo& ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::insert( int, const TQSqlFieldInfo& ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::remove( int ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::clear() + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setGenerated( const TQString&, bool ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setGenerated( int, bool ) + \reimp +*/ + +/*! \fn TQSqlRecord* TQSqlSelectCursor::editBuffer( bool ) + \reimp +*/ + +/*! \fn TQSqlRecord* TQSqlSelectCursor::primeInsert() + \reimp +*/ + +/*! \fn TQSqlRecord* TQSqlSelectCursor::primeUpdate() + \reimp +*/ + +/*! \fn TQSqlRecord* TQSqlSelectCursor::primeDelete() + \reimp +*/ + +/*! \fn int TQSqlSelectCursor::insert( bool ) + \reimp +*/ + +/*! \fn int TQSqlSelectCursor::update( bool ) + \reimp +*/ + +/*! \fn int TQSqlSelectCursor::del( bool ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setMode( int ) + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setSort( const TQSqlIndex& ) + \reimp +*/ + +/*! \fn TQSqlIndex TQSqlSelectCursor::sort() const + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setFilter( const TQString& ) + \reimp +*/ + +/*! \fn TQString TQSqlSelectCursor::filter() const + \reimp +*/ + +/*! \fn void TQSqlSelectCursor::setName( const TQString&, bool ) + \reimp +*/ + +/*! \fn TQString TQSqlSelectCursor::name() const + \reimp +*/ + +/*! \fn TQString TQSqlSelectCursor::toString( const TQString&, const TQString& ) const + \reimp +*/ +#endif // QT_NO_SQL diff --git a/src/sql/qsqlselectcursor.h b/src/sql/qsqlselectcursor.h new file mode 100644 index 000000000..f1c03611f --- /dev/null +++ b/src/sql/qsqlselectcursor.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Definition of TQSqlSelectCursor class +** +** Created : 2002-11-13 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the sql module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSQLSELECTCURSOR_H +#define TQSQLSELECTCURSOR_H + +#ifndef QT_H +#include "qsqlcursor.h" +#endif // QT_H + +#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL ) +#define TQM_EXPORT_SQL +#else +#define TQM_EXPORT_SQL Q_EXPORT +#endif + +#ifndef QT_NO_SQL + +class TQSqlSelectCursorPrivate; + +class TQM_EXPORT_SQL TQSqlSelectCursor : public TQSqlCursor +{ +public: + TQSqlSelectCursor( const TQString& query = TQString::null, TQSqlDatabase* db = 0 ); + TQSqlSelectCursor( const TQSqlSelectCursor& other ); + ~TQSqlSelectCursor(); + bool exec( const TQString& query ); + bool select() { return TQSqlCursor::select(); } + +protected: + TQSqlIndex primaryIndex( bool = TRUE ) const { return TQSqlIndex(); } + TQSqlIndex index( const TQStringList& ) const { return TQSqlIndex(); } + TQSqlIndex index( const TQString& ) const { return TQSqlIndex(); } + TQSqlIndex index( const char* ) const { return TQSqlIndex(); } + void setPrimaryIndex( const TQSqlIndex& ) {} + void append( const TQSqlFieldInfo& ) {} + void insert( int, const TQSqlFieldInfo& ) {} + void remove( int ) {} + void clear() {} + void setGenerated( const TQString&, bool ) {} + void setGenerated( int, bool ) {} + TQSqlRecord* editBuffer( bool = FALSE ) { return 0; } + TQSqlRecord* primeInsert() { return 0; } + TQSqlRecord* primeUpdate() { return 0; } + TQSqlRecord* primeDelete() { return 0; } + int insert( bool = TRUE ) { return 0; } + int update( bool = TRUE ) { return 0; } + int del( bool = TRUE ) { return 0; } + void setMode( int ) {} + + void setSort( const TQSqlIndex& ) {} + TQSqlIndex sort() const { return TQSqlIndex(); } + void setFilter( const TQString& ) {} + TQString filter() const { return TQString::null; } + void setName( const TQString&, bool = TRUE ) {} + TQString name() const { return TQString::null; } + TQString toString( const TQString& = TQString::null, const TQString& = "," ) const { return TQString::null; } + bool select( const TQString &, const TQSqlIndex& = TQSqlIndex() ); + +private: + void populateCursor(); + + TQSqlSelectCursorPrivate * d; +}; + +#endif // QT_NO_SQL +#endif // TQSQLSELECTCURSOR_H diff --git a/src/sql/qt_sql.pri b/src/sql/qt_sql.pri new file mode 100644 index 000000000..5533c6fbe --- /dev/null +++ b/src/sql/qt_sql.pri @@ -0,0 +1,254 @@ +# Qt sql module + +sql { + + !table { + message(table must be enabled for sql support) + REQUIRES += table + } + + SQL_P = sql + HEADERS += $$SQL_H/qsql.h \ + $$SQL_H/qsqlquery.h \ + $$SQL_H/qsqldatabase.h \ + $$SQL_H/qsqlfield.h \ + $$SQL_H/qsqlrecord.h \ + $$SQL_H/qsqlcursor.h \ + $$SQL_H/qsqlform.h \ + $$SQL_H/qeditorfactory.h \ + $$SQL_H/qsqleditorfactory.h \ + $$SQL_H/qsqldriver.h \ + $$SQL_P/qsqldriverinterface_p.h \ + $$SQL_P/qsqlextension_p.h \ + $$SQL_H/qsqldriverplugin.h \ + $$SQL_H/qsqlerror.h \ + $$SQL_H/qsqlresult.h \ + $$SQL_H/qsqlindex.h \ + $$SQL_H/qsqlpropertymap.h \ + $$SQL_P/qsqlmanager_p.h \ + $$SQL_H/qdatatable.h \ + $$SQL_H/qdataview.h \ + $$SQL_H/qdatabrowser.h \ + $$SQL_H/qsqlselectcursor.h + + SOURCES += $$SQL_CPP/qsqlquery.cpp \ + $$SQL_CPP/qsqldatabase.cpp \ + $$SQL_CPP/qsqlfield.cpp \ + $$SQL_CPP/qsqlrecord.cpp \ + $$SQL_CPP/qsqlform.cpp \ + $$SQL_CPP/qsqlcursor.cpp \ + $$SQL_CPP/qeditorfactory.cpp \ + $$SQL_CPP/qsqleditorfactory.cpp \ + $$SQL_CPP/qsqldriver.cpp \ + $$SQL_CPP/qsqlextension_p.cpp \ + $$SQL_CPP/qsqldriverplugin.cpp \ + $$SQL_CPP/qsqlerror.cpp \ + $$SQL_CPP/qsqlresult.cpp \ + $$SQL_CPP/qsqlindex.cpp \ + $$SQL_CPP/qsqlpropertymap.cpp \ + $$SQL_CPP/qsqlmanager_p.cpp \ + $$SQL_CPP/qdatatable.cpp \ + $$SQL_CPP/qdataview.cpp \ + $$SQL_CPP/qdatabrowser.cpp \ + $$SQL_CPP/qsqlselectcursor.cpp \ + $$SQL_CPP/drivers/cache/qsqlcachedresult.cpp + + contains(sql-drivers, all ) { + sql-driver += psql mysql odbc oci tds db2 sqlite ibase + } + + contains(sql-drivers, psql) { + HEADERS += $$SQL_CPP/drivers/psql/qsql_psql.h + SOURCES += $$SQL_CPP/drivers/psql/qsql_psql.cpp + DEFINES += QT_SQL_POSTGRES + unix { + !contains( LIBS, .*pq.* ) { + LIBS *= -lpq + } + } + win32 { + !contains( LIBS, .*libpq.* ) { + LIBS *= libpqdll.lib + } +# win32-msvc: { +# LIBS *= delayimp.lib +# QMAKE_LFLAGS += /DELAYLOAD:libpqdll.dll +# } +# win32-borland: { +# QMAKE_LFLAGS += /dlibpqdll.dll +# } + } + } + + contains(sql-drivers, mysql) { + HEADERS += $$SQL_CPP/drivers/mysql/qsql_mysql.h + SOURCES += $$SQL_CPP/drivers/mysql/qsql_mysql.cpp + DEFINES += QT_SQL_MYSQL + unix { + !contains( LIBS, .*mysql.* ) { + LIBS *= -lmysqlclient + } + } + win32 { + !contains( LIBS, .*mysql.* ) { + LIBS *= libmysql.lib + } +# win32-msvc: { +# LIBS *= delayimp.lib +# QMAKE_LFLAGS += /DELAYLOAD:libmysql.dll +# } +# win32-borland: { +# QMAKE_LFLAGS += /dlibmysql.dll +# } + } + } + + contains(sql-drivers, odbc) { + HEADERS += $$SQL_CPP/drivers/odbc/qsql_odbc.h + SOURCES += $$SQL_CPP/drivers/odbc/qsql_odbc.cpp + DEFINES += QT_SQL_ODBC + + mac { + !contains( LIBS, .*odbc.* ) { + LIBS *= -liodbc + } + } + + unix { + !contains( LIBS, .*odbc.* ) { + LIBS *= -liodbc + } + } + + win32 { + !win32-borland:LIBS *= odbc32.lib + win32-borland:LIBS *= $(BCB)/lib/PSDK/odbc32.lib + } + + } + + contains(sql-drivers, oci) { + HEADERS += $$SQL_CPP/drivers/oci/qsql_oci.h + SOURCES += $$SQL_CPP/drivers/oci/qsql_oci.cpp + DEFINES += QT_SQL_OCI + unix { + !contains( LIBS, .*clnts.* ) { + LIBS += -lclntsh -lwtc8 + } + } + win32 { + LIBS += oci.lib +# win32-msvc: { +# LIBS *= delayimp.lib +# QMAKE_LFLAGS += /DELAYLOAD:oci.dll +# } +# win32-borland: { +# QMAKE_LFLAGS += /doci.dll +# } + } + } + + contains(sql-drivers, tds) { + HEADERS += $$SQL_CPP/drivers/tds/qsql_tds.h \ + $$SQL_CPP/drivers/shared/qsql_result.h + SOURCES += $$SQL_CPP/drivers/tds/qsql_tds.cpp \ + $$SQL_CPP/drivers/shared/qsql_result.cpp + DEFINES += QT_SQL_TDS + unix { + LIBS += -L$SYBASE/lib -lsybdb + } + win32 { + !win32-borland:LIBS += NTWDBLIB.LIB + win32-borland:LIBS += $(BCB)/lib/PSDK/NTWDBLIB.LIB +# win32-msvc: { +# LIBS *= delayimp.lib +# QMAKE_LFLAGS += /DELAYLOAD:ntwdblib.dll +# } +# win32-borland: { +# QMAKE_LFLAGS += /dntwdblib.dll +# } + } + } + + contains(sql-drivers, db2) { + HEADERS += $$SQL_CPP/drivers/db2/qsql_db2.h + SOURCES += $$SQL_CPP/drivers/db2/qsql_db2.cpp + DEFINES += QT_SQL_DB2 + unix { + LIBS += -ldb2 + } + win32 { + !win32-borland:LIBS += db2cli.lib +# win32-borland:LIBS += $(BCB)/lib/PSDK/db2cli.lib + } + } + + contains(sql-drivers, ibase) { + HEADERS += $$SQL_CPP/drivers/ibase/qsql_ibase.h + SOURCES += $$SQL_CPP/drivers/ibase/qsql_ibase.cpp + DEFINES += QT_SQL_IBASE + unix { + LIBS *= -lfbclient + } + win32 { + !win32-borland:LIBS *= gds32_ms.lib + win32-borland:LIBS += gds32.lib + } + } + + contains(sql-drivers, sqlite) { + !contains( LIBS, .*sqlite.* ) { + + INCLUDEPATH += $$SQL_CPP/../3rdparty/sqlite/ + + HEADERS += $$SQL_CPP/../3rdparty/sqlite/btree.h \ + $$SQL_CPP/../3rdparty/sqlite/config.h \ + $$SQL_CPP/../3rdparty/sqlite/hash.h \ + $$SQL_CPP/../3rdparty/sqlite/opcodes.h \ + $$SQL_CPP/../3rdparty/sqlite/os.h \ + $$SQL_CPP/../3rdparty/sqlite/pager.h \ + $$SQL_CPP/../3rdparty/sqlite/parse.h \ + $$SQL_CPP/../3rdparty/sqlite/sqlite.h \ + $$SQL_CPP/../3rdparty/sqlite/sqliteInt.h \ + $$SQL_CPP/../3rdparty/sqlite/vdbe.h \ + $$SQL_CPP/../3rdparty/sqlite/vdbeInt.h + + SOURCES += $$SQL_CPP/../3rdparty/sqlite/attach.c \ + $$SQL_CPP/../3rdparty/sqlite/auth.c \ + $$SQL_CPP/../3rdparty/sqlite/btree.c \ + $$SQL_CPP/../3rdparty/sqlite/btree_rb.c \ + $$SQL_CPP/../3rdparty/sqlite/build.c \ + $$SQL_CPP/../3rdparty/sqlite/copy.c \ + $$SQL_CPP/../3rdparty/sqlite/date.c \ + $$SQL_CPP/../3rdparty/sqlite/delete.c \ + $$SQL_CPP/../3rdparty/sqlite/expr.c \ + $$SQL_CPP/../3rdparty/sqlite/func.c \ + $$SQL_CPP/../3rdparty/sqlite/hash.c \ + $$SQL_CPP/../3rdparty/sqlite/insert.c \ + $$SQL_CPP/../3rdparty/sqlite/main.c \ + $$SQL_CPP/../3rdparty/sqlite/opcodes.c \ + $$SQL_CPP/../3rdparty/sqlite/os.c \ + $$SQL_CPP/../3rdparty/sqlite/pager.c \ + $$SQL_CPP/../3rdparty/sqlite/parse.c \ + $$SQL_CPP/../3rdparty/sqlite/pragma.c \ + $$SQL_CPP/../3rdparty/sqlite/printf.c \ + $$SQL_CPP/../3rdparty/sqlite/random.c \ + $$SQL_CPP/../3rdparty/sqlite/select.c \ + $$SQL_CPP/../3rdparty/sqlite/shell.c \ + $$SQL_CPP/../3rdparty/sqlite/table.c \ + $$SQL_CPP/../3rdparty/sqlite/tokenize.c \ + $$SQL_CPP/../3rdparty/sqlite/trigger.c \ + $$SQL_CPP/../3rdparty/sqlite/update.c \ + $$SQL_CPP/../3rdparty/sqlite/util.c \ + $$SQL_CPP/../3rdparty/sqlite/vacuum.c \ + $$SQL_CPP/../3rdparty/sqlite/vdbe.c \ + $$SQL_CPP/../3rdparty/sqlite/vdbeaux.c \ + $$SQL_CPP/../3rdparty/sqlite/where.c + } + + HEADERS += $$SQL_CPP/drivers/sqlite/qsql_sqlite.h + SOURCES += $$SQL_CPP/drivers/sqlite/qsql_sqlite.cpp + DEFINES += QT_SQL_SQLITE + } +} + |