summaryrefslogtreecommitdiffstats
path: root/src/sql
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
commitbd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch)
tree7a520322212d48ebcb9fbe1087e7fca28b76185c /src/sql
downloadqt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz
qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip
Add Qt3 development HEAD version
Diffstat (limited to 'src/sql')
-rw-r--r--src/sql/README.module37
-rw-r--r--src/sql/drivers/cache/qsqlcachedresult.cpp259
-rw-r--r--src/sql/drivers/cache/qsqlcachedresult.h104
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp1078
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.h117
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.cpp775
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.h131
-rw-r--r--src/sql/drivers/odbc/debian_qsql_odbc.h10
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.cpp2035
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.h162
-rw-r--r--src/sql/drivers/psql/qsql_psql.cpp1117
-rw-r--r--src/sql/drivers/psql/qsql_psql.h131
-rw-r--r--src/sql/drivers/sqlite/qsql_sqlite.cpp513
-rw-r--r--src/sql/drivers/sqlite/qsql_sqlite.h90
-rw-r--r--src/sql/qdatabrowser.cpp1284
-rw-r--r--src/sql/qdatabrowser.h177
-rw-r--r--src/sql/qdatatable.cpp2322
-rw-r--r--src/sql/qdatatable.h244
-rw-r--r--src/sql/qdataview.cpp208
-rw-r--r--src/sql/qdataview.h90
-rw-r--r--src/sql/qeditorfactory.cpp192
-rw-r--r--src/sql/qeditorfactory.h76
-rw-r--r--src/sql/qsql.cpp114
-rw-r--r--src/sql/qsql.h100
-rw-r--r--src/sql/qsqlcursor.cpp1549
-rw-r--r--src/sql/qsqlcursor.h160
-rw-r--r--src/sql/qsqldatabase.cpp1332
-rw-r--r--src/sql/qsqldatabase.h155
-rw-r--r--src/sql/qsqldriver.cpp509
-rw-r--r--src/sql/qsqldriver.h125
-rw-r--r--src/sql/qsqldriverinterface_p.h85
-rw-r--r--src/sql/qsqldriverplugin.cpp161
-rw-r--r--src/sql/qsqldriverplugin.h72
-rw-r--r--src/sql/qsqleditorfactory.cpp221
-rw-r--r--src/sql/qsqleditorfactory.h77
-rw-r--r--src/sql/qsqlerror.cpp228
-rw-r--r--src/sql/qsqlerror.h93
-rw-r--r--src/sql/qsqlextension_p.cpp169
-rw-r--r--src/sql/qsqlextension_p.h148
-rw-r--r--src/sql/qsqlfield.cpp563
-rw-r--r--src/sql/qsqlfield.h154
-rw-r--r--src/sql/qsqlform.cpp403
-rw-r--r--src/sql/qsqlform.h108
-rw-r--r--src/sql/qsqlindex.cpp301
-rw-r--r--src/sql/qsqlindex.h99
-rw-r--r--src/sql/qsqlmanager_p.cpp941
-rw-r--r--src/sql/qsqlmanager_p.h163
-rw-r--r--src/sql/qsqlpropertymap.cpp304
-rw-r--r--src/sql/qsqlpropertymap.h78
-rw-r--r--src/sql/qsqlquery.cpp1215
-rw-r--r--src/sql/qsqlquery.h133
-rw-r--r--src/sql/qsqlrecord.cpp774
-rw-r--r--src/sql/qsqlrecord.h141
-rw-r--r--src/sql/qsqlresult.cpp368
-rw-r--r--src/sql/qsqlresult.h115
-rw-r--r--src/sql/qsqlselectcursor.cpp249
-rw-r--r--src/sql/qsqlselectcursor.h104
-rw-r--r--src/sql/qt_sql.pri254
58 files changed, 22917 insertions, 0 deletions
diff --git a/src/sql/README.module b/src/sql/README.module
new file mode 100644
index 0000000..511d90e
--- /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 require 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 0000000..4891b97
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Implementation of cached Qt SQL result classes
+**
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlcachedresult.h"
+#include <qdatetime.h>
+
+#ifndef QT_NO_SQL
+static const uint initial_cache_size = 128;
+
+class QtSqlCachedResultPrivate
+{
+public:
+ QtSqlCachedResultPrivate();
+ bool seek(int i);
+ void init(int count, bool fo);
+ void cleanup();
+ QtSqlCachedResult::RowCache* next();
+ void revertLast();
+
+ QtSqlCachedResult::RowsetCache *cache;
+ QtSqlCachedResult::RowCache *current;
+ int rowCacheEnd;
+ int colCount;
+ bool forwardOnly;
+};
+
+QtSqlCachedResultPrivate::QtSqlCachedResultPrivate():
+ cache(0), current(0), rowCacheEnd(0), colCount(0), forwardOnly(FALSE)
+{
+}
+
+void QtSqlCachedResultPrivate::cleanup()
+{
+ if (cache) {
+ for (int i = 0; i < rowCacheEnd; ++i)
+ delete (*cache)[i];
+ delete cache;
+ cache = 0;
+ }
+ if (forwardOnly)
+ delete current;
+ current = 0;
+ forwardOnly = FALSE;
+ colCount = 0;
+ rowCacheEnd = 0;
+}
+
+void QtSqlCachedResultPrivate::init(int count, bool fo)
+{
+ cleanup();
+ forwardOnly = fo;
+ colCount = count;
+ if (fo)
+ current = new QtSqlCachedResult::RowCache(count);
+ else
+ cache = new QtSqlCachedResult::RowsetCache(initial_cache_size);
+}
+
+QtSqlCachedResult::RowCache *QtSqlCachedResultPrivate::next()
+{
+ if (forwardOnly)
+ return current;
+
+ Q_ASSERT(cache);
+ current = new QtSqlCachedResult::RowCache(colCount);
+ if (rowCacheEnd == (int)cache->size())
+ cache->resize(cache->size() * 2);
+ cache->insert(rowCacheEnd++, current);
+ return current;
+}
+
+bool QtSqlCachedResultPrivate::seek(int i)
+{
+ if (forwardOnly || i < 0)
+ return FALSE;
+ if (i >= rowCacheEnd)
+ return FALSE;
+ current = (*cache)[i];
+ return TRUE;
+}
+
+void QtSqlCachedResultPrivate::revertLast()
+{
+ if (forwardOnly)
+ return;
+ --rowCacheEnd;
+ delete current;
+ current = 0;
+}
+
+//////////////
+
+QtSqlCachedResult::QtSqlCachedResult(const QSqlDriver * db ): QSqlResult ( db )
+{
+ d = new QtSqlCachedResultPrivate();
+}
+
+QtSqlCachedResult::~QtSqlCachedResult()
+{
+ delete d;
+}
+
+void QtSqlCachedResult::init(int colCount)
+{
+ d->init(colCount, isForwardOnly());
+}
+
+bool QtSqlCachedResult::fetch(int i)
+{
+ if ((!isActive()) || (i < 0))
+ return FALSE;
+ if (at() == i)
+ return TRUE;
+ if (d->forwardOnly) {
+ // speed hack - do not copy values if not needed
+ if (at() > i || at() == QSql::AfterLast)
+ return FALSE;
+ while(at() < i - 1) {
+ if (!gotoNext(0))
+ return FALSE;
+ setAt(at() + 1);
+ }
+ if (!gotoNext(d->current))
+ return FALSE;
+ setAt(at() + 1);
+ return TRUE;
+ }
+ if (d->seek(i)) {
+ setAt(i);
+ return TRUE;
+ }
+ setAt(d->rowCacheEnd - 1);
+ while (at() < i) {
+ if (!cacheNext())
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QtSqlCachedResult::fetchNext()
+{
+ if (d->seek(at() + 1)) {
+ setAt(at() + 1);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool QtSqlCachedResult::fetchPrev()
+{
+ return fetch(at() - 1);
+}
+
+bool QtSqlCachedResult::fetchFirst()
+{
+ if (d->forwardOnly && at() != QSql::BeforeFirst) {
+ return FALSE;
+ }
+ if (d->seek(0)) {
+ setAt(0);
+ return TRUE;
+ }
+ return cacheNext();
+}
+
+bool QtSqlCachedResult::fetchLast()
+{
+ if (at() == QSql::AfterLast) {
+ if (d->forwardOnly)
+ return FALSE;
+ else
+ return fetch(d->rowCacheEnd - 1);
+ }
+
+ int i = at();
+ while (fetchNext())
+ i++; /* brute force */
+ if (d->forwardOnly && at() == QSql::AfterLast) {
+ setAt(i);
+ return TRUE;
+ } else {
+ return fetch(d->rowCacheEnd - 1);
+ }
+}
+
+QVariant QtSqlCachedResult::data(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return QVariant();
+
+ return (*d->current)[i];
+}
+
+bool QtSqlCachedResult::isNull(int i)
+{
+ if (!d->current || i >= (int)d->current->size() || i < 0)
+ return TRUE;
+
+ return (*d->current)[i].isNull();
+}
+
+void QtSqlCachedResult::cleanup()
+{
+ setAt(QSql::BeforeFirst);
+ setActive(FALSE);
+ d->cleanup();
+}
+
+bool QtSqlCachedResult::cacheNext()
+{
+ if (!gotoNext(d->next())) {
+ d->revertLast();
+ return FALSE;
+ }
+ setAt(at() + 1);
+ return TRUE;
+}
+
+int QtSqlCachedResult::colCount() const
+{
+ return d->colCount;
+}
+#endif // QT_NO_SQL
diff --git a/src/sql/drivers/cache/qsqlcachedresult.h b/src/sql/drivers/cache/qsqlcachedresult.h
new file mode 100644
index 0000000..e401eef
--- /dev/null
+++ b/src/sql/drivers/cache/qsqlcachedresult.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Definition of shared Qt SQL module classes
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLCACHEDRESULT_H
+#define QSQLCACHEDRESULT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include <qglobal.h>
+#include <qvariant.h>
+#include <qptrvector.h>
+#include <qvaluevector.h>
+#include <qsqlresult.h>
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QtSqlCachedResultPrivate;
+
+class QM_EXPORT_SQL QtSqlCachedResult: public QSqlResult
+{
+public:
+ virtual ~QtSqlCachedResult();
+
+ typedef QValueVector<QVariant> RowCache;
+ typedef QPtrVector<RowCache> RowsetCache;
+
+protected:
+ QtSqlCachedResult(const QSqlDriver * db);
+
+ void init(int colCount);
+ void cleanup();
+ bool cacheNext();
+
+ virtual bool gotoNext(RowCache* row) = 0;
+
+ QVariant data(int i);
+ bool isNull(int i);
+ bool fetch(int i);
+ bool fetchNext();
+ bool fetchPrev();
+ bool fetchFirst();
+ bool fetchLast();
+
+ int colCount() const;
+
+private:
+ QtSqlCachedResultPrivate *d;
+};
+
+
+#endif
+
+#endif
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
new file mode 100644
index 0000000..6954ef8
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.cpp
@@ -0,0 +1,1078 @@
+/****************************************************************************
+**
+** Implementation of Interbase driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsql_ibase.h"
+
+#include <qdatetime.h>
+#include <private/qsqlextension_p.h>
+
+#include <ibase.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+
+#define QIBASE_DRIVER_NAME "QIBASE"
+
+class QIBasePreparedExtension : public QSqlExtension
+{
+public:
+ QIBasePreparedExtension(QIBaseResult *r)
+ : result(r) {}
+
+ bool prepare(const QString &query)
+ {
+ return result->prepare(query);
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ QIBaseResult *result;
+};
+
+static bool getIBaseError(QString& msg, ISC_STATUS* status, long &sqlcode)
+{
+ if (status[0] != 1 || status[1] <= 0)
+ return FALSE;
+
+ sqlcode = isc_sqlcode(status);
+ char buf[512];
+ isc_sql_interprete(sqlcode, buf, 512);
+ msg = QString::fromUtf8(buf);
+ return TRUE;
+}
+
+static void createDA(XSQLDA *&sqlda)
+{
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
+ sqlda->sqln = 1;
+ sqlda->sqld = 0;
+ sqlda->version = SQLDA_VERSION1;
+ sqlda->sqlvar[0].sqlind = 0;
+ sqlda->sqlvar[0].sqldata = 0;
+}
+
+static void enlargeDA(XSQLDA *&sqlda, int n)
+{
+ free(sqlda);
+ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ sqlda->sqln = n;
+ sqlda->version = SQLDA_VERSION1;
+}
+
+static void initDA(XSQLDA *sqlda)
+{
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ switch (sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_INT64:
+ case SQL_LONG:
+ case SQL_SHORT:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIME:
+ case SQL_TYPE_DATE:
+ case SQL_TEXT:
+ case SQL_BLOB:
+ sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
+ break;
+ case SQL_VARYING:
+ sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
+ break;
+ default:
+ // not supported - do not bind.
+ sqlda->sqlvar[i].sqldata = 0;
+ break;
+ }
+ if (sqlda->sqlvar[i].sqltype & 1) {
+ sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short));
+ *(sqlda->sqlvar[i].sqlind) = 0;
+ } else {
+ sqlda->sqlvar[i].sqlind = 0;
+ }
+ }
+}
+
+static void delDA(XSQLDA *&sqlda)
+{
+ if (!sqlda)
+ return;
+ for (int i = 0; i < sqlda->sqld; ++i) {
+ free(sqlda->sqlvar[i].sqlind);
+ free(sqlda->sqlvar[i].sqldata);
+ }
+ free(sqlda);
+ sqlda = 0;
+}
+
+static QVariant::Type qIBaseTypeName(int iType)
+{
+ switch (iType) {
+ case blr_varying:
+ case blr_varying2:
+ case blr_text:
+ case blr_cstring:
+ case blr_cstring2:
+ return QVariant::String;
+ case blr_sql_time:
+ return QVariant::Time;
+ case blr_sql_date:
+ return QVariant::Date;
+ case blr_timestamp:
+ return QVariant::DateTime;
+ case blr_blob:
+ return QVariant::ByteArray;
+ case blr_quad:
+ case blr_short:
+ case blr_long:
+ return QVariant::Int;
+ case blr_int64:
+ return QVariant::LongLong;
+ case blr_float:
+ case blr_d_float:
+ case blr_double:
+ return QVariant::Double;
+ }
+ return QVariant::Invalid;
+}
+
+static QVariant::Type qIBaseTypeName2(int iType)
+{
+ switch(iType & ~1) {
+ case SQL_VARYING:
+ case SQL_TEXT:
+ return QVariant::String;
+ case SQL_LONG:
+ case SQL_SHORT:
+ return QVariant::Int;
+ case SQL_INT64:
+ return QVariant::LongLong;
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ return QVariant::Double;
+ case SQL_TIMESTAMP:
+ return QVariant::DateTime;
+ case SQL_TYPE_DATE:
+ return QVariant::Date;
+ case SQL_TYPE_TIME:
+ return QVariant::Time;
+ default:
+ return QVariant::Invalid;
+ }
+}
+
+static ISC_TIME toTime(const QTime &t)
+{
+ static const QTime midnight(0, 0, 0, 0);
+ return (ISC_TIME)midnight.msecsTo(t) * 10;
+}
+
+static ISC_DATE toDate(const QDate &d)
+{
+ static const QDate basedate(1858, 11, 17);
+ return (ISC_DATE)basedate.daysTo(d);
+}
+
+static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
+{
+ ISC_TIMESTAMP ts;
+ ts.timestamp_time = toTime(dt.time());
+ ts.timestamp_date = toDate(dt.date());
+ return ts;
+}
+
+static QTime toQTime(ISC_TIME time)
+{
+ // have to demangle the structure ourselves because isc_decode_time
+ // strips the msecs
+ static const QTime t;
+ return t.addMSecs(time / 10);
+}
+
+static QDate toQDate(ISC_DATE d)
+{
+ static const QDate bd(1858, 11, 17);
+ return bd.addDays(d);
+}
+
+static QDateTime toQDateTime(ISC_TIMESTAMP *ts)
+{
+ return QDateTime(toQDate(ts->timestamp_date), toQTime(ts->timestamp_time));
+}
+
+class QIBaseDriverPrivate
+{
+public:
+ QIBaseDriverPrivate(QIBaseDriver *d): q(d)
+ {
+ ibase = 0;
+ trans = 0;
+ }
+
+ bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
+ {
+ QString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+public:
+ QIBaseDriver* q;
+ isc_db_handle ibase;
+ isc_tr_handle trans;
+ ISC_STATUS status[20];
+};
+
+class QIBaseResultPrivate
+{
+public:
+ QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb);
+ ~QIBaseResultPrivate() { cleanup(); }
+
+ void cleanup();
+ bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
+ {
+ QString imsg;
+ long sqlcode;
+ if (!getIBaseError(imsg, status, sqlcode))
+ return FALSE;
+
+ q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
+ return TRUE;
+ }
+
+ bool transaction();
+ bool commit();
+
+ bool isSelect();
+ QVariant fetchBlob(ISC_QUAD *bId);
+ void writeBlob(int i, const QByteArray &ba);
+
+public:
+ QIBaseResult *q;
+ const QIBaseDriver *db;
+ ISC_STATUS status[20];
+ isc_tr_handle trans;
+ //indicator whether we have a local transaction or a transaction on driver level
+ bool localTransaction;
+ isc_stmt_handle stmt;
+ isc_db_handle ibase;
+ XSQLDA *sqlda; // output sqlda
+ XSQLDA *inda; // input parameters
+ int queryType;
+};
+
+QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb):
+ q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1)
+{
+ localTransaction = (ddb->d->ibase == 0);
+}
+
+void QIBaseResultPrivate::cleanup()
+{
+ if (stmt) {
+ isc_dsql_free_statement(status, &stmt, DSQL_drop);
+ stmt = 0;
+ }
+
+ commit();
+ if (!localTransaction)
+ trans = 0;
+
+ delDA(sqlda);
+ delDA(inda);
+
+ queryType = -1;
+ q->cleanup();
+}
+
+void QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
+{
+ isc_blob_handle handle = 0;
+ ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
+ isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (!isError("Unable to create BLOB", QSqlError::Statement)) {
+ uint i = 0;
+ while (i < ba.size()) {
+ isc_put_segment(status, &handle, QMIN(ba.size() - i, SHRT_MAX), ba.data());
+ if (isError("Unable to write BLOB"))
+ break;
+ i += SHRT_MAX;
+ }
+ }
+ isc_close_blob(status, &handle);
+}
+
+QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
+{
+ isc_blob_handle handle = 0;
+
+ isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
+ if (isError("Unable to open BLOB", QSqlError::Statement))
+ return QVariant();
+
+ unsigned short len = 0;
+ QByteArray ba(255);
+ ISC_STATUS stat = isc_get_segment(status, &handle, &len, ba.size(), ba.data());
+ while (status[1] == isc_segment) {
+ uint osize = ba.size();
+ // double the amount of data fetched on each iteration
+ ba.resize(QMIN(ba.size() * 2, SHRT_MAX));
+ stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize);
+ }
+ bool isErr = isError("Unable to read BLOB", QSqlError::Statement);
+ isc_close_blob(status, &handle);
+ if (isErr)
+ return QVariant();
+
+ if (ba.size() > 255)
+ ba.resize(ba.size() / 2 + len);
+ else
+ ba.resize(len);
+
+ return ba;
+}
+
+bool QIBaseResultPrivate::isSelect()
+{
+ char acBuffer[9];
+ char qType = isc_info_sql_stmt_type;
+ isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
+ if (isError("Could not get query info", QSqlError::Statement))
+ return FALSE;
+ int iLength = isc_vax_integer(&acBuffer[1], 2);
+ queryType = isc_vax_integer(&acBuffer[3], iLength);
+ return (queryType == isc_info_sql_stmt_select);
+}
+
+bool QIBaseResultPrivate::transaction()
+{
+ if (trans)
+ return TRUE;
+ if (db->d->trans) {
+ localTransaction = FALSE;
+ trans = db->d->trans;
+ return TRUE;
+ }
+ localTransaction = TRUE;
+
+ isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
+ if (isError("Could not start transaction", QSqlError::Statement))
+ return FALSE;
+
+ return TRUE;
+}
+
+// does nothing if the transaction is on the
+// driver level
+bool QIBaseResultPrivate::commit()
+{
+ if (!trans)
+ return FALSE;
+ // don't commit driver's transaction, the driver will do it for us
+ if (!localTransaction)
+ return TRUE;
+
+ isc_commit_transaction(status, &trans);
+ trans = 0;
+ return !isError("Unable to commit transaction", QSqlError::Statement);
+}
+
+//////////
+
+QIBaseResult::QIBaseResult(const QIBaseDriver* db):
+ QtSqlCachedResult(db)
+{
+ d = new QIBaseResultPrivate(this, db);
+ setExtension(new QIBasePreparedExtension(this));
+}
+
+QIBaseResult::~QIBaseResult()
+{
+ delete d;
+}
+
+bool QIBaseResult::prepare(const QString& query)
+{
+ //qDebug("prepare: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ createDA(d->sqlda);
+ createDA(d->inda);
+
+ if (!d->transaction())
+ return FALSE;
+
+ isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
+ if (d->isError("Could not allocate statement", QSqlError::Statement))
+ return FALSE;
+ isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
+ if (d->isError("Could not prepare statement", QSqlError::Statement))
+ return FALSE;
+
+ isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
+ if (d->isError("Could not describe input statement", QSqlError::Statement))
+ return FALSE;
+ if (d->inda->sqld > d->inda->sqln) {
+ enlargeDA(d->inda, d->inda->sqld);
+
+ isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
+ if (d->isError("Could not describe input statement", QSqlError::Statement))
+ return FALSE;
+ }
+ initDA(d->inda);
+ if (d->sqlda->sqld > d->sqlda->sqln) {
+ // need more field descriptors
+ enlargeDA(d->sqlda, d->sqlda->sqld);
+
+ isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
+ if (d->isError("Could not describe statement", QSqlError::Statement))
+ return FALSE;
+ }
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (!isSelect()) {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ return TRUE;
+}
+
+bool QIBaseResult::exec()
+{
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ if (d->inda && extension()->index.count() > 0) {
+ QMap<int, QString>::ConstIterator it;
+ if ((int)extension()->index.count() > d->inda->sqld) {
+ qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", d->inda->sqld, extension()->index.count());
+ return FALSE;
+ }
+ int para = 0;
+ for (it = extension()->index.constBegin(); it != extension()->index.constEnd(); ++it, ++para) {
+ if (para >= d->inda->sqld)
+ break;
+ if (!d->inda->sqlvar[para].sqldata)
+ continue;
+ const QVariant val(extension()->values[it.data()].value);
+ if (d->inda->sqlvar[para].sqltype & 1) {
+ if (val.isNull()) {
+ // set null indicator
+ *(d->inda->sqlvar[para].sqlind) = 1;
+ // and set the value to 0, otherwise it would count as empty string.
+ *((short*)d->inda->sqlvar[para].sqldata) = 0;
+ continue;
+ }
+ // a value of 0 means non-null.
+ *(d->inda->sqlvar[para].sqlind) = 0;
+ }
+ switch(d->inda->sqlvar[para].sqltype & ~1) {
+ case SQL_INT64:
+ if (d->inda->sqlvar[para].sqlscale < 0)
+ *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = Q_LLONG(val.toDouble() *
+ pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
+ else
+ *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
+ break;
+ case SQL_LONG:
+ *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
+ break;
+ case SQL_SHORT:
+ *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
+ break;
+ case SQL_FLOAT:
+ *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
+ break;
+ case SQL_DOUBLE:
+ *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
+ break;
+ case SQL_TIMESTAMP:
+ *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
+ break;
+ case SQL_TYPE_TIME:
+ *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
+ break;
+ case SQL_TYPE_DATE:
+ *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
+ break;
+ case SQL_VARYING: {
+ QCString str(val.toString().utf8()); // keep a copy of the string alive in this scope
+ short buflen = d->inda->sqlvar[para].sqllen;
+ if (str.length() < (uint)buflen)
+ buflen = str.length();
+ *(short*)d->inda->sqlvar[para].sqldata = buflen; // first two bytes is the length
+ memcpy(d->inda->sqlvar[para].sqldata + sizeof(short), str.data(), buflen);
+ break; }
+ case SQL_TEXT: {
+ QCString str(val.toString().utf8().leftJustify(d->inda->sqlvar[para].sqllen, ' ', TRUE));
+ memcpy(d->inda->sqlvar[para].sqldata, str.data(), d->inda->sqlvar[para].sqllen);
+ break; }
+ case SQL_BLOB:
+ d->writeBlob(para, val.toByteArray());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (colCount()) {
+ isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
+ if (d->isError("Unable to close statement"))
+ return FALSE;
+ cleanup();
+ }
+ if (d->sqlda)
+ init(d->sqlda->sqld);
+ isc_dsql_execute2(d->status, &d->trans, &d->stmt, 1, d->inda, 0);
+ if (d->isError("Unable to execute query"))
+ return FALSE;
+
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QIBaseResult::reset (const QString& query)
+{
+// qDebug("reset: %s", query.ascii());
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return FALSE;
+ d->cleanup();
+ setActive(FALSE);
+ setAt(QSql::BeforeFirst);
+
+ createDA(d->sqlda);
+
+ if (!d->transaction())
+ return FALSE;
+
+ isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
+ if (d->isError("Could not allocate statement", QSqlError::Statement))
+ return FALSE;
+ isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
+ if (d->isError("Could not prepare statement", QSqlError::Statement))
+ return FALSE;
+
+ if (d->sqlda->sqld > d->sqlda->sqln) {
+ // need more field descriptors
+ int n = d->sqlda->sqld;
+ free(d->sqlda);
+ d->sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ d->sqlda->sqln = n;
+ d->sqlda->version = SQLDA_VERSION1;
+
+ isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
+ if (d->isError("Could not describe statement", QSqlError::Statement))
+ return FALSE;
+ }
+
+ initDA(d->sqlda);
+
+ setSelect(d->isSelect());
+ if (isSelect()) {
+ init(d->sqlda->sqld);
+ } else {
+ free(d->sqlda);
+ d->sqlda = 0;
+ }
+
+ isc_dsql_execute(d->status, &d->trans, &d->stmt, 1, 0);
+ if (d->isError("Unable to execute query"))
+ return FALSE;
+
+ // commit non-select queries (if they are local)
+ if (!isSelect() && !d->commit())
+ return FALSE;
+
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QIBaseResult::gotoNext(QtSqlCachedResult::RowCache* row)
+{
+ ISC_STATUS stat = isc_dsql_fetch(d->status, &d->stmt, 1, d->sqlda);
+
+ if (stat == 100) {
+ // no more rows
+ setAt(QSql::AfterLast);
+ return FALSE;
+ }
+ if (d->isError("Could not fetch next item", QSqlError::Statement))
+ return FALSE;
+ if (!row) // not interested in actual values
+ return TRUE;
+
+ Q_ASSERT(row);
+ Q_ASSERT((int)row->size() == d->sqlda->sqld);
+ for (int i = 0; i < d->sqlda->sqld; ++i) {
+ char *buf = d->sqlda->sqlvar[i].sqldata;
+ int size = d->sqlda->sqlvar[i].sqllen;
+ Q_ASSERT(buf);
+
+ if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
+ // null value
+ QVariant v;
+ v.cast(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype));
+ (*row)[i] = v;
+ continue;
+ }
+
+ switch(d->sqlda->sqlvar[i].sqltype & ~1) {
+ case SQL_VARYING:
+ // pascal strings - a short with a length information followed by the data
+ (*row)[i] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
+ break;
+ case SQL_INT64:
+ if (d->sqlda->sqlvar[i].sqlscale < 0)
+ (*row)[i] = *(Q_LLONG*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
+ else
+ (*row)[i] = QVariant(*(Q_LLONG*)buf);
+ break;
+ case SQL_LONG:
+ if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out.
+ (*row)[i] = QVariant((int)(*(long*)buf));
+ else
+ (*row)[i] = QVariant((Q_LLONG)(*(long*)buf));
+ break;
+ case SQL_SHORT:
+ (*row)[i] = QVariant((int)(*(short*)buf));
+ break;
+ case SQL_FLOAT:
+ (*row)[i] = QVariant((double)(*(float*)buf));
+ break;
+ case SQL_DOUBLE:
+ (*row)[i] = QVariant(*(double*)buf);
+ break;
+ case SQL_TIMESTAMP:
+ (*row)[i] = toQDateTime((ISC_TIMESTAMP*)buf);
+ break;
+ case SQL_TYPE_TIME:
+ (*row)[i] = toQTime(*(ISC_TIME*)buf);
+ break;
+ case SQL_TYPE_DATE:
+ (*row)[i] = toQDate(*(ISC_DATE*)buf);
+ break;
+ case SQL_TEXT:
+ (*row)[i] = QString::fromUtf8(buf, size);
+ break;
+ case SQL_BLOB:
+ (*row)[i] = d->fetchBlob((ISC_QUAD*)buf);
+ break;
+ default:
+ // unknown type - don't even try to fetch
+ (*row)[i] = QVariant();
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+int QIBaseResult::size()
+{
+ static char sizeInfo[] = {isc_info_sql_records};
+ char buf[33];
+
+ if (!isActive() || !isSelect())
+ return -1;
+
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
+ for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
+ char ct = *c++;
+ short len = isc_vax_integer(c, 2);
+ c += 2;
+ int val = isc_vax_integer(c, len);
+ c += len;
+ if (ct == isc_info_req_select_count)
+ return val;
+ }
+ return -1;
+}
+
+int QIBaseResult::numRowsAffected()
+{
+ static char acCountInfo[] = {isc_info_sql_records};
+ char cCountType;
+
+ switch (d->queryType) {
+ case isc_info_sql_stmt_select:
+ cCountType = isc_info_req_select_count;
+ break;
+ case isc_info_sql_stmt_update:
+ cCountType = isc_info_req_update_count;
+ break;
+ case isc_info_sql_stmt_delete:
+ cCountType = isc_info_req_delete_count;
+ break;
+ case isc_info_sql_stmt_insert:
+ cCountType = isc_info_req_insert_count;
+ break;
+ }
+
+ char acBuffer[33];
+ int iResult = -1;
+ isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
+ if (d->isError("Could not get statement info", QSqlError::Statement))
+ return -1;
+ for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
+ char cType = *pcBuf++;
+ short sLength = isc_vax_integer (pcBuf, 2);
+ pcBuf += 2;
+ int iValue = isc_vax_integer (pcBuf, sLength);
+ pcBuf += sLength;
+
+ if (cType == cCountType) {
+ iResult = iValue;
+ break;
+ }
+ }
+ return iResult;
+}
+
+/*********************************/
+
+QIBaseDriver::QIBaseDriver(QObject * parent, const char * name)
+ : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
+{
+ d = new QIBaseDriverPrivate(this);
+}
+
+QIBaseDriver::QIBaseDriver(void *connection, QObject *parent, const char *name)
+ : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
+{
+ d = new QIBaseDriverPrivate(this);
+ d->ibase = (isc_db_handle)(long int)connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+QIBaseDriver::~QIBaseDriver()
+{
+ delete d;
+}
+
+bool QIBaseDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+// case QuerySize:
+ case PreparedQueries:
+ case PositionalPlaceholders:
+ case Unicode:
+ case BLOB:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QIBaseDriver::open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int /*port*/,
+ const QString & /* connOpts */)
+{
+ if (isOpen())
+ close();
+
+ static const char enc[8] = "UTF_FSS";
+ QCString usr = user.local8Bit();
+ QCString pass = password.local8Bit();
+ usr.truncate(255);
+ pass.truncate(255);
+
+ QByteArray ba(usr.length() + pass.length() + sizeof(enc) + 6);
+ int i = -1;
+ ba[++i] = isc_dpb_version1;
+ ba[++i] = isc_dpb_user_name;
+ ba[++i] = usr.length();
+ memcpy(&ba[++i], usr.data(), usr.length());
+ i += usr.length();
+ ba[i] = isc_dpb_password;
+ ba[++i] = pass.length();
+ memcpy(&ba[++i], pass.data(), pass.length());
+ i += pass.length();
+ ba[i] = isc_dpb_lc_ctype;
+ ba[++i] = sizeof(enc) - 1;
+ memcpy(&ba[++i], enc, sizeof(enc) - 1);
+ i += sizeof(enc) - 1;
+
+ QString ldb;
+ if (!host.isEmpty())
+ ldb += host + ":";
+ ldb += db;
+ isc_attach_database(d->status, 0, (char*)ldb.latin1(), &d->ibase, i, ba.data());
+ if (d->isError("Error opening database", QSqlError::Connection)) {
+ setOpenError(TRUE);
+ return FALSE;
+ }
+
+ setOpen(TRUE);
+ return TRUE;
+}
+
+void QIBaseDriver::close()
+{
+ if (isOpen()) {
+ isc_detach_database(d->status, &d->ibase);
+ d->ibase = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+QSqlQuery QIBaseDriver::createQuery() const
+{
+ return QSqlQuery(new QIBaseResult(this));
+}
+
+bool QIBaseDriver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (d->trans)
+ return FALSE;
+
+ isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
+ return !d->isError("Could not start transaction", QSqlError::Transaction);
+}
+
+bool QIBaseDriver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (!d->trans)
+ return FALSE;
+
+ isc_commit_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError("Unable to commit transaction", QSqlError::Transaction);
+}
+
+bool QIBaseDriver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+ if (!d->trans)
+ return FALSE;
+
+ isc_rollback_transaction(d->status, &d->trans);
+ d->trans = 0;
+ return !d->isError("Unable to rollback transaction", QSqlError::Transaction);
+}
+
+QStringList QIBaseDriver::tables(const QString& typeName) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+
+ int type = typeName.isEmpty() ? (int)QSql::Tables | (int)QSql::Views : typeName.toInt();
+ QString typeFilter;
+
+ if (type == (int)QSql::SystemTables) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0";
+ } else if (type == ((int)QSql::SystemTables | (int)QSql::Views)) {
+ typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL";
+ } else {
+ if (!(type & (int)QSql::SystemTables))
+ typeFilter += "RDB$SYSTEM_FLAG = 0 AND ";
+ if (!(type & (int)QSql::Views))
+ typeFilter += "RDB$VIEW_BLR IS NULL AND ";
+ if (!(type & (int)QSql::Tables))
+ typeFilter += "RDB$VIEW_BLR IS NOT NULL AND ";
+ if (!typeFilter.isEmpty())
+ typeFilter.truncate(typeFilter.length() - 5);
+ }
+ if (!typeFilter.isEmpty())
+ typeFilter.prepend("where ");
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ if (!q.exec("select rdb$relation_name from rdb$relations " + typeFilter))
+ return res;
+ while(q.next())
+ res << q.value(0).toString().stripWhiteSpace();
+
+ return res;
+}
+
+QSqlRecord QIBaseDriver::record(const QString& tablename) const
+{
+ QSqlRecord rec;
+ if (!isOpen())
+ return rec;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+
+ q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '" + tablename.upper()+ "' "
+ "ORDER BY RDB$FIELD_POSITION");
+ while (q.next()) {
+ QSqlField field(q.value(0).toString().stripWhiteSpace(), qIBaseTypeName(q.value(1).toInt()));
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QSqlRecordInfo QIBaseDriver::recordInfo(const QString& tablename) const
+{
+ QSqlRecordInfo rec;
+ if (!isOpen())
+ return rec;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+
+ q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, b.RDB$FIELD_SCALE, "
+ "b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
+ "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
+ "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
+ "AND a.RDB$RELATION_NAME = '" + tablename.upper() + "' "
+ "ORDER BY a.RDB$FIELD_POSITION");
+
+ while (q.next()) {
+ QVariant::Type type = qIBaseTypeName(q.value(1).toInt());
+ QSqlFieldInfo field(q.value(0).toString().stripWhiteSpace(), type, q.value(5).toInt(),
+ q.value(2).toInt(), q.value(4).toInt(), QVariant());
+
+ rec.append(field);
+ }
+
+ return rec;
+}
+
+QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
+{
+ QSqlIndex index(table);
+ if (!isOpen())
+ return index;
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE "
+ "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
+ "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
+ "AND a.RDB$RELATION_NAME = '" + table.upper() + "' "
+ "AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
+ "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
+ "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
+ "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
+ "ORDER BY b.RDB$FIELD_POSITION");
+
+ while (q.next()) {
+ QSqlField field(q.value(1).toString().stripWhiteSpace(), qIBaseTypeName(q.value(2).toInt()));
+ index.append(field); //TODO: asc? desc?
+ index.setName(q.value(0).toString());
+ }
+
+ return index;
+}
+
+QSqlRecord QIBaseDriver::record(const QSqlQuery& query) const
+{
+ QSqlRecord rec;
+ if (query.isActive() && query.driver() == this) {
+ QIBaseResult* result = (QIBaseResult*)query.result();
+ if (!result->d->sqlda)
+ return rec;
+ XSQLVAR v;
+ for (int i = 0; i < result->d->sqlda->sqld; ++i) {
+ v = result->d->sqlda->sqlvar[i];
+ QSqlField f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype));
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+QSqlRecordInfo QIBaseDriver::recordInfo(const QSqlQuery& query) const
+{
+ QSqlRecordInfo rec;
+ if (query.isActive() && query.driver() == this) {
+ QIBaseResult* result = (QIBaseResult*)query.result();
+ if (!result->d->sqlda)
+ return rec;
+ XSQLVAR v;
+ for (int i = 0; i < result->d->sqlda->sqld; ++i) {
+ v = result->d->sqlda->sqlvar[i];
+ QSqlFieldInfo f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
+ qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype),
+ -1, v.sqllen, QABS(v.sqlscale), QVariant(), v.sqltype);
+ rec.append(f);
+ }
+ }
+ return rec;
+}
+
+QString QIBaseDriver::formatValue(const QSqlField* field, bool trimStrings) const
+{
+ switch (field->type()) {
+ case QVariant::DateTime: {
+ QDateTime datetime = field->value().toDateTime();
+ if (datetime.isValid())
+ return "'" + QString::number(datetime.date().year()) + "-" +
+ QString::number(datetime.date().month()) + "-" +
+ QString::number(datetime.date().day()) + " " +
+ QString::number(datetime.time().hour()) + ":" +
+ QString::number(datetime.time().minute()) + ":" +
+ QString::number(datetime.time().second()) + "." +
+ QString::number(datetime.time().msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case QVariant::Time: {
+ QTime time = field->value().toTime();
+ if (time.isValid())
+ return "'" + QString::number(time.hour()) + ":" +
+ QString::number(time.minute()) + ":" +
+ QString::number(time.second()) + "." +
+ QString::number(time.msec()).rightJustify(3, '0', TRUE) + "'";
+ else
+ return "NULL";
+ }
+ case QVariant::Date: {
+ QDate date = field->value().toDate();
+ if (date.isValid())
+ return "'" + QString::number(date.year()) + "-" +
+ QString::number(date.month()) + "-" +
+ QString::number(date.day()) + "'";
+ else
+ return "NULL";
+ }
+ default:
+ return QSqlDriver::formatValue(field, trimStrings);
+ }
+}
diff --git a/src/sql/drivers/ibase/qsql_ibase.h b/src/sql/drivers/ibase/qsql_ibase.h
new file mode 100644
index 0000000..563f509
--- /dev/null
+++ b/src/sql/drivers/ibase/qsql_ibase.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Definition of Interbase driver classes
+**
+** Created : 030911
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_IBASE_H
+#define QSQL_IBASE_H
+
+#include "qsqlresult.h"
+#include "qsqldriver.h"
+#include "../cache/qsqlcachedresult.h"
+
+
+class QIBaseDriverPrivate;
+class QIBaseResultPrivate;
+class QIBaseDriver;
+
+class QIBaseResult : public QtSqlCachedResult
+{
+ friend class QIBaseDriver;
+ friend class QIBaseResultPrivate;
+
+public:
+ QIBaseResult(const QIBaseDriver* db);
+ virtual ~QIBaseResult();
+
+ bool prepare(const QString& query);
+ bool exec();
+
+protected:
+ bool gotoNext(QtSqlCachedResult::RowCache* row);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ QIBaseResultPrivate* d;
+};
+
+class QIBaseDriver : public QSqlDriver
+{
+ friend class QIBaseDriverPrivate;
+ friend class QIBaseResultPrivate;
+ friend class QIBaseResult;
+public:
+ QIBaseDriver(QObject *parent = 0, const char *name = 0);
+ QIBaseDriver(void *connection, QObject *parent = 0, const char *name = 0);
+ virtual ~QIBaseDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port ) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(const QString& typeName) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlRecordInfo recordInfo(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+ QSqlRecord record(const QSqlQuery& query) const;
+ QSqlRecordInfo recordInfo(const QSqlQuery& query) const;
+
+ QString formatValue(const QSqlField* field, bool trimStrings) const;
+
+private:
+ QIBaseDriverPrivate* d;
+};
+
+
+#endif
+
diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp
new file mode 100644
index 0000000..7cfe0a7
--- /dev/null
+++ b/src/sql/drivers/mysql/qsql_mysql.cpp
@@ -0,0 +1,775 @@
+/****************************************************************************
+**
+** Implementation of MYSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_mysql.h"
+#include <private/qsqlextension_p.h>
+
+#include <qdatetime.h>
+#include <qvaluevector.h>
+#include <qsqlrecord.h>
+
+#define QMYSQL_DRIVER_NAME "QMYSQL3"
+
+#ifdef Q_OS_WIN32
+// comment the next line out if you want to use MySQL/embedded on Win32 systems.
+// note that it will crash if you don't statically link to the mysql/e library!
+# define Q_NO_MYSQL_EMBEDDED
+#endif
+
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+static int qMySqlConnectionCount = 0;
+static bool qMySqlInitHandledByUser = FALSE;
+
+class QMYSQLOpenExtension : public QSqlOpenExtension
+{
+public:
+ QMYSQLOpenExtension( QMYSQLDriver *dri )
+ : QSqlOpenExtension(), driver(dri) {}
+ ~QMYSQLOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+private:
+ QMYSQLDriver *driver;
+};
+
+bool QMYSQLOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+class QMYSQLDriverPrivate
+{
+public:
+ QMYSQLDriverPrivate() : mysql(0) {}
+ MYSQL* mysql;
+};
+
+class QMYSQLResultPrivate : public QMYSQLDriverPrivate
+{
+public:
+ QMYSQLResultPrivate() : QMYSQLDriverPrivate(), result(0) {}
+ MYSQL_RES* result;
+ MYSQL_ROW row;
+ QValueVector<QVariant::Type> fieldTypes;
+};
+
+QSqlError qMakeError( const QString& err, int type, const QMYSQLDriverPrivate* p )
+{
+ return QSqlError(QMYSQL_DRIVER_NAME ": " + err, QString(mysql_error( p->mysql )), type, mysql_errno( p->mysql ));
+}
+
+QVariant::Type qDecodeMYSQLType( int mysqltype, uint flags )
+{
+ QVariant::Type type;
+ switch ( mysqltype ) {
+ case FIELD_TYPE_TINY :
+ case FIELD_TYPE_SHORT :
+ case FIELD_TYPE_LONG :
+ case FIELD_TYPE_INT24 :
+ type = (flags & UNSIGNED_FLAG) ? QVariant::UInt : QVariant::Int;
+ break;
+ case FIELD_TYPE_YEAR :
+ type = QVariant::Int;
+ break;
+ case FIELD_TYPE_LONGLONG :
+ type = (flags & UNSIGNED_FLAG) ? QVariant::ULongLong : QVariant::LongLong;
+ break;
+ case FIELD_TYPE_DECIMAL :
+ case FIELD_TYPE_FLOAT :
+ case FIELD_TYPE_DOUBLE :
+ type = QVariant::Double;
+ break;
+ case FIELD_TYPE_DATE :
+ type = QVariant::Date;
+ break;
+ case FIELD_TYPE_TIME :
+ type = QVariant::Time;
+ break;
+ case FIELD_TYPE_DATETIME :
+ case FIELD_TYPE_TIMESTAMP :
+ type = QVariant::DateTime;
+ break;
+ case FIELD_TYPE_BLOB :
+ case FIELD_TYPE_TINY_BLOB :
+ case FIELD_TYPE_MEDIUM_BLOB :
+ case FIELD_TYPE_LONG_BLOB :
+ type = (flags & BINARY_FLAG) ? QVariant::ByteArray : QVariant::CString;
+ break;
+ default:
+ case FIELD_TYPE_ENUM :
+ case FIELD_TYPE_SET :
+ case FIELD_TYPE_STRING :
+ case FIELD_TYPE_VAR_STRING :
+ type = QVariant::String;
+ break;
+ }
+ return type;
+}
+
+QMYSQLResult::QMYSQLResult( const QMYSQLDriver* db )
+: QSqlResult( db )
+{
+ d = new QMYSQLResultPrivate();
+ d->mysql = db->d->mysql;
+}
+
+QMYSQLResult::~QMYSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+MYSQL_RES* QMYSQLResult::result()
+{
+ return d->result;
+}
+
+void QMYSQLResult::cleanup()
+{
+ if ( d->result ) {
+ mysql_free_result( d->result );
+ }
+ d->result = NULL;
+ d->row = NULL;
+ setAt( -1 );
+ setActive( FALSE );
+}
+
+bool QMYSQLResult::fetch( int i )
+{
+ if ( isForwardOnly() ) { // fake a forward seek
+ if ( at() < i ) {
+ int x = i - at();
+ while ( --x && fetchNext() );
+ return fetchNext();
+ } else {
+ return FALSE;
+ }
+ }
+ if ( at() == i )
+ return TRUE;
+ mysql_data_seek( d->result, i );
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( i );
+ return TRUE;
+}
+
+bool QMYSQLResult::fetchNext()
+{
+ d->row = mysql_fetch_row( d->result );
+ if ( !d->row )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool QMYSQLResult::fetchLast()
+{
+ if ( isForwardOnly() ) { // fake this since MySQL can't seek on forward only queries
+ bool success = fetchNext(); // did we move at all?
+ while ( fetchNext() );
+ return success;
+ }
+ my_ulonglong numRows = mysql_num_rows( d->result );
+ if ( !numRows )
+ return FALSE;
+ return fetch( numRows - 1 );
+}
+
+bool QMYSQLResult::fetchFirst()
+{
+ if ( isForwardOnly() ) // again, fake it
+ return fetchNext();
+ return fetch( 0 );
+}
+
+QVariant QMYSQLResult::data( int field )
+{
+ if ( !isSelect() || field >= (int) d->fieldTypes.count() ) {
+ qWarning( "QMYSQLResult::data: column %d out of range", field );
+ return QVariant();
+ }
+
+ QString val( d->row[field] );
+ switch ( d->fieldTypes.at( field ) ) {
+ case QVariant::LongLong:
+ return QVariant( val.toLongLong() );
+ case QVariant::ULongLong:
+ return QVariant( val.toULongLong() );
+ case QVariant::Int:
+ return QVariant( val.toInt() );
+ case QVariant::UInt:
+ return QVariant( val.toUInt() );
+ case QVariant::Double:
+ return QVariant( val.toDouble() );
+ case QVariant::Date:
+ if ( val.isEmpty() ) {
+ return QVariant( QDate() );
+ } else {
+ return QVariant( QDate::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::Time:
+ if ( val.isEmpty() ) {
+ return QVariant( QTime() );
+ } else {
+ return QVariant( QTime::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::DateTime:
+ if ( val.isEmpty() )
+ return QVariant( QDateTime() );
+ if ( val.length() == 14u )
+ // TIMESTAMPS have the format yyyyMMddhhmmss
+ val.insert(4, "-").insert(7, "-").insert(10, 'T').insert(13, ':').insert(16, ':');
+ return QVariant( QDateTime::fromString( val, Qt::ISODate ) );
+ case QVariant::ByteArray: {
+ unsigned long* fl = mysql_fetch_lengths( d->result );
+ QByteArray ba;
+ ba.duplicate( d->row[field], fl[field] );
+ return QVariant( ba );
+ }
+ default:
+ case QVariant::String:
+ case QVariant::CString:
+ return QVariant( val );
+ }
+#ifdef QT_CHECK_RANGE
+ qWarning("QMYSQLResult::data: unknown data type");
+#endif
+ return QVariant();
+}
+
+bool QMYSQLResult::isNull( int field )
+{
+ if ( d->row[field] == NULL )
+ return TRUE;
+ return FALSE;
+}
+
+bool QMYSQLResult::reset ( const QString& query )
+{
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()-> isOpen() || driver()->isOpenError() )
+ return FALSE;
+ cleanup();
+
+ const char *encQuery = query.ascii();
+ if ( mysql_real_query( d->mysql, encQuery, qstrlen(encQuery) ) ) {
+ setLastError( qMakeError("Unable to execute query", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ if ( isForwardOnly() ) {
+ if ( isActive() || isValid() ) // have to empty the results from previous query
+ fetchLast();
+ d->result = mysql_use_result( d->mysql );
+ } else {
+ d->result = mysql_store_result( d->mysql );
+ }
+ if ( !d->result && mysql_field_count( d->mysql ) > 0 ) {
+ setLastError( qMakeError( "Unable to store result", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ int numFields = mysql_field_count( d->mysql );
+ setSelect( !( numFields == 0) );
+ d->fieldTypes.resize( numFields );
+ if ( isSelect() ) {
+ for( int i = 0; i < numFields; i++) {
+ MYSQL_FIELD* field = mysql_fetch_field_direct( d->result, i );
+ if ( field->type == FIELD_TYPE_DECIMAL )
+ d->fieldTypes[i] = QVariant::String;
+ else
+ d->fieldTypes[i] = qDecodeMYSQLType( field->type, field->flags );
+ }
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+int QMYSQLResult::size()
+{
+ return isSelect() ? (int)mysql_num_rows( d->result ) : -1;
+}
+
+int QMYSQLResult::numRowsAffected()
+{
+ return (int)mysql_affected_rows( d->mysql );
+}
+
+/////////////////////////////////////////////////////////
+static void qServerEnd()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ mysql_server_end();
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+static void qServerInit()
+{
+#ifndef Q_NO_MYSQL_EMBEDDED
+# if MYSQL_VERSION_ID >= 40000
+ if ( qMySqlInitHandledByUser || qMySqlConnectionCount > 1 )
+ return;
+
+ // this should only be called once
+ // has no effect on client/server library
+ // but is vital for the embedded lib
+ if ( mysql_server_init( 0, 0, 0 ) ) {
+# ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::qServerInit: unable to start server." );
+# endif
+ }
+
+# endif // MYSQL_VERSION_ID
+#endif // Q_NO_MYSQL_EMBEDDED
+}
+
+QMYSQLDriver::QMYSQLDriver( QObject * parent, const char * name )
+ : QSqlDriver( parent, name ? name : QMYSQL_DRIVER_NAME )
+{
+ init();
+ qServerInit();
+}
+
+/*!
+ Create a driver instance with an already open connection handle.
+*/
+
+QMYSQLDriver::QMYSQLDriver( MYSQL * con, QObject * parent, const char * name )
+ : QSqlDriver( parent, name ? name : QMYSQL_DRIVER_NAME )
+{
+ init();
+ if ( con ) {
+ d->mysql = (MYSQL *) con;
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ if (qMySqlConnectionCount == 1)
+ qMySqlInitHandledByUser = TRUE;
+ } else {
+ qServerInit();
+ }
+}
+
+void QMYSQLDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new QMYSQLOpenExtension(this) );
+ d = new QMYSQLDriverPrivate();
+ d->mysql = 0;
+ qMySqlConnectionCount++;
+}
+
+QMYSQLDriver::~QMYSQLDriver()
+{
+ qMySqlConnectionCount--;
+ if (qMySqlConnectionCount == 0 && !qMySqlInitHandledByUser)
+ qServerEnd();
+
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool QMYSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+// CLIENT_TRANSACTION should be defined in all recent mysql client libs > 3.23.34
+#ifdef CLIENT_TRANSACTIONS
+ if ( d->mysql ) {
+ if ( ( d->mysql->server_capabilities & CLIENT_TRANSACTIONS ) == CLIENT_TRANSACTIONS )
+ return TRUE;
+ }
+#endif
+ return FALSE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QMYSQLDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QMYSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QMYSQLDriver::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+
+ unsigned int optionFlags = 0;
+
+ QStringList raw = QStringList::split( ';', connOpts );
+ QStringList opts;
+ QStringList::ConstIterator it;
+
+ // extract the real options from the string
+ for ( it = raw.begin(); it != raw.end(); ++it ) {
+ QString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 ) {
+ QString val( tmp.mid( idx + 1 ) );
+ val.simplifyWhiteSpace();
+ if ( val == "TRUE" || val == "1" )
+ opts << tmp.left( idx );
+ else
+ qWarning( "QMYSQLDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ } else {
+ opts << tmp;
+ }
+ }
+
+ for ( it = opts.begin(); it != opts.end(); ++it ) {
+ QString opt( (*it).upper() );
+ if ( opt == "CLIENT_COMPRESS" )
+ optionFlags |= CLIENT_COMPRESS;
+ else if ( opt == "CLIENT_FOUND_ROWS" )
+ optionFlags |= CLIENT_FOUND_ROWS;
+ else if ( opt == "CLIENT_IGNORE_SPACE" )
+ optionFlags |= CLIENT_IGNORE_SPACE;
+ else if ( opt == "CLIENT_INTERACTIVE" )
+ optionFlags |= CLIENT_INTERACTIVE;
+ else if ( opt == "CLIENT_NO_SCHEMA" )
+ optionFlags |= CLIENT_NO_SCHEMA;
+ else if ( opt == "CLIENT_ODBC" )
+ optionFlags |= CLIENT_ODBC;
+ else if ( opt == "CLIENT_SSL" )
+ optionFlags |= CLIENT_SSL;
+ else
+ qWarning( "QMYSQLDriver::open: Unknown connect option '%s'", (*it).latin1() );
+ }
+
+ if ( (d->mysql = mysql_init((MYSQL*) 0)) &&
+ mysql_real_connect( d->mysql,
+ host,
+ user,
+ password,
+ db.isNull() ? QString("") : db,
+ (port > -1) ? port : 0,
+ NULL,
+ optionFlags ) )
+ {
+ if ( !db.isEmpty() && mysql_select_db( d->mysql, db )) {
+ setLastError( qMakeError("Unable open database '" + db + "'", QSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ } else {
+ setLastError( qMakeError( "Unable to connect", QSqlError::Connection, d ) );
+ mysql_close( d->mysql );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QMYSQLDriver::close()
+{
+ if ( isOpen() ) {
+ mysql_close( d->mysql );
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+QSqlQuery QMYSQLDriver::createQuery() const
+{
+ return QSqlQuery( new QMYSQLResult( this ) );
+}
+
+QStringList QMYSQLDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ if ( !typeName.isEmpty() && !(typeName.toInt() & (int)QSql::Tables) )
+ return tl;
+
+ MYSQL_RES* tableRes = mysql_list_tables( d->mysql, NULL );
+ MYSQL_ROW row;
+ int i = 0;
+ while ( tableRes && TRUE ) {
+ mysql_data_seek( tableRes, i );
+ row = mysql_fetch_row( tableRes );
+ if ( !row )
+ break;
+ tl.append( QString(row[0]) );
+ i++;
+ }
+ mysql_free_result( tableRes );
+ return tl;
+}
+
+QSqlIndex QMYSQLDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex idx;
+ if ( !isOpen() )
+ return idx;
+ QSqlQuery i = createQuery();
+ QString stmt( "show index from %1;" );
+ QSqlRecord fil = record( tablename );
+ i.exec( stmt.arg( tablename ) );
+ while ( i.isActive() && i.next() ) {
+ if ( i.value(2).toString() == "PRIMARY" ) {
+ idx.append( *fil.field( i.value(4).toString() ) );
+ idx.setCursorName( i.value(0).toString() );
+ idx.setName( i.value(2).toString() );
+ }
+ }
+ return idx;
+}
+
+QSqlRecord QMYSQLDriver::record( const QString& tablename ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return fil;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ QSqlField f ( QString( field->name ) , qDecodeMYSQLType( (int)field->type, field->flags ) );
+ fil.append ( f );
+ }
+ mysql_free_result( r );
+ return fil;
+}
+
+QSqlRecord QMYSQLDriver::record( const QSqlQuery& query ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ QMYSQLResult* result = (QMYSQLResult*)query.result();
+ QMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* f = mysql_fetch_field( p->result );
+ if ( f ) {
+ QSqlField fi( QString((const char*)f->name), qDecodeMYSQLType( f->type, f->flags ) );
+ fil.append( fi );
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return fil;
+}
+
+QSqlRecordInfo QMYSQLDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ MYSQL_RES* r = mysql_list_fields( d->mysql, tablename.local8Bit().data(), 0);
+ if ( !r ) {
+ return info;
+ }
+ MYSQL_FIELD* field;
+ while ( (field = mysql_fetch_field( r ))) {
+ info.append ( QSqlFieldInfo( QString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ QString( field->def ),
+ (int)field->type ) );
+ }
+ mysql_free_result( r );
+ return info;
+}
+
+QSqlRecordInfo QMYSQLDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.isSelect() && query.driver() == this ) {
+ QMYSQLResult* result = (QMYSQLResult*)query.result();
+ QMYSQLResultPrivate* p = result->d;
+ if ( !mysql_errno( p->mysql ) ) {
+ for ( ;; ) {
+ MYSQL_FIELD* field = mysql_fetch_field( p->result );
+ if ( field ) {
+ info.append ( QSqlFieldInfo( QString( field->name ),
+ qDecodeMYSQLType( (int)field->type, field->flags ),
+ IS_NOT_NULL( field->flags ),
+ (int)field->length,
+ (int)field->decimals,
+ QVariant(),
+ (int)field->type ) );
+
+ } else
+ break;
+ }
+ }
+ mysql_field_seek( p->result, 0 );
+ }
+ return info;
+}
+
+MYSQL* QMYSQLDriver::mysql()
+{
+ return d->mysql;
+}
+
+bool QMYSQLDriver::beginTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "BEGIN WORK" ) ) {
+ setLastError( qMakeError("Unable to begin transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QMYSQLDriver::commitTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "COMMIT" ) ) {
+ setLastError( qMakeError("Unable to commit transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QMYSQLDriver::rollbackTransaction()
+{
+#ifndef CLIENT_TRANSACTIONS
+ return FALSE;
+#endif
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QMYSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ if ( mysql_query( d->mysql, "ROLLBACK" ) ) {
+ setLastError( qMakeError("Unable to rollback transaction", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+QString QMYSQLDriver::formatValue( const QSqlField* field, bool trimStrings ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch( field->type() ) {
+ case QVariant::ByteArray: {
+
+ const QByteArray ba = field->value().toByteArray();
+ // buffer has to be at least length*2+1 bytes
+ char* buffer = new char[ ba.size() * 2 + 1 ];
+ /*uint escapedSize =*/ mysql_escape_string( buffer, ba.data(), ba.size() );
+ r.append("'").append(buffer).append("'");
+ delete[] buffer;
+ }
+ break;
+ case QVariant::String:
+ case QVariant::CString: {
+ // Escape '\' characters
+ r = QSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ default:
+ r = QSqlDriver::formatValue( field, trimStrings );
+ }
+ }
+ return r;
+}
diff --git a/src/sql/drivers/mysql/qsql_mysql.h b/src/sql/drivers/mysql/qsql_mysql.h
new file mode 100644
index 0000000..ff8ace6
--- /dev/null
+++ b/src/sql/drivers/mysql/qsql_mysql.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Definition of MySQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_MYSQL_H
+#define QSQL_MYSQL_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlfield.h>
+#include <qsqlindex.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#endif
+
+#include <mysql.h>
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_MYSQL
+#else
+#define Q_EXPORT_SQLDRIVER_MYSQL Q_EXPORT
+#endif
+
+class QMYSQLDriverPrivate;
+class QMYSQLResultPrivate;
+class QMYSQLDriver;
+class QSqlRecordInfo;
+
+class QMYSQLResult : public QSqlResult
+{
+ friend class QMYSQLDriver;
+public:
+ QMYSQLResult( const QMYSQLDriver* db );
+ ~QMYSQLResult();
+
+ MYSQL_RES* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchNext();
+ bool fetchLast();
+ bool fetchFirst();
+ QVariant data( int field );
+ bool isNull( int field );
+ bool reset ( const QString& query );
+ int size();
+ int numRowsAffected();
+private:
+ QMYSQLResultPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_MYSQL QMYSQLDriver : public QSqlDriver
+{
+ friend class QMYSQLResult;
+public:
+ QMYSQLDriver( QObject * parent=0, const char * name=0 );
+ QMYSQLDriver( MYSQL * con, QObject * parent=0, const char * name=0 );
+ ~QMYSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+ MYSQL* mysql();
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ QMYSQLDriverPrivate* d;
+};
+
+
+#endif
diff --git a/src/sql/drivers/odbc/debian_qsql_odbc.h b/src/sql/drivers/odbc/debian_qsql_odbc.h
new file mode 100644
index 0000000..4b91f47
--- /dev/null
+++ b/src/sql/drivers/odbc/debian_qsql_odbc.h
@@ -0,0 +1,10 @@
+#ifdef UNICODE
+typedef SQLWCHAR SQLTCHAR;
+#else
+typedef SQLCHAR SQLTCHAR;
+#endif
+
+#define SQL_WCHAR (-8)
+#define SQL_WVARCHAR (-9)
+#define SQL_WLONGVARCHAR (-10)
+#define SQL_C_WCHAR SQL_WCHAR
diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp
new file mode 100644
index 0000000..b09afd2
--- /dev/null
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp
@@ -0,0 +1,2035 @@
+/****************************************************************************
+**
+** Implementation of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_odbc.h"
+#include <qsqlrecord.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#include <qapplication.h>
+#endif
+#include <qdatetime.h>
+#include <private/qsqlextension_p.h>
+#include <private/qinternal_p.h>
+#include <stdlib.h>
+
+// undefine this to prevent initial check of the ODBC driver
+#define ODBC_CHECK_DRIVER
+
+#if defined(Q_ODBC_VERSION_2)
+//crude hack to get non-unicode capable driver managers to work
+# undef UNICODE
+# define SQLTCHAR SQLCHAR
+# define SQL_C_WCHAR SQL_C_CHAR
+#endif
+
+// newer platform SDKs use SQLLEN instead of SQLINTEGER
+#ifdef SQLLEN
+# define QSQLLEN SQLLEN
+#else
+# define QSQLLEN SQLINTEGER
+#endif
+
+#ifdef SQLULEN
+# define QSQLULEN SQLULEN
+#else
+# define QSQLULEN SQLUINTEGER
+#endif
+
+
+static const QSQLLEN COLNAMESIZE = 256;
+//Map Qt parameter types to ODBC types
+static const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
+
+class QODBCPrivate
+{
+public:
+ QODBCPrivate()
+ : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE)
+ {
+ sql_char_type = sql_varchar_type = sql_longvarchar_type = QVariant::CString;
+ unicode = FALSE;
+ }
+
+ SQLHANDLE hEnv;
+ SQLHANDLE hDbc;
+ SQLHANDLE hStmt;
+
+ bool unicode;
+ bool useSchema;
+ QVariant::Type sql_char_type;
+ QVariant::Type sql_varchar_type;
+ QVariant::Type sql_longvarchar_type;
+
+ QSqlRecordInfo rInf;
+
+ bool checkDriver() const;
+ void checkUnicode();
+ void checkSchemaUsage();
+ bool setConnectionOptions( const QString& connOpts );
+ void splitTableQualifier(const QString &qualifier, QString &catalog,
+ QString &schema, QString &table);
+};
+
+class QODBCPreparedExtension : public QSqlExtension
+{
+public:
+ QODBCPreparedExtension( QODBCResult * r )
+ : result( r ) {}
+
+ bool prepare( const QString& query )
+ {
+ return result->prepare( query );
+ }
+
+ bool exec()
+ {
+ return result->exec();
+ }
+
+ QODBCResult * result;
+};
+
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+class QODBCOpenExtension : public QSqlOpenExtension
+{
+public:
+ QODBCOpenExtension( QODBCDriver *dri )
+ : QSqlOpenExtension(), driver(dri) {}
+ ~QODBCOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+private:
+ QODBCDriver *driver;
+};
+
+bool QODBCOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static QString qWarnODBCHandle(int handleType, SQLHANDLE handle)
+{
+ SQLINTEGER nativeCode_;
+ SQLSMALLINT msgLen;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR state_[SQL_SQLSTATE_SIZE+1];
+ SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH];
+ r = SQLGetDiagRec( handleType,
+ handle,
+ 1,
+ (SQLTCHAR*)state_,
+ &nativeCode_,
+ (SQLTCHAR*)description_,
+ SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */
+ &msgLen);
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )
+#ifdef UNICODE
+ return QString( (const QChar*)description_, (uint)msgLen );
+#else
+ return QString::fromLocal8Bit( (const char*)description_ );
+#endif
+ return QString::null;
+}
+
+static QString qODBCWarn( const QODBCPrivate* odbc)
+{
+ return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " "
+ + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );
+}
+
+static void qSqlWarning( const QString& message, const QODBCPrivate* odbc )
+{
+#ifdef QT_CHECK_RANGE
+ qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );
+#endif
+}
+
+static QSqlError qMakeError( const QString& err, int type, const QODBCPrivate* p )
+{
+ return QSqlError( "QODBC3: " + err, qODBCWarn(p), type );
+}
+
+static QVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const QODBCPrivate* p )
+{
+ QVariant::Type type = QVariant::Invalid;
+ switch ( sqltype ) {
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ type = QVariant::Double;
+ break;
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIT:
+ case SQL_TINYINT:
+ type = QVariant::Int;
+ break;
+ case SQL_BIGINT:
+ type = QVariant::LongLong;
+ break;
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ type = QVariant::ByteArray;
+ break;
+ case SQL_DATE:
+ case SQL_TYPE_DATE:
+ type = QVariant::Date;
+ break;
+ case SQL_TIME:
+ case SQL_TYPE_TIME:
+ type = QVariant::Time;
+ break;
+ case SQL_TIMESTAMP:
+ case SQL_TYPE_TIMESTAMP:
+ type = QVariant::DateTime;
+ break;
+#ifndef Q_ODBC_VERSION_2
+ case SQL_WCHAR:
+ case SQL_WVARCHAR:
+ case SQL_WLONGVARCHAR:
+ type = QVariant::String;
+ break;
+#endif
+ case SQL_CHAR:
+ type = p->sql_char_type;
+ break;
+ case SQL_VARCHAR:
+ type = p->sql_varchar_type;
+ break;
+ case SQL_LONGVARCHAR:
+ type = p->sql_longvarchar_type;
+ break;
+ default:
+ type = QVariant::CString;
+ break;
+ }
+ return type;
+}
+
+static QString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE )
+{
+ QString fieldVal;
+ SQLRETURN r = SQL_ERROR;
+ QSQLLEN lengthIndicator = 0;
+
+ if ( colSize <= 0 ) {
+ colSize = 256;
+ } else if ( colSize > 65536 ) { // limit buffer size to 64 KB
+ colSize = 65536;
+ } else {
+ colSize++; // make sure there is room for more than the 0 termination
+ if ( unicode ) {
+ colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call
+ }
+ }
+ char* buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = SQLGetData( hStmt,
+ column+1,
+ unicode ? SQL_C_WCHAR : SQL_C_CHAR,
+ (SQLPOINTER)buf,
+ (QSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) {
+ fieldVal = QString::null;
+ isNull = TRUE;
+ break;
+ }
+ // if SQL_SUCCESS_WITH_INFO is returned, indicating that
+ // more data can be fetched, the length indicator does NOT
+ // contain the number of bytes returned - it contains the
+ // total number of bytes that CAN be fetched
+ // colSize-1: remove 0 termination when there is more data to fetch
+ int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator;
+ if ( unicode ) {
+ fieldVal += QString( (QChar*) buf, rSize / 2 );
+ } else {
+ buf[ rSize ] = 0;
+ fieldVal += buf;
+ }
+ if ( lengthIndicator < colSize ) {
+ // workaround for Drivermanagers that don't return SQL_NO_DATA
+ break;
+ }
+ } else if ( r == SQL_NO_DATA ) {
+ break;
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "qGetStringData: Error while fetching data (%d)", r );
+#endif
+ fieldVal = QString::null;
+ break;
+ }
+ }
+ delete[] buf;
+ return fieldVal;
+}
+
+static QByteArray qGetBinaryData( SQLHANDLE hStmt, int column, QSQLLEN& lengthIndicator, bool& isNull )
+{
+ QByteArray fieldVal;
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ QSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+
+ SQLTCHAR colName[COLNAMESIZE];
+ r = SQLDescribeCol( hStmt,
+ column+1,
+ colName,
+ COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qWarning( "qGetBinaryData: Unable to describe column %d", column );
+#endif
+ // SQLDescribeCol may return 0 if size cannot be determined
+ if (!colSize) {
+ colSize = 256;
+ }
+ if ( colSize > 65536 ) { // read the field in 64 KB chunks
+ colSize = 65536;
+ }
+ char * buf = new char[ colSize ];
+ while ( TRUE ) {
+ r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_BINARY,
+ (SQLPOINTER) buf,
+ (QSQLLEN)colSize,
+ &lengthIndicator );
+ if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) {
+ if ( lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ break;
+ } else {
+ int rSize;
+ r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize;
+ if ( lengthIndicator == SQL_NO_TOTAL ) { // size cannot be determined
+ rSize = colSize;
+ }
+ // NB! This is not a memleak - the mem will be deleted by QByteArray when
+ // no longer ref'd
+ char * tmp = (char *) malloc( rSize + fieldVal.size() );
+ if ( fieldVal.size() ) {
+ memcpy( tmp, fieldVal.data(), fieldVal.size() );
+ }
+ memcpy( tmp + fieldVal.size(), buf, rSize );
+ fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize );
+
+ if ( r == SQL_SUCCESS ) { // the whole field was read in one chunk
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ delete [] buf;
+ return fieldVal;
+}
+
+static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ QSQLLEN intbuf = 0;
+ isNull = FALSE;
+ QSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SLONG,
+ (SQLPOINTER)&intbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0;
+ }
+ return (int)intbuf;
+}
+
+static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLDOUBLE dblbuf;
+ QSQLLEN lengthIndicator = 0;
+ isNull = FALSE;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_DOUBLE,
+ (SQLPOINTER)&dblbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) {
+ isNull = TRUE;
+ return 0.0;
+ }
+
+ return (double) dblbuf;
+}
+
+static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull )
+{
+ SQLBIGINT lngbuf = Q_INT64_C( 0 );
+ isNull = FALSE;
+ QSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData( hStmt,
+ column+1,
+ SQL_C_SBIGINT,
+ (SQLPOINTER) &lngbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA )
+ isNull = TRUE;
+
+ return lngbuf;
+}
+
+// creates a QSqlFieldInfo from a valid hStmt generated
+// by SQLColumns. The hStmt has to point to a valid position.
+static QSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const QODBCPrivate* p )
+{
+ bool isNull;
+ QString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode );
+ int type = qGetIntData( hStmt, 4, isNull ); // column type
+ int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag
+ // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ if ( required == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( required == SQL_NULLABLE ) {
+ required = 0;
+ } else {
+ required = -1;
+ }
+ int size = qGetIntData( hStmt, 6, isNull ); // column size
+ int prec = qGetIntData( hStmt, 8, isNull ); // precision
+ return QSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, QVariant(), type );
+}
+
+static QSqlFieldInfo qMakeFieldInfo( const QODBCPrivate* p, int i )
+{
+ SQLSMALLINT colNameLen;
+ SQLSMALLINT colType;
+ QSQLULEN colSize;
+ SQLSMALLINT colScale;
+ SQLSMALLINT nullable;
+ SQLRETURN r = SQL_ERROR;
+ SQLTCHAR colName[ COLNAMESIZE ];
+ r = SQLDescribeCol( p->hStmt,
+ i+1,
+ colName,
+ (QSQLULEN)COLNAMESIZE,
+ &colNameLen,
+ &colType,
+ &colSize,
+ &colScale,
+ &nullable);
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( QString("qMakeField: Unable to describe column %1").arg(i), p );
+#endif
+ return QSqlFieldInfo();
+ }
+#ifdef UNICODE
+ QString qColName( (const QChar*)colName, (uint)colNameLen );
+#else
+ QString qColName = QString::fromLocal8Bit( (const char*)colName );
+#endif
+ // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+ int required = -1;
+ if ( nullable == SQL_NO_NULLS ) {
+ required = 1;
+ } else if ( nullable == SQL_NULLABLE ) {
+ required = 0;
+ }
+ QVariant::Type type = qDecodeODBCType( colType, p );
+ return QSqlFieldInfo( qColName,
+ type,
+ required,
+ (int)colSize == 0 ? -1 : (int)colSize,
+ (int)colScale == 0 ? -1 : (int)colScale,
+ QVariant(),
+ (int)colType );
+}
+
+bool QODBCPrivate::setConnectionOptions( const QString& connOpts )
+{
+ // Set any connection attributes
+ QStringList raw = QStringList::split( ';', connOpts );
+ QStringList opts;
+ SQLRETURN r = SQL_SUCCESS;
+ QMap<QString, QString> connMap;
+ for ( QStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) {
+ QString tmp( *it );
+ int idx;
+ if ( (idx = tmp.find( '=' )) != -1 )
+ connMap[ tmp.left( idx ) ] = tmp.mid( idx + 1 ).simplifyWhiteSpace();
+ else
+ qWarning( "QODBCDriver::open: Illegal connect option value '%s'", tmp.latin1() );
+ }
+ if ( connMap.count() ) {
+ QMap<QString, QString>::ConstIterator it;
+ QString opt, val;
+ SQLUINTEGER v = 0;
+ for ( it = connMap.begin(); it != connMap.end(); ++it ) {
+ opt = it.key().upper();
+ val = it.data().upper();
+ r = SQL_SUCCESS;
+ if ( opt == "SQL_ATTR_ACCESS_MODE" ) {
+ if ( val == "SQL_MODE_READ_ONLY" ) {
+ v = SQL_MODE_READ_ONLY;
+ } else if ( val == "SQL_MODE_READ_WRITE" ) {
+ v = SQL_MODE_READ_WRITE;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CONNECTION_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_LOGIN_TIMEOUT" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_CURRENT_CATALOG" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_CURRENT_CATALOG,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_METADATA_ID" ) {
+ if ( val == "SQL_TRUE" ) {
+ v = SQL_TRUE;
+ } else if ( val == "SQL_FALSE" ) {
+ v = SQL_FALSE;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_METADATA_ID, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_PACKET_SIZE" ) {
+ v = val.toUInt();
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) v, 0 );
+ } else if ( opt == "SQL_ATTR_TRACEFILE" ) {
+ val.ucs2(); // 0 terminate
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACEFILE,
+#ifdef UNICODE
+ (SQLWCHAR*) val.unicode(),
+#else
+ (SQLCHAR*) val.latin1(),
+#endif
+ SQL_NTS );
+ } else if ( opt == "SQL_ATTR_TRACE" ) {
+ if ( val == "SQL_OPT_TRACE_OFF" ) {
+ v = SQL_OPT_TRACE_OFF;
+ } else if ( val == "SQL_OPT_TRACE_ON" ) {
+ v = SQL_OPT_TRACE_ON;
+ } else {
+ qWarning( QString( "QODBCDriver::open: Unknown option value '%1'" ).arg( *it ) );
+ break;
+ }
+ r = SQLSetConnectAttr( hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0 );
+ }
+#ifdef QT_CHECK_RANGE
+ else {
+ qWarning( QString("QODBCDriver::open: Unknown connection attribute '%1'").arg( opt ) );
+ }
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( QString("QODBCDriver::open: Unable to set connection attribute '%1'").arg( opt ), this );
+#endif
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void QODBCPrivate::splitTableQualifier(const QString & qualifier, QString &catalog,
+ QString &schema, QString &table)
+{
+ if (!useSchema) {
+ table = qualifier;
+ return;
+ }
+ QStringList l = QStringList::split( ".", qualifier, TRUE );
+ if ( l.count() > 3 )
+ return; // can't possibly be a valid table qualifier
+ int i = 0, n = l.count();
+ if ( n == 1 ) {
+ table = qualifier;
+ } else {
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ if ( n == 3 ) {
+ if ( i == 0 ) {
+ catalog = *it;
+ } else if ( i == 1 ) {
+ schema = *it;
+ } else if ( i == 2 ) {
+ table = *it;
+ }
+ } else if ( n == 2 ) {
+ if ( i == 0 ) {
+ schema = *it;
+ } else if ( i == 1 ) {
+ table = *it;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+QODBCResult::QODBCResult( const QODBCDriver * db, QODBCPrivate* p )
+: QSqlResult(db)
+{
+ d = new QODBCPrivate();
+ (*d) = (*p);
+ setExtension( new QODBCPreparedExtension( this ) );
+}
+
+QODBCResult::~QODBCResult()
+{
+ if ( d->hStmt && driver()->isOpen() ) {
+ SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
+#endif
+ }
+
+ delete d;
+}
+
+bool QODBCResult::reset ( const QString& query )
+{
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ // Always reallocate the statement handle - the statement attributes
+ // are not reset if SQLFreeStmt() is called which causes some problems.
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to free statement handle", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLExecDirect( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ QCString query8 = query.local8Bit();
+ r = SQLExecDirect( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+ return TRUE;
+}
+
+bool QODBCResult::fetch(int i)
+{
+ if ( isForwardOnly() && i < at() )
+ return FALSE;
+ if ( i == at() )
+ return TRUE;
+ fieldCache.clear();
+ nullCache.clear();
+ int actualIdx = i + 1;
+ if ( actualIdx <= 0 ) {
+ setAt( QSql::BeforeFirst );
+ return FALSE;
+ }
+ SQLRETURN r;
+ if ( isForwardOnly() ) {
+ bool ok = TRUE;
+ while ( ok && i > at() )
+ ok = fetchNext();
+ return ok;
+ } else {
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_ABSOLUTE,
+ actualIdx );
+ }
+ if ( r != SQL_SUCCESS ){
+ return FALSE;
+ }
+ setAt( i );
+ return TRUE;
+}
+
+bool QODBCResult::fetchNext()
+{
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() + 1 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchFirst()
+{
+ if ( isForwardOnly() && at() != QSql::BeforeFirst )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ if ( isForwardOnly() ) {
+ return fetchNext();
+ }
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_FIRST,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( 0 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchPrior()
+{
+ if ( isForwardOnly() )
+ return FALSE;
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_PRIOR,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( at() - 1 );
+ return TRUE;
+}
+
+bool QODBCResult::fetchLast()
+{
+ SQLRETURN r;
+ fieldCache.clear();
+ nullCache.clear();
+
+ if ( isForwardOnly() ) {
+ // cannot seek to last row in forwardOnly mode, so we have to use brute force
+ int i = at();
+ if ( i == QSql::AfterLast )
+ return FALSE;
+ if ( i == QSql::BeforeFirst )
+ i = 0;
+ while ( fetchNext() )
+ ++i;
+ setAt( i );
+ return TRUE;
+ }
+
+ r = SQLFetchScroll( d->hStmt,
+ SQL_FETCH_LAST,
+ 0 );
+ if ( r != SQL_SUCCESS ) {
+ return FALSE;
+ }
+ SQLINTEGER currRow;
+ r = SQLGetStmtAttr( d->hStmt,
+ SQL_ROW_NUMBER,
+ &currRow,
+ SQL_IS_INTEGER,
+ 0 );
+ if ( r != SQL_SUCCESS )
+ return FALSE;
+ setAt( currRow-1 );
+ return TRUE;
+}
+
+QVariant QODBCResult::data( int field )
+{
+ if ( field >= (int) d->rInf.count() ) {
+ qWarning( "QODBCResult::data: column %d out of range", field );
+ return QVariant();
+ }
+ if ( fieldCache.contains( field ) )
+ return fieldCache[ field ];
+ SQLRETURN r(0);
+ QSQLLEN lengthIndicator = 0;
+ bool isNull = FALSE;
+ int current = fieldCache.count();
+ for ( ; current < (field + 1); ++current ) {
+ const QSqlFieldInfo info = d->rInf[ current ];
+ switch ( info.type() ) {
+ case QVariant::LongLong:
+ fieldCache[ current ] = QVariant( (Q_LLONG) qGetBigIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Int:
+ fieldCache[ current ] = QVariant( qGetIntData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Date:
+ DATE_STRUCT dbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_DATE,
+ (SQLPOINTER)&dbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QDate( dbuf.year, dbuf.month, dbuf.day ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QDate() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::Time:
+ TIME_STRUCT tbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIME,
+ (SQLPOINTER)&tbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QTime( tbuf.hour, tbuf.minute, tbuf.second ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::DateTime:
+ TIMESTAMP_STRUCT dtbuf;
+ r = SQLGetData( d->hStmt,
+ current+1,
+ SQL_C_TIMESTAMP,
+ (SQLPOINTER)&dtbuf,
+ (QSQLLEN)0,
+ &lengthIndicator );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( lengthIndicator != SQL_NULL_DATA ) ) {
+ fieldCache[ current ] = QVariant( QDateTime( QDate( dtbuf.year, dtbuf.month, dtbuf.day ), QTime( dtbuf.hour, dtbuf.minute, dtbuf.second, dtbuf.fraction / 1000000 ) ) );
+ nullCache[ current ] = FALSE;
+ } else {
+ fieldCache[ current ] = QVariant( QDateTime() );
+ nullCache[ current ] = TRUE;
+ }
+ break;
+ case QVariant::ByteArray: {
+ isNull = FALSE;
+ QByteArray val = qGetBinaryData( d->hStmt, current, lengthIndicator, isNull );
+ fieldCache[ current ] = QVariant( val );
+ nullCache[ current ] = isNull;
+ break; }
+ case QVariant::String:
+ isNull = FALSE;
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, TRUE ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::Double:
+ if ( info.typeID() == SQL_DECIMAL || info.typeID() == SQL_NUMERIC )
+ // bind Double values as string to prevent loss of precision
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length() + 1, isNull, FALSE ) ); // length + 1 for the comma
+ else
+ fieldCache[ current ] = QVariant( qGetDoubleData( d->hStmt, current, isNull ) );
+ nullCache[ current ] = isNull;
+ break;
+ case QVariant::CString:
+ default:
+ isNull = FALSE;
+ fieldCache[ current ] = QVariant( qGetStringData( d->hStmt, current,
+ info.length(), isNull, FALSE ) );
+ nullCache[ current ] = isNull;
+ break;
+ }
+ }
+ return fieldCache[ --current ];
+}
+
+bool QODBCResult::isNull( int field )
+{
+ if ( !fieldCache.contains( field ) ) {
+ // since there is no good way to find out whether the value is NULL
+ // without fetching the field we'll fetch it here.
+ // (data() also sets the NULL flag)
+ data( field );
+ }
+ return nullCache[ field ];
+}
+
+int QODBCResult::size()
+{
+ return -1;
+}
+
+int QODBCResult::numRowsAffected()
+{
+ QSQLLEN affectedRowCount(0);
+ SQLRETURN r = SQLRowCount( d->hStmt, &affectedRowCount );
+ if ( r == SQL_SUCCESS )
+ return affectedRowCount;
+#ifdef QT_CHECK_RANGE
+ else
+ qSqlWarning( "QODBCResult::numRowsAffected: Unable to count affected rows", d );
+#endif
+ return -1;
+}
+
+bool QODBCResult::prepare( const QString& query )
+{
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ SQLRETURN r;
+
+ d->rInf.clear();
+ if ( d->hStmt ) {
+ r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to close statement", d );
+#endif
+ return FALSE;
+ }
+ }
+ r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &d->hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to allocate statement handle", d );
+#endif
+ return FALSE;
+ }
+
+ if ( isForwardOnly() ) {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ } else {
+ r = SQLSetStmtAttr( d->hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_STATIC,
+ SQL_IS_UINTEGER );
+ }
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );
+#endif
+ return FALSE;
+ }
+
+#ifdef UNICODE
+ r = SQLPrepare( d->hStmt,
+ (SQLWCHAR*) query.unicode(),
+ (SQLINTEGER) query.length() );
+#else
+ QCString query8 = query.local8Bit();
+ r = SQLPrepare( d->hStmt,
+ (SQLCHAR*) query8.data(),
+ (SQLINTEGER) query8.length() );
+#endif
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::prepare: Unable to prepare statement", d );
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QODBCResult::exec()
+{
+ SQLRETURN r;
+ QPtrList<QVirtualDestructor> tmpStorage; // holds temporary ptrs. which will be deleted on fu exit
+ tmpStorage.setAutoDelete( TRUE );
+
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ d->rInf.clear();
+
+ if ( !d->hStmt ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCResult::exec: No statement handle available", d );
+#endif
+ return FALSE;
+ } else {
+ r = SQLFreeStmt( d->hStmt, SQL_CLOSE );
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCResult::exec: Unable to close statement handle", d );
+ return FALSE;
+ }
+ }
+
+ // bind parameters - only positional binding allowed
+ if ( extension()->index.count() > 0 ) {
+ QMap<int, QString>::Iterator it;
+ int para = 1;
+ QVariant val;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+ val = extension()->values[ it.data() ].value;
+ QSQLLEN *ind = new QSQLLEN( SQL_NTS );
+ tmpStorage.append( qAutoDeleter(ind) );
+ if ( val.isNull() ) {
+ *ind = SQL_NULL_DATA;
+ }
+ switch ( val.type() ) {
+ case QVariant::Date: {
+ DATE_STRUCT * dt = new DATE_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QDate qdt = val.toDate();
+ dt->year = qdt.year();
+ dt->month = qdt.month();
+ dt->day = qdt.day();
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DATE,
+ SQL_DATE,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Time: {
+ TIME_STRUCT * dt = new TIME_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QTime qdt = val.toTime();
+ dt->hour = qdt.hour();
+ dt->minute = qdt.minute();
+ dt->second = qdt.second();
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIME,
+ SQL_TIME,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = new TIMESTAMP_STRUCT;
+ tmpStorage.append( qAutoDeleter(dt) );
+ QDateTime qdt = val.toDateTime();
+ dt->year = qdt.date().year();
+ dt->month = qdt.date().month();
+ dt->day = qdt.date().day();
+ dt->hour = qdt.time().hour();
+ dt->minute = qdt.time().minute();
+ dt->second = qdt.time().second();
+ dt->fraction = 0;
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_TIMESTAMP,
+ SQL_TIMESTAMP,
+ 0,
+ 0,
+ (void *) dt,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Int: {
+ int * v = new int( val.toInt() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_SLONG,
+ SQL_INTEGER,
+ 0,
+ 0,
+ (void *) v,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::Double: {
+ double * v = new double( val.toDouble() );
+ tmpStorage.append( qAutoDeleter(v) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_DOUBLE,
+ SQL_DOUBLE,
+ 0,
+ 0,
+ (void *) v,
+ (QSQLLEN)0,
+ *ind == SQL_NULL_DATA ? ind : NULL );
+ break; }
+ case QVariant::ByteArray: {
+ if ( *ind != SQL_NULL_DATA ) {
+ *ind = val.asByteArray().size();
+ }
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_BINARY,
+ SQL_LONGVARBINARY,
+ val.asByteArray().size(),
+ 0,
+ (void *) val.asByteArray().data(),
+ (QSQLLEN)val.asByteArray().size(),
+ ind );
+ break; }
+#ifndef Q_ODBC_VERSION_2
+ case QVariant::String:
+ if ( d->unicode ) {
+ QString * str = new QString( val.asString() );
+ str->ucs2();
+ int len = str->length()*2;
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_WCHAR,
+ len > 8000 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ len > 8000 ? len : 0,
+ 0,
+ (void *) str->unicode(),
+ (QSQLLEN) len,
+ ind );
+ break;
+ }
+#endif
+ // fall through
+ default: {
+ QCString * str = new QCString( val.asString().local8Bit() );
+ tmpStorage.append( qAutoDeleter(str) );
+ r = SQLBindParameter( d->hStmt,
+ para,
+ qParamType[ (int)extension()->values[ it.data() ].typ ],
+ SQL_C_CHAR,
+ str->length() > 4000 ? SQL_LONGVARCHAR : SQL_VARCHAR,
+ str->length() + 1,
+ 0,
+ (void *) str->data(),
+ (QSQLLEN)(str->length() + 1),
+ ind );
+ break; }
+ }
+ para++;
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCResult::exec: unable to bind variable: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to bind variable", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ }
+ }
+ r = SQLExecute( d->hStmt );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCResult::exec: Unable to execute statement: %s", qODBCWarn( d ).local8Bit().data() );
+#endif
+ setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) );
+ return FALSE;
+ }
+ SQLSMALLINT count;
+ r = SQLNumResultCols( d->hStmt, &count );
+ if ( count ) {
+ setSelect( TRUE );
+ for ( int i = 0; i < count; ++i ) {
+ d->rInf.append( qMakeFieldInfo( d, i ) );
+ }
+ } else {
+ setSelect( FALSE );
+ }
+ setActive( TRUE );
+
+ //get out parameters
+ if ( extension()->index.count() > 0 ) {
+ QMap<int, QString>::Iterator it;
+ for ( it = extension()->index.begin(); it != extension()->index.end(); ++it ) {
+
+ SQLINTEGER* indPtr = qAutoDeleterData( (QAutoDeleter<SQLINTEGER>*)tmpStorage.getFirst() );
+ if ( !indPtr )
+ return FALSE;
+ bool isNull = (*indPtr == SQL_NULL_DATA);
+ tmpStorage.removeFirst();
+
+ QVariant::Type type = extension()->values[ it.data() ].value.type();
+ if ( isNull ) {
+ QVariant v;
+ v.cast(type);
+ extension()->values[ it.data() ].value = v;
+ if (type != QVariant::ByteArray)
+ tmpStorage.removeFirst();
+ continue;
+ }
+
+ switch (type) {
+ case QVariant::Date: {
+ DATE_STRUCT * ds = qAutoDeleterData( (QAutoDeleter<DATE_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QDate( ds->year, ds->month, ds->day ) );
+ break; }
+ case QVariant::Time: {
+ TIME_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIME_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QTime( dt->hour, dt->minute, dt->second ) );
+ break; }
+ case QVariant::DateTime: {
+ TIMESTAMP_STRUCT * dt = qAutoDeleterData( (QAutoDeleter<TIMESTAMP_STRUCT>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( QDateTime( QDate( dt->year, dt->month, dt->day ),
+ QTime( dt->hour, dt->minute, dt->second ) ) );
+ break; }
+ case QVariant::Int: {
+ int * v = qAutoDeleterData( (QAutoDeleter<int>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *v );
+ break; }
+ case QVariant::Double: {
+ double * v = qAutoDeleterData( (QAutoDeleter<double>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *v );
+ break; }
+ case QVariant::ByteArray:
+ break;
+ case QVariant::String:
+ if ( d->unicode ) {
+ QString * str = qAutoDeleterData( (QAutoDeleter<QString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *str );
+ break;
+ }
+ // fall through
+ default: {
+ QCString * str = qAutoDeleterData( (QAutoDeleter<QCString>*)tmpStorage.getFirst() );
+ extension()->values[ it.data() ].value = QVariant( *str );
+ break; }
+ }
+ if (type != QVariant::ByteArray)
+ tmpStorage.removeFirst();
+ }
+ }
+
+ return TRUE;
+}
+
+////////////////////////////////////////
+
+
+QODBCDriver::QODBCDriver( QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QODBC")
+{
+ init();
+}
+
+QODBCDriver::QODBCDriver( SQLHANDLE env, SQLHANDLE con, QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QODBC")
+{
+ init();
+ d->hEnv = env;
+ d->hDbc = con;
+ if ( env && con ) {
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void QODBCDriver::init()
+{
+ qSqlOpenExtDict()->insert( this, new QODBCOpenExtension(this) );
+ d = new QODBCPrivate();
+}
+
+QODBCDriver::~QODBCDriver()
+{
+ cleanup();
+ delete d;
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+bool QODBCDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions: {
+ if ( !d->hDbc )
+ return FALSE;
+ SQLUSMALLINT txn;
+ SQLSMALLINT t;
+ int r = SQLGetInfo( d->hDbc,
+ (SQLUSMALLINT)SQL_TXN_CAPABLE,
+ &txn,
+ sizeof(txn),
+ &t);
+ if ( r != SQL_SUCCESS || txn == SQL_TC_NONE )
+ return FALSE;
+ else
+ return TRUE;
+ }
+ case QuerySize:
+ return FALSE;
+ case BLOB:
+ return TRUE;
+ case Unicode:
+ return d->unicode;
+ case PreparedQueries:
+ return TRUE;
+ case PositionalPlaceholders:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+bool QODBCDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QODBCDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QODBCDriver::open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString &,
+ int,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ SQLRETURN r;
+ r = SQLAllocHandle( SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &d->hEnv);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::open: Unable to allocate environment", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+ r = SQLSetEnvAttr( d->hEnv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER)SQL_OV_ODBC2,
+ SQL_IS_UINTEGER );
+ r = SQLAllocHandle( SQL_HANDLE_DBC,
+ d->hEnv,
+ &d->hDbc);
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::open: Unable to allocate connection", d );
+#endif
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->setConnectionOptions( connOpts ) )
+ return FALSE;
+
+ // Create the connection string
+ QString connQStr;
+ // support the "DRIVER={SQL SERVER};SERVER=blah" syntax
+ if ( db.contains(".dsn") )
+ connQStr = "FILEDSN=" + db;
+ else if ( db.contains( "DRIVER" ) || db.contains( "SERVER" ) )
+ connQStr = db;
+ else
+ connQStr = "DSN=" + db;
+ connQStr += ";UID=" + user + ";PWD=" + password;
+ SQLSMALLINT cb;
+ SQLTCHAR connOut[1024];
+ r = SQLDriverConnect( d->hDbc,
+ NULL,
+#ifdef UNICODE
+ (SQLWCHAR*)connQStr.unicode(),
+#else
+ (SQLCHAR*)connQStr.latin1(),
+#endif
+ (SQLSMALLINT)connQStr.length(),
+ connOut,
+ 1024,
+ &cb,
+ SQL_DRIVER_NOPROMPT );
+ if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) {
+ setLastError( qMakeError( "Unable to connect", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ if ( !d->checkDriver() ) {
+ setLastError( qMakeError( "Unable to connect - Driver doesn't support all needed functionality", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ d->checkUnicode();
+ d->checkSchemaUsage();
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QODBCDriver::close()
+{
+ cleanup();
+ setOpen( FALSE );
+ setOpenError( FALSE );
+}
+
+void QODBCDriver::cleanup()
+{
+ SQLRETURN r;
+ if ( !d )
+ return;
+
+ if( d->hDbc ) {
+ // Open statements/descriptors handles are automatically cleaned up by SQLDisconnect
+ if ( isOpen() ) {
+ r = SQLDisconnect( d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::disconnect: Unable to disconnect datasource", d );
+#endif
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_DBC, d->hDbc );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::cleanup: Unable to free connection handle", d );
+#endif
+ d->hDbc = 0;
+ }
+
+ if ( d->hEnv ) {
+ r = SQLFreeHandle( SQL_HANDLE_ENV, d->hEnv );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::cleanup: Unable to free environment handle", d );
+#endif
+ d->hEnv = 0;
+ }
+}
+
+// checks whether the server can return char, varchar and longvarchar
+// as two byte unicode characters
+void QODBCPrivate::checkUnicode()
+{
+#if defined(Q_WS_WIN)
+ if ( !qt_winunicode ) {
+ unicode = FALSE;
+ return;
+ }
+#endif
+ SQLRETURN r;
+ SQLUINTEGER fFunc;
+
+ unicode = FALSE;
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_CHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WCHAR ) ) {
+ sql_char_type = QVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_VARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WVARCHAR ) ) {
+ sql_varchar_type = QVariant::String;
+ unicode = TRUE;
+ }
+
+ r = SQLGetInfo( hDbc,
+ SQL_CONVERT_LONGVARCHAR,
+ (SQLPOINTER)&fFunc,
+ sizeof(fFunc),
+ NULL );
+ if ( ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) && ( fFunc & SQL_CVT_WLONGVARCHAR ) ) {
+ sql_longvarchar_type = QVariant::String;
+ unicode = TRUE;
+ }
+}
+
+bool QODBCPrivate::checkDriver() const
+{
+#ifdef ODBC_CHECK_DRIVER
+ // do not query for SQL_API_SQLFETCHSCROLL because it can't be used at this time
+ static const SQLUSMALLINT reqFunc[] = {
+ SQL_API_SQLDESCRIBECOL, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
+ SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETDIAGREC, SQL_API_SQLEXECDIRECT,
+ SQL_API_SQLGETINFO, SQL_API_SQLTABLES, 0
+ };
+
+ // these functions are optional
+ static const SQLUSMALLINT optFunc[] = {
+ SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLROWCOUNT, 0
+ };
+
+ SQLRETURN r;
+ SQLUSMALLINT sup;
+
+
+ int i;
+ // check the required functions
+ for ( i = 0; reqFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, reqFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning ( "QODBCDriver::open: Warning - Driver doesn't support all needed functionality (%d). "
+ "Please look at the Qt SQL Module Driver documentation for more information.", reqFunc[ i ] );
+#endif
+ return FALSE;
+ }
+ }
+
+ // these functions are optional and just generate a warning
+ for ( i = 0; optFunc[ i ] != 0; ++i ) {
+
+ r = SQLGetFunctions( hDbc, optFunc[ i ], &sup );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS ) {
+ qSqlWarning( "QODBCDriver::checkDriver: Cannot get list of supported functions", this );
+ return FALSE;
+ }
+#endif
+ if ( sup == SQL_FALSE ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QODBCDriver::checkDriver: Warning - Driver doesn't support some non-critical functions (%d)", optFunc[ i ] );
+#endif
+ return TRUE;
+ }
+ }
+#endif //ODBC_CHECK_DRIVER
+
+ return TRUE;
+}
+
+void QODBCPrivate::checkSchemaUsage()
+{
+ SQLRETURN r;
+ SQLUINTEGER val;
+
+ r = SQLGetInfo(hDbc,
+ SQL_SCHEMA_USAGE,
+ (SQLPOINTER) &val,
+ sizeof(val),
+ NULL);
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ useSchema = (val != 0);
+}
+
+QSqlQuery QODBCDriver::createQuery() const
+{
+ return QSqlQuery( new QODBCResult( this, d ) );
+}
+
+bool QODBCDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_OFF);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac) );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to disable autocommit", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool QODBCDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_COMMIT );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError("Unable to commit transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool QODBCDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning(" QODBCDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC,
+ d->hDbc,
+ SQL_ROLLBACK );
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to rollback transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return endTrans();
+}
+
+bool QODBCDriver::endTrans()
+{
+ SQLUINTEGER ac(SQL_AUTOCOMMIT_ON);
+ SQLRETURN r = SQLSetConnectAttr( d->hDbc,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)ac,
+ sizeof(ac));
+ if ( r != SQL_SUCCESS ) {
+ setLastError( qMakeError( "Unable to enable autocommit", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+QStringList QODBCDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ SQLHANDLE hStmt;
+
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::tables: Unable to allocate handle", d );
+#endif
+ return tl;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ QString tableType;
+ if ( typeName.isEmpty() || ((type & (int)QSql::Tables) == (int)QSql::Tables) )
+ tableType += "TABLE,";
+ if ( (type & (int)QSql::Views) == (int)QSql::Views )
+ tableType += "VIEW,";
+ if ( (type & (int)QSql::SystemTables) == (int)QSql::SystemTables )
+ tableType += "SYSTEM TABLE,";
+ if ( tableType.isEmpty() )
+ return tl;
+ tableType.truncate( tableType.length() - 1 );
+
+ r = SQLTables( hStmt,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0,
+#ifdef UNICODE
+ (SQLWCHAR*)tableType.unicode(),
+#else
+ (SQLCHAR*)tableType.latin1(),
+#endif
+ tableType.length() /* characters, not bytes */ );
+
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::tables Unable to execute table list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ while ( r == SQL_SUCCESS ) {
+ bool isNull;
+ QString fieldVal = qGetStringData( hStmt, 2, -1, isNull, d->unicode );
+ tl.append( fieldVal );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
+ return tl;
+}
+
+QSqlIndex QODBCDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex index( tablename );
+ if ( !isOpen() )
+ return index;
+ bool usingSpecialColumns = FALSE;
+ QSqlRecord rec = record( tablename );
+
+ SQLHANDLE hStmt;
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::primaryIndex: Unable to list primary key", d );
+#endif
+ return index;
+ }
+ QString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLPrimaryKeys( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length() /* in characters, not in bytes */);
+
+ // if the SQLPrimaryKeys() call does not succeed (e.g the driver
+ // does not support it) - try an alternative method to get hold of
+ // the primary index (e.g MS Access and FoxPro)
+ if ( r != SQL_SUCCESS ) {
+ r = SQLSpecialColumns( hStmt,
+ SQL_BEST_ROWID,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+
+ table.length(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE );
+
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::primaryIndex: Unable to execute primary key list", d );
+#endif
+ } else {
+ usingSpecialColumns = TRUE;
+ }
+ }
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ bool isNull;
+ int fakeId = 0;
+ QString cName, idxName;
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == SQL_SUCCESS ) {
+ if ( usingSpecialColumns ) {
+ cName = qGetStringData( hStmt, 1, -1, isNull, d->unicode ); // column name
+ idxName = QString::number( fakeId++ ); // invent a fake index name
+ } else {
+ cName = qGetStringData( hStmt, 3, -1, isNull, d->unicode ); // column name
+ idxName = qGetStringData( hStmt, 5, -1, isNull, d->unicode ); // pk index name
+ }
+ QSqlField *fld = rec.field(cName);
+ if (fld)
+ index.append(*fld);
+ index.setName( idxName );
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0 );
+ }
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );
+ return index;
+}
+
+QSqlRecord QODBCDriver::record( const QString& tablename ) const
+{
+ return recordInfo( tablename ).toRecord();
+}
+
+QSqlRecord QODBCDriver::record( const QSqlQuery& query ) const
+{
+ return recordInfo( query ).toRecord();
+}
+
+QSqlRecordInfo QODBCDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+
+ SQLHANDLE hStmt;
+ QString catalog, schema, table;
+ d->splitTableQualifier( tablename, catalog, schema, table );
+ SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT,
+ d->hDbc,
+ &hStmt );
+ if ( r != SQL_SUCCESS ) {
+#ifdef QT_CHECK_RANGE
+ qSqlWarning( "QODBCDriver::record: Unable to allocate handle", d );
+#endif
+ return fil;
+ }
+ r = SQLSetStmtAttr( hStmt,
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER );
+ r = SQLColumns( hStmt,
+#ifdef UNICODE
+ catalog.length() == 0 ? NULL : (SQLWCHAR*)catalog.unicode(),
+#else
+ catalog.length() == 0 ? NULL : (SQLCHAR*)catalog.latin1(),
+#endif
+ catalog.length(),
+#ifdef UNICODE
+ schema.length() == 0 ? NULL : (SQLWCHAR*)schema.unicode(),
+#else
+ schema.length() == 0 ? NULL : (SQLCHAR*)schema.latin1(),
+#endif
+ schema.length(),
+#ifdef UNICODE
+ (SQLWCHAR*)table.unicode(),
+#else
+ (SQLCHAR*)table.latin1(),
+#endif
+ table.length(),
+ NULL,
+ 0 );
+#ifdef QT_CHECK_RANGE
+ if ( r != SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver::record: Unable to execute column list", d );
+#endif
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ // Store all fields in a StringList because some drivers can't detail fields in this FETCH loop
+ while ( r == SQL_SUCCESS ) {
+
+ fil.append( qMakeFieldInfo( hStmt, d ) );
+
+ r = SQLFetchScroll( hStmt,
+ SQL_FETCH_NEXT,
+ 0);
+ }
+
+ r = SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
+ if ( r!= SQL_SUCCESS )
+ qSqlWarning( "QODBCDriver: Unable to free statement handle " + QString::number(r), d );
+
+ return fil;
+}
+
+QSqlRecordInfo QODBCDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ QODBCResult* result = (QODBCResult*)query.result();
+ fil = result->d->rInf;
+ }
+ return fil;
+}
+
+SQLHANDLE QODBCDriver::environment()
+{
+ return d->hEnv;
+}
+
+SQLHANDLE QODBCDriver::connection()
+{
+ return d->hDbc;
+}
+
+QString QODBCDriver::formatValue( const QSqlField* field,
+ bool trimStrings ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else if ( field->type() == QVariant::DateTime ) {
+ // Use an escape sequence for the datetime fields
+ if ( field->value().toDateTime().isValid() ){
+ QDate dt = field->value().toDateTime().date();
+ QTime tm = field->value().toDateTime().time();
+ // Dateformat has to be "yyyy-MM-dd hh:mm:ss", with leading zeroes if month or day < 10
+ r = "{ ts '" +
+ QString::number(dt.year()) + "-" +
+ QString::number(dt.month()).rightJustify( 2, '0', TRUE ) + "-" +
+ QString::number(dt.day()).rightJustify( 2, '0', TRUE ) + " " +
+ tm.toString() +
+ "' }";
+ } else
+ r = nullText();
+ } else if ( field->type() == QVariant::ByteArray ) {
+ QByteArray ba = field->value().toByteArray();
+ QString res;
+ static const char hexchars[] = "0123456789abcdef";
+ for ( uint i = 0; i < ba.size(); ++i ) {
+ uchar s = (uchar) ba[(int)i];
+ res += hexchars[s >> 4];
+ res += hexchars[s & 0x0f];
+ }
+ r = "0x" + res;
+ } else {
+ r = QSqlDriver::formatValue( field, trimStrings );
+ }
+ return r;
+}
diff --git a/src/sql/drivers/odbc/qsql_odbc.h b/src/sql/drivers/odbc/qsql_odbc.h
new file mode 100644
index 0000000..67285c8
--- /dev/null
+++ b/src/sql/drivers/odbc/qsql_odbc.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Definition of ODBC driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_ODBC_H
+#define QSQL_ODBC_H
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qsqldriver.h>
+#include <qsqlfield.h>
+#include <qsqlresult.h>
+#include <qsqlindex.h>
+
+#if defined (Q_OS_WIN32)
+#include <qt_windows.h>
+#endif
+
+#if defined (Q_OS_MAC)
+// assume we use iodbc on MAC
+// comment next line out if you use a
+// unicode compatible manager
+# define Q_ODBC_VERSION_2
+#endif
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_ODBC
+#else
+#define Q_EXPORT_SQLDRIVER_ODBC Q_EXPORT
+#endif
+
+#ifdef Q_OS_UNIX
+#define HAVE_LONG_LONG 1 // force UnixODBC NOT to fall back to a struct for BIGINTs
+#endif
+
+#if defined(Q_CC_BOR)
+// workaround for Borland to make sure that SQLBIGINT is defined
+# define _MSC_VER 900
+#endif
+#include <sql.h>
+#if defined(Q_CC_BOR)
+# undef _MSC_VER
+#endif
+
+#include <sqlext.h>
+#include "debian_qsql_odbc.h"
+
+class QODBCPrivate;
+class QODBCDriver;
+class QSqlRecordInfo;
+
+class QODBCResult : public QSqlResult
+{
+ friend class QODBCDriver;
+public:
+ QODBCResult( const QODBCDriver * db, QODBCPrivate* p );
+ ~QODBCResult();
+
+ SQLHANDLE statement();
+ bool prepare( const QString& query );
+ bool exec();
+
+protected:
+ bool fetchNext();
+ bool fetchFirst();
+ bool fetchLast();
+ bool fetchPrior();
+ bool fetch(int i);
+ bool reset ( const QString& query );
+ QVariant data( int field );
+ bool isNull( int field );
+ int size();
+ int numRowsAffected();
+private:
+ QODBCPrivate* d;
+ typedef QMap<int,QVariant> FieldCache;
+ FieldCache fieldCache;
+ typedef QMap<int,bool> NullCache;
+ NullCache nullCache;
+};
+
+class Q_EXPORT_SQLDRIVER_ODBC QODBCDriver : public QSqlDriver
+{
+public:
+ QODBCDriver( QObject * parent=0, const char * name=0 );
+ QODBCDriver( SQLHANDLE env, SQLHANDLE con, QObject * parent=0, const char * name=0 );
+ ~QODBCDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ SQLHANDLE environment();
+ SQLHANDLE connection();
+
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ bool endTrans();
+ void cleanup();
+ QODBCPrivate* d;
+};
+
+#endif
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp
new file mode 100644
index 0000000..7fe1a91
--- /dev/null
+++ b/src/sql/drivers/psql/qsql_psql.cpp
@@ -0,0 +1,1117 @@
+/****************************************************************************
+**
+** Implementation of PostgreSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsql_psql.h"
+#include <private/qsqlextension_p.h>
+
+#include <math.h>
+
+#include <qpointarray.h>
+#include <qsqlrecord.h>
+#include <qregexp.h>
+#include <qdatetime.h>
+// PostgreSQL header <utils/elog.h> included by <postgres.h> redefines DEBUG.
+#if defined(DEBUG)
+# undef DEBUG
+#endif
+#include <postgres.h>
+#include <libpq/libpq-fs.h>
+// PostgreSQL header <catalog/pg_type.h> redefines errno erroneously.
+#if defined(errno)
+# undef errno
+#endif
+#define errno qt_psql_errno
+#include <catalog/pg_type.h>
+#undef errno
+#ifdef open
+# undef open
+#endif
+
+QPtrDict<QSqlDriverExtension> *qSqlDriverExtDict();
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+class QPSQLPrivate
+{
+public:
+ QPSQLPrivate():connection(0), result(0), isUtf8(FALSE) {}
+ PGconn *connection;
+ PGresult *result;
+ bool isUtf8;
+};
+
+class QPSQLDriverExtension : public QSqlDriverExtension
+{
+public:
+ QPSQLDriverExtension( QPSQLDriver *dri )
+ : QSqlDriverExtension(), driver(dri) { }
+ ~QPSQLDriverExtension() {}
+
+ bool isOpen() const;
+private:
+ QPSQLDriver *driver;
+};
+
+bool QPSQLDriverExtension::isOpen() const
+{
+ return PQstatus( driver->connection() ) == CONNECTION_OK;
+}
+
+class QPSQLOpenExtension : public QSqlOpenExtension
+{
+public:
+ QPSQLOpenExtension( QPSQLDriver *dri )
+ : QSqlOpenExtension(), driver(dri) { }
+ ~QPSQLOpenExtension() {}
+
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+private:
+ QPSQLDriver *driver;
+};
+
+bool QPSQLOpenExtension::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ return driver->open( db, user, password, host, port, connOpts );
+}
+
+static QSqlError qMakeError( const QString& err, int type, const QPSQLPrivate* p )
+{
+ const char *s = PQerrorMessage(p->connection);
+ QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s);
+ return QSqlError("QPSQL: " + err, msg, type);
+}
+
+static QVariant::Type qDecodePSQLType( int t )
+{
+ QVariant::Type type = QVariant::Invalid;
+ switch ( t ) {
+ case BOOLOID :
+ type = QVariant::Bool;
+ break;
+ case INT8OID :
+ type = QVariant::LongLong;
+ break;
+ case INT2OID :
+ // case INT2VECTOROID : // 7.x
+ case INT4OID :
+ type = QVariant::Int;
+ break;
+ case NUMERICOID :
+ case FLOAT4OID :
+ case FLOAT8OID :
+ type = QVariant::Double;
+ break;
+ case ABSTIMEOID :
+ case RELTIMEOID :
+ case DATEOID :
+ type = QVariant::Date;
+ break;
+ case TIMEOID :
+#ifdef TIMETZOID // 7.x
+ case TIMETZOID :
+#endif
+ type = QVariant::Time;
+ break;
+ case TIMESTAMPOID :
+#ifdef DATETIMEOID
+ // Postgres 6.x datetime workaround.
+ // DATETIMEOID == TIMESTAMPOID (only the names have changed)
+ case DATETIMEOID :
+#endif
+#ifdef TIMESTAMPTZOID
+ // Postgres 7.2 workaround
+ // TIMESTAMPTZOID == TIMESTAMPOID == DATETIMEOID
+ case TIMESTAMPTZOID :
+#endif
+ type = QVariant::DateTime;
+ break;
+ // case ZPBITOID : // 7.x
+ // case VARBITOID : // 7.x
+ case OIDOID :
+ case BYTEAOID :
+ type = QVariant::ByteArray;
+ break;
+ case REGPROCOID :
+ case TIDOID :
+ case XIDOID :
+ case CIDOID :
+ // case OIDVECTOROID : // 7.x
+ case UNKNOWNOID :
+ // case TINTERVALOID : // 7.x
+ type = QVariant::Invalid;
+ break;
+ default:
+ case CHAROID :
+ case BPCHAROID :
+ // case LZTEXTOID : // 7.x
+ case VARCHAROID :
+ case TEXTOID :
+ case NAMEOID :
+ case CASHOID :
+ case INETOID :
+ case CIDROID :
+ case CIRCLEOID :
+ type = QVariant::String;
+ break;
+ }
+ return type;
+}
+
+QPSQLResult::QPSQLResult( const QPSQLDriver* db, const QPSQLPrivate* p )
+: QSqlResult( db ),
+ currentSize( 0 )
+{
+ d = new QPSQLPrivate();
+ (*d) = (*p);
+}
+
+QPSQLResult::~QPSQLResult()
+{
+ cleanup();
+ delete d;
+}
+
+PGresult* QPSQLResult::result()
+{
+ return d->result;
+}
+
+void QPSQLResult::cleanup()
+{
+ if ( d->result )
+ PQclear( d->result );
+ d->result = 0;
+ setAt( -1 );
+ currentSize = 0;
+ setActive( FALSE );
+}
+
+bool QPSQLResult::fetch( int i )
+{
+ if ( !isActive() )
+ return FALSE;
+ if ( i < 0 )
+ return FALSE;
+ if ( i >= currentSize )
+ return FALSE;
+ if ( at() == i )
+ return TRUE;
+ setAt( i );
+ return TRUE;
+}
+
+bool QPSQLResult::fetchFirst()
+{
+ return fetch( 0 );
+}
+
+bool QPSQLResult::fetchLast()
+{
+ return fetch( PQntuples( d->result ) - 1 );
+}
+
+// some Postgres conversions
+static QPoint pointFromString( const QString& s)
+{
+ // format '(x,y)'
+ int pivot = s.find( ',' );
+ if ( pivot != -1 ) {
+ int x = s.mid( 1, pivot-1 ).toInt();
+ int y = s.mid( pivot+1, s.length()-pivot-2 ).toInt();
+ return QPoint( x, y ) ;
+ } else
+ return QPoint();
+}
+
+QVariant QPSQLResult::data( int i )
+{
+ if ( i >= PQnfields( d->result ) ) {
+ qWarning( "QPSQLResult::data: column %d out of range", i );
+ return QVariant();
+ }
+ int ptype = PQftype( d->result, i );
+ QVariant::Type type = qDecodePSQLType( ptype );
+ const QString val = ( d->isUtf8 && ptype != BYTEAOID ) ?
+ QString::fromUtf8( PQgetvalue( d->result, at(), i ) ) :
+ QString::fromLocal8Bit( PQgetvalue( d->result, at(), i ) );
+ if ( PQgetisnull( d->result, at(), i ) ) {
+ QVariant v;
+ v.cast( type );
+ return v;
+ }
+ switch ( type ) {
+ case QVariant::Bool:
+ {
+ QVariant b ( (bool)(val == "t"), 0 );
+ return ( b );
+ }
+ case QVariant::String:
+ return QVariant( val );
+ case QVariant::LongLong:
+ if ( val[0] == '-' )
+ return QVariant( val.toLongLong() );
+ else
+ return QVariant( val.toULongLong() );
+ case QVariant::Int:
+ return QVariant( val.toInt() );
+ case QVariant::Double:
+ if ( ptype == NUMERICOID )
+ return QVariant( val );
+ return QVariant( val.toDouble() );
+ case QVariant::Date:
+ if ( val.isEmpty() ) {
+ return QVariant( QDate() );
+ } else {
+ return QVariant( QDate::fromString( val, Qt::ISODate ) );
+ }
+ case QVariant::Time:
+ if ( val.isEmpty() )
+ return QVariant( QTime() );
+ if ( val.at( val.length() - 3 ) == '+' )
+ // strip the timezone
+ return QVariant( QTime::fromString( val.left( val.length() - 3 ), Qt::ISODate ) );
+ return QVariant( QTime::fromString( val, Qt::ISODate ) );
+ case QVariant::DateTime: {
+ if ( val.length() < 10 )
+ return QVariant( QDateTime() );
+ // remove the timezone
+ QString dtval = val;
+ if ( dtval.at( dtval.length() - 3 ) == '+' )
+ dtval.truncate( dtval.length() - 3 );
+ // milliseconds are sometimes returned with 2 digits only
+ if ( dtval.at( dtval.length() - 3 ).isPunct() )
+ dtval += '0';
+ if ( dtval.isEmpty() )
+ return QVariant( QDateTime() );
+ else
+ return QVariant( QDateTime::fromString( dtval, Qt::ISODate ) );
+ }
+ case QVariant::Point:
+ return QVariant( pointFromString( val ) );
+ case QVariant::Rect: // format '(x,y),(x',y')'
+ {
+ int pivot = val.find( "),(" );
+ if ( pivot != -1 )
+ return QVariant( QRect( pointFromString( val.mid(pivot+2,val.length()) ), pointFromString( val.mid(0,pivot+1) ) ) );
+ return QVariant( QRect() );
+ }
+ case QVariant::PointArray: // format '((x,y),(x1,y1),...,(xn,yn))'
+ {
+ QRegExp pointPattern("\\([0-9-]*,[0-9-]*\\)");
+ int points = val.contains( pointPattern );
+ QPointArray parray( points );
+ int idx = 1;
+ for ( int i = 0; i < points; i++ ){
+ int start = val.find( pointPattern, idx );
+ int end = -1;
+ if ( start != -1 ) {
+ end = val.find( ')', start+1 );
+ if ( end != -1 ) {
+ parray.setPoint( i, pointFromString( val.mid(idx, end-idx+1) ) );
+ }
+ else
+ parray.setPoint( i, QPoint() );
+ } else {
+ parray.setPoint( i, QPoint() );
+ break;
+ }
+ idx = end+2;
+ }
+ return QVariant( parray );
+ }
+ case QVariant::ByteArray: {
+ if ( ptype == BYTEAOID ) {
+ uint i = 0;
+ int index = 0;
+ uint len = val.length();
+ static const QChar backslash( '\\' );
+ QByteArray ba( (int)len );
+ while ( i < len ) {
+ if ( val.at( i ) == backslash ) {
+ if ( val.at( i + 1 ).isDigit() ) {
+ ba[ index++ ] = (char)(val.mid( i + 1, 3 ).toInt( 0, 8 ));
+ i += 4;
+ } else {
+ ba[ index++ ] = val.at( i + 1 );
+ i += 2;
+ }
+ } else {
+ ba[ index++ ] = val.at( i++ ).unicode();
+ }
+ }
+ ba.resize( index );
+ return QVariant( ba );
+ }
+
+ QByteArray ba;
+ ((QSqlDriver*)driver())->beginTransaction();
+ Oid oid = val.toInt();
+ int fd = lo_open( d->connection, oid, INV_READ );
+#ifdef QT_CHECK_RANGE
+ if ( fd < 0) {
+ qWarning( "QPSQLResult::data: unable to open large object for read" );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+#endif
+ int size = 0;
+ int retval = lo_lseek( d->connection, fd, 0L, SEEK_END );
+ if ( retval >= 0 ) {
+ size = lo_tell( d->connection, fd );
+ lo_lseek( d->connection, fd, 0L, SEEK_SET );
+ }
+ if ( size == 0 ) {
+ lo_close( d->connection, fd );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+ char * buf = new char[ size ];
+
+#ifdef Q_OS_WIN32
+ // ### For some reason lo_read() fails if we try to read more than
+ // ### 32760 bytes
+ char * p = buf;
+ int nread = 0;
+
+ while( size < nread ){
+ retval = lo_read( d->connection, fd, p, 32760 );
+ nread += retval;
+ p += retval;
+ }
+#else
+ retval = lo_read( d->connection, fd, buf, size );
+#endif
+
+ if (retval < 0) {
+ qWarning( "QPSQLResult::data: unable to read large object" );
+ } else {
+ ba.duplicate( buf, size );
+ }
+ delete [] buf;
+ lo_close( d->connection, fd );
+ ((QSqlDriver*)driver())->commitTransaction();
+ return QVariant( ba );
+ }
+ default:
+ case QVariant::Invalid:
+#ifdef QT_CHECK_RANGE
+ qWarning("QPSQLResult::data: unknown data type");
+#endif
+ ;
+ }
+ return QVariant();
+}
+
+bool QPSQLResult::isNull( int field )
+{
+ PQgetvalue( d->result, at(), field );
+ return PQgetisnull( d->result, at(), field );
+}
+
+bool QPSQLResult::reset ( const QString& query )
+{
+ cleanup();
+ if ( !driver() )
+ return FALSE;
+ if ( !driver()->isOpen() || driver()->isOpenError() )
+ return FALSE;
+ setActive( FALSE );
+ setAt( QSql::BeforeFirst );
+ if ( d->result )
+ PQclear( d->result );
+ if ( d->isUtf8 ) {
+ d->result = PQexec( d->connection, query.utf8().data() );
+ } else {
+ d->result = PQexec( d->connection, query.local8Bit().data() );
+ }
+ int status = PQresultStatus( d->result );
+ if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) {
+ if ( status == PGRES_TUPLES_OK ) {
+ setSelect( TRUE );
+ currentSize = PQntuples( d->result );
+ } else {
+ setSelect( FALSE );
+ currentSize = -1;
+ }
+ setActive( TRUE );
+ return TRUE;
+ }
+ setLastError( qMakeError( "Unable to create query", QSqlError::Statement, d ) );
+ return FALSE;
+}
+
+int QPSQLResult::size()
+{
+ return currentSize;
+}
+
+int QPSQLResult::numRowsAffected()
+{
+ return QString( PQcmdTuples( d->result ) ).toInt();
+}
+
+///////////////////////////////////////////////////////////////////
+
+static bool setEncodingUtf8( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "SET CLIENT_ENCODING TO 'UNICODE'" );
+ int status = PQresultStatus( result );
+ PQclear( result );
+ return status == PGRES_COMMAND_OK;
+}
+
+static void setDatestyle( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "SET DATESTYLE TO 'ISO'" );
+#ifdef QT_CHECK_RANGE
+ int status = PQresultStatus( result );
+ if ( status != PGRES_COMMAND_OK )
+ qWarning( "%s", PQerrorMessage( connection ) );
+#endif
+ PQclear( result );
+}
+
+static QPSQLDriver::Protocol getPSQLVersion( PGconn* connection )
+{
+ PGresult* result = PQexec( connection, "select version()" );
+ int status = PQresultStatus( result );
+ if ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) {
+ QString val( PQgetvalue( result, 0, 0 ) );
+ PQclear( result );
+ QRegExp rx( "(\\d+)\\.(\\d+)" );
+ rx.setMinimal ( TRUE ); // enforce non-greedy RegExp
+ if ( rx.search( val ) != -1 ) {
+ int vMaj = rx.cap( 1 ).toInt();
+ int vMin = rx.cap( 2 ).toInt();
+ if ( vMaj < 6 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "This version of PostgreSQL is not supported and may not work." );
+#endif
+ return QPSQLDriver::Version6;
+ }
+ if ( vMaj == 6 ) {
+ return QPSQLDriver::Version6;
+ } else if ( vMaj == 7 ) {
+ if ( vMin < 1 )
+ return QPSQLDriver::Version7;
+ else if ( vMin < 3 )
+ return QPSQLDriver::Version71;
+ }
+ return QPSQLDriver::Version73;
+ }
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "This version of PostgreSQL is not supported and may not work." );
+#endif
+ }
+
+ return QPSQLDriver::Version6;
+}
+
+QPSQLDriver::QPSQLDriver( QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QPSQL"), pro( QPSQLDriver::Version6 )
+{
+ init();
+}
+
+QPSQLDriver::QPSQLDriver( PGconn * conn, QObject * parent, const char * name )
+ : QSqlDriver(parent,name ? name : "QPSQL"), pro( QPSQLDriver::Version6 )
+{
+ init();
+ d->connection = conn;
+ if ( conn ) {
+ pro = getPSQLVersion( d->connection );
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ }
+}
+
+void QPSQLDriver::init()
+{
+ qSqlDriverExtDict()->insert( this, new QPSQLDriverExtension(this) );
+ qSqlOpenExtDict()->insert( this, new QPSQLOpenExtension(this) );
+
+ d = new QPSQLPrivate();
+}
+
+QPSQLDriver::~QPSQLDriver()
+{
+ if ( d->connection )
+ PQfinish( d->connection );
+ delete d;
+ if ( !qSqlDriverExtDict()->isEmpty() ) {
+ QSqlDriverExtension *ext = qSqlDriverExtDict()->take( this );
+ delete ext;
+ }
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->take( this );
+ delete ext;
+ }
+}
+
+PGconn* QPSQLDriver::connection()
+{
+ return d->connection;
+}
+
+
+bool QPSQLDriver::hasFeature( DriverFeature f ) const
+{
+ switch ( f ) {
+ case Transactions:
+ return TRUE;
+ case QuerySize:
+ return TRUE;
+ case BLOB:
+ return pro >= QPSQLDriver::Version71;
+ case Unicode:
+ return d->isUtf8;
+ default:
+ return FALSE;
+ }
+}
+
+bool QPSQLDriver::open( const QString&,
+ const QString&,
+ const QString&,
+ const QString&,
+ int )
+{
+ qWarning("QPSQLDriver::open(): This version of open() is no longer supported." );
+ return FALSE;
+}
+
+bool QPSQLDriver::open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString& connOpts )
+{
+ if ( isOpen() )
+ close();
+ QString connectString;
+ if ( host.length() )
+ connectString.append( "host=" ).append( host );
+ if ( db.length() )
+ connectString.append( " dbname=" ).append( db );
+ if ( user.length() )
+ connectString.append( " user=" ).append( user );
+ if ( password.length() )
+ connectString.append( " password=" ).append( password );
+ if ( port > -1 )
+ connectString.append( " port=" ).append( QString::number( port ) );
+
+ // add any connect options - the server will handle error detection
+ if ( !connOpts.isEmpty() )
+ connectString += " " + QStringList::split( ';', connOpts ).join( " " );
+
+ d->connection = PQconnectdb( connectString.local8Bit().data() );
+ if ( PQstatus( d->connection ) == CONNECTION_BAD ) {
+ setLastError( qMakeError("Unable to connect", QSqlError::Connection, d ) );
+ setOpenError( TRUE );
+ return FALSE;
+ }
+
+ pro = getPSQLVersion( d->connection );
+ d->isUtf8 = setEncodingUtf8( d->connection );
+ setDatestyle( d->connection );
+
+ setOpen( TRUE );
+ setOpenError( FALSE );
+ return TRUE;
+}
+
+void QPSQLDriver::close()
+{
+ if ( isOpen() ) {
+ if (d->connection)
+ PQfinish( d->connection );
+ d->connection = 0;
+ setOpen( FALSE );
+ setOpenError( FALSE );
+ }
+}
+
+QSqlQuery QPSQLDriver::createQuery() const
+{
+ return QSqlQuery( new QPSQLResult( this, d ) );
+}
+
+bool QPSQLDriver::beginTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::beginTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "BEGIN" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ PQclear( res );
+ setLastError( qMakeError( "Could not begin transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool QPSQLDriver::commitTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::commitTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "COMMIT" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ PQclear( res );
+ setLastError( qMakeError( "Could not commit transaction", QSqlError::Transaction, d ) );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+bool QPSQLDriver::rollbackTransaction()
+{
+ if ( !isOpen() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QPSQLDriver::rollbackTransaction: Database not open" );
+#endif
+ return FALSE;
+ }
+ PGresult* res = PQexec( d->connection, "ROLLBACK" );
+ if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK ) {
+ setLastError( qMakeError( "Could not rollback transaction", QSqlError::Transaction, d ) );
+ PQclear( res );
+ return FALSE;
+ }
+ PQclear( res );
+ return TRUE;
+}
+
+QStringList QPSQLDriver::tables( const QString& typeName ) const
+{
+ QStringList tl;
+ if ( !isOpen() )
+ return tl;
+ int type = typeName.toInt();
+ QSqlQuery t = createQuery();
+ t.setForwardOnly( TRUE );
+
+ if ( typeName.isEmpty() || ((type & (int)QSql::Tables) == (int)QSql::Tables) ) {
+
+ QString query("select relname from pg_class where (relkind = 'r') "
+ "and (relname !~ '^Inv') "
+ "and (relname !~ '^pg_') ");
+ if (pro >= QPSQLDriver::Version73)
+ query.append("and (relnamespace not in "
+ "(select oid from pg_namespace where nspname = 'information_schema')) "
+ "and pg_table_is_visible(pg_class.oid) ");
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+ if ( (type & (int)QSql::Views) == (int)QSql::Views ) {
+ QString query("select relname from pg_class where ( relkind = 'v' ) "
+ "and ( relname !~ '^Inv' ) "
+ "and ( relname !~ '^pg_' ) ");
+ if (pro >= QPSQLDriver::Version73)
+ query.append("and (relnamespace not in "
+ "(select oid from pg_namespace where nspname = 'information_schema')) "
+ "and pg_table_is_visible(pg_class.oid) ");
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+ if ( (type & (int)QSql::SystemTables) == (int)QSql::SystemTables ) {
+ QString query( "select relname from pg_class where ( relkind = 'r' ) "
+ "and ( relname like 'pg_%' ) " );
+ if (pro >= QPSQLDriver::Version73)
+ query.append( "and pg_table_is_visible(pg_class.oid) " );
+ t.exec(query);
+ while ( t.next() )
+ tl.append( t.value(0).toString() );
+ }
+
+ return tl;
+}
+
+QSqlIndex QPSQLDriver::primaryIndex( const QString& tablename ) const
+{
+ QSqlIndex idx( tablename );
+ if ( !isOpen() )
+ return idx;
+ QSqlQuery i = createQuery();
+ QString stmt;
+
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_att1.attname, int(pg_att1.atttypid), pg_att2.attnum, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' ";
+ break;
+ case QPSQLDriver::Version7:
+ case QPSQLDriver::Version71:
+ stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
+ "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
+ "where lower(pg_cl.relname) = '%1_pkey' "
+ "and pg_table_is_visible(pg_cl.oid) "
+ "and pg_att1.attisdropped = false ";
+ break;
+ }
+ stmt += "and pg_cl.oid = pg_ind.indexrelid "
+ "and pg_att2.attrelid = pg_ind.indexrelid "
+ "and pg_att1.attrelid = pg_ind.indrelid "
+ "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
+ "order by pg_att2.attnum";
+
+ i.exec( stmt.arg( tablename.lower() ) );
+ while ( i.isActive() && i.next() ) {
+ QSqlField f( i.value(0).toString(), qDecodePSQLType( i.value(1).toInt() ) );
+ idx.append( f );
+ idx.setName( i.value(2).toString() );
+ }
+ return idx;
+}
+
+QSqlRecord QPSQLDriver::record( const QString& tablename ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ QString stmt;
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_attribute.attname, int(pg_attribute.atttypid) "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version7:
+ case QPSQLDriver::Version71:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_table_is_visible(pg_class.oid) "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attisdropped = false "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ }
+
+ QSqlQuery fi = createQuery();
+ fi.exec( stmt.arg( tablename.lower() ) );
+ while ( fi.next() ) {
+ QSqlField f( fi.value(0).toString(), qDecodePSQLType( fi.value(1).toInt() ) );
+ fil.append( f );
+ }
+ return fil;
+}
+
+QSqlRecord QPSQLDriver::record( const QSqlQuery& query ) const
+{
+ QSqlRecord fil;
+ if ( !isOpen() )
+ return fil;
+ if ( query.isActive() && query.driver() == this ) {
+ QPSQLResult* result = (QPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ QString name = PQfname( result->d->result, i );
+ QVariant::Type type = qDecodePSQLType( PQftype( result->d->result, i ) );
+ QSqlField rf( name, type );
+ fil.append( rf );
+ }
+ }
+ return fil;
+}
+
+QSqlRecordInfo QPSQLDriver::recordInfo( const QString& tablename ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+
+ QString stmt;
+ switch( pro ) {
+ case QPSQLDriver::Version6:
+ stmt = "select pg_attribute.attname, int(pg_attribute.atttypid), pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, int(pg_attribute.attrelid), pg_attribute.attnum "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version7:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attribute.attrelid::int, pg_attribute.attnum "
+ "from pg_class, pg_attribute "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid ";
+ break;
+ case QPSQLDriver::Version71:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "order by pg_attribute.attnum ";
+ break;
+ case QPSQLDriver::Version73:
+ stmt = "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.attnotnull, "
+ "pg_attribute.attlen, pg_attribute.atttypmod, pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where lower(pg_class.relname) = '%1' "
+ "and pg_table_is_visible(pg_class.oid) "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "and pg_attribute.attisdropped = false "
+ "order by pg_attribute.attnum ";
+ break;
+ }
+
+ QSqlQuery query = createQuery();
+ query.exec( stmt.arg( tablename.lower() ) );
+ if ( pro >= QPSQLDriver::Version71 ) {
+ while ( query.next() ) {
+ int len = query.value( 3 ).toInt();
+ int precision = query.value( 4 ).toInt();
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ QString defVal = query.value( 5 ).toString();
+ if ( !defVal.isEmpty() && defVal.startsWith( "'" ) )
+ defVal = defVal.mid( 1, defVal.length() - 2 );
+ info.append( QSqlFieldInfo( query.value( 0 ).toString(),
+ qDecodePSQLType( query.value( 1 ).toInt() ),
+ query.value( 2 ).toBool(),
+ len,
+ precision,
+ defVal,
+ query.value( 1 ).toInt() ) );
+ }
+ } else {
+ // Postgres < 7.1 cannot handle outer joins
+ while ( query.next() ) {
+ QString defVal;
+ QString stmt2 = "select pg_attrdef.adsrc from pg_attrdef where "
+ "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ";
+ QSqlQuery query2 = createQuery();
+ query2.exec( stmt2.arg( query.value( 5 ).toInt() ).arg( query.value( 6 ).toInt() ) );
+ if ( query2.isActive() && query2.next() )
+ defVal = query2.value( 0 ).toString();
+ if ( !defVal.isEmpty() && defVal.startsWith( "'" ) )
+ defVal = defVal.mid( 1, defVal.length() - 2 );
+ int len = query.value( 3 ).toInt();
+ int precision = query.value( 4 ).toInt();
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ info.append( QSqlFieldInfo( query.value( 0 ).toString(),
+ qDecodePSQLType( query.value( 1 ).toInt() ),
+ query.value( 2 ).toBool(),
+ len,
+ precision,
+ defVal,
+ query.value( 1 ).toInt() ) );
+ }
+ }
+
+ return info;
+}
+
+QSqlRecordInfo QPSQLDriver::recordInfo( const QSqlQuery& query ) const
+{
+ QSqlRecordInfo info;
+ if ( !isOpen() )
+ return info;
+ if ( query.isActive() && query.driver() == this ) {
+ QPSQLResult* result = (QPSQLResult*)query.result();
+ int count = PQnfields( result->d->result );
+ for ( int i = 0; i < count; ++i ) {
+ QString name = PQfname( result->d->result, i );
+ int len = PQfsize( result->d->result, i );
+ int precision = PQfmod( result->d->result, i );
+ // swap length and precision if length == -1
+ if ( len == -1 && precision > -1 ) {
+ len = precision - 4;
+ precision = -1;
+ }
+ info.append( QSqlFieldInfo( name,
+ qDecodePSQLType( PQftype( result->d->result, i ) ),
+ -1,
+ len,
+ precision,
+ QVariant(),
+ PQftype( result->d->result, i ) ) );
+ }
+ }
+ return info;
+}
+
+QString QPSQLDriver::formatValue( const QSqlField* field,
+ bool ) const
+{
+ QString r;
+ if ( field->isNull() ) {
+ r = nullText();
+ } else {
+ switch ( field->type() ) {
+ case QVariant::DateTime:
+ if ( field->value().toDateTime().isValid() ) {
+ QDate dt = field->value().toDateTime().date();
+ QTime tm = field->value().toDateTime().time();
+ // msecs need to be right aligned otherwise psql
+ // interpretes them wrong
+ r = "'" + QString::number( dt.year() ) + "-" +
+ QString::number( dt.month() ) + "-" +
+ QString::number( dt.day() ) + " " +
+ tm.toString() + "." +
+ QString::number( tm.msec() ).rightJustify( 3, '0' ) + "'";
+ } else {
+ r = nullText();
+ }
+ break;
+ case QVariant::Time:
+ if ( field->value().toTime().isValid() ) {
+ r = field->value().toTime().toString( Qt::ISODate );
+ } else {
+ r = nullText();
+ }
+ case QVariant::String:
+ case QVariant::CString: {
+ switch ( field->value().type() ) {
+ case QVariant::Rect: {
+ QRect rec = field->value().toRect();
+ // upper right corner then lower left according to psql docs
+ r = "'(" + QString::number( rec.right() ) +
+ "," + QString::number( rec.bottom() ) +
+ "),(" + QString::number( rec.left() ) +
+ "," + QString::number( rec.top() ) + ")'";
+ break;
+ }
+ case QVariant::Point: {
+ QPoint p = field->value().toPoint();
+ r = "'(" + QString::number( p.x() ) +
+ "," + QString::number( p.y() ) + ")'";
+ break;
+ }
+ case QVariant::PointArray: {
+ QPointArray pa = field->value().toPointArray();
+ r = "' ";
+ for ( int i = 0; i < (int)pa.size(); ++i ) {
+ r += "(" + QString::number( pa[i].x() ) +
+ "," + QString::number( pa[i].y() ) + "),";
+ }
+ r.truncate( r.length() - 1 );
+ r += "'";
+ break;
+ }
+ default:
+ // Escape '\' characters
+ r = QSqlDriver::formatValue( field );
+ r.replace( "\\", "\\\\" );
+ break;
+ }
+ break;
+ }
+ case QVariant::Bool:
+ if ( field->value().toBool() )
+ r = "TRUE";
+ else
+ r = "FALSE";
+ break;
+ case QVariant::ByteArray: {
+ QByteArray ba = field->value().asByteArray();
+ QString res;
+ r = "'";
+ unsigned char uc;
+ for ( int i = 0; i < (int)ba.size(); ++i ) {
+ uc = (unsigned char) ba[ i ];
+ if ( uc > 40 && uc < 92 ) {
+ r += uc;
+ } else {
+ r += "\\\\";
+ r += QString::number( (unsigned char) ba[ i ], 8 ).rightJustify( 3, '0', TRUE );
+ }
+ }
+ r += "'";
+ break;
+ }
+ default:
+ r = QSqlDriver::formatValue( field );
+ break;
+ }
+ }
+ return r;
+}
diff --git a/src/sql/drivers/psql/qsql_psql.h b/src/sql/drivers/psql/qsql_psql.h
new file mode 100644
index 0000000..fd9cbe6
--- /dev/null
+++ b/src/sql/drivers/psql/qsql_psql.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Definition of PostgreSQL driver classes
+**
+** Created : 001103
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_PSQL_H
+#define QSQL_PSQL_H
+
+#include <qsqlresult.h>
+#include <qsqlfield.h>
+#include <qsqldriver.h>
+#include <libpq-fe.h>
+
+#ifdef QT_PLUGIN
+#define Q_EXPORT_SQLDRIVER_PSQL
+#else
+#define Q_EXPORT_SQLDRIVER_PSQL Q_EXPORT
+#endif
+
+class QPSQLPrivate;
+class QPSQLDriver;
+class QSqlRecordInfo;
+
+class QPSQLResult : public QSqlResult
+{
+ friend class QPSQLDriver;
+public:
+ QPSQLResult( const QPSQLDriver* db, const QPSQLPrivate* p );
+ ~QPSQLResult();
+ PGresult* result();
+protected:
+ void cleanup();
+ bool fetch( int i );
+ bool fetchFirst();
+ bool fetchLast();
+ QVariant data( int i );
+ bool isNull( int field );
+ bool reset ( const QString& query );
+ int size();
+ int numRowsAffected();
+private:
+ int currentSize;
+ QPSQLPrivate* d;
+};
+
+class Q_EXPORT_SQLDRIVER_PSQL QPSQLDriver : public QSqlDriver
+{
+public:
+ enum Protocol {
+ Version6 = 6,
+ Version7 = 7,
+ Version71 = 8,
+ Version73 = 9
+ };
+
+ QPSQLDriver( QObject * parent=0, const char * name=0 );
+ QPSQLDriver( PGconn * conn, QObject * parent=0, const char * name=0 );
+ ~QPSQLDriver();
+ bool hasFeature( DriverFeature f ) const;
+ bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 );
+ void close();
+ QSqlQuery createQuery() const;
+ QStringList tables( const QString& user ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+
+ Protocol protocol() const { return pro; }
+ PGconn* connection();
+ QString formatValue( const QSqlField* field,
+ bool trimStrings ) const;
+
+ // ### remove me for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+protected:
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+private:
+ void init();
+ Protocol pro;
+ QPSQLPrivate* d;
+};
+
+#endif
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp
new file mode 100644
index 0000000..f8c7ffa
--- /dev/null
+++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** Implementation of SQLite driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsql_sqlite.h"
+
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <qfile.h>
+
+#if (QT_VERSION-0 < 0x030000)
+# include <qvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+# include "../../../3rdparty/libraries/sqlite/sqlite.h"
+#else
+# include <qptrvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+# include <sqlite.h>
+#endif
+
+typedef struct sqlite_vm sqlite_vm;
+
+#define QSQLITE_DRIVER_NAME "QSQLITE"
+
+static QSqlVariant::Type nameToType(const QString& typeName)
+{
+ QString tName = typeName.upper();
+ if (tName.startsWith("INT"))
+ return QSqlVariant::Int;
+ if (tName.startsWith("FLOAT") || tName.startsWith("NUMERIC"))
+ return QSqlVariant::Double;
+ if (tName.startsWith("BOOL"))
+ return QSqlVariant::Bool;
+ // SQLite is typeless - consider everything else as string
+ return QSqlVariant::String;
+}
+
+class QSQLiteDriverPrivate
+{
+public:
+ QSQLiteDriverPrivate();
+ sqlite *access;
+ bool utf8;
+};
+
+QSQLiteDriverPrivate::QSQLiteDriverPrivate() : access(0)
+{
+ utf8 = (qstrcmp(sqlite_encoding, "UTF-8") == 0);
+}
+
+class QSQLiteResultPrivate
+{
+public:
+ QSQLiteResultPrivate(QSQLiteResult *res);
+ void cleanup();
+ bool fetchNext(QtSqlCachedResult::RowCache *row);
+ bool isSelect();
+ // initializes the recordInfo and the cache
+ void init(const char **cnames, int numCols, QtSqlCachedResult::RowCache **row = 0);
+ void finalize();
+
+ QSQLiteResult* q;
+ sqlite *access;
+
+ // and we have too keep our own struct for the data (sqlite works via
+ // callback.
+ const char *currentTail;
+ sqlite_vm *currentMachine;
+
+ uint skippedStatus: 1; // the status of the fetchNext() that's skipped
+ QtSqlCachedResult::RowCache *skipRow;
+
+ uint utf8: 1;
+ QSqlRecordInfo rInf;
+};
+
+static const uint initial_cache_size = 128;
+
+QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0), currentTail(0),
+ currentMachine(0), skippedStatus(FALSE), skipRow(0), utf8(FALSE)
+{
+}
+
+void QSQLiteResultPrivate::cleanup()
+{
+ finalize();
+ rInf.clear();
+ currentTail = 0;
+ currentMachine = 0;
+ skippedStatus = FALSE;
+ delete skipRow;
+ skipRow = 0;
+ q->setAt(QSql::BeforeFirst);
+ q->setActive(FALSE);
+ q->cleanup();
+}
+
+void QSQLiteResultPrivate::finalize()
+{
+ if (!currentMachine)
+ return;
+
+ char* err = 0;
+ int res = sqlite_finalize(currentMachine, &err);
+ if (err) {
+ q->setLastError(QSqlError("Unable to fetch results", err, QSqlError::Statement, res));
+ sqlite_freemem(err);
+ }
+ currentMachine = 0;
+}
+
+// called on first fetch
+void QSQLiteResultPrivate::init(const char **cnames, int numCols, QtSqlCachedResult::RowCache **row)
+{
+ if (!cnames)
+ return;
+
+ rInf.clear();
+ if (numCols <= 0)
+ return;
+
+ for (int i = 0; i < numCols; ++i) {
+ const char* lastDot = strrchr(cnames[i], '.');
+ const char* fieldName = lastDot ? lastDot + 1 : cnames[i];
+ rInf.append(QSqlFieldInfo(fieldName, nameToType(cnames[i+numCols])));
+ }
+ // skip the first fetch
+ if (row && !*row) {
+ *row = new QtSqlCachedResult::RowCache(numCols);
+ skipRow = *row;
+ }
+}
+
+bool QSQLiteResultPrivate::fetchNext(QtSqlCachedResult::RowCache* row)
+{
+ // may be caching.
+ const char **fvals;
+ const char **cnames;
+ int colNum;
+ int res;
+ int i;
+
+ if (skipRow) {
+ // already fetched
+ if (row)
+ *row = *skipRow;
+ delete skipRow;
+ skipRow = 0;
+ return skippedStatus;
+ }
+
+ if (!currentMachine)
+ return FALSE;
+
+ // keep trying while busy, wish I could implement this better.
+ while ((res = sqlite_step(currentMachine, &colNum, &fvals, &cnames)) == SQLITE_BUSY) {
+ // sleep instead requesting result again immidiately.
+#if defined Q_WS_WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+
+ switch(res) {
+ case SQLITE_ROW:
+ // check to see if should fill out columns
+ if (rInf.isEmpty())
+ // must be first call.
+ init(cnames, colNum, &row);
+ if (!fvals)
+ return FALSE;
+ if (!row)
+ return TRUE;
+ for (i = 0; i < colNum; ++i)
+ (*row)[i] = utf8 ? QString::fromUtf8(fvals[i]) : QString(fvals[i]);
+ return TRUE;
+ case SQLITE_DONE:
+ if (rInf.isEmpty())
+ // must be first call.
+ init(cnames, colNum);
+ q->setAt(QSql::AfterLast);
+ return FALSE;
+ case SQLITE_ERROR:
+ case SQLITE_MISUSE:
+ default:
+ // something wrong, don't get col info, but still return false
+ finalize(); // finalize to get the error message.
+ q->setAt(QSql::AfterLast);
+ return FALSE;
+ }
+ return FALSE;
+}
+
+QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
+: QtSqlCachedResult(db)
+{
+ d = new QSQLiteResultPrivate(this);
+ d->access = db->d->access;
+ d->utf8 = db->d->utf8;
+}
+
+QSQLiteResult::~QSQLiteResult()
+{
+ d->cleanup();
+ delete d;
+}
+
+/*
+ Execute \a query.
+*/
+bool QSQLiteResult::reset (const QString& query)
+{
+ // this is where we build a query.
+ if (!driver())
+ return FALSE;
+ if (!driver()-> isOpen() || driver()->isOpenError())
+ return FALSE;
+
+ d->cleanup();
+
+ // Um, ok. callback based so.... pass private static function for this.
+ setSelect(FALSE);
+ char *err = 0;
+ int res = sqlite_compile(d->access,
+ d->utf8 ? (const char*)query.utf8().data() : query.ascii(),
+ &(d->currentTail),
+ &(d->currentMachine),
+ &err);
+ if (res != SQLITE_OK || err) {
+ setLastError(QSqlError("Unable to execute statement", err, QSqlError::Statement, res));
+ sqlite_freemem(err);
+ }
+ //if (*d->currentTail != '\000' then there is more sql to eval
+ if (!d->currentMachine) {
+ setActive(FALSE);
+ return FALSE;
+ }
+ // we have to fetch one row to find out about
+ // the structure of the result set
+ d->skippedStatus = d->fetchNext(0);
+ setSelect(!d->rInf.isEmpty());
+ if (isSelect())
+ init(d->rInf.count());
+ setActive(TRUE);
+ return TRUE;
+}
+
+bool QSQLiteResult::gotoNext(QtSqlCachedResult::RowCache* row)
+{
+ return d->fetchNext(row);
+}
+
+int QSQLiteResult::size()
+{
+ return -1;
+}
+
+int QSQLiteResult::numRowsAffected()
+{
+ return sqlite_changes(d->access);
+}
+
+/////////////////////////////////////////////////////////
+
+QSQLiteDriver::QSQLiteDriver(QObject * parent, const char * name)
+ : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
+{
+ d = new QSQLiteDriverPrivate();
+}
+
+QSQLiteDriver::QSQLiteDriver(sqlite *connection, QObject *parent, const char *name)
+ : QSqlDriver(parent, name ? name : QSQLITE_DRIVER_NAME)
+{
+ d = new QSQLiteDriverPrivate();
+ d->access = connection;
+ setOpen(TRUE);
+ setOpenError(FALSE);
+}
+
+
+QSQLiteDriver::~QSQLiteDriver()
+{
+ delete d;
+}
+
+bool QSQLiteDriver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+ return TRUE;
+#if (QT_VERSION-0 >= 0x030000)
+ case Unicode:
+ return d->utf8;
+#endif
+// case BLOB:
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ SQLite dbs have no user name, passwords, hosts or ports.
+ just file names.
+*/
+bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
+{
+ if (isOpen())
+ close();
+
+ if (db.isEmpty())
+ return FALSE;
+
+ char* err = 0;
+ d->access = sqlite_open(QFile::encodeName(db), 0, &err);
+ if (err) {
+ setLastError(QSqlError("Error to open database", err, QSqlError::Connection));
+ sqlite_freemem(err);
+ err = 0;
+ }
+
+ if (d->access) {
+ setOpen(TRUE);
+ setOpenError(FALSE);
+ return TRUE;
+ }
+ setOpenError(TRUE);
+ return FALSE;
+}
+
+void QSQLiteDriver::close()
+{
+ if (isOpen()) {
+ sqlite_close(d->access);
+ d->access = 0;
+ setOpen(FALSE);
+ setOpenError(FALSE);
+ }
+}
+
+QSqlQuery QSQLiteDriver::createQuery() const
+{
+ return QSqlQuery(new QSQLiteResult(this));
+}
+
+bool QSQLiteDriver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "BEGIN", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to begin transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool QSQLiteDriver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "COMMIT", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to commit transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+bool QSQLiteDriver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return FALSE;
+
+ char* err;
+ int res = sqlite_exec(d->access, "ROLLBACK", 0, this, &err);
+
+ if (res == SQLITE_OK)
+ return TRUE;
+
+ setLastError(QSqlError("Unable to rollback Transaction", err, QSqlError::Transaction, res));
+ sqlite_freemem(err);
+ return FALSE;
+}
+
+QStringList QSQLiteDriver::tables(const QString &typeName) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+ int type = typeName.toInt();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+#if (QT_VERSION-0 >= 0x030000)
+ if ((type & (int)QSql::Tables) && (type & (int)QSql::Views))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+ else if (typeName.isEmpty() || (type & (int)QSql::Tables))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table'");
+ else if (type & (int)QSql::Views)
+ q.exec("SELECT name FROM sqlite_master WHERE type='view'");
+#else
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+#endif
+
+
+ if (q.isActive()) {
+ while(q.next())
+ res.append(q.value(0).toString());
+ }
+
+#if (QT_VERSION-0 >= 0x030000)
+ if (type & (int)QSql::SystemTables) {
+ // there are no internal tables beside this one:
+ res.append("sqlite_master");
+ }
+#endif
+
+ return res;
+}
+
+QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
+{
+ QSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
+
+ if (!isOpen())
+ return QSqlIndex();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ // finrst find a UNIQUE INDEX
+ q.exec("PRAGMA index_list('" + tblname + "');");
+ QString indexname;
+ while(q.next()) {
+ if (q.value(2).toInt()==1) {
+ indexname = q.value(1).toString();
+ break;
+ }
+ }
+ if (indexname.isEmpty())
+ return QSqlIndex();
+
+ q.exec("PRAGMA index_info('" + indexname + "');");
+
+ QSqlIndex index(tblname, indexname);
+ while(q.next()) {
+ QString name = q.value(2).toString();
+ QSqlVariant::Type type = QSqlVariant::Invalid;
+ if (rec.contains(name))
+ type = rec.find(name).type();
+ index.append(QSqlField(name, type));
+ }
+ return index;
+}
+
+QSqlRecordInfo QSQLiteDriver::recordInfo(const QString &tbl) const
+{
+ if (!isOpen())
+ return QSqlRecordInfo();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT * FROM " + tbl + " LIMIT 1");
+ return recordInfo(q);
+}
+
+QSqlRecord QSQLiteDriver::record(const QString &tblname) const
+{
+ if (!isOpen())
+ return QSqlRecord();
+
+ return recordInfo(tblname).toRecord();
+}
+
+QSqlRecord QSQLiteDriver::record(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLiteResult* result = (QSQLiteResult*)query.result();
+ return result->d->rInf.toRecord();
+ }
+ return QSqlRecord();
+}
+
+QSqlRecordInfo QSQLiteDriver::recordInfo(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLiteResult* result = (QSQLiteResult*)query.result();
+ return result->d->rInf;
+ }
+ return QSqlRecordInfo();
+}
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.h b/src/sql/drivers/sqlite/qsql_sqlite.h
new file mode 100644
index 0000000..f3b9192
--- /dev/null
+++ b/src/sql/drivers/sqlite/qsql_sqlite.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Definition of SQLite driver classes.
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef QSQL_SQLITE_H
+#define QSQL_SQLITE_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlrecord.h>
+#include <qsqlindex.h>
+#include "../cache/qsqlcachedresult.h"
+
+#if (QT_VERSION-0 >= 0x030000)
+typedef QVariant QSqlVariant;
+#endif
+
+#if defined (Q_OS_WIN32)
+# include <qt_windows.h>
+#endif
+
+class QSQLiteDriverPrivate;
+class QSQLiteResultPrivate;
+class QSQLiteDriver;
+struct sqlite;
+
+class QSQLiteResult : public QtSqlCachedResult
+{
+ friend class QSQLiteDriver;
+ friend class QSQLiteResultPrivate;
+public:
+ QSQLiteResult(const QSQLiteDriver* db);
+ ~QSQLiteResult();
+
+protected:
+ bool gotoNext(QtSqlCachedResult::RowCache* row);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ QSQLiteResultPrivate* d;
+};
+
+class QSQLiteDriver : public QSqlDriver
+{
+ friend class QSQLiteResult;
+public:
+ QSQLiteDriver(QObject *parent = 0, const char *name = 0);
+ QSQLiteDriver(sqlite *connection, QObject *parent = 0, const char *name = 0);
+ ~QSQLiteDriver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port ) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(const QString& user) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlRecordInfo recordInfo(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+ QSqlRecord record(const QSqlQuery& query) const;
+ QSqlRecordInfo recordInfo(const QSqlQuery& query) const;
+
+private:
+ QSQLiteDriverPrivate* d;
+};
+#endif
diff --git a/src/sql/qdatabrowser.cpp b/src/sql/qdatabrowser.cpp
new file mode 100644
index 0000000..a09430c
--- /dev/null
+++ b/src/sql/qdatabrowser.cpp
@@ -0,0 +1,1284 @@
+/****************************************************************************
+**
+** Implementation of QDataBrowser class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qdatabrowser.h"
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+#include "qsqlform.h"
+#include "qsqlmanager_p.h"
+#include "qsqlresult.h"
+
+class QDataBrowserPrivate
+{
+public:
+ QDataBrowserPrivate() : boundaryCheck( TRUE ), readOnly( FALSE ) {}
+ QSqlCursorManager cur;
+ QSqlFormManager frm;
+ QDataManager dat;
+ bool boundaryCheck;
+ bool readOnly;
+};
+
+/*!
+ \class QDataBrowser qdatabrowser.h
+ \brief The QDataBrowser 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
+ QDataView; if you want a table-based presentation of your data use
+ QDataTable.
+
+ A QDataBrowser is used to associate a dataset with a form in much
+ the same way as a QDataTable 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 QDataBrowser::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.
+*/
+
+QDataBrowser::QDataBrowser( QWidget *parent, const char *name, WFlags fl )
+ : QWidget( parent, name, fl )
+{
+ d = new QDataBrowserPrivate();
+ d->dat.setMode( QSql::Update );
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QDataBrowser::~QDataBrowser()
+{
+ 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 QDataBrowser::Boundary.
+
+ \sa Boundary
+*/
+
+QDataBrowser::Boundary QDataBrowser::boundary()
+{
+ QSqlCursor* cur = d->cur.cursor();
+ if ( !cur || !cur->isActive() )
+ return Unknown;
+ if ( !cur->isValid() ) {
+ if ( cur->at() == QSql::BeforeFirst )
+ return BeforeBeginning;
+ if ( cur->at() == QSql::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 QDataBrowser::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 QDataBrowser::setBoundaryChecking( bool active )
+{
+ d->boundaryCheck = active;
+}
+
+bool QDataBrowser::boundaryChecking() const
+{
+ return d->boundaryCheck;
+}
+
+/*!
+ \property QDataBrowser::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
+ QStringList list = myDataBrowser.sort();
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+
+void QDataBrowser::setSort( const QStringList& sort )
+{
+ d->cur.setSort( sort );
+}
+
+/*!
+ \overload
+
+ Sets the data browser's sort to the QSqlIndex \a sort. To apply
+ the new sort, use refresh().
+
+*/
+void QDataBrowser::setSort( const QSqlIndex& sort )
+{
+ d->cur.setSort( sort );
+}
+
+QStringList QDataBrowser::sort() const
+{
+ return d->cur.sort();
+}
+
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setFilter( const QString& filter )
+{
+ d->cur.setFilter( filter );
+}
+
+
+QString QDataBrowser::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 QDataBrowser::setSqlCursor( QSqlCursor* 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()
+*/
+
+QSqlCursor* QDataBrowser::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 QDataBrowser::setForm( QSqlForm* form )
+{
+ d->frm.setForm( form );
+}
+
+
+/*!
+ Returns the data browser's default form or 0 if no form has been
+ set.
+*/
+
+QSqlForm* QDataBrowser::form()
+{
+ return d->frm.form();
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setReadOnly( bool active )
+{
+ d->readOnly = active;
+}
+
+bool QDataBrowser::isReadOnly() const
+{
+ return d->readOnly;
+}
+
+void QDataBrowser::setConfirmEdits( bool confirm )
+{
+ d->dat.setConfirmEdits( confirm );
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setConfirmInsert( bool confirm )
+{
+ d->dat.setConfirmInsert( confirm );
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setConfirmUpdate( bool confirm )
+{
+ d->dat.setConfirmUpdate( confirm );
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setConfirmDelete( bool confirm )
+{
+ d->dat.setConfirmDelete( confirm );
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::confirmEdits() const
+{
+ return ( d->dat.confirmEdits() );
+}
+
+bool QDataBrowser::confirmInsert() const
+{
+ return ( d->dat.confirmInsert() );
+}
+
+bool QDataBrowser::confirmUpdate() const
+{
+ return ( d->dat.confirmUpdate() );
+}
+
+bool QDataBrowser::confirmDelete() const
+{
+ return ( d->dat.confirmDelete() );
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setConfirmCancels( bool confirm )
+{
+ d->dat.setConfirmCancels( confirm );
+}
+
+bool QDataBrowser::confirmCancels() const
+{
+ return d->dat.confirmCancels();
+}
+
+/*!
+ \property QDataBrowser::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 QDataBrowser::setAutoEdit( bool autoEdit )
+{
+ d->dat.setAutoEdit( autoEdit );
+}
+
+bool QDataBrowser::autoEdit() const
+{
+ return d->dat.autoEdit();
+}
+
+/*!
+ \fn void QDataBrowser::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 QDataBrowser::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 QDataBrowser::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 QDataBrowser::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 QDataBrowser::currentChanged( const QSqlRecord* 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 QDataBrowser::primeInsert( QSqlRecord* 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 QSqlCursor::primeInsert() is \e not called on the
+ default cursor, as this would corrupt values in the form.)
+
+ \sa insert()
+*/
+
+
+/*!
+ \fn void QDataBrowser::primeUpdate( QSqlRecord* 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 QSqlCursor::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 QDataBrowser::primeDelete( QSqlRecord* 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 QSqlCursor::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 QDataBrowser::cursorChanged( QSqlCursor::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 QSqlCursor::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 QDataBrowser::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 QSqlCursor::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 QDataBrowser::insert()
+{
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* cur = d->cur.cursor();
+ if ( !buf || !cur )
+ return;
+ bool doIns = TRUE;
+ QSql::Confirm conf = QSql::Yes;
+ switch ( d->dat.mode() ) {
+ case QSql::Insert:
+ if ( autoEdit() ) {
+ if ( confirmInsert() )
+ conf = confirmEdit( QSql::Insert );
+ switch ( conf ) {
+ case QSql::Yes:
+ insertCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ doIns = FALSE;
+ break;
+ }
+ }
+ break;
+ default:
+ if ( autoEdit() && currentEdited() ) {
+ if ( confirmUpdate() )
+ conf = confirmEdit( QSql::Update );
+ switch ( conf ) {
+ case QSql::Yes:
+ updateCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ doIns = FALSE;
+ break;
+ }
+ }
+ break;
+ }
+ if ( doIns ) {
+ d->dat.setMode( QSql::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 QDataBrowser::update()
+{
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* cur = d->cur.cursor();
+ if ( !buf || !cur )
+ return;
+ QSql::Confirm conf = QSql::Yes;
+ switch ( d->dat.mode() ){
+ case QSql::Insert:
+ if ( confirmInsert() )
+ conf = confirmEdit( QSql::Insert );
+ switch ( conf ) {
+ case QSql::Yes:
+ if ( insertCurrent() )
+ d->dat.setMode( QSql::Update );
+ break;
+ case QSql::No:
+ d->dat.setMode( QSql::Update );
+ cur->editBuffer( TRUE );
+ readFields();
+ break;
+ case QSql::Cancel:
+ break;
+ }
+ break;
+ default:
+ d->dat.setMode( QSql::Update );
+ if ( confirmUpdate() )
+ conf = confirmEdit( QSql::Update );
+ switch ( conf ) {
+ case QSql::Yes:
+ updateCurrent();
+ break;
+ case QSql::No:
+ case QSql::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 QDataBrowser::del()
+{
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* cur = d->cur.cursor();
+ if ( !buf || !cur )
+ return;
+ QSql::Confirm conf = QSql::Yes;
+ switch ( d->dat.mode() ){
+ case QSql::Insert:
+ if ( confirmCancels() )
+ conf = confirmCancel( QSql::Insert );
+ if ( conf == QSql::Yes ) {
+ cur->editBuffer( TRUE ); /* restore from cursor */
+ readFields();
+ d->dat.setMode( QSql::Update );
+ } else
+ d->dat.setMode( QSql::Insert );
+ break;
+ default:
+ if ( confirmDelete() )
+ conf = confirmEdit( QSql::Delete );
+ switch ( conf ) {
+ case QSql::Yes:
+ emit primeDelete( buf );
+ deleteCurrent();
+ break;
+ case QSql::No:
+ case QSql::Cancel:
+ break;
+ }
+ d->dat.setMode( QSql::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 QDataBrowser::seek( int i, bool relative )
+{
+ int b = 0;
+ QSqlCursor* 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 QDataBrowser::first()
+{
+ nav( &QSqlCursor::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 QDataBrowser::last()
+{
+ nav( &QSqlCursor::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 QDataBrowser::next()
+{
+ nav( &QSqlCursor::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 QDataBrowser::prev()
+{
+ nav( &QSqlCursor::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 QDataBrowser::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 QDataBrowser::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 QSqlPropertyMap constructor for the default
+ mappings of widgets to properties.
+*/
+
+void QDataBrowser::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 QDataBrowser::insertCurrent()
+{
+ if ( isReadOnly() )
+ return FALSE;
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* 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( QSqlCursor::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 QDataBrowser::updateCurrent()
+{
+ if ( isReadOnly() )
+ return FALSE;
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* 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( QSqlCursor::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 QDataBrowser::deleteCurrent()
+{
+ if ( isReadOnly() )
+ return FALSE;
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* 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( QSqlCursor::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 QDataBrowser::currentEdited()
+{
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* 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 QDataBrowser::preNav()
+{
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* cur = d->cur.cursor();
+ if ( !buf || !cur )
+ return FALSE;
+
+ if ( !isReadOnly() && autoEdit() && currentEdited() ) {
+ bool ok = TRUE;
+ QSql::Confirm conf = QSql::Yes;
+ switch ( d->dat.mode() ){
+ case QSql::Insert:
+ if ( confirmInsert() )
+ conf = confirmEdit( QSql::Insert );
+ switch ( conf ) {
+ case QSql::Yes:
+ ok = insertCurrent();
+ d->dat.setMode( QSql::Update );
+ break;
+ case QSql::No:
+ d->dat.setMode( QSql::Update );
+ break;
+ case QSql::Cancel:
+ return FALSE;
+ }
+ break;
+ default:
+ if ( confirmUpdate() )
+ conf = confirmEdit( QSql::Update );
+ switch ( conf ) {
+ case QSql::Yes:
+ ok = updateCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ return FALSE;
+ }
+ }
+ return ok;
+ }
+ return TRUE;
+}
+
+/*! \internal
+
+ Handles post-navigation according to \a primeUpd.
+*/
+
+void QDataBrowser::postNav( bool primeUpd )
+{
+ if ( primeUpd ) {
+ QSqlRecord* buf = d->frm.record();
+ QSqlCursor* 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 QDataBrowser::nav( Nav nav )
+{
+ int b = 0;
+ QSqlCursor* 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 QDataBrowser::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 QDataBrowser::handleError( const QSqlError& 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.
+*/
+
+QSql::Confirm QDataBrowser::confirmEdit( QSql::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.
+*/
+
+QSql::Confirm QDataBrowser::confirmCancel( QSql::Op m )
+{
+ return d->dat.confirmCancel( this, m );
+}
+
+/*!
+ \fn void QDataBrowser::beforeInsert( QSqlRecord* 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 QDataBrowser::beforeUpdate( QSqlRecord* 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 QDataBrowser::beforeDelete( QSqlRecord* 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 0000000..d2db1fa
--- /dev/null
+++ b/src/sql/qdatabrowser.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Definition of QDataBrowser class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QDATABROWSER_H
+#define QDATABROWSER_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 QSqlForm;
+class QDataBrowserPrivate;
+
+class Q_EXPORT QDataBrowser : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY( bool boundaryChecking READ boundaryChecking WRITE setBoundaryChecking )
+ Q_PROPERTY( QString filter READ filter WRITE setFilter )
+ Q_PROPERTY( QStringList 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:
+ QDataBrowser( QWidget* parent=0, const char* name=0, WFlags fl = 0 );
+ ~QDataBrowser();
+
+ enum Boundary {
+ Unknown,
+ None,
+ BeforeBeginning,
+ Beginning,
+ End,
+ AfterEnd
+ };
+
+ Boundary boundary();
+ void setBoundaryChecking( bool active );
+ bool boundaryChecking() const;
+
+ void setSort( const QSqlIndex& sort );
+ void setSort( const QStringList& sort );
+ QStringList sort() const;
+ void setFilter( const QString& filter );
+ QString filter() const;
+ virtual void setSqlCursor( QSqlCursor* cursor, bool autoDelete = FALSE );
+ QSqlCursor* sqlCursor() const;
+ virtual void setForm( QSqlForm* form );
+ QSqlForm* 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 QSqlRecord* record );
+ void primeInsert( QSqlRecord* buf );
+ void primeUpdate( QSqlRecord* buf );
+ void primeDelete( QSqlRecord* buf );
+ void beforeInsert( QSqlRecord* buf );
+ void beforeUpdate( QSqlRecord* buf );
+ void beforeDelete( QSqlRecord* buf );
+ void cursorChanged( QSqlCursor::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 QSql::Confirm confirmEdit( QSql::Op m );
+ virtual QSql::Confirm confirmCancel( QSql::Op m );
+
+ virtual void handleError( const QSqlError& error );
+
+private:
+ typedef bool (QSqlCursor::*Nav)();
+ bool preNav();
+ void postNav( bool primeUpd );
+ void nav( Nav nav );
+ QDataBrowserPrivate* d;
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QDataBrowser( const QDataBrowser & );
+ QDataBrowser &operator=( const QDataBrowser & );
+#endif
+};
+
+
+#endif
+#endif
diff --git a/src/sql/qdatatable.cpp b/src/sql/qdatatable.cpp
new file mode 100644
index 0000000..d1404d9
--- /dev/null
+++ b/src/sql/qdatatable.cpp
@@ -0,0 +1,2322 @@
+/****************************************************************************
+**
+** Implementation of QDataTable class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QDataTablePrivate
+{
+public:
+ QDataTablePrivate()
+ : 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 )
+ {}
+ ~QDataTablePrivate() { if ( propertyMap ) delete propertyMap; }
+
+ QString nullTxt;
+ bool nullTxtChanged;
+ typedef QValueList< uint > ColIndex;
+ ColIndex colIndex;
+ bool haveAllRows;
+ bool continuousEdit;
+ QSqlEditorFactory* editorFactory;
+ QSqlPropertyMap* propertyMap;
+ QString trueTxt;
+ Qt::DateFormat datefmt;
+ QString falseTxt;
+ int editRow;
+ int editCol;
+ int insertRowLast;
+ QString insertHeaderLabelLast;
+ int insertPreRows;
+ QSqlRecord* editBuffer;
+ bool cancelMode;
+ bool cancelInsert;
+ bool cancelUpdate;
+ int lastAt;
+ QString ftr;
+ QStringList srt;
+ QStringList fld;
+ QStringList fldLabel;
+ QValueList<int> fldWidth;
+ QValueList<QIconSet> fldIcon;
+ QValueList<bool> fldHidden;
+ QSqlCursorManager cur;
+ QDataManager dat;
+};
+
+#ifdef QT_DEBUG_DATATABLE
+void qt_debug_buffer( const QString& msg, QSqlRecord* cursor )
+{
+ qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ qDebug(msg);
+ for ( uint j = 0; j < cursor->count(); ++j ) {
+ qDebug(cursor->field(j)->name() + " type:" + QString(cursor->field(j)->value().typeName()) + " value:" + cursor->field(j)->value().toString() );
+ }
+}
+#endif
+
+/*!
+ \enum QDataTable::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 QDataTable qdatatable.h
+ \brief The QDataTable class provides a flexible SQL table widget that supports browsing and editing.
+
+ \ingroup database
+ \mainclass
+ \module sql
+
+ QDataTable supports various functions for presenting and editing
+ SQL data from a \l QSqlCursor in a table.
+
+ If you want a to present your data in a form use QDataBrowser, or
+ for read-only forms, QDataView.
+
+ When displaying data, QDataTable only retrieves data for visible
+ rows. If the driver supports the 'query size' property the
+ QDataTable 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
+ quickly as possible, with minimum memory usage.
+
+ QDataTable inherits QTable'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 QSqlCursor).
+ QDataTable 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 QSqlCursor 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.
+
+ QDataTable creates editors using the default \l QSqlEditorFactory.
+ 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 QString with text() or as
+ a QVariant 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 QSqlSelectCursor 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.
+*/
+
+QDataTable::QDataTable ( QWidget * parent, const char * name )
+ : QTable( 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.
+*/
+
+QDataTable::QDataTable ( QSqlCursor* cursor, bool autoPopulate, QWidget * parent, const char * name )
+ : QTable( parent, name )
+{
+ init();
+ setSqlCursor( cursor, autoPopulate );
+}
+
+/*! \internal
+*/
+
+
+void QDataTable::init()
+{
+ d = new QDataTablePrivate();
+ setAutoEdit( TRUE );
+ setSelectionMode( SingleRow );
+ setFocusStyle( FollowStyle );
+ d->trueTxt = tr( "True" );
+ d->falseTxt = tr( "False" );
+ d->datefmt = Qt::LocalDate;
+ reset();
+ connect( this, SIGNAL( selectionChanged() ),
+ SLOT( updateCurrentSelection()));
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QDataTable::~QDataTable()
+{
+ 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 QDataTable::addColumn( const QString& fieldName,
+ const QString& label,
+ int width,
+ const QIconSet& 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 QDataTable::setColumn( uint col, const QString& fieldName,
+ const QString& label,
+ int width,
+ const QIconSet& 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 QSqlField
+*/
+
+void QDataTable::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 QTable
+ the QDataTable is not immediately redrawn, you must call
+ refresh(QDataTable::RefreshColumns)
+ yourself.
+
+ \sa refresh()
+*/
+void QDataTable::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 QSqlCursor 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 QDataTable::adjustColumn( int col )
+{
+ QSqlCursor * 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( QSql::BeforeFirst );
+ while ( cur->next() ) {
+ w = QMAX( w, fontMetrics().width( fieldToString( cur->field( indexOf( col ) ) ) ) + 10 );
+ }
+ setColumnWidth( col, w );
+ cur->seek( oldRow );
+ refresh( RefreshColumns );
+}
+
+/*! \reimp
+*/
+void QDataTable::setColumnStretchable( int col, bool s )
+{
+ if ( numCols() == 0 ) {
+ refresh( RefreshColumns );
+ }
+ if ( numCols() > col ) {
+ QTable::setColumnStretchable( col, s );
+ }
+}
+
+QString QDataTable::filter() const
+{
+ return d->cur.filter();
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setFilter( const QString& filter )
+{
+ d->cur.setFilter( filter );
+}
+
+
+/*!
+ \property QDataTable::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
+ QStringList list = myDataTable.sort();
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+ \sa filter() refresh()
+*/
+
+void QDataTable::setSort( const QStringList& 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 QSqlIndex contains
+ field names and their ordering (ASC or DESC); these are used to
+ compose the ORDER BY clause.
+
+ \sa sort()
+*/
+
+void QDataTable::setSort( const QSqlIndex& sort )
+{
+ d->cur.setSort( sort );
+}
+
+QStringList QDataTable::sort() const
+{
+ return d->cur.sort();
+}
+
+/*!
+ Returns the cursor used by the data table.
+*/
+
+QSqlCursor* QDataTable::sqlCursor() const
+{
+ return d->cur.cursor();
+}
+
+void QDataTable::setConfirmEdits( bool confirm )
+{
+ d->dat.setConfirmEdits( confirm );
+}
+
+void QDataTable::setConfirmInsert( bool confirm )
+{
+ d->dat.setConfirmInsert( confirm );
+}
+
+void QDataTable::setConfirmUpdate( bool confirm )
+{
+ d->dat.setConfirmUpdate( confirm );
+}
+
+void QDataTable::setConfirmDelete( bool confirm )
+{
+ d->dat.setConfirmDelete( confirm );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::confirmEdits() const
+{
+ return ( d->dat.confirmEdits() );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::confirmInsert() const
+{
+ return ( d->dat.confirmInsert() );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::confirmUpdate() const
+{
+ return ( d->dat.confirmUpdate() );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::confirmDelete() const
+{
+ return ( d->dat.confirmDelete() );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setConfirmCancels( bool confirm )
+{
+ d->dat.setConfirmCancels( confirm );
+}
+
+bool QDataTable::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 QDataTable cell.
+*/
+
+QWidget * QDataTable::createEditor( int , int col, bool initFromCell ) const
+{
+ if ( d->dat.mode() == QSql::None )
+ return 0;
+
+ QSqlEditorFactory * f = (d->editorFactory == 0) ?
+ QSqlEditorFactory::defaultFactory() : d->editorFactory;
+
+ QSqlPropertyMap * m = (d->propertyMap == 0) ?
+ QSqlPropertyMap::defaultMap() : d->propertyMap;
+
+ QWidget * 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 QDataTable::eventFilter( QObject *o, QEvent *e )
+{
+ if ( d->cancelMode )
+ return TRUE;
+
+ int r = currentRow();
+ int c = currentColumn();
+
+ if ( d->dat.mode() != QSql::None ) {
+ r = d->editRow;
+ c = d->editCol;
+ }
+
+ d->cancelInsert = FALSE;
+ d->cancelUpdate = FALSE;
+ switch ( e->type() ) {
+ case QEvent::KeyPress: {
+ int conf = QSql::Yes;
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if ( ( ke->key() == Key_Tab || ke->key() == Qt::Key_BackTab )
+ && ke->state() & Qt::ControlButton )
+ return FALSE;
+
+ if ( ke->key() == Key_Escape && d->dat.mode() == QSql::Insert ){
+ if ( confirmCancels() && !d->cancelMode ) {
+ d->cancelMode = TRUE;
+ conf = confirmCancel( QSql::Insert );
+ d->cancelMode = FALSE;
+ }
+ if ( conf == QSql::Yes ) {
+ d->cancelInsert = TRUE;
+ } else {
+ QWidget *editorWidget = cellWidget( r, c );
+ if ( editorWidget ) {
+ editorWidget->setActiveWindow();
+ editorWidget->setFocus();
+ }
+ return TRUE;
+ }
+ }
+ if ( ke->key() == Key_Escape && d->dat.mode() == QSql::Update ) {
+ if ( confirmCancels() && !d->cancelMode ) {
+ d->cancelMode = TRUE;
+ conf = confirmCancel( QSql::Update );
+ d->cancelMode = FALSE;
+ }
+ if ( conf == QSql::Yes ){
+ d->cancelUpdate = TRUE;
+ } else {
+ QWidget *editorWidget = cellWidget( r, c );
+ if ( editorWidget ) {
+ editorWidget->setActiveWindow();
+ editorWidget->setFocus();
+ }
+ return TRUE;
+ }
+ }
+ if ( ke->key() == Key_Insert && d->dat.mode() == QSql::None ) {
+ beginInsert();
+ return TRUE;
+ }
+ if ( ke->key() == Key_Delete && d->dat.mode() == QSql::None ) {
+ deleteCurrent();
+ return TRUE;
+ }
+ if ( d->dat.mode() != QSql::None ) {
+ if ( (ke->key() == Key_Tab) && (c < numCols() - 1) && (!isColumnReadOnly( c+1 ) || d->dat.mode() == QSql::Insert) )
+ d->continuousEdit = TRUE;
+ else if ( (ke->key() == Key_BackTab) && (c > 0) && (!isColumnReadOnly( c-1 ) || d->dat.mode() == QSql::Insert) )
+ d->continuousEdit = TRUE;
+ else
+ d->continuousEdit = FALSE;
+ }
+ QSqlCursor * sql = sqlCursor();
+ if ( sql && sql->driver() &&
+ !sql->driver()->hasFeature( QSqlDriver::QuerySize ) &&
+ ke->key() == Key_End && d->dat.mode() == QSql::None ) {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::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
+ QApplication::restoreOverrideCursor();
+#endif
+ return TRUE;
+ }
+ break;
+ }
+ case QEvent::FocusOut: {
+ QWidget *editorWidget = cellWidget( r, c );
+ repaintCell( currentRow(), currentColumn() );
+ if ( !d->cancelMode && editorWidget && o == editorWidget &&
+ ( d->dat.mode() == QSql::Insert) && !d->continuousEdit) {
+ setCurrentCell( r, c );
+ d->cancelInsert = TRUE;
+ }
+ d->continuousEdit = FALSE;
+ break;
+ }
+ case QEvent::FocusIn:
+ repaintCell( currentRow(), currentColumn() );
+ break;
+ default:
+ break;
+ }
+ return QTable::eventFilter( o, e );
+}
+
+/*! \reimp */
+void QDataTable::resizeEvent ( QResizeEvent * e )
+{
+ if ( sqlCursor() &&
+ sqlCursor()->driver() &&
+ !sqlCursor()->driver()->hasFeature( QSqlDriver::QuerySize ) )
+ loadNextPage();
+ QTable::resizeEvent( e );
+}
+
+/*! \reimp */
+void QDataTable::contentsContextMenuEvent( QContextMenuEvent* e )
+{
+ QTable::contentsContextMenuEvent( e );
+ if ( isEditing() && d->dat.mode() != QSql::None )
+ endEdit( d->editRow, d->editCol, autoEdit(), FALSE );
+ if ( !sqlCursor() )
+ return;
+ if ( d->dat.mode() == QSql::None ) {
+ if ( isReadOnly() )
+ return;
+ enum {
+ IdInsert,
+ IdUpdate,
+ IdDelete
+ };
+ QGuardedPtr<QPopupMenu> popup = new QPopupMenu( 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 (QPopupMenu*) 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 QDataTable::contentsMousePressEvent( QMouseEvent* e )
+{
+ QTable::contentsMousePressEvent( e );
+}
+
+/*! \reimp */
+QWidget* QDataTable::beginEdit ( int row, int col, bool replace )
+{
+ d->editRow = -1;
+ d->editCol = -1;
+ if ( !sqlCursor() )
+ return 0;
+ if ( d->dat.mode() == QSql::Insert && !sqlCursor()->canInsert() )
+ return 0;
+ if ( d->dat.mode() == QSql::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 );
+ QWidget* w = QTable::beginEdit( row, col, replace );
+ setColumnReadOnly( col, fakeReadOnly );
+ return w;
+ }
+ if ( d->dat.mode() == QSql::None && sqlCursor()->canUpdate() && sqlCursor()->primaryIndex().count() > 0 )
+ return beginUpdate( row, col, replace );
+ return 0;
+}
+
+/*! \reimp */
+void QDataTable::endEdit( int row, int col, bool, bool )
+{
+ bool accept = autoEdit() && !d->cancelInsert && !d->cancelUpdate;
+
+ QWidget *editor = cellWidget( row, col );
+ if ( !editor )
+ return;
+ if ( d->cancelMode )
+ return;
+ if ( d->dat.mode() != QSql::None && d->editBuffer ) {
+ QSqlPropertyMap * m = (d->propertyMap == 0) ?
+ QSqlPropertyMap::defaultMap() : d->propertyMap;
+ d->editBuffer->setValue( indexOf( col ), m->property( editor ) );
+ clearCellWidget( row, col );
+ if ( !d->continuousEdit ) {
+ switch ( d->dat.mode() ) {
+ case QSql::Insert:
+ if ( accept )
+ QTimer::singleShot( 0, this, SLOT( doInsertCurrent() ) );
+ else
+ endInsert();
+ break;
+ case QSql::Update:
+ if ( accept )
+ QTimer::singleShot( 0, this, SLOT( doUpdateCurrent() ) );
+ else
+ endUpdate();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ setEditMode( NotEditing, -1, -1 );
+ }
+ if ( d->dat.mode() == QSql::None )
+ viewport()->setFocus();
+ updateCell( row, col );
+ emit valueChanged( row, col );
+}
+
+/*! \internal */
+void QDataTable::doInsertCurrent()
+{
+ insertCurrent();
+}
+
+/*! \internal */
+void QDataTable::doUpdateCurrent()
+{
+ updateCurrent();
+ if ( d->dat.mode() == QSql::None ) {
+ viewport()->setFocus();
+ }
+}
+
+/*! \reimp */
+void QDataTable::activateNextCell()
+{
+// if ( d->dat.mode() == QSql::None )
+// QTable::activateNextCell();
+}
+
+/*! \internal
+*/
+
+void QDataTable::endInsert()
+{
+ if ( d->dat.mode() != QSql::Insert )
+ return;
+ d->dat.setMode( QSql::None );
+ d->editBuffer = 0;
+ verticalHeader()->setLabel( d->editRow, QString::number( d->editRow +1 ) );
+ d->editRow = -1;
+ d->editCol = -1;
+ d->insertRowLast = -1;
+ d->insertHeaderLabelLast = QString::null;
+ setEditMode( NotEditing, -1, -1 );
+ setNumRows( d->insertPreRows );
+ d->insertPreRows = -1;
+ viewport()->setFocus();
+}
+
+/*! \internal
+*/
+
+void QDataTable::endUpdate()
+{
+ d->dat.setMode( QSql::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
+ QSqlCursor::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 QDataTable::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( QSql::Insert );
+ int lastRow = row;
+ int lastY = contentsY() + visibleHeight();
+ for ( i = row; i < numRows() ; ++i ) {
+ QRect 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 ( QTable::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
+ QSqlCursor::editBuffer()).
+
+ \a row and \a col refer to the row and column in the QDataTable.
+
+ (\a replace is provided for reimplementors and reflects the API of
+ QTable::beginEdit().)
+*/
+
+QWidget* QDataTable::beginUpdate ( int row, int col, bool replace )
+{
+ if ( !sqlCursor() || isReadOnly() || isColumnReadOnly( col ) )
+ return 0;
+ setCurrentCell( row, col );
+ d->dat.setMode( QSql::Update );
+ if ( sqlCursor()->seek( row ) ) {
+ d->editBuffer = sqlCursor()->primeUpdate();
+ sqlCursor()->seek( currentRow() );
+ emit primeUpdate( d->editBuffer );
+ return QTable::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 QDataTable::insertCurrent()
+{
+ if ( d->dat.mode() != QSql::Insert || ! numCols() )
+ return FALSE;
+ if ( !sqlCursor()->canInsert() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QDataTable::insertCurrent: insert not allowed for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endInsert();
+ return FALSE;
+ }
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmInsert() )
+ conf = confirmEdit( QSql::Insert );
+ switch ( conf ) {
+ case QSql::Yes: {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ emit beforeInsert( d->editBuffer );
+ b = sqlCursor()->insert();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
+ handleError( sqlCursor()->lastError() );
+ endInsert(); // cancel the insert if anything goes wrong
+ refresh();
+ } else {
+ endInsert();
+ refresh();
+ QSqlIndex idx = sqlCursor()->primaryIndex();
+ findBuffer( idx, d->lastAt );
+ repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE );
+ emit cursorChanged( QSql::Insert );
+ }
+ break;
+ }
+ case QSql::No:
+ endInsert();
+ break;
+ case QSql::Cancel:
+ if ( QTable::beginEdit( currentRow(), currentColumn(), FALSE ) )
+ setEditMode( Editing, currentRow(), currentColumn() );
+ break;
+ }
+ return ( b > 0 );
+}
+
+/*! \internal
+
+ Updates the row \a row.
+*/
+
+void QDataTable::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 QDataTable::updateCurrent()
+{
+ if ( d->dat.mode() != QSql::Update )
+ return FALSE;
+ if ( sqlCursor()->primaryIndex().count() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QDataTable::updateCurrent: no primary index for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endUpdate();
+ return FALSE;
+ }
+ if ( !sqlCursor()->canUpdate() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QDataTable::updateCurrent: updates not allowed for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endUpdate();
+ return FALSE;
+ }
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmUpdate() )
+ conf = confirmEdit( QSql::Update );
+ switch ( conf ) {
+ case QSql::Yes: {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ emit beforeUpdate( d->editBuffer );
+ b = sqlCursor()->update();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
+ handleError( sqlCursor()->lastError() );
+ endUpdate();
+ refresh();
+ setCurrentCell( d->editRow, d->editCol );
+ if ( QTable::beginEdit( d->editRow, d->editCol, FALSE ) )
+ setEditMode( Editing, d->editRow, d->editCol );
+ } else {
+ emit cursorChanged( QSql::Update );
+ refresh();
+ endUpdate();
+ }
+ break;
+ }
+ case QSql::No:
+ endUpdate();
+ setEditMode( NotEditing, -1, -1 );
+ break;
+ case QSql::Cancel:
+ setCurrentCell( d->editRow, d->editCol );
+ if ( QTable::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 QDataTable::deleteCurrent()
+{
+ if ( !sqlCursor() || isReadOnly() )
+ return FALSE;
+ if ( sqlCursor()->primaryIndex().count() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QDataTable::deleteCurrent: no primary index %s",
+ sqlCursor()->name().latin1() );
+#endif
+ return FALSE;
+ }
+ if ( !sqlCursor()->canDelete() )
+ return FALSE;
+
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmDelete() )
+ conf = confirmEdit( QSql::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 QSql::Yes:{
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ sqlCursor()->primeDelete();
+ emit primeDelete( sqlCursor()->editBuffer() );
+ emit beforeDelete( sqlCursor()->editBuffer() );
+ b = sqlCursor()->del();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( !b )
+ handleError( sqlCursor()->lastError() );
+ refresh();
+ emit cursorChanged( QSql::Delete );
+ setCurrentCell( currentRow(), currentColumn() );
+ repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE );
+ verticalHeader()->repaint(); // get rid of trailing garbage
+ }
+ break;
+ case QSql::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.
+*/
+
+QSql::Confirm QDataTable::confirmEdit( QSql::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.
+*/
+
+QSql::Confirm QDataTable::confirmCancel( QSql::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 QDataTable::find( const QString & str, bool caseSensitive, bool backwards )
+{
+ if ( !sqlCursor() )
+ return;
+
+ QSqlCursor * r = sqlCursor();
+ QString 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
+ QApplication::setOverrideCursor( Qt::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
+ QApplication::restoreOverrideCursor();
+#endif
+}
+
+
+/*!
+ Resets the table so that it displays no data.
+
+ \sa setSqlCursor()
+*/
+
+void QDataTable::reset()
+{
+ clearCellWidget( currentRow(), currentColumn() );
+ switch ( d->dat.mode() ) {
+ case QSql::Insert:
+ endInsert();
+ break;
+ case QSql::Update:
+ endUpdate();
+ break;
+ default:
+ break;
+ }
+ ensureVisible( 0, 0 );
+ verticalScrollBar()->setValue(0);
+ setNumRows(0);
+
+ d->haveAllRows = FALSE;
+ d->continuousEdit = FALSE;
+ d->dat.setMode( QSql::None );
+ d->editRow = -1;
+ d->editCol = -1;
+ d->insertRowLast = -1;
+ d->insertHeaderLabelLast = QString::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 QDataTable::indexOf( uint i ) const
+{
+ QDataTablePrivate::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 QDataTable::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 QDataTable::setAutoDelete( bool enable )
+{
+ d->cur.setAutoDelete( enable );
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setAutoEdit( bool autoEdit )
+{
+ d->dat.setAutoEdit( autoEdit );
+}
+
+bool QDataTable::autoEdit() const
+{
+ return d->dat.autoEdit();
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setNullText( const QString& nullText )
+{
+ d->nullTxt = nullText;
+ d->nullTxtChanged = TRUE;
+}
+
+QString QDataTable::nullText() const
+{
+ return d->nullTxt;
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setTrueText( const QString& trueText )
+{
+ d->trueTxt = trueText;
+}
+
+QString QDataTable::trueText() const
+{
+ return d->trueTxt;
+}
+
+/*!
+ \property QDataTable::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 QDataTable::setFalseText( const QString& falseText )
+{
+ d->falseTxt = falseText;
+}
+
+QString QDataTable::falseText() const
+{
+ return d->falseTxt;
+}
+
+/*!
+ \property QDataTable::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 Qt::LocalDate.
+*/
+
+void QDataTable::setDateFormat( const DateFormat f )
+{
+ d->datefmt = f;
+}
+
+Qt::DateFormat QDataTable::dateFormat() const
+{
+ return d->datefmt;
+}
+
+/*!
+ \property QDataTable::numRows
+
+ \brief the number of rows in the table
+*/
+
+int QDataTable::numRows() const
+{
+ return QTable::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 QDataTable::setNumRows ( int r )
+{
+ QTable::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 QDataTable::setNumCols ( int r )
+{
+ QTable::setNumCols( r );
+}
+
+/*!
+ \property QDataTable::numCols
+
+ \brief the number of columns in the table
+*/
+
+int QDataTable::numCols() const
+{
+ return QTable::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 QString::null is
+ returned.
+*/
+
+QString QDataTable::text ( int row, int col ) const
+{
+ if ( !sqlCursor() )
+ return QString::null;
+
+ QString 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.
+*/
+
+QVariant QDataTable::value ( int row, int col ) const
+{
+ if ( !sqlCursor() )
+ return QVariant();
+
+ QVariant 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 QDataTable::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() == QSql::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 QTable 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 QDataTable::sliderPressed()
+{
+ disconnect( verticalScrollBar(), SIGNAL( valueChanged(int) ),
+ this, SLOT( loadNextPage() ) );
+}
+
+/*! \internal */
+void QDataTable::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; QDataTable always sorts
+ whole rows by the specified column.
+*/
+
+void QDataTable::sortColumn ( int col, bool ascending,
+ bool )
+{
+ if ( sorting() ) {
+ if ( isEditing() && d->dat.mode() != QSql::None )
+ endEdit( d->editRow, d->editCol, autoEdit(), FALSE );
+ if ( !sqlCursor() )
+ return;
+ QSqlIndex lastSort = sqlCursor()->sort();
+ QSqlIndex newSort( lastSort.cursorName(), "newSort" );
+ QSqlField *field = sqlCursor()->field( indexOf( col ) );
+ if ( field )
+ newSort.append( *field );
+ newSort.setDescending( 0, !ascending );
+ horizontalHeader()->setSortIndicator( col, ascending );
+ setSort( newSort );
+ refresh();
+ }
+}
+
+/*! \reimp */
+void QDataTable::columnClicked ( int col )
+{
+ if ( sorting() ) {
+ if ( !sqlCursor() )
+ return;
+ QSqlIndex 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 QDataTable::repaintCell( int row, int col )
+{
+ QRect cg = cellGeometry( row, col );
+ QRect re( QPoint( cg.x() - 2, cg.y() - 2 ),
+ QSize( 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 QSql::isNull()
+*/
+
+void QDataTable::paintCell( QPainter * p, int row, int col, const QRect & cr,
+ bool selected, const QColorGroup &cg )
+{
+ QTable::paintCell( p, row, col, cr, selected, cg ); // empty cell
+
+ if ( !sqlCursor() )
+ return;
+
+ p->setPen( selected ? cg.highlightedText() : cg.text() );
+ if ( d->dat.mode() != QSql::None ) {
+ if ( row == d->editRow && d->editBuffer ) {
+ paintField( p, d->editBuffer->field( indexOf( col ) ), cr,
+ selected );
+ } else if ( row > d->editRow && d->dat.mode() == QSql::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 QDataTable::paintField( QPainter * p, const QSqlField* field,
+ const QRect & 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 QDataTable::fieldAlignment( const QSqlField* /*field*/ )
+{
+ return Qt::AlignLeft | Qt::AlignVCenter; //## Reggie: add alignment to QTable
+}
+
+
+/*!
+ 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 QDataTable::setSize( QSqlCursor* sql )
+{
+ // ### what are the connect/disconnect calls doing here!? move to refresh()
+ if ( sql->driver() && sql->driver()->hasFeature( QSqlDriver::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() QSqlDriver::nullText()
+*/
+
+void QDataTable::setSqlCursor( QSqlCursor* 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 QDataTable::handleError( const QSqlError& e )
+{
+ d->dat.handleError( this, e );
+}
+
+/*! \reimp
+ */
+
+void QDataTable::keyPressEvent( QKeyEvent* 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:
+ QTable::keyPressEvent( e );
+ default:
+ return;
+ }
+}
+
+/*! \reimp
+*/
+
+void QDataTable::resizeData ( int )
+{
+
+}
+
+/*! \reimp
+*/
+
+QTableItem * QDataTable::item ( int, int ) const
+{
+ return 0;
+}
+
+/*! \reimp
+*/
+
+void QDataTable::setItem ( int , int , QTableItem * )
+{
+
+}
+
+/*! \reimp
+*/
+
+void QDataTable::clearCell ( int , int )
+{
+
+}
+
+/*! \reimp
+*/
+
+void QDataTable::setPixmap ( int , int , const QPixmap & )
+{
+
+}
+
+/*! \reimp */
+void QDataTable::takeItem ( QTableItem * )
+{
+
+}
+
+/*!
+ 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 QDataTable takes ownership of this pointer, and will
+ delete it when it is no longer needed or when
+ installEditorFactory() is called again.
+
+ \sa QSqlEditorFactory
+*/
+
+void QDataTable::installEditorFactory( QSqlEditorFactory * 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 QDataTable takes ownership of this pointer, and will
+ delete it when it is no longer needed or when installPropertMap()
+ is called again.
+
+ \sa QSqlPropertyMap
+*/
+
+void QDataTable::installPropertyMap( QSqlPropertyMap* m )
+{
+ if ( m ) {
+ delete d->propertyMap;
+ d->propertyMap = m;
+ }
+}
+
+/*! \internal
+
+ Sets the current selection to \a row, \a col.
+*/
+
+void QDataTable::setCurrentSelection( int row, int )
+{
+ if ( !sqlCursor() )
+ return;
+ if ( row == d->lastAt )
+ return;
+ if ( !sqlCursor()->seek( row ) )
+ return;
+ d->lastAt = row;
+ emit currentChanged( sqlCursor() );
+}
+
+void QDataTable::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.
+*/
+
+QSqlRecord* QDataTable::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 QDataTable::sortAscending( int col )
+{
+ sortColumn( col, TRUE );
+}
+
+/*!
+ Sorts column \a col in descending order.
+
+ \sa setSorting()
+*/
+
+void QDataTable::sortDescending( int col )
+{
+ sortColumn( col, FALSE );
+}
+
+/*!
+ \overload void QDataTable::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 QDataTable::refresh( QDataTable::Refresh mode )
+{
+ QSqlCursor* 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() ) {
+ QSqlField* 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 ] ) {
+ QTable::showColumn( i ); // ugly but necessary
+ QTable::hideColumn( i );
+ } else {
+ QTable::showColumn( i );
+ }
+ if ( d->fldWidth[ i ] > -1 )
+ QTable::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( QDataTable::RefreshData ).
+*/
+
+void QDataTable::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 QDataTable::findBuffer( const QSqlIndex& idx, int atHint )
+{
+ QSqlCursor* 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.
+*/
+QString QDataTable::fieldToString( const QSqlField * field )
+{
+ QString text;
+ if ( field->isNull() ) {
+ text = nullText();
+ } else {
+ QVariant val = field->value();
+ switch ( val.type() ) {
+ case QVariant::Bool:
+ text = val.toBool() ? d->trueTxt : d->falseTxt;
+ break;
+ case QVariant::Date:
+ text = val.toDate().toString( d->datefmt );
+ break;
+ case QVariant::Time:
+ text = val.toTime().toString( d->datefmt );
+ break;
+ case QVariant::DateTime:
+ text = val.toDateTime().toString( d->datefmt );
+ break;
+ default:
+ text = val.toString();
+ break;
+ }
+ }
+ return text;
+}
+
+/*!
+ \reimp
+*/
+
+void QDataTable::swapColumns( int col1, int col2, bool )
+{
+ QString fld = d->fld[ col1 ];
+ QString fldLabel = d->fldLabel[ col1 ];
+ QIconSet 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 QDataTable::drawContents( QPainter * p, int cx, int cy, int cw, int ch )
+{
+ QTable::drawContents( p, cx, cy, cw, ch );
+ if ( sqlCursor() && currentRow() >= 0 )
+ sqlCursor()->seek( currentRow() );
+}
+
+/*!
+ \reimp
+*/
+
+void QDataTable::hideColumn( int col )
+{
+ d->fldHidden[col] = TRUE;
+ refresh( RefreshColumns );
+}
+
+/*!
+ \reimp
+*/
+
+void QDataTable::showColumn( int col )
+{
+ d->fldHidden[col] = FALSE;
+ refresh( RefreshColumns );
+}
+
+/*!
+ \fn void QDataTable::currentChanged( QSqlRecord* 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 QDataTable::primeInsert( QSqlRecord* 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 QDataTable::primeUpdate( QSqlRecord* 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 QDataTable::primeDelete( QSqlRecord* 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 QDataTable::beforeInsert( QSqlRecord* 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 QDataTable::beforeUpdate( QSqlRecord* 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 QDataTable::beforeDelete( QSqlRecord* 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 QDataTable::cursorChanged( QSql::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 0000000..5d3bed1
--- /dev/null
+++ b/src/sql/qdatatable.h
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Definition of QDataTable class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QDATATABLE_H
+#define QDATATABLE_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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+class QPainter;
+class QSqlField;
+class QSqlPropertyMap;
+class QDataTablePrivate;
+
+class QM_EXPORT_SQL QDataTable : public QTable
+{
+ Q_OBJECT
+
+ Q_PROPERTY( QString nullText READ nullText WRITE setNullText )
+ Q_PROPERTY( QString trueText READ trueText WRITE setTrueText )
+ Q_PROPERTY( QString 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( QString filter READ filter WRITE setFilter )
+ Q_PROPERTY( QStringList sort READ sort WRITE setSort )
+ Q_PROPERTY( int numCols READ numCols )
+ Q_PROPERTY( int numRows READ numRows )
+
+public:
+ QDataTable ( QWidget* parent=0, const char* name=0 );
+ QDataTable ( QSqlCursor* cursor, bool autoPopulate = FALSE, QWidget* parent=0, const char* name=0 );
+ ~QDataTable();
+
+ virtual void addColumn( const QString& fieldName,
+ const QString& label = QString::null,
+ int width = -1,
+ const QIconSet& iconset = QIconSet() );
+ virtual void removeColumn( uint col );
+ virtual void setColumn( uint col, const QString& fieldName,
+ const QString& label = QString::null,
+ int width = -1,
+ const QIconSet& iconset = QIconSet() );
+
+ QString nullText() const;
+ QString trueText() const;
+ QString 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;
+ QString filter() const;
+ QStringList sort() const;
+
+ virtual void setSqlCursor( QSqlCursor* cursor = 0,
+ bool autoPopulate = FALSE, bool autoDelete = FALSE );
+ QSqlCursor* sqlCursor() const;
+
+ virtual void setNullText( const QString& nullText );
+ virtual void setTrueText( const QString& trueText );
+ virtual void setFalseText( const QString& 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 QString& filter );
+ virtual void setSort( const QStringList& sort );
+ virtual void setSort( const QSqlIndex& sort );
+
+ enum Refresh {
+ RefreshData = 1,
+ RefreshColumns = 2,
+ RefreshAll = 3
+ };
+ void refresh( Refresh mode );
+ void sortColumn ( int col, bool ascending = TRUE,
+ bool wholeRows = FALSE );
+ QString text ( int row, int col ) const;
+ QVariant value ( int row, int col ) const;
+ QSqlRecord* currentRecord() const;
+
+ void installEditorFactory( QSqlEditorFactory * f );
+ void installPropertyMap( QSqlPropertyMap* m );
+
+ int numCols() const;
+ int numRows() const;
+ void setNumCols( int c );
+ void setNumRows ( int r );
+ bool findBuffer( const QSqlIndex& idx, int atHint = 0 );
+
+ void hideColumn( int col );
+ void showColumn( int col );
+signals:
+ void currentChanged( QSqlRecord* record );
+ void primeInsert( QSqlRecord* buf );
+ void primeUpdate( QSqlRecord* buf );
+ void primeDelete( QSqlRecord* buf );
+ void beforeInsert( QSqlRecord* buf );
+ void beforeUpdate( QSqlRecord* buf );
+ void beforeDelete( QSqlRecord* buf );
+ void cursorChanged( QSql::Op mode );
+
+public slots:
+ virtual void find( const QString & 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 QSql::Confirm confirmEdit( QSql::Op m );
+ virtual QSql::Confirm confirmCancel( QSql::Op m );
+
+ virtual void handleError( const QSqlError& e );
+
+ virtual bool beginInsert();
+ virtual QWidget* beginUpdate ( int row, int col, bool replace );
+
+ bool eventFilter( QObject *o, QEvent *e );
+ void keyPressEvent( QKeyEvent* );
+ void resizeEvent ( QResizeEvent * );
+ void contentsMousePressEvent( QMouseEvent* e );
+ void contentsContextMenuEvent( QContextMenuEvent* e );
+ void endEdit( int row, int col, bool accept, bool replace );
+ QWidget * 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( QSqlCursor* sql );
+ void repaintCell( int row, int col );
+ void paintCell ( QPainter * p, int row, int col, const QRect & cr,
+ bool selected, const QColorGroup &cg );
+ virtual void paintField( QPainter * p, const QSqlField* field, const QRect & cr,
+ bool selected );
+ void drawContents( QPainter * p, int cx, int cy, int cw, int ch );
+ virtual int fieldAlignment( const QSqlField* field );
+ void columnClicked ( int col );
+ void resizeData ( int len );
+
+ QTableItem * item ( int row, int col ) const;
+ void setItem ( int row, int col, QTableItem * item );
+ void clearCell ( int row, int col ) ;
+ void setPixmap ( int row, int col, const QPixmap & pix );
+ void takeItem ( QTableItem * i );
+
+private slots:
+ void loadNextPage();
+ void setCurrentSelection( int row, int col );
+ void updateCurrentSelection();
+ void sliderPressed();
+ void sliderReleased();
+ void doInsertCurrent();
+ void doUpdateCurrent();
+
+private:
+ QString fieldToString( const QSqlField * field );
+ void init();
+ QWidget* beginEdit ( int row, int col, bool replace );
+ void updateRow( int row );
+ void endInsert();
+ void endUpdate();
+ QDataTablePrivate* d;
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QDataTable( const QDataTable & );
+ QDataTable &operator=( const QDataTable & );
+#endif
+};
+
+#endif
+#endif
diff --git a/src/sql/qdataview.cpp b/src/sql/qdataview.cpp
new file mode 100644
index 0000000..ceafe09
--- /dev/null
+++ b/src/sql/qdataview.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Implementation of QDataView class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qdataview.h"
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+#include "qsqlmanager_p.h"
+
+class QDataViewPrivate
+{
+public:
+ QDataViewPrivate() {}
+ QSqlFormManager frm;
+};
+
+
+/*!
+ \class QDataView qdataview.h
+ \brief The QDataView 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 QDataView does not support editing it uses
+ less resources than a QDataBrowser. 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
+ QDataBrowser; if you want a table-based presentation of your data
+ use QDataTable.
+
+ The form is associated with the data view with setForm() and the
+ record is associated with setRecord(). You can also pass a
+ QSqlRecord 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.
+*/
+
+QDataView::QDataView( QWidget *parent, const char *name, WFlags fl )
+ : QWidget( parent, name, fl )
+{
+ d = new QDataViewPrivate();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QDataView::~QDataView()
+{
+ 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 QDataView::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 QDataView::setForm( QSqlForm* form )
+{
+ d->frm.setForm( form );
+}
+
+
+/*!
+ Returns the default form used by the data view, or 0 if there is
+ none.
+
+ \sa setForm()
+*/
+
+QSqlForm* QDataView::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 QDataView::setRecord( QSqlRecord* record )
+{
+ d->frm.setRecord( record );
+}
+
+
+/*!
+ Returns the default record used by the data view, or 0 if there is
+ none.
+
+ \sa setRecord()
+*/
+
+QSqlRecord* QDataView::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 QDataView::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 QDataView::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 QDataView::refresh( QSqlRecord* 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 0000000..cd1cf92
--- /dev/null
+++ b/src/sql/qdataview.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Definition of QDataView class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QDATAVIEW_H
+#define QDATAVIEW_H
+
+#ifndef QT_H
+#include "qwidget.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+class QSqlForm;
+class QSqlRecord;
+class QDataViewPrivate;
+
+class QM_EXPORT_SQL QDataView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QDataView( QWidget* parent=0, const char* name=0, WFlags fl = 0 );
+ ~QDataView();
+
+ virtual void setForm( QSqlForm* form );
+ QSqlForm* form();
+ virtual void setRecord( QSqlRecord* record );
+ QSqlRecord* record();
+
+public slots:
+ virtual void refresh( QSqlRecord* buf );
+ virtual void readFields();
+ virtual void writeFields();
+ virtual void clearValues();
+
+private:
+ QDataViewPrivate* d;
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QDataView( const QDataView & );
+ QDataView &operator=( const QDataView & );
+#endif
+};
+
+
+#endif
+#endif
diff --git a/src/sql/qeditorfactory.cpp b/src/sql/qeditorfactory.cpp
new file mode 100644
index 0000000..370f577
--- /dev/null
+++ b/src/sql/qeditorfactory.cpp
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Implementation of QEditorFactory class
+**
+** Created : 2000-11-17
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QEditorFactory qeditorfactory.h
+ \brief The QEditorFactory class is used to create editor widgets
+ for QVariant data types.
+
+ \ingroup database
+ \module sql
+
+ Each editor factory provides the createEditor() function which
+ given a QVariant will create and return a QWidget that can edit
+ that QVariant. For example if you have a QVariant::String type, a
+ QLineEdit would be the default editor returned, whereas a
+ QVariant::Int's default editor would be a QSpinBox.
+
+ If you want to create different editors for fields with the same
+ data type, subclass QEditorFactory and reimplement the
+ createEditor() function.
+*/
+
+/*!
+ Constructs an editor factory with parent \a parent, called \a name.
+*/
+
+QEditorFactory::QEditorFactory ( QObject * parent, const char * name )
+ : QObject( parent, name )
+{
+
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QEditorFactory::~QEditorFactory()
+{
+
+}
+
+static QEditorFactory * defaultfactory = 0;
+static QCleanupHandler< QEditorFactory > q_cleanup_editor_factory;
+
+/*!
+ Returns an instance of a default editor factory.
+*/
+
+QEditorFactory * QEditorFactory::defaultFactory()
+{
+ if( defaultfactory == 0 ){
+ defaultfactory = new QEditorFactory();
+ q_cleanup_editor_factory.add( &defaultfactory );
+ }
+
+ return defaultfactory;
+}
+
+/*!
+ Replaces the default editor factory with \a factory.
+ \e{QEditorFactory takes ownership of factory, and destroys it
+ when it is no longer needed.}
+*/
+
+void QEditorFactory::installDefaultFactory( QEditorFactory * 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 QVariant \a v.
+ If the QVariant is invalid, 0 is returned. The \a parent is passed
+ to the appropriate editor's constructor.
+*/
+
+QWidget * QEditorFactory::createEditor( QWidget * parent, const QVariant & v )
+{
+ QWidget * w = 0;
+ switch( v.type() ){
+ case QVariant::Invalid:
+ w = 0;
+ break;
+ case QVariant::Bool:
+ w = new QComboBox( parent, "qt_editor_bool" );
+ ((QComboBox *) w)->insertItem( "False" );
+ ((QComboBox *) w)->insertItem( "True" );
+ break;
+ case QVariant::UInt:
+ w = new QSpinBox( 0, 999999, 1, parent, "qt_editor_spinbox" );
+ break;
+ case QVariant::Int:
+ w = new QSpinBox( -999999, 999999, 1, parent, "qt_editor_int" );
+ break;
+ case QVariant::String:
+ case QVariant::CString:
+ case QVariant::Double:
+ w = new QLineEdit( parent, "qt_editor_double" );
+ ((QLineEdit*)w)->setFrame( FALSE );
+ break;
+ case QVariant::Date:
+ w = new QDateEdit( parent, "qt_editor_date" );
+ break;
+ case QVariant::Time:
+ w = new QTimeEdit( parent, "qt_editor_time" );
+ break;
+ case QVariant::DateTime:
+ w = new QDateTimeEdit( parent, "qt_editor_datetime" );
+ break;
+#ifndef QT_NO_LABEL
+ case QVariant::Pixmap:
+ w = new QLabel( parent, "qt_editor_pixmap" );
+ break;
+#endif
+ case QVariant::Palette:
+ case QVariant::ColorGroup:
+ case QVariant::Color:
+ case QVariant::Font:
+ case QVariant::Brush:
+ case QVariant::Bitmap:
+ case QVariant::Cursor:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::Rect:
+ case QVariant::Size:
+ case QVariant::IconSet:
+ case QVariant::Point:
+ case QVariant::PointArray:
+ case QVariant::Region:
+ case QVariant::SizePolicy:
+ case QVariant::ByteArray:
+ default:
+ w = new QWidget( 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 0000000..8a0fb91
--- /dev/null
+++ b/src/sql/qeditorfactory.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Definition of QEditorFactory class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QEDITORFACTORY_H
+#define QEDITORFACTORY_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qvariant.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+class QM_EXPORT_SQL QEditorFactory : public QObject
+{
+public:
+ QEditorFactory ( QObject * parent = 0, const char * name = 0 );
+ ~QEditorFactory();
+
+ virtual QWidget * createEditor( QWidget * parent, const QVariant & v );
+
+ static QEditorFactory * defaultFactory();
+ static void installDefaultFactory( QEditorFactory * factory);
+
+private:
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QEditorFactory( const QEditorFactory & );
+ QEditorFactory &operator=( const QEditorFactory & );
+#endif
+};
+
+#endif // QT_NO_SQL
+#endif // QEDITORFACTORY_H
diff --git a/src/sql/qsql.cpp b/src/sql/qsql.cpp
new file mode 100644
index 0000000..3bf97ae
--- /dev/null
+++ b/src/sql/qsql.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Implementation of QSql class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+
+/*!
+ \class QSql qsql.h
+ \brief The QSql class is a namespace for Qt SQL identifiers that
+ need to be global-like.
+
+ \ingroup database
+ \mainclass
+ \module sql
+
+ Normally, you can ignore this class. Several Qt SQL classes
+ inherit it, so all the identifiers in the Qt SQL namespace are
+ visible without qualification.
+*/
+
+/*!
+ \enum QSql::Confirm
+
+ This enum type describes edit confirmations.
+
+ \value Yes
+ \value No
+ \value Cancel
+*/
+
+/*!
+ \enum QSql::Op
+
+ This enum type describes edit operations.
+
+ \value None
+ \value Insert
+ \value Update
+ \value Delete
+*/
+
+
+/*!
+ \enum QSql::Location
+
+ This enum type describes SQL navigation locations.
+
+ \value BeforeFirst
+ \value AfterLast
+*/
+
+/*!
+ \enum QSql::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 QSql::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 QSql::QSql()
+
+ Constructs a Qt SQL namespace class
+*/
diff --git a/src/sql/qsql.h b/src/sql/qsql.h
new file mode 100644
index 0000000..3d4dc6f
--- /dev/null
+++ b/src/sql/qsql.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Definition of QSql class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQL_H
+#define QSQL_H
+
+#ifndef QT_H
+#include "qglobal.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QM_EXPORT_SQL QSql
+{
+public:
+ QSql() {}
+ 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)
+ QSql( const QSql & );
+ QSql &operator=( const QSql & );
+#endif
+
+};
+
+#endif
+#endif
diff --git a/src/sql/qsqlcursor.cpp b/src/sql/qsqlcursor.cpp
new file mode 100644
index 0000000..a1be1f8
--- /dev/null
+++ b/src/sql/qsqlcursor.cpp
@@ -0,0 +1,1549 @@
+/****************************************************************************
+**
+** Implementation of QSqlCursor class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlcursor.h"
+
+#ifndef QT_NO_SQL
+
+#include "qsqldriver.h"
+#include "qsqlresult.h"
+#include "qdatetime.h"
+#include "qsqldatabase.h"
+#include "qsql.h"
+
+class QSqlCursorPrivate
+{
+public:
+
+ QSqlCursorPrivate( const QString& name, QSqlDatabase* sdb )
+ : lastAt( QSql::BeforeFirst ), nm( name ), srt( name ), md( 0 ), db( sdb ), q( 0 )
+ {}
+ ~QSqlCursorPrivate()
+ {
+ delete q;
+ }
+
+ QSqlQuery* query()
+ {
+ if ( !q )
+ q = new QSqlQuery( 0, db );
+ return q;
+ }
+
+ int lastAt;
+ QString nm; //name
+ QSqlIndex srt; //sort
+ QString ftr; //filter
+ int md; //mode
+ QSqlIndex priIndx; //primary index
+ QSqlRecord editBuffer;
+ // the primary index as it was before the user changed the values in editBuffer
+ QString editIndex;
+ QSqlRecordInfo infoBuffer;
+ QSqlDatabase* db;
+ QSqlQuery* q;
+};
+
+QString qOrderByClause( const QSqlIndex & i, const QString& prefix = QString::null )
+{
+ QString str;
+ int k = i.count();
+ if( k == 0 ) return QString::null;
+ str = " order by " + i.toString( prefix );
+ return str;
+}
+
+QString qWhereClause( const QString& prefix, QSqlField* field, const QSqlDriver* driver )
+{
+ QString f;
+ if ( field && driver ) {
+ f = ( prefix.length() > 0 ? prefix + QString(".") : QString::null ) + field->name();
+ if ( field->isNull() ) {
+ f += " IS NULL";
+ } else {
+ f += " = " + driver->formatValue( field );
+ }
+ }
+ return f;
+}
+
+QString qWhereClause( QSqlRecord* rec, const QString& prefix, const QString& sep,
+ const QSqlDriver* driver )
+{
+ static QString blank( " " );
+ QString filter;
+ bool separator = FALSE;
+ for ( uint j = 0; j < rec->count(); ++j ) {
+ QSqlField* 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 QSqlCursor qsqlcursor.h
+ \brief The QSqlCursor class provides browsing and editing of SQL
+ tables and views.
+
+ \ingroup database
+ \module sql
+
+ A QSqlCursor is a database record (see \l QSqlRecord) that
+ corresponds to a table or view within an SQL database (see \l
+ QSqlDatabase). 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 QSqlCursor
+ \printline QSqlCursor
+ \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()
+ require 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.
+
+ QSqlCursor 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. QSqlCursor
+ generates SQL statements which are sent to the database engine;
+ you can control which fields are included in these statements
+ using setGenerated().
+
+ Note that QSqlCursor does not inherit from QObject. This means
+ that you are responsible for destroying instances of this class
+ yourself. However if you create a QSqlCursor and use it in a
+ \l QDataTable, \l QDataBrowser or a \l QDataView these classes will
+ usually take ownership of the cursor and destroy it when they
+ don't need it anymore. The documentation for QDataTable,
+ QDataBrowser and QDataView explicitly states which calls take
+ ownership of the cursor.
+*/
+
+/*!
+ \enum QSqlCursor::Mode
+
+ This enum type describes how QSqlCursor 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 QSqlCursor::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()
+*/
+
+QSqlCursor::QSqlCursor( const QString & name, bool autopopulate, QSqlDatabase* db )
+ : QSqlRecord(), QSqlQuery( QString::null, db )
+{
+ d = new QSqlCursorPrivate( name, db );
+ setMode( Writable );
+ if ( !d->nm.isNull() )
+ setName( d->nm, autopopulate );
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QSqlCursor::QSqlCursor( const QSqlCursor & other )
+ : QSqlRecord( other ), QSqlQuery( other )
+{
+ d = new QSqlCursorPrivate( 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.
+*/
+
+QSqlCursor::~QSqlCursor()
+{
+ delete d;
+}
+
+/*!
+ Sets the cursor equal to \a other.
+*/
+
+QSqlCursor& QSqlCursor::operator=( const QSqlCursor& other )
+{
+ QSqlRecord::operator=( other );
+ QSqlQuery::operator=( other );
+ delete d;
+ d = new QSqlCursorPrivate( 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 QSqlCursor::setSort( const QSqlIndex& sort )
+{
+ d->srt = sort;
+}
+
+/*!
+ Returns the current sort, or an empty index if there is no current
+ sort.
+*/
+QSqlIndex QSqlCursor::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 QSqlCursor::setFilter( const QString& filter )
+{
+ d->ftr = filter;
+}
+
+/*!
+ Returns the current filter, or an empty string if there is no
+ current filter.
+*/
+QString QSqlCursor::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 QSqlCursor constructor documentation for
+ more information.
+*/
+void QSqlCursor::setName( const QString& 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("QSqlCursor::setName: unable to build record, does '%s' exist?", name.latin1() );
+#endif
+ }
+}
+
+/*!
+ Returns the name of the cursor.
+*/
+
+QString QSqlCursor::name() const
+{
+ return d->nm;
+}
+
+/*! \reimp
+*/
+
+QString QSqlCursor::toString( const QString& prefix, const QString& sep ) const
+{
+ QString pflist;
+ QString pfix = prefix.isEmpty() ? QString::null : prefix + ".";
+ bool comma = FALSE;
+
+ for ( uint i = 0; i < count(); ++i ) {
+ const QString fname = fieldName( i );
+ if ( isGenerated( i ) ) {
+ if( comma )
+ pflist += sep + " ";
+ pflist += pfix + fname;
+ comma = TRUE;
+ }
+ }
+ return pflist;
+}
+
+/*!
+ \internal
+
+ Assigns the record \a list.
+
+*/
+QSqlRecord & QSqlCursor::operator=( const QSqlRecord & list )
+{
+ return QSqlRecord::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 QSqlCursor::append( const QSqlFieldInfo& fieldInfo )
+{
+ d->editBuffer.append( fieldInfo.toField() );
+ d->editBuffer.setGenerated( d->editBuffer.count() - 1, fieldInfo.isGenerated() );
+ d->infoBuffer.append( fieldInfo );
+ QSqlRecord::append( fieldInfo.toField() );
+ QSqlRecord::setGenerated( QSqlRecord::count() - 1, fieldInfo.isGenerated() );
+}
+
+/*!
+ Removes all fields from the cursor. Note that all references to
+ the cursor edit buffer become invalidated.
+*/
+void QSqlCursor::clear()
+{
+ d->editBuffer.clear();
+ d->infoBuffer.clear();
+ QSqlRecord::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 QSqlCursor::insert( int pos, const QSqlFieldInfo& fieldInfo )
+{
+ d->editBuffer.insert( pos, fieldInfo.toField() );
+ d->editBuffer.setGenerated( pos, fieldInfo.isGenerated() );
+ d->infoBuffer[ pos ] = fieldInfo;
+ QSqlRecord::insert( pos, fieldInfo.toField() );
+ QSqlRecord::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 QSqlCursor::remove( int pos )
+{
+ d->editBuffer.remove( pos );
+ d->infoBuffer[ pos ] = QSqlFieldInfo();
+ QSqlRecord::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 QSqlCursor::setGenerated( const QString& name, bool generated )
+{
+ int pos = position( name );
+ if ( pos == -1 )
+ return;
+ QSqlRecord::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 QSqlCursor::setGenerated( int i, bool generated )
+{
+ if ( i < 0 || i >= (int)d->infoBuffer.count() )
+ return;
+ QSqlRecord::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.
+*/
+
+QSqlIndex QSqlCursor::primaryIndex( bool setFromCursor ) const
+{
+ if ( setFromCursor ) {
+ for ( uint i = 0; i < d->priIndx.count(); ++i ) {
+ const QString 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 QSqlCursor::setPrimaryIndex( const QSqlIndex& 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 QSqlIndex
+*/
+
+QSqlIndex QSqlCursor::index( const QStringList& fieldNames ) const
+{
+ QSqlIndex idx;
+ for ( QStringList::ConstIterator it = fieldNames.begin(); it != fieldNames.end(); ++it ) {
+ const QSqlField* 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.
+*/
+
+QSqlIndex QSqlCursor::index( const QString& fieldName ) const
+{
+ QStringList fl( fieldName );
+ return index( fl );
+}
+
+/*!
+ \overload
+
+ Returns an index based on \a fieldName.
+*/
+
+QSqlIndex QSqlCursor::index( const char* fieldName ) const
+{
+ return index( QStringList( QString( 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
+ QSqlCursor 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
+ QSqlCursor 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 QSqlCursor::select( const QString & filter, const QSqlIndex & sort )
+{
+ QString fieldList = toString( d->nm );
+ if ( fieldList.isEmpty() )
+ return FALSE;
+ QString str= "select " + fieldList;
+ str += " from " + d->nm;
+ if ( !filter.isEmpty() ) {
+ d->ftr = filter;
+ str += " where " + filter;
+ } else
+ d->ftr = QString::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 QSqlCursor::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 QSqlCursor::select( const QSqlIndex& 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
+ QSqlCursor cur( "Employee" );
+ QSqlIndex 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 QSqlIndex, 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 QSqlCursor::select( const QSqlIndex & filter, const QSqlIndex & 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 QSqlCursor::Mode values. The default mode for a
+ cursor is \c QSqlCursor::Writable.
+
+ \code
+ QSqlCursor cur( "Employee" );
+ cur.setMode( QSqlCursor::Writable ); // allow insert/update/delete
+ ...
+ cur.setMode( QSqlCursor::Insert | QSqlCursor::Update ); // allow inserts and updates only
+ ...
+ cur.setMode( QSqlCursor::ReadOnly ); // no inserts/updates/deletes allowed
+
+ \endcode
+*/
+
+void QSqlCursor::setMode( int mode )
+{
+ d->md = mode;
+}
+
+/*!
+ Returns the current cursor mode.
+
+ \sa setMode()
+*/
+
+int QSqlCursor::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 QVariant). Calculated
+ fields do not appear in generated SQL statements sent to the
+ database.
+
+ \sa calculateField() QSqlRecord::setGenerated()
+*/
+
+void QSqlCursor::setCalculated( const QString& 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 QSqlCursor::isCalculated( const QString& 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() QVariant
+*/
+
+void QSqlCursor::setTrimmed( const QString& 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 QSqlCursor::isTrimmed( const QString& 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 QSqlCursor::isReadOnly() const
+{
+ return d->md == 0;
+}
+
+/*!
+ Returns TRUE if the cursor will perform inserts; otherwise returns
+ FALSE.
+
+ \sa setMode()
+*/
+
+bool QSqlCursor::canInsert() const
+{
+ return ( ( d->md & Insert ) == Insert ) ;
+}
+
+
+/*!
+ Returns TRUE if the cursor will perform updates; otherwise returns
+ FALSE.
+
+ \sa setMode()
+*/
+
+bool QSqlCursor::canUpdate() const
+{
+ return ( ( d->md & Update ) == Update ) ;
+}
+
+/*!
+ Returns TRUE if the cursor will perform deletes; otherwise returns
+ FALSE.
+
+ \sa setMode()
+*/
+
+bool QSqlCursor::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.
+*/
+
+QString QSqlCursor::toString( const QString& prefix, QSqlField* field, const QString& fieldSep ) const
+{
+ QString f;
+ if ( field && driver() ) {
+ f = ( prefix.length() > 0 ? prefix + QString(".") : QString::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.
+*/
+
+QString QSqlCursor::toString( QSqlRecord* rec, const QString& prefix, const QString& fieldSep,
+ const QString& sep ) const
+{
+ static QString blank( " " );
+ QString filter;
+ bool separator = FALSE;
+ for ( uint j = 0; j < count(); ++j ) {
+ QSqlField* 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.
+*/
+
+QString QSqlCursor::toString( const QSqlIndex& i, QSqlRecord* rec, const QString& prefix,
+ const QString& fieldSep, const QString& sep ) const
+{
+ QString filter;
+ bool separator = FALSE;
+ for( uint j = 0; j < i.count(); ++j ){
+ if ( rec->isGenerated( j ) ) {
+ if( separator ) {
+ filter += " " + sep + " " ;
+ }
+ QString fn = i.fieldName( j );
+ QSqlField* 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 aquired 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 QSqlCursor::insert( bool invalidate )
+{
+ if ( ( d->md & Insert ) != Insert || !driver() )
+ return FALSE;
+ int k = d->editBuffer.count();
+ if ( k == 0 )
+ return 0;
+
+ QString fList;
+ QString vList;
+ bool comma = FALSE;
+ // use a prepared query if the driver supports it
+ if ( driver()->hasFeature( QSqlDriver::PreparedQueries ) ) {
+ int cnt = 0;
+ bool oraStyle = driver()->hasFeature( QSqlDriver::NamedPlaceholders );
+ for( int j = 0; j < k; ++j ) {
+ QSqlField* f = d->editBuffer.field( j );
+ if ( d->editBuffer.isGenerated( j ) ) {
+ if ( comma ) {
+ fList += ",";
+ vList += ",";
+ }
+ fList += f->name();
+ vList += (oraStyle == TRUE) ? ":f" + QString::number(cnt) : QString("?");
+ cnt++;
+ comma = TRUE;
+ }
+ }
+ if ( !comma ) {
+ return 0;
+ }
+ QString 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 ) {
+ QSqlField* 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;
+ }
+ QString 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()
+*/
+
+QSqlRecord* QSqlCursor::editBuffer( bool copy )
+{
+ if ( copy ) {
+ for(uint i = 0; i < d->editBuffer.count(); i++) {
+ if ( QSqlRecord::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()
+*/
+
+QSqlRecord* QSqlCursor::primeUpdate()
+{
+ // memorize the primary keys as they were before the user changed the values in editBuffer
+ QSqlRecord* buf = editBuffer( TRUE );
+ QSqlIndex 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()
+*/
+
+QSqlRecord* QSqlCursor::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()
+*/
+
+QSqlRecord* QSqlCursor::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 acquired 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 QSqlCursor::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 QSqlCursor::update( const QString & 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( QSqlDriver::PreparedQueries ) ) {
+ QString fList;
+ bool comma = FALSE;
+ int cnt = 0;
+ bool oraStyle = driver()->hasFeature( QSqlDriver::NamedPlaceholders );
+ for( int j = 0; j < k; ++j ) {
+ QSqlField* f = d->editBuffer.field( j );
+ if ( d->editBuffer.isGenerated( j ) ) {
+ if ( comma ) {
+ fList += ",";
+ }
+ fList += f->name() + " = " + (oraStyle == TRUE ? ":f" + QString::number(cnt) : QString("?"));
+ cnt++;
+ comma = TRUE;
+ }
+ }
+ if ( !comma ) {
+ return 0;
+ }
+ QString str = "update " + name() + " set " + fList;
+ if ( filter.length() ) {
+ str+= " where " + filter;
+ }
+ return applyPrepared( str, invalidate );
+ } else {
+ QString str = "update " + name();
+ str += " set " + toString( &d->editBuffer, QString::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 QSqlCursor::del( bool invalidate )
+{
+ QSqlIndex 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 QSqlCursor::del( const QString & filter, bool invalidate )
+{
+ if ( ( d->md & Delete ) != Delete )
+ return 0;
+ int k = count();
+ if( k == 0 ) return 0;
+ QString str = "delete from " + name();
+ if ( filter.length() )
+ str+= " where " + filter;
+ return apply( str, invalidate );
+}
+
+/*
+ \internal
+*/
+
+int QSqlCursor::apply( const QString& q, bool invalidate )
+{
+ int ar = 0;
+ if ( invalidate ) {
+ if ( exec( q ) )
+ ar = numRowsAffected();
+ } else if ( driver() ) {
+ QSqlQuery* sql = d->query();
+ if ( sql && sql->exec( q ) )
+ ar = sql->numRowsAffected();
+ }
+ return ar;
+}
+
+/*
+ \internal
+*/
+
+int QSqlCursor::applyPrepared( const QString& q, bool invalidate )
+{
+ int ar = 0;
+ QSqlQuery* sql = 0;
+
+ if ( invalidate ) {
+ sql = (QSqlQuery*)this;
+ d->lastAt = QSql::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 QSqlField* 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 QSqlCursor::exec( const QString & sql )
+{
+ d->lastAt = QSql::BeforeFirst;
+ QSqlQuery::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 QVariant.
+
+ \sa setCalculated()
+*/
+
+QVariant QSqlCursor::calculateField( const QString& )
+{
+ return QVariant();
+}
+
+/*! \internal
+ Ensure fieldlist is synced with query.
+
+*/
+
+static QString qTrim( const QString& s )
+{
+ QString 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 QSqlCursor::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 ( QSqlRecord::isGenerated( i ) ) {
+ QVariant v = QSqlQuery::value( j );
+ if ( ( v.type() == QVariant::String || v.type() == QVariant::CString ) &&
+ d->infoBuffer[ i ].isTrim() ) {
+ v = qTrim( v.toString() );
+ }
+ QSqlRecord::setValue( i, v );
+ if ( QSqlQuery::isNull( j ) )
+ QSqlRecord::field( i )->setNull();
+ j++;
+ }
+ }
+ if ( haveCalculatedFields ) {
+ for ( i = 0; i < count(); ++i ) {
+ if ( d->infoBuffer[i].isCalculated() )
+ QSqlRecord::setValue( i, calculateField( fieldName( i ) ) );
+ }
+ }
+ }
+}
+
+/*! \reimp
+
+*/
+
+void QSqlCursor::afterSeek()
+{
+ sync();
+}
+
+/*!
+ \reimp
+
+ Returns the value of field number \a i.
+*/
+
+QVariant QSqlCursor::value( int i ) const
+{
+ return QSqlRecord::value( i );
+}
+
+/*!
+ \reimp
+
+ Returns the value of the field called \a name.
+*/
+
+QVariant QSqlCursor::value( const QString& name ) const
+{
+ return QSqlRecord::value( name );
+}
+
+/*! \internal
+ cursors should be filled with QSqlFieldInfos...
+*/
+void QSqlCursor::append( const QSqlField& field )
+{
+ append( QSqlFieldInfo( field ) );
+}
+/*! \internal
+ cursors should be filled with QSqlFieldInfos...
+*/
+void QSqlCursor::insert( int pos, const QSqlField& field )
+{
+ insert( pos, QSqlFieldInfo( 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 QSqlRecord::isNull( \a i )
+*/
+bool QSqlCursor::isNull( int i ) const
+{
+ return QSqlRecord::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 QSqlRecord::isNull( \a name )
+*/
+bool QSqlCursor::isNull( const QString& name ) const
+{
+ return QSqlRecord::isNull( name );
+}
+
+/*! \reimp */
+void QSqlCursor::setValue( int i, const QVariant& val )
+{
+#ifdef QT_DEBUG
+ qDebug("QSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete().");
+#endif
+ QSqlRecord::setValue( i, val );
+}
+
+/*! \reimp */
+void QSqlCursor::setValue( const QString& name, const QVariant& val )
+{
+#ifdef QT_DEBUG
+ qDebug("QSqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete().");
+#endif
+ QSqlRecord::setValue( name, val );
+}
+#endif
diff --git a/src/sql/qsqlcursor.h b/src/sql/qsqlcursor.h
new file mode 100644
index 0000000..22d15b0
--- /dev/null
+++ b/src/sql/qsqlcursor.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Definition of QSqlCursor class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLCURSOR_H
+#define QSQLCURSOR_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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlDatabase;
+class QSqlCursorPrivate;
+
+class QM_EXPORT_SQL QSqlCursor : public QSqlRecord, public QSqlQuery
+{
+public:
+ QSqlCursor( const QString & name = QString::null, bool autopopulate = TRUE, QSqlDatabase* db = 0 );
+ QSqlCursor( const QSqlCursor & other );
+ QSqlCursor& operator=( const QSqlCursor& other );
+ ~QSqlCursor();
+
+ enum Mode {
+ ReadOnly = 0,
+ Insert = 1,
+ Update = 2,
+ Delete = 4,
+ Writable = 7
+ };
+
+ QVariant value( int i ) const;
+ QVariant value( const QString& name ) const;
+ void setValue( int i, const QVariant& val );
+ void setValue( const QString& name, const QVariant& val );
+ virtual QSqlIndex primaryIndex( bool prime = TRUE ) const;
+ virtual QSqlIndex index( const QStringList& fieldNames ) const;
+ QSqlIndex index( const QString& fieldName ) const;
+ QSqlIndex index( const char* fieldName ) const;
+ virtual void setPrimaryIndex( const QSqlIndex& idx );
+
+ virtual void append( const QSqlFieldInfo& fieldInfo );
+ virtual void insert( int pos, const QSqlFieldInfo& fieldInfo );
+ void remove( int pos );
+ void clear();
+ void setGenerated( const QString& name, bool generated );
+ void setGenerated( int i, bool generated );
+
+ virtual QSqlRecord* editBuffer( bool copy = FALSE );
+ virtual QSqlRecord* primeInsert();
+ virtual QSqlRecord* primeUpdate();
+ virtual QSqlRecord* 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 QString& name, bool calculated );
+ bool isCalculated( const QString& name ) const;
+ virtual void setTrimmed( const QString& name, bool trim );
+ bool isTrimmed( const QString& name ) const;
+
+ bool isReadOnly() const;
+ bool canInsert() const;
+ bool canUpdate() const;
+ bool canDelete() const;
+
+ bool select();
+ bool select( const QSqlIndex& sort );
+ bool select( const QSqlIndex & filter, const QSqlIndex & sort );
+ virtual bool select( const QString & filter, const QSqlIndex & sort = QSqlIndex() );
+
+ virtual void setSort( const QSqlIndex& sort );
+ QSqlIndex sort() const;
+ virtual void setFilter( const QString& filter );
+ QString filter() const;
+ virtual void setName( const QString& name, bool autopopulate = TRUE );
+ QString name() const;
+ QString toString( const QString& prefix = QString::null,
+ const QString& sep = "," ) const;
+ bool isNull( int i ) const;
+ bool isNull( const QString& name ) const;
+
+protected:
+ void afterSeek();
+ bool exec( const QString & sql );
+
+ virtual QVariant calculateField( const QString& name );
+ virtual int update( const QString & filter, bool invalidate = TRUE );
+ virtual int del( const QString & filter, bool invalidate = TRUE );
+
+ virtual QString toString( const QString& prefix, QSqlField* field, const QString& fieldSep ) const;
+ virtual QString toString( QSqlRecord* rec, const QString& prefix, const QString& fieldSep,
+ const QString& sep ) const;
+ virtual QString toString( const QSqlIndex& i, QSqlRecord* rec, const QString& prefix,
+ const QString& fieldSep, const QString& sep ) const;
+
+private:
+ void sync();
+ int apply( const QString& q, bool invalidate );
+ int applyPrepared( const QString& q, bool invalidate );
+ QSqlRecord& operator=( const QSqlRecord & list );
+ void append( const QSqlField& field );
+ void insert( int pos, const QSqlField& field );
+
+ QSqlCursorPrivate* d;
+};
+
+
+
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqldatabase.cpp b/src/sql/qsqldatabase.cpp
new file mode 100644
index 0000000..eaa7a70
--- /dev/null
+++ b/src/sql/qsqldatabase.cpp
@@ -0,0 +1,1332 @@
+/****************************************************************************
+**
+** Implementation of QSqlDatabase class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QSqlDatabase::defaultConnection = "qt_sql_default_connection";
+
+QPtrDict<QSqlDriverExtension> *qt_driver_extension_dict = 0;
+QPtrDict<QSqlOpenExtension> *qt_open_extension_dict = 0;
+
+static QSingleCleanupHandler< QPtrDict<QSqlDriverExtension> > qt_driver_ext_cleanup;
+static QSingleCleanupHandler< QPtrDict<QSqlOpenExtension> > qt_open_ext_cleanup;
+
+Q_EXPORT QPtrDict<QSqlDriverExtension> *qSqlDriverExtDict()
+{
+ if ( !qt_driver_extension_dict ) {
+ qt_driver_extension_dict = new QPtrDict<QSqlDriverExtension>;
+ qt_driver_ext_cleanup.set( &qt_driver_extension_dict );
+ }
+ return qt_driver_extension_dict;
+}
+
+Q_EXPORT QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict()
+{
+ if ( !qt_open_extension_dict ) {
+ qt_open_extension_dict = new QPtrDict<QSqlOpenExtension>;
+ qt_open_ext_cleanup.set( &qt_open_extension_dict );
+ }
+ return qt_open_extension_dict;
+}
+
+class QNullResult : public QSqlResult
+{
+public:
+ QNullResult(const QSqlDriver* d): QSqlResult(d){}
+ ~QNullResult(){}
+protected:
+ QVariant data( int ) { return QVariant(); }
+ bool reset ( const QString& sqlquery ) { QString 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; }
+ QSqlRecord record() {return QSqlRecord();}
+ int size() {return 0;}
+ int numRowsAffected() {return 0;}
+};
+
+class QNullDriver : public QSqlDriver
+{
+public:
+ QNullDriver(): QSqlDriver(){}
+ ~QNullDriver(){}
+ bool hasFeature( DriverFeature /* f */ ) const { return FALSE; } ;
+ bool open( const QString & ,
+ const QString & ,
+ const QString & ,
+ const QString &,
+ int ) {
+ return FALSE;
+ }
+ void close() {}
+ QSqlQuery createQuery() const { return QSqlQuery( new QNullResult(this) ); }
+};
+
+typedef QDict<QSqlDriverCreatorBase> QDriverDict;
+
+class QSqlDatabaseManager : public QObject
+{
+public:
+ QSqlDatabaseManager( QObject * parent = 0, const char * name = 0 );
+ ~QSqlDatabaseManager();
+ static QSqlDatabase* database( const QString& name, bool open );
+ static QSqlDatabase* addDatabase( QSqlDatabase* db, const QString & name );
+ static void removeDatabase( const QString& name );
+ static void removeDatabase( QSqlDatabase* db );
+ static bool contains( const QString& name );
+ static QDriverDict* driverDict();
+
+protected:
+ static QSqlDatabaseManager* instance();
+ QDict< QSqlDatabase > dbDict;
+ QDriverDict* drDict;
+};
+
+/*!
+ Constructs an SQL database manager.
+*/
+
+QSqlDatabaseManager::QSqlDatabaseManager( QObject * parent, const char * name )
+ : QObject( 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.
+*/
+
+QSqlDatabaseManager::~QSqlDatabaseManager()
+{
+ QDictIterator< QSqlDatabase > it( dbDict );
+ while ( it.current() ) {
+ it.current()->close();
+ delete it.current();
+ ++it;
+ }
+ delete drDict;
+}
+
+/*!
+ \internal
+*/
+QDriverDict* QSqlDatabaseManager::driverDict()
+{
+ QSqlDatabaseManager* sqlConnection = instance();
+ if ( !sqlConnection->drDict ) {
+ sqlConnection->drDict = new QDriverDict();
+ sqlConnection->drDict->setAutoDelete( TRUE );
+ }
+ return sqlConnection->drDict;
+}
+
+
+/*!
+ \internal
+*/
+QSqlDatabaseManager* QSqlDatabaseManager::instance()
+{
+ static QGuardedPtr<QSqlDatabaseManager> sqlConnection = 0;
+ if ( !sqlConnection ) {
+ if( qApp == 0 ){
+ qFatal( "QSqlDatabaseManager: A QApplication object has to be "
+ "instantiated in order to use the SQL module." );
+ return 0;
+ }
+ sqlConnection = new QSqlDatabaseManager( qApp, "database manager" );
+ }
+ return (QSqlDatabaseManager*)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.
+*/
+
+QSqlDatabase* QSqlDatabaseManager::database( const QString& name, bool open )
+{
+ if ( !contains( name ) )
+ return 0;
+
+ QSqlDatabaseManager* sqlConnection = instance();
+ QSqlDatabase* db = sqlConnection->dbDict.find( name );
+ if ( db && !db->isOpen() && open ) {
+ db->open();
+#ifdef QT_CHECK_RANGE
+ if ( !db->isOpen() )
+ qWarning("QSqlDatabaseManager::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 QSqlDatabaseManager::contains( const QString& name )
+{
+ QSqlDatabaseManager* sqlConnection = instance();
+ QSqlDatabase* 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 QApplication object has been instantiated. Use
+ the output of drivers() to determine whether a particular driver
+ is available or not.
+
+ The returned QSqlDatabase object is owned by the framework and
+ must not be deleted. If you want to explicitly remove the connection,
+ use removeDatabase().
+
+ \sa QSqlDatabase database()
+*/
+
+QSqlDatabase* QSqlDatabaseManager::addDatabase( QSqlDatabase* db, const QString & name )
+{
+ QSqlDatabaseManager* 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 QSqlDatabaseManager::removeDatabase( const QString& name )
+{
+ QSqlDatabaseManager* 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 QSqlDatabase 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 QSqlDatabaseManager::removeDatabase( QSqlDatabase* db )
+{
+ QSqlDatabaseManager* sqlConnection = instance();
+ if ( !sqlConnection )
+ return;
+ QDictIterator< QSqlDatabase > it( sqlConnection->dbDict );
+ while ( it.current() ) {
+ if ( it.current() == db ) {
+ sqlConnection->dbDict.remove( it.currentKey() );
+ db->close();
+ delete db;
+ break;
+ }
+ ++it;
+ }
+}
+
+class QSqlDatabasePrivate
+{
+public:
+ QSqlDatabasePrivate():
+ driver(0),
+#ifndef QT_NO_COMPONENT
+ plugIns(0),
+#endif
+ port(-1) {}
+ ~QSqlDatabasePrivate()
+ {
+ }
+ QSqlDriver* driver;
+#ifndef QT_NO_COMPONENT
+ QPluginManager<QSqlDriverFactoryInterface> *plugIns;
+#endif
+ QString dbname;
+ QString uname;
+ QString pword;
+ QString hname;
+ QString drvName;
+ int port;
+ QString connOptions;
+};
+
+/*!
+ \class QSqlDatabase qsqldatabase.h
+ \brief The QSqlDatabase 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 QSqlDriver::hasFeature().
+
+ The QSqlDatabase 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 QSqlDriver). Result set objects provide the interface
+ for executing and manipulating SQL queries (see QSqlQuery).
+*/
+
+/*!
+ 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 QSqlDatabase 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 QSqlDatabase::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()
+*/
+QSqlDatabase* QSqlDatabase::addDatabase( const QString& type, const QString& connectionName )
+{
+ return QSqlDatabaseManager::addDatabase( new QSqlDatabase( 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 QSqlDatabase 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 Qt\endlink document for more information about
+ threading and SQL databases.
+*/
+
+QSqlDatabase* QSqlDatabase::database( const QString& connectionName, bool open )
+{
+ return QSqlDatabaseManager::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 QSqlDatabase::removeDatabase( const QString& connectionName )
+{
+ QSqlDatabaseManager::removeDatabase( connectionName );
+}
+
+/*!
+ \overload
+
+ Removes the database connection \a db from the list of database
+ connections. The QSqlDatabase 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 QSqlDatabase::removeDatabase( QSqlDatabase* db )
+{
+ QSqlDatabaseManager::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
+ QStringList list = QSqlDatabase::drivers();
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+
+QStringList QSqlDatabase::drivers()
+{
+ QStringList l;
+
+#ifndef QT_NO_COMPONENT
+ QPluginManager<QSqlDriverFactoryInterface> *plugIns;
+ plugIns = new QPluginManager<QSqlDriverFactoryInterface>( IID_QSqlDriverFactory, QApplication::libraryPaths(), "/sqldrivers" );
+
+ l = plugIns->featureList();
+ delete plugIns;
+#endif
+
+ QDictIterator<QSqlDriverCreatorBase> itd( *QSqlDatabaseManager::driverDict() );
+ while ( itd.current() ) {
+ if ( !l.contains( itd.currentKey() ) )
+ l << itd.currentKey();
+ ++itd;
+ }
+
+#ifdef QT_SQL_POSTGRES
+ if ( !l.contains( "QPSQL7" ) )
+ l << "QPSQL7";
+#endif
+#ifdef QT_SQL_MYSQL
+ if ( !l.contains( "QMYSQL3" ) )
+ l << "QMYSQL3";
+#endif
+#ifdef QT_SQL_ODBC
+ if ( !l.contains( "QODBC3" ) )
+ l << "QODBC3";
+#endif
+#ifdef QT_SQL_OCI
+ if ( !l.contains( "QOCI8" ) )
+ l << "QOCI8";
+#endif
+#ifdef QT_SQL_TDS
+ if ( !l.contains( "QTDS7" ) )
+ l << "QTDS7";
+#endif
+#ifdef QT_SQL_DB2
+ if ( !l.contains( "QDB2" ) )
+ l << "QDB2";
+#endif
+#ifdef QT_SQL_SQLITE
+ if ( !l.contains( "QSQLITE" ) )
+ l << "QSQLITE";
+#endif
+#ifdef QT_SQL_IBASE
+ if ( !l.contains( "QIBASE" ) )
+ l << "QIBASE";
+#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
+ QSqlDatabase::registerSqlDriver( "MYDRIVER", new QSqlDriverCreator<MyDatabaseDriver> );
+ QSqlDatabase* db = QSqlDatabase::addDatabase( "MYDRIVER" );
+ ...
+ \endcode
+
+ \warning The framework takes ownership of the \a creator pointer,
+ so it should not be deleted.
+*/
+void QSqlDatabase::registerSqlDriver( const QString& name, const QSqlDriverCreatorBase* creator )
+{
+ QSqlDatabaseManager::driverDict()->remove( name );
+ if ( creator )
+ QSqlDatabaseManager::driverDict()->insert( name, creator );
+}
+
+/*!
+ Returns TRUE if the list of database connections contains \a
+ connectionName; otherwise returns FALSE.
+*/
+
+bool QSqlDatabase::contains( const QString& connectionName )
+{
+ return QSqlDatabaseManager::contains( connectionName );
+}
+
+
+/*!
+ Creates a QSqlDatabase 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 QODBC3 \i ODBC Driver (includes Microsoft SQL Server)
+ \row \i QOCI8 \i Oracle Call Interface Driver
+ \row \i QPSQL7 \i PostgreSQL v6.x and v7.x Driver
+ \row \i QTDS7 \i Sybase Adaptive Server
+ \row \i QMYSQL3 \i MySQL Driver
+ \row \i QDB2 \i IBM DB2, v7.1 and higher
+ \row \i QSQLITE \i SQLite Driver
+ \row \i QIBASE \i Borland Interbase Driver
+ \endtable
+
+ Additional third party drivers, including your own custom drivers,
+ can be loaded dynamically.
+
+ \sa registerSqlDriver()
+*/
+
+QSqlDatabase::QSqlDatabase( const QString& type, const QString& name, QObject * parent, const char * objname )
+ : QObject( 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.
+*/
+
+QSqlDatabase::QSqlDatabase( QSqlDriver* driver, QObject * parent, const char * objname )
+ : QObject( parent, objname )
+{
+ d = new QSqlDatabasePrivate();
+ d->driver = driver;
+}
+
+/*!
+ \internal
+
+ Create the actual driver instance \a type.
+*/
+
+void QSqlDatabase::init( const QString& type, const QString& )
+{
+ d = new QSqlDatabasePrivate();
+ d->drvName = type;
+
+ if ( !d->driver ) {
+
+#ifdef QT_SQL_POSTGRES
+ if ( type == "QPSQL7" )
+ d->driver = new QPSQLDriver();
+#endif
+
+#ifdef QT_SQL_MYSQL
+ if ( type == "QMYSQL3" )
+ d->driver = new QMYSQLDriver();
+#endif
+
+#ifdef QT_SQL_ODBC
+ if ( type == "QODBC3" )
+ d->driver = new QODBCDriver();
+#endif
+
+#ifdef QT_SQL_OCI
+ if ( type == "QOCI8" )
+ d->driver = new QOCIDriver();
+#endif
+
+#ifdef QT_SQL_TDS
+ if ( type == "QTDS7" )
+ d->driver = new QTDSDriver();
+#endif
+
+#ifdef QT_SQL_DB2
+ if ( type == "QDB2" )
+ d->driver = new QDB2Driver();
+#endif
+
+#ifdef QT_SQL_SQLITE
+ if ( type == "QSQLITE" )
+ d->driver = new QSQLiteDriver();
+#endif
+
+#ifdef QT_SQL_IBASE
+ if ( type == "QIBASE" )
+ d->driver = new QIBaseDriver();
+#endif
+
+ }
+
+ if ( !d->driver ) {
+ QDictIterator<QSqlDriverCreatorBase> it( *QSqlDatabaseManager::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 QPluginManager<QSqlDriverFactoryInterface>( IID_QSqlDriverFactory, QApplication::libraryPaths(), "/sqldrivers" );
+
+ QInterfacePtr<QSqlDriverFactoryInterface> iface = 0;
+ d->plugIns->queryInterface( type, &iface );
+ if( iface )
+ d->driver = iface->create( type );
+ }
+#endif
+
+ if ( !d->driver ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QSqlDatabase: %s driver not loaded", type.latin1() );
+ qWarning( "QSqlDatabase: available drivers: %s", drivers().join(" ").latin1() );
+#endif
+ d->driver = new QNullDriver();
+ d->driver->setLastError( QSqlError( "Driver not loaded", "Driver not loaded" ) );
+ }
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlDatabase::~QSqlDatabase()
+{
+ 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 QSqlQuery object.
+ Use lastError() to retrieve error information. If \a query is
+ QString::null, an empty, invalid query is returned and lastError()
+ is not affected.
+
+ \sa QSqlQuery lastError()
+*/
+
+QSqlQuery QSqlDatabase::exec( const QString & query ) const
+{
+ QSqlQuery 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 QSqlDatabase::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 QSqlDatabase::open( const QString& user, const QString& password )
+{
+ setUserName( user );
+ return d->driver->open( d->dbname, user, password, d->hname,
+ d->port, d->connOptions );
+}
+
+/*!
+ Closes the database connection, freeing any resources acquired.
+
+ \sa removeDatabase()
+*/
+
+void QSqlDatabase::close()
+{
+ d->driver->close();
+}
+
+/*!
+ Returns TRUE if the database connection is currently open;
+ otherwise returns FALSE.
+*/
+
+bool QSqlDatabase::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 QSqlDatabase::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 QSqlDriver::hasFeature() commit() rollback()
+*/
+
+bool QSqlDatabase::transaction()
+{
+ if ( !d->driver->hasFeature( QSqlDriver::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 QSqlDriver::hasFeature() rollback()
+*/
+
+bool QSqlDatabase::commit()
+{
+ if ( !d->driver->hasFeature( QSqlDriver::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 QSqlDriver::hasFeature() commit() transaction()
+*/
+
+bool QSqlDatabase::rollback()
+{
+ if ( !d->driver->hasFeature( QSqlDriver::Transactions ) )
+ return FALSE;
+ return d->driver->rollbackTransaction();
+}
+
+/*!
+ \property QSqlDatabase::databaseName
+ \brief the name of the database
+
+ Note that the database name is the TNS Service Name for the QOCI8
+ (Oracle) driver.
+
+ For the QODBC3 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 = QSqlDatabase::addDatabase( "QODBC3" );
+ db->setDatabaseName( "DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=myaccessfile.mdb" );
+ if ( db->open() ) {
+ // success!
+ }
+ ...
+ \endcode
+ ("FIL" is the required spelling in Microsoft's API.)
+
+ There is no default value.
+*/
+
+void QSqlDatabase::setDatabaseName( const QString& name )
+{
+ d->dbname = name;
+}
+
+/*!
+ \property QSqlDatabase::userName
+ \brief the user name connected to the database
+
+ There is no default value.
+*/
+
+void QSqlDatabase::setUserName( const QString& name )
+{
+ d->uname = name;
+}
+
+/*!
+ \property QSqlDatabase::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
+ Qt. Use the open() call that takes a password as parameter to
+ avoid this behaviour.
+
+ \sa open()
+*/
+
+void QSqlDatabase::setPassword( const QString& password )
+{
+ d->pword = password;
+}
+
+/*!
+ \property QSqlDatabase::hostName
+ \brief the host name where the database resides
+
+ There is no default value.
+*/
+
+void QSqlDatabase::setHostName( const QString& host )
+{
+ d->hname = host;
+}
+
+/*!
+ \property QSqlDatabase::port
+ \brief the port used to connect to the database
+
+ There is no default value.
+*/
+
+void QSqlDatabase::setPort( int p )
+{
+ d->port = p;
+}
+
+QString QSqlDatabase::databaseName() const
+{
+ return d->dbname;
+}
+
+QString QSqlDatabase::userName() const
+{
+ return d->uname;
+}
+
+QString QSqlDatabase::password() const
+{
+ return d->pword;
+}
+
+QString QSqlDatabase::hostName() const
+{
+ return d->hname;
+}
+
+/*!
+ Returns the name of the driver used by the database connection.
+*/
+QString QSqlDatabase::driverName() const
+{
+ return d->drvName;
+}
+
+int QSqlDatabase::port() const
+{
+ return d->port;
+}
+
+/*!
+ Returns the database driver used to access the database
+ connection.
+*/
+
+QSqlDriver* QSqlDatabase::driver() const
+{
+ return d->driver;
+}
+
+/*!
+ Returns information about the last error that occurred on the
+ database. See QSqlError for more information.
+*/
+
+QSqlError QSqlDatabase::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
+ QStringList list = myDatabase.tables();
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+
+QStringList QSqlDatabase::tables() const
+{
+ return tables( QSql::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
+ QStringList list = myDatabase.tables( QSql::Tables | QSql::Views );
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+
+QStringList QSqlDatabase::tables( QSql::TableType type ) const
+{
+ return d->driver->tables( QString::number( (int)type ) );
+}
+
+/*!
+ Returns the primary index for table \a tablename. If no primary
+ index exists an empty QSqlIndex will be returned.
+*/
+
+QSqlIndex QSqlDatabase::primaryIndex( const QString& tablename ) const
+{
+ return d->driver->primaryIndex( tablename );
+}
+
+
+/*!
+ Returns a QSqlRecord 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()
+*/
+
+QSqlRecord QSqlDatabase::record( const QString& tablename ) const
+{
+ return d->driver->record( tablename );
+}
+
+
+/*!
+ \overload
+
+ Returns a QSqlRecord 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()
+*/
+
+QSqlRecord QSqlDatabase::record( const QSqlQuery& query ) const
+{
+ return d->driver->record( query );
+}
+
+/*!
+ Returns a QSqlRecordInfo populated with meta data about the table
+ or view \a tablename. If no such table (or view) exists, an empty
+ record is returned.
+
+ \sa QSqlRecordInfo, QSqlFieldInfo, record()
+*/
+QSqlRecordInfo QSqlDatabase::recordInfo( const QString& tablename ) const
+{
+ return d->driver->recordInfo( tablename );
+}
+
+/*!
+ \overload
+
+ Returns a QSqlRecordInfo object with meta data for the QSqlQuery
+ \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 QSqlRecordInfo, QSqlFieldInfo, record()
+*/
+QSqlRecordInfo QSqlDatabase::recordInfo( const QSqlQuery& query ) const
+{
+ return d->driver->recordInfo( query );
+}
+
+/*!
+ \property QSqlDatabase::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 requiressl
+ \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( "requiressl=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 QSqlDatabase::setConnectOptions( const QString& options )
+{
+ d->connOptions = options;
+}
+
+QString QSqlDatabase::connectOptions() const
+{
+ return d->connOptions;
+}
+
+/*!
+ Returns TRUE if a driver called \a name is available; otherwise
+ returns FALSE.
+
+ \sa drivers()
+*/
+
+bool QSqlDatabase::isDriverAvailable( const QString& name )
+{
+ QStringList l = drivers();
+ QStringList::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 QPSQL7 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 Qt is installed.)
+ This will pull in the code that is needed to use the PostgreSQL
+ client library and to instantiate a QPSQLDriver 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" );
+ QPSQLDriver* drv = new QPSQLDriver( con );
+ QSqlDatabase* db = QSqlDatabase::addDatabase( drv ); // becomes the new default connection
+ QSqlQuery q;
+ q.exec( "SELECT * FROM people" );
+ ...
+ \endcode
+
+ The above code sets up a PostgreSQL connection and instantiates a
+ QPSQLDriver object. Next, addDatabase() is called to add the
+ connection to the known connections so that it can be used by the
+ Qt SQL classes. When a driver is instantiated with a connection
+ handle (or set of handles), Qt 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 QPSQL7
+ \i QPSQLDriver
+ \i PGconn* connection
+ \i \c qsql_psql.cpp
+ \row
+ \i QMYSQL3
+ \i QMYSQLDriver
+ \i MYSQL* connection
+ \i \c qsql_mysql.cpp
+ \row
+ \i QOCI8
+ \i QOCIDriver
+ \i OCIEnv* environment, OCIError* error, OCISvcCtx* serviceContext
+ \i \c qsql_oci.cpp
+ \row
+ \i QODBC3
+ \i QODBCDriver
+ \i SQLHANDLE environment, SQLHANDLE connection
+ \i \c qsql_odbc.cpp
+ \row
+ \i QDB2
+ \i QDB2
+ \i SQLHANDLE environment, SQLHANDLE connection
+ \i \c qsql_db2.cpp
+ \row
+ \i QTDS7
+ \i QTDSDriver
+ \i LOGINREC* loginRecord, DBPROCESS* dbProcess, const QString& hostName
+ \i \c qsql_tds.cpp
+ \row
+ \i QSQLITE
+ \i QSQLiteDriver
+ \i sqlite* connection
+ \i \c qsql_sqlite.cpp
+ \row
+ \i QIBASE
+ \i QIBaseDriver
+ \i isc_db_handle connection
+ \i \c qsql_ibase.cpp
+ \endtable
+
+ Note: The host name (or service name) is needed when constructing
+ the QTDSDriver for creating new connections for internal
+ queries. This is to prevent the simultaneous usage of several
+ QSqlQuery/\l{QSqlCursor} objects from blocking each other.
+
+ \warning The SQL framework takes ownership of the \a driver pointer,
+ and it should not be deleted. The returned QSqlDatabase object is
+ owned by the framework and must not be deleted. If you want to
+ explicitly remove the connection, use removeDatabase()
+
+ \sa drivers()
+*/
+
+QSqlDatabase* QSqlDatabase::addDatabase( QSqlDriver* driver, const QString& connectionName )
+{
+ return QSqlDatabaseManager::addDatabase( new QSqlDatabase( driver ), connectionName );
+}
+#endif // QT_NO_SQL
diff --git a/src/sql/qsqldatabase.h b/src/sql/qsqldatabase.h
new file mode 100644
index 0000000..3186956
--- /dev/null
+++ b/src/sql/qsqldatabase.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Definition of QSqlDatabase class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLDATABASE_H
+#define QSQLDATABASE_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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlError;
+class QSqlDriver;
+class QSqlIndex;
+class QSqlRecord;
+class QSqlRecordInfo;
+class QSqlDatabasePrivate;
+
+class QM_EXPORT_SQL QSqlDriverCreatorBase
+{
+public:
+ virtual QSqlDriver* createObject() = 0;
+};
+
+template <class type>
+class QM_EXPORT_SQL QSqlDriverCreator: public QSqlDriverCreatorBase
+{
+public:
+ QSqlDriver* createObject() { return new type; }
+};
+
+class QM_EXPORT_SQL QSqlDatabase : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY( QString databaseName READ databaseName WRITE setDatabaseName )
+ Q_PROPERTY( QString userName READ userName WRITE setUserName )
+ Q_PROPERTY( QString password READ password WRITE setPassword )
+ Q_PROPERTY( QString hostName READ hostName WRITE setHostName )
+ Q_PROPERTY( int port READ port WRITE setPort )
+ Q_PROPERTY( QString connectOptions READ connectOptions WRITE setConnectOptions )
+
+public:
+ ~QSqlDatabase();
+
+ bool open();
+ bool open( const QString& user, const QString& password );
+ void close();
+ bool isOpen() const;
+ bool isOpenError() const;
+ QStringList tables() const;
+ QStringList tables( QSql::TableType type ) const;
+ QSqlIndex primaryIndex( const QString& tablename ) const;
+ QSqlRecord record( const QString& tablename ) const;
+ QSqlRecord record( const QSqlQuery& query ) const;
+ QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ QSqlQuery exec( const QString& query = QString::null ) const;
+ QSqlError lastError() const;
+
+ bool transaction();
+ bool commit();
+ bool rollback();
+
+ virtual void setDatabaseName( const QString& name );
+ virtual void setUserName( const QString& name );
+ virtual void setPassword( const QString& password );
+ virtual void setHostName( const QString& host );
+ virtual void setPort( int p );
+ void setConnectOptions( const QString& options = QString::null );
+ QString databaseName() const;
+ QString userName() const;
+ QString password() const;
+ QString hostName() const;
+ QString driverName() const;
+ int port() const;
+ QString connectOptions() const;
+
+ QSqlDriver* driver() const;
+
+ // MOC_SKIP_BEGIN
+ QT_STATIC_CONST char * const defaultConnection;
+ // MOC_SKIP_END
+
+ static QSqlDatabase* addDatabase( const QString& type, const QString& connectionName = defaultConnection );
+ static QSqlDatabase* addDatabase( QSqlDriver* driver, const QString& connectionName = defaultConnection );
+ static QSqlDatabase* database( const QString& connectionName = defaultConnection, bool open = TRUE );
+ static void removeDatabase( const QString& connectionName );
+ static void removeDatabase( QSqlDatabase* db );
+ static bool contains( const QString& connectionName = defaultConnection );
+ static QStringList drivers();
+ static void registerSqlDriver( const QString& name, const QSqlDriverCreatorBase* creator ); // ### 4.0: creator should not be const
+ static bool isDriverAvailable( const QString& name );
+
+protected:
+ QSqlDatabase( const QString& type, const QString& name, QObject * parent=0, const char * objname=0 );
+ QSqlDatabase( QSqlDriver* driver, QObject * parent=0, const char * objname=0 );
+private:
+ void init( const QString& type, const QString& name );
+ QSqlDatabasePrivate* d;
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QSqlDatabase( const QSqlDatabase & );
+ QSqlDatabase &operator=( const QSqlDatabase & );
+#endif
+
+};
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqldriver.cpp b/src/sql/qsqldriver.cpp
new file mode 100644
index 0000000..78db42c
--- /dev/null
+++ b/src/sql/qsqldriver.cpp
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** Implementation of QSqlDriver class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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!
+QPtrDict<QSqlDriverExtension> *qSqlDriverExtDict();
+QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();
+
+/*!
+ \class QSqlDriver qsqldriver.h
+ \brief The QSqlDriver class is an abstract base class for accessing
+ SQL databases.
+
+ \ingroup database
+ \module sql
+
+ This class should not be used directly. Use QSqlDatabase instead.
+*/
+
+/*!
+ Default constructor. Creates a new driver with parent \a parent,
+ called \a name.
+
+*/
+
+QSqlDriver::QSqlDriver( QObject * parent, const char * name )
+: QObject(parent, name),
+ dbState(0),
+ error()
+{
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlDriver::~QSqlDriver()
+{
+}
+
+/*!
+ \fn bool QSqlDriver::open( const QString& db, const QString& user,
+ const QString& password, const QString& 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 QSqlDriver::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 QSqlQuery QSqlDriver::createQuery() const
+
+ Creates an empty SQL result on the database. Derived classes must
+ reimplement this function and return a QSqlQuery object
+ appropriate for their database to the caller.
+
+*/
+
+//void QSqlDriver::destroyResult( QSqlResult* r ) const
+//{
+// if ( r )
+// delete r;
+//}
+
+/*!
+ Returns TRUE if the database connection is open; otherwise returns
+ FALSE.
+*/
+
+bool QSqlDriver::isOpen() const
+{
+ if ( !qSqlDriverExtDict()->isEmpty() ) {
+ QSqlDriverExtension *ext = qSqlDriverExtDict()->find( (QSqlDriver *) 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 QSqlDriver::isOpenError() const
+{
+ return ((dbState & DBState_OpenError) == DBState_OpenError);
+}
+
+/*!
+ \enum QSqlDriver::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
+ QSqlQuery::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 Qt SQL driver\endlink documentation.
+
+ \sa hasFeature()
+*/
+
+/*!
+ \fn bool QSqlDriver::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 QSqlDriver::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 QSqlDriver::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 QSqlDriver::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 QSqlDriver::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 QSqlDriver::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 QSqlDriver::setLastError( const QSqlError& e )
+{
+ error = e;
+}
+
+/*!
+ Returns a QSqlError object which contains information about the
+ last error that occurred on the database.
+*/
+
+QSqlError QSqlDriver::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 QSql::TableTypes as text.
+ An empty string should be treated as QSql::Tables for
+ downward compatibility.
+
+ \sa QSql::TableType
+*/
+
+QStringList QSqlDriver::tables( const QString& ) const
+{
+ return QStringList();
+}
+
+/*!
+ Returns the primary index for table \a tableName. Returns an empty
+ QSqlIndex if the table doesn't have a primary index. The default
+ implementation returns an empty index.
+*/
+
+QSqlIndex QSqlDriver::primaryIndex( const QString& ) const
+{
+ return QSqlIndex();
+}
+
+
+/*!
+ Returns a QSqlRecord 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.
+*/
+
+QSqlRecord QSqlDriver::record( const QString& ) const
+{
+ return QSqlRecord();
+}
+
+/*!
+ \overload
+
+ Returns a QSqlRecord populated with the names of the fields in the
+ SQL \a query. The default implementation returns an empty record.
+*/
+
+QSqlRecord QSqlDriver::record( const QSqlQuery& ) const
+{
+ return QSqlRecord();
+}
+
+/*!
+ Returns a QSqlRecordInfo object with meta data about the table \a
+ tablename.
+*/
+QSqlRecordInfo QSqlDriver::recordInfo( const QString& tablename ) const
+{
+ return QSqlRecordInfo( record( tablename ) );
+}
+
+/*!
+ \overload
+
+ Returns a QSqlRecordInfo object with meta data for the QSqlQuery
+ \a query. Note that this overloaded function may return less
+ information than the recordInfo() function which takes the name of
+ a table as parameter.
+*/
+QSqlRecordInfo QSqlDriver::recordInfo( const QSqlQuery& query ) const
+{
+ return QSqlRecordInfo( 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".
+*/
+
+QString QSqlDriver::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 QVariant::toString().
+
+*/
+QString QSqlDriver::formatValue( const QSqlField* field, bool trimStrings ) const
+{
+ QString r;
+ if ( field->isNull() )
+ r = nullText();
+ else {
+ switch ( field->type() ) {
+ case QVariant::Int:
+ case QVariant::UInt:
+ if ( field->value().type() == QVariant::Bool )
+ r = field->value().toBool() ? "1" : "0";
+ else
+ r = field->value().toString();
+ break;
+ case QVariant::Date:
+ if ( field->value().toDate().isValid() )
+ r = "'" + field->value().toDate().toString( Qt::ISODate ) + "'";
+ else
+ r = nullText();
+ break;
+ case QVariant::Time:
+ if ( field->value().toTime().isValid() )
+ r = "'" + field->value().toTime().toString( Qt::ISODate ) + "'";
+ else
+ r = nullText();
+ break;
+ case QVariant::DateTime:
+ if ( field->value().toDateTime().isValid() )
+ r = "'" +
+ field->value().toDateTime().toString( Qt::ISODate ) + "'";
+ else
+ r = nullText();
+ break;
+ case QVariant::String:
+ case QVariant::CString: {
+ QString 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( QChar( '\'' ), "''" );
+ r = "'" + result + "'";
+ break;
+ }
+ case QVariant::Bool:
+ if ( field->value().toBool() )
+ r = "1";
+ else
+ r = "0";
+ break;
+ case QVariant::ByteArray : {
+ if ( hasFeature( BLOB ) ) {
+ QByteArray ba = field->value().toByteArray();
+ QString res;
+ static const char hexchars[] = "0123456789abcdef";
+ for ( uint i = 0; i < ba.size(); ++i ) {
+ uchar s = (uchar) ba[(int)i];
+ res += hexchars[s >> 4];
+ res += hexchars[s & 0x0f];
+ }
+ r = "'" + 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 QSqlDriver::open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts )
+{
+ if ( !qSqlOpenExtDict()->isEmpty() ) {
+ QSqlOpenExtension *ext = qSqlOpenExtDict()->find( (QSqlDriver *) 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 0000000..4115bc6
--- /dev/null
+++ b/src/sql/qsqldriver.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Definition of QSqlDriver class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLDRIVER_H
+#define QSQLDRIVER_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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlDriverExtension;
+
+class QSqlDatabase;
+
+class QM_EXPORT_SQL QSqlDriver : public QObject
+{
+ friend class QSqlDatabase;
+ Q_OBJECT
+public:
+ enum DriverFeature { Transactions, QuerySize, BLOB, Unicode, PreparedQueries,
+ NamedPlaceholders, PositionalPlaceholders };
+
+ QSqlDriver( QObject * parent=0, const char * name=0 );
+ ~QSqlDriver();
+ bool isOpen() const;
+ bool isOpenError() const;
+
+ virtual bool beginTransaction();
+ virtual bool commitTransaction();
+ virtual bool rollbackTransaction();
+ virtual QStringList tables( const QString& tableType ) const;
+ virtual QSqlIndex primaryIndex( const QString& tableName ) const;
+ virtual QSqlRecord record( const QString& tableName ) const;
+ virtual QSqlRecord record( const QSqlQuery& query ) const;
+ virtual QSqlRecordInfo recordInfo( const QString& tablename ) const;
+ virtual QSqlRecordInfo recordInfo( const QSqlQuery& query ) const;
+ virtual QString nullText() const;
+ virtual QString formatValue( const QSqlField* field, bool trimStrings = FALSE ) const;
+ QSqlError lastError() const;
+
+ virtual bool hasFeature( DriverFeature f ) const = 0;
+ virtual bool open( const QString & db,
+ const QString & user = QString::null,
+ const QString & password = QString::null,
+ const QString & host = QString::null,
+ int port = -1 ) = 0;
+ virtual void close() = 0;
+ virtual QSqlQuery createQuery() const = 0;
+
+ // ### remove for 4.0
+ bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts );
+protected:
+ virtual void setOpen( bool o );
+ virtual void setOpenError( bool e );
+ virtual void setLastError( const QSqlError& e );
+private:
+ // ### This class needs a d-pointer in 4.0.
+ int dbState;
+ QSqlError error;
+#if defined(Q_DISABLE_COPY)
+ QSqlDriver( const QSqlDriver & );
+ QSqlDriver &operator=( const QSqlDriver & );
+#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 0000000..94df19a
--- /dev/null
+++ b/src/sql/qsqldriverinterface_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Definition of QSqlDriverInterface class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLDRIVERINTERFACE_H
+#define QSQLDRIVERINTERFACE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt 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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+#ifndef QT_NO_COMPONENT
+
+// {EDDD5AD5-DF3C-400c-A711-163B72FE5F61}
+#ifndef IID_QSqlDriverFactory
+#define IID_QSqlDriverFactory QUuid(0xeddd5ad5, 0xdf3c, 0x400c, 0xa7, 0x11, 0x16, 0x3b, 0x72, 0xfe, 0x5f, 0x61)
+#endif
+
+class QSqlDriver;
+
+struct QM_EXPORT_SQL QSqlDriverFactoryInterface : public QFeatureListInterface
+{
+ virtual QSqlDriver* create( const QString& name ) = 0;
+};
+
+#endif //QT_NO_COMPONENT
+#endif // QT_NO_SQL
+
+#endif // QSQLDRIVERINTERFACE_P_H
diff --git a/src/sql/qsqldriverplugin.cpp b/src/sql/qsqldriverplugin.cpp
new file mode 100644
index 0000000..224c616
--- /dev/null
+++ b/src/sql/qsqldriverplugin.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Implementation of QSqlDriverPlugin class
+**
+** Created : 2001-09-20
+**
+** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqldriverplugin.h"
+
+#ifndef QT_NO_SQL
+#ifndef QT_NO_COMPONENT
+
+#include "qsqldriverinterface_p.h"
+
+/*!
+ \class QSqlDriverPlugin qsqldriverplugin.h
+ \brief The QSqlDriverPlugin class provides an abstract base for custom QSqlDriver 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 Qt.
+
+ 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 Qt 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 QStringList QSqlDriverPlugin::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 QSqlDriver* QSqlDriverPlugin::create( const QString& key )
+
+ Creates and returns a QSqlDriver object for the driver key \a key.
+ The driver key is usually the class name of the required driver.
+
+ \sa keys()
+*/
+
+class QSqlDriverPluginPrivate : public QSqlDriverFactoryInterface
+{
+public:
+ QSqlDriverPluginPrivate( QSqlDriverPlugin *p )
+ : plugin( p )
+ {
+ }
+ virtual ~QSqlDriverPluginPrivate();
+
+ QRESULT queryInterface( const QUuid &iid, QUnknownInterface **iface );
+ Q_REFCOUNT;
+
+ QStringList featureList() const;
+ QSqlDriver *create( const QString &key );
+
+private:
+ QSqlDriverPlugin *plugin;
+};
+
+QSqlDriverPluginPrivate::~QSqlDriverPluginPrivate()
+{
+ delete plugin;
+}
+
+QRESULT QSqlDriverPluginPrivate::queryInterface( const QUuid &iid, QUnknownInterface **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 QE_NOINTERFACE;
+
+ (*iface)->addRef();
+ return QS_OK;
+}
+
+QStringList QSqlDriverPluginPrivate::featureList() const
+{
+ return plugin->keys();
+}
+
+QSqlDriver *QSqlDriverPluginPrivate::create( const QString &key )
+{
+ return plugin->create( key );
+}
+
+/*!
+ Constructs a SQL driver plugin. This is invoked automatically by
+ the \c Q_EXPORT_PLUGIN macro.
+*/
+
+QSqlDriverPlugin::QSqlDriverPlugin()
+ : QGPlugin( d = new QSqlDriverPluginPrivate( this ) )
+{
+}
+
+/*!
+ Destroys the SQL driver plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QSqlDriverPlugin::~QSqlDriverPlugin()
+{
+ // 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 0000000..8949fd9
--- /dev/null
+++ b/src/sql/qsqldriverplugin.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Definition of QSqlDriverPlugin class
+**
+** Created : 2001-09-20
+**
+** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLDRIVERPLUGIN_H
+#define QSQLDRIVERPLUGIN_H
+
+#ifndef QT_H
+#include "qgplugin.h"
+#include "qstringlist.h"
+#endif // QT_H
+
+#ifndef QT_NO_SQL
+#ifndef QT_NO_COMPONENT
+
+class QSqlDriver;
+class QSqlDriverPluginPrivate;
+
+class Q_EXPORT QSqlDriverPlugin : public QGPlugin
+{
+ Q_OBJECT
+public:
+ QSqlDriverPlugin();
+ ~QSqlDriverPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QSqlDriver *create( const QString &key ) = 0;
+
+private:
+ QSqlDriverPluginPrivate *d;
+};
+
+#endif // QT_NO_COMPONENT
+#endif // QT_NO_SQL
+
+#endif // QSQLDRIVERPLUGIN_H
diff --git a/src/sql/qsqleditorfactory.cpp b/src/sql/qsqleditorfactory.cpp
new file mode 100644
index 0000000..d0ce4e7
--- /dev/null
+++ b/src/sql/qsqleditorfactory.cpp
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Implementation of QSqlEditorFactory class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QSqlEditorFactory qsqleditorfactory.h
+ \brief The QSqlEditorFactory class is used to create the editors
+ used by QDataTable and QSqlForm.
+
+ \ingroup database
+ \module sql
+
+ QSqlEditorFactory is used by QDataTable and QSqlForm to
+ automatically create appropriate editors for a given QSqlField.
+ For example if the field is a QVariant::String a QLineEdit would
+ be the default editor, whereas a QVariant::Int's default editor
+ would be a QSpinBox.
+
+ If you want to create different editors for fields with the same
+ data type, subclass QSqlEditorFactory and reimplement the
+ createEditor() function.
+
+ \sa QDataTable, QSqlForm
+*/
+
+
+/*!
+ Constructs a SQL editor factory with parent \a parent, called \a
+ name.
+*/
+
+QSqlEditorFactory::QSqlEditorFactory ( QObject * parent, const char * name )
+ : QEditorFactory( parent, name )
+{
+
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlEditorFactory::~QSqlEditorFactory()
+{
+
+}
+
+static QSqlEditorFactory * defaultfactory = 0;
+static QCleanupHandler< QSqlEditorFactory > qsql_cleanup_editor_factory;
+
+/*!
+ Returns an instance of a default editor factory.
+*/
+
+QSqlEditorFactory * QSqlEditorFactory::defaultFactory()
+{
+ if( defaultfactory == 0 ){
+ defaultfactory = new QSqlEditorFactory();
+ qsql_cleanup_editor_factory.add( &defaultfactory );
+ }
+
+ return defaultfactory;
+}
+
+/*!
+ Replaces the default editor factory with \a factory. All
+ QDataTable and QSqlForm instantiations will use this new factory
+ for creating field editors. \e{QSqlEditorFactory takes ownership
+ of \a factory, and destroys it when it is no longer needed.}
+*/
+
+void QSqlEditorFactory::installDefaultFactory( QSqlEditorFactory * 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 QVariant
+ \a variant.
+
+ The widget that is returned has the parent \a parent (which may be
+ zero). If \a variant is invalid, 0 is returned.
+*/
+
+QWidget * QSqlEditorFactory::createEditor( QWidget * parent,
+ const QVariant & variant )
+{
+ return QEditorFactory::createEditor( parent, variant );
+}
+
+/*!
+ \overload
+
+ Creates and returns the appropriate editor for the QSqlField \a
+ field.
+*/
+
+QWidget * QSqlEditorFactory::createEditor( QWidget * parent,
+ const QSqlField * field )
+{
+ if ( !field ) {
+ return 0;
+ }
+
+ QWidget * w = 0;
+ switch( field->type() ){
+ case QVariant::Invalid:
+ w = 0;
+ break;
+ case QVariant::Bool:
+ w = new QComboBox( parent, "qt_editor_bool" );
+ ((QComboBox *) w)->insertItem( "False" );
+ ((QComboBox *) w)->insertItem( "True" );
+ break;
+ case QVariant::UInt:
+ w = new QSpinBox( 0, 2147483647, 1, parent, "qt_editor_spinbox" );
+ break;
+ case QVariant::Int:
+ w = new QSpinBox( -2147483647, 2147483647, 1, parent, "qt_editor_int" );
+ break;
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::String:
+ case QVariant::CString:
+ case QVariant::Double:
+ w = new QLineEdit( parent, "qt_editor_double" );
+ ((QLineEdit*)w)->setFrame( FALSE );
+ break;
+ case QVariant::Date:
+ w = new QDateEdit( parent, "qt_editor_date" );
+ break;
+ case QVariant::Time:
+ w = new QTimeEdit( parent, "qt_editor_time" );
+ break;
+ case QVariant::DateTime:
+ w = new QDateTimeEdit( parent, "qt_editor_datetime" );
+ break;
+#ifndef QT_NO_LABEL
+ case QVariant::Pixmap:
+ w = new QLabel( parent, "qt_editor_pixmap" );
+ break;
+#endif
+ case QVariant::Palette:
+ case QVariant::ColorGroup:
+ case QVariant::Color:
+ case QVariant::Font:
+ case QVariant::Brush:
+ case QVariant::Bitmap:
+ case QVariant::Cursor:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::Rect:
+ case QVariant::Size:
+ case QVariant::IconSet:
+ case QVariant::Point:
+ case QVariant::PointArray:
+ case QVariant::Region:
+ case QVariant::SizePolicy:
+ case QVariant::ByteArray:
+ default:
+ w = new QWidget( 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 0000000..50c572e
--- /dev/null
+++ b/src/sql/qsqleditorfactory.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Definition of QSqlEditorFactory class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLEDITORFACTORY_H
+#define QSQLEDITORFACTORY_H
+
+#ifndef QT_H
+#include "qeditorfactory.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+class QSqlField;
+
+class QM_EXPORT_SQL QSqlEditorFactory : public QEditorFactory
+{
+public:
+ QSqlEditorFactory ( QObject * parent = 0, const char * name = 0 );
+ ~QSqlEditorFactory();
+ virtual QWidget * createEditor( QWidget * parent, const QVariant & variant );
+ virtual QWidget * createEditor( QWidget * parent, const QSqlField * field );
+
+ static QSqlEditorFactory * defaultFactory();
+ static void installDefaultFactory( QSqlEditorFactory * factory );
+
+private:
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QSqlEditorFactory( const QSqlEditorFactory & );
+ QSqlEditorFactory &operator=( const QSqlEditorFactory & );
+#endif
+};
+
+#endif // QT_NO_SQL
+#endif // QSQLEDITORFACTORY_H
diff --git a/src/sql/qsqlerror.cpp b/src/sql/qsqlerror.cpp
new file mode 100644
index 0000000..137be84
--- /dev/null
+++ b/src/sql/qsqlerror.cpp
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Implementation of QSqlError class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlerror.h"
+#include <qmessagebox.h>
+
+#ifndef QT_NO_SQL
+
+/*!
+ \class QSqlError qsqlerror.h
+ \brief The QSqlError 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 QSqlError::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.
+*/
+
+QSqlError::QSqlError( const QString& driverText,
+ const QString& databaseText,
+ int type,
+ int number )
+: driverError(driverText),
+ databaseError(databaseText),
+ errorType(type),
+ errorNumber(number)
+{
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+
+QSqlError::QSqlError( const QSqlError& other )
+: driverError(other.driverError),
+ databaseError(other.databaseError),
+ errorType(other.errorType),
+ errorNumber(other.errorNumber)
+{
+}
+
+/*!
+ Sets the error equal to \a other.
+*/
+
+QSqlError& QSqlError::operator=( const QSqlError& other )
+{
+ driverError = other.driverError;
+ databaseError = other.databaseError;
+ errorType = other.errorType;
+ errorNumber = other.errorNumber;
+ return *this;
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlError::~QSqlError()
+{
+}
+
+/*!
+ Returns the text of the error as reported by the driver. This may
+ contain database-specific descriptions.
+*/
+QString QSqlError::driverText() const
+{
+ return driverError;
+}
+
+/*!
+ Sets the driver error text to the value of \a driverText.
+*/
+
+void QSqlError::setDriverText( const QString& driverText )
+{
+ driverError = driverText;
+}
+
+/*!
+ Returns the text of the error as reported by the database. This
+ may contain database-specific descriptions.
+*/
+
+QString QSqlError::databaseText() const
+{
+ return databaseError;
+}
+
+/*!
+ Sets the database error text to the value of \a databaseText.
+*/
+
+void QSqlError::setDatabaseText( const QString& databaseText )
+{
+ databaseError = databaseText;
+}
+
+/*!
+ Returns the error type, or -1 if the type cannot be determined.
+
+ \sa QSqlError::Type.
+*/
+
+int QSqlError::type() const
+{
+ return errorType;
+}
+
+/*!
+ Sets the error type to the value of \a type.
+*/
+
+void QSqlError::setType( int type )
+{
+ errorType = type;
+}
+
+/*!
+ Returns the database-specific error number, or -1 if it cannot be
+ determined.
+*/
+
+int QSqlError::number() const
+{
+ return errorNumber;
+}
+
+/*!
+ Sets the database-specific error number to \a number.
+*/
+
+void QSqlError::setNumber( int number )
+{
+ errorNumber = number;
+}
+
+/*!
+ This is a convenience function that returns databaseText() and
+ driverText() concatenated into a single string.
+
+ \sa showMessage(), driverText(), databaseText()
+*/
+
+QString QSqlError::text() const
+{
+ if ( databaseError.endsWith("\n") )
+ return databaseError + driverError;
+ else
+ return databaseError + " " + driverError;
+}
+
+/*!
+ \obsolete
+
+ This is a convenience function that pops up a QMessageBox
+ 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 QSqlError::showMessage( const QString& msg ) const
+{
+#ifndef QT_NO_MESSAGEBOX
+ QMessageBox::warning( NULL, "SQL Error", msg + text(),
+ QMessageBox::Ok, QMessageBox::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 0000000..addbd71
--- /dev/null
+++ b/src/sql/qsqlerror.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Definition of QSqlError class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLERROR_H
+#define QSQLERROR_H
+
+#ifndef QT_H
+#include "qstring.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QM_EXPORT_SQL QSqlError
+{
+public:
+ enum Type {
+ None,
+ Connection,
+ Statement,
+ Transaction,
+ Unknown
+ };
+ QSqlError( const QString& driverText = QString::null,
+ const QString& databaseText = QString::null,
+ int type = QSqlError::None,
+ int number = -1 );
+ QSqlError( const QSqlError& other );
+ QSqlError& operator=( const QSqlError& other );
+ virtual ~QSqlError();
+
+ QString driverText() const;
+ virtual void setDriverText( const QString& driverText );
+ QString databaseText() const;
+ virtual void setDatabaseText( const QString& databaseText );
+ int type() const;
+ virtual void setType( int type );
+ int number() const;
+ virtual void setNumber( int number );
+ QString text() const;
+ void showMessage( const QString& msg = QString::null ) const;
+
+private:
+ QString driverError;
+ QString 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 0000000..4e68fee
--- /dev/null
+++ b/src/sql/qsqlextension_p.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Implementation of the QSqlExtension class
+**
+** Created : 2002-06-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlextension_p.h"
+
+#ifndef QT_NO_SQL
+QSqlExtension::QSqlExtension()
+ : bindm( BindByPosition ), bindCount( 0 )
+{
+}
+
+QSqlExtension::~QSqlExtension()
+{
+}
+
+bool QSqlExtension::prepare( const QString& /*query*/ )
+{
+ return FALSE;
+}
+
+bool QSqlExtension::exec()
+{
+ return FALSE;
+}
+
+void QSqlExtension::bindValue( const QString& placeholder, const QVariant& val, QSql::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 QSqlExtension::bindValue( int pos, const QVariant& val, QSql::ParameterType tp )
+{
+ bindm = BindByPosition;
+ index[ pos ] = QString::number( pos );
+ QString nm = QString::number( pos );
+ values[ nm ] = Param( val, tp );
+}
+
+void QSqlExtension::addBindValue( const QVariant& val, QSql::ParameterType tp )
+{
+ bindm = BindByPosition;
+ bindValue( bindCount++, val, tp );
+}
+
+void QSqlExtension::clearValues()
+{
+ values.clear();
+ bindCount = 0;
+}
+
+void QSqlExtension::resetBindCount()
+{
+ bindCount = 0;
+}
+
+void QSqlExtension::clearIndex()
+{
+ index.clear();
+ holders.clear();
+}
+
+void QSqlExtension::clear()
+{
+ clearValues();
+ clearIndex();
+}
+
+QVariant QSqlExtension::parameterValue( const QString& holder )
+{
+ return values[ holder ].value;
+}
+
+QVariant QSqlExtension::parameterValue( int pos )
+{
+ return values[ index[ pos ] ].value;
+}
+
+QVariant QSqlExtension::boundValue( const QString& holder ) const
+{
+ return values[ holder ].value;
+}
+
+QVariant QSqlExtension::boundValue( int pos ) const
+{
+ return values[ index[ pos ] ].value;
+}
+
+QMap<QString, QVariant> QSqlExtension::boundValues() const
+{
+ QMap<QString, Param>::ConstIterator it;
+ QMap<QString, QVariant> m;
+ if ( bindm == BindByName ) {
+ for ( it = values.begin(); it != values.end(); ++it )
+ m.insert( it.key(), it.data().value );
+ } else {
+ QString key, tmp, fmt;
+ fmt.sprintf( "%%0%dd", QString::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;
+}
+
+QSqlExtension::BindMethod QSqlExtension::bindMethod()
+{
+ return bindm;
+}
+
+QSqlDriverExtension::QSqlDriverExtension()
+{
+}
+
+QSqlDriverExtension::~QSqlDriverExtension()
+{
+}
+
+QSqlOpenExtension::QSqlOpenExtension()
+{
+}
+
+QSqlOpenExtension::~QSqlOpenExtension()
+{
+}
+#endif
diff --git a/src/sql/qsqlextension_p.h b/src/sql/qsqlextension_p.h
new file mode 100644
index 0000000..01e540e
--- /dev/null
+++ b/src/sql/qsqlextension_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Definition of the QSqlExtension class
+**
+** Created : 2002-06-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLEXTENSION_P_H
+#define QSQLEXTENSION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#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 QM_EXPORT_SQL
+#define QM_TEMPLATE_EXTERN_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#define QM_TEMPLATE_EXTERN_SQL Q_TEMPLATE_EXTERN
+#endif
+
+struct Param {
+ Param( const QVariant& v = QVariant(), QSql::ParameterType t = QSql::In ): value( v ), typ( t ) {}
+ QVariant value;
+ QSql::ParameterType typ;
+ Q_DUMMY_COMPARISON_OPERATOR(Param)
+};
+
+struct Holder {
+ Holder( const QString& hldr = QString::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; }
+ QString holderName;
+ int holderPos;
+};
+
+#define Q_DEFINED_QSQLEXTENSION
+#include "qwinexport.h"
+
+class QM_EXPORT_SQL QSqlExtension {
+public:
+ QSqlExtension();
+ virtual ~QSqlExtension();
+ virtual bool prepare( const QString& query );
+ virtual bool exec();
+ virtual void bindValue( const QString& holder, const QVariant& value, QSql::ParameterType = QSql::In );
+ virtual void bindValue( int pos, const QVariant& value, QSql::ParameterType = QSql::In );
+ virtual void addBindValue( const QVariant& value, QSql::ParameterType = QSql::In );
+ virtual QVariant parameterValue( const QString& holder );
+ virtual QVariant parameterValue( int pos );
+ QVariant boundValue( const QString& holder ) const;
+ QVariant boundValue( int pos ) const;
+ QMap<QString, QVariant> 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;
+
+ QMap<int, QString> index;
+ typedef QMap<QString, Param> ValueMap;
+ ValueMap values;
+
+ // convenience container for QSqlQuery
+ // to map holders <-> positions
+ typedef QValueVector<Holder> HolderVector;
+ HolderVector holders;
+};
+
+class QM_EXPORT_SQL QSqlDriverExtension
+{
+public:
+ QSqlDriverExtension();
+ virtual ~QSqlDriverExtension();
+ virtual bool isOpen() const = 0;
+};
+
+class QM_EXPORT_SQL QSqlOpenExtension
+{
+public:
+ QSqlOpenExtension();
+ virtual ~QSqlOpenExtension();
+ virtual bool open( const QString& db,
+ const QString& user,
+ const QString& password,
+ const QString& host,
+ int port,
+ const QString& connOpts ) = 0;
+};
+#endif
+
+#endif
diff --git a/src/sql/qsqlfield.cpp b/src/sql/qsqlfield.cpp
new file mode 100644
index 0000000..bf66011
--- /dev/null
+++ b/src/sql/qsqlfield.cpp
@@ -0,0 +1,563 @@
+/****************************************************************************
+**
+** Implementation of QSqlField class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlfield.h"
+
+#ifndef QT_NO_SQL
+
+
+/*!
+ \class QSqlField qsqlfield.h
+ \brief The QSqlField class manipulates the fields in SQL database tables
+ and views.
+
+ \ingroup database
+ \module sql
+
+ QSqlField 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 QVariants. Using an incompatible
+ type is not permitted. For example:
+
+ \code
+ QSqlField f( "myfield", QVariant::Int );
+ f.setValue( QPixmap() ); // will not work
+ \endcode
+
+ However, the field will attempt to cast certain data types to the
+ field data type where possible:
+
+ \code
+ QSqlField f( "myfield", QVariant::Int );
+ f.setValue( QString("123") ); // casts QString to int
+ \endcode
+
+ QSqlField objects are rarely created explicitly in application
+ code. They are usually accessed indirectly through \l QSqlRecord
+ or \l QSqlCursor which already contain a list of fields. For
+ example:
+
+ \code
+ QSqlCursor cur( "Employee" ); // create cursor using the 'Employee' table
+ QSqlField* 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
+ QSqlCursor cur( "Employee" );
+ cur.setValue( "name", "Dave" );
+ ...
+ \endcode
+*/
+
+/*!
+ Constructs an empty field called \a fieldName of type \a type.
+*/
+
+QSqlField::QSqlField( const QString& fieldName, QVariant::Type type )
+ : nm(fieldName), ro(FALSE), nul(FALSE)
+{
+ d = new QSqlFieldPrivate();
+ d->type = type;
+ val.cast( type );
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QSqlField::QSqlField( const QSqlField& other )
+ : nm( other.nm ), val( other.val ), ro( other.ro ), nul( other.nul )
+{
+ d = new QSqlFieldPrivate();
+ d->type = other.d->type;
+}
+
+/*!
+ Sets the field equal to \a other.
+*/
+
+QSqlField& QSqlField::operator=( const QSqlField& 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 QSqlField::operator==(const QSqlField& 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.
+*/
+
+QSqlField::~QSqlField()
+{
+ delete d;
+}
+
+
+/*!
+ \fn QVariant QSqlField::value() const
+
+ Returns the value of the field as a QVariant.
+*/
+
+/*!
+ 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 QString to an
+ integer data type. For example:
+
+ \code
+ QSqlCursor cur( "Employee" ); // 'Employee' table
+ QSqlField* f = cur.field( "student_count" ); // an integer field
+ ...
+ f->setValue( myLineEdit->text() ); // cast the line edit text to an integer
+ \endcode
+
+ \sa isReadOnly()
+*/
+
+void QSqlField::setValue( const QVariant& value )
+{
+ if ( isReadOnly() )
+ return;
+ if ( value.type() != d->type ) {
+ if ( !val.canCast( d->type ) )
+ qWarning("QSqlField::setValue: %s cannot cast from %s to %s",
+ nm.local8Bit().data(), value.typeName(), QVariant::typeToName( d->type ) );
+ }
+ val = value;
+
+ if ( value.isNull() )
+ nul = TRUE;
+ else
+ nul = val.type() == QVariant::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 QSqlField::clear( bool nullify )
+{
+ if ( isReadOnly() )
+ return;
+ QVariant v;
+ v.cast( type() );
+ val = v;
+ if ( nullify )
+ nul = TRUE;
+}
+
+/*!
+ \fn void QSqlField::setName( const QString& name )
+
+ Sets the name of the field to \a name.
+*/
+
+void QSqlField::setName( const QString& name )
+{
+ nm = name;
+}
+
+/*!
+ \fn void QSqlField::setNull()
+
+ Sets the field to NULL and clears the value using clear(). If the
+ field is read-only, nothing happens.
+
+ \sa isReadOnly() clear()
+*/
+
+void QSqlField::setNull()
+{
+ clear( TRUE );
+}
+
+/*!
+ \fn void QSqlField::setReadOnly( bool readOnly )
+
+ Sets the read only flag of the field's value to \a readOnly.
+
+ \sa setValue()
+*/
+void QSqlField::setReadOnly( bool readOnly )
+{
+ ro = readOnly;
+}
+
+/*!
+ \fn QString QSqlField::name() const
+
+ Returns the name of the field.
+*/
+
+/*!
+ \fn QVariant::Type QSqlField::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 QSqlField::isReadOnly() const
+
+ Returns TRUE if the field's value is read only; otherwise returns
+ FALSE.
+*/
+
+/*!
+ \fn bool QSqlField::isNull() const
+
+ Returns TRUE if the field is currently NULL; otherwise returns
+ FALSE.
+*/
+
+
+/******************************************/
+/******* QSqlFieldInfo Impl ******/
+/******************************************/
+
+struct QSqlFieldInfoPrivate
+{
+ int required, len, prec, typeID;
+ uint generated: 1;
+ uint trim: 1;
+ uint calculated: 1;
+ QString name;
+ QString typeName;
+ QVariant::Type typ;
+ QVariant defValue;
+};
+
+/*!
+ \class QSqlFieldInfo qsqlfield.h
+ \brief The QSqlFieldInfo class stores meta data associated with a SQL field.
+
+ \ingroup database
+ \module sql
+
+ QSqlFieldInfo objects only store meta data; field values are
+ stored in QSqlField objects.
+
+ All values must be set in the constructor, and may be retrieved
+ using isRequired(), type(), length(), precision(), defaultValue(),
+ name(), isGenerated() and typeID().
+*/
+
+/*!
+ Constructs a QSqlFieldInfo 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 QVariant.
+ \row \i \a required \i greater than 0 if the field is required, 0
+ if its value can be NULL and less than 0 if it cannot be
+ determined whether the field is required 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. QVariant() 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 QSqlCursor.
+ \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 QSqlCursor and overriding
+ QSqlCursor::calculateField().
+ \endtable
+*/
+QSqlFieldInfo::QSqlFieldInfo( const QString& name,
+ QVariant::Type typ,
+ int required,
+ int len,
+ int prec,
+ const QVariant& defValue,
+ int typeID,
+ bool generated,
+ bool trim,
+ bool calculated )
+{
+ d = new QSqlFieldInfoPrivate();
+ d->name = name;
+ d->typ = typ;
+ d->required = required;
+ 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.
+*/
+QSqlFieldInfo::QSqlFieldInfo( const QSqlFieldInfo & other )
+{
+ d = new QSqlFieldInfoPrivate( *(other.d) );
+}
+
+/*!
+ Creates a QSqlFieldInfo object with the type and the name of the
+ QSqlField \a other. If \a generated is TRUE this field will be
+ included in auto-generated SQL statments, e.g. in QSqlCursor.
+*/
+QSqlFieldInfo::QSqlFieldInfo( const QSqlField & other, bool generated )
+{
+ d = new QSqlFieldInfoPrivate();
+ d->name = other.name();
+ d->typ = other.type();
+ d->required = -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.
+*/
+QSqlFieldInfo::~QSqlFieldInfo()
+{
+ delete d;
+}
+
+/*!
+ Assigns \a other to this field info and returns a reference to it.
+*/
+QSqlFieldInfo& QSqlFieldInfo::operator=( const QSqlFieldInfo& other )
+{
+ delete d;
+ d = new QSqlFieldInfoPrivate( *(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 QSqlFieldInfo::operator==( const QSqlFieldInfo& f ) const
+{
+ return ( d->name == f.d->name &&
+ d->typ == f.d->typ &&
+ d->required == f.d->required &&
+ 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 QSqlField based on the information in this
+ QSqlFieldInfo.
+*/
+QSqlField QSqlFieldInfo::toField() const
+{ return QSqlField( d->name, d->typ ); }
+
+/*!
+ Returns a value greater than 0 if the field is required (NULL
+ values are not allowed), 0 if it isn't required (NULL values are
+ allowed) or less than 0 if it cannot be determined whether the
+ field is required or not.
+*/
+int QSqlFieldInfo::isRequired() const
+{ return d->required; }
+
+/*!
+ Returns the field's type or QVariant::Invalid if the type is
+ unknown.
+*/
+QVariant::Type QSqlFieldInfo::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 QSqlFieldInfo::length() const
+{ return d->len; }
+
+/*!
+ Returns the field's precision or -1 if the field has no precision
+ or it cannot be determined.
+*/
+int QSqlFieldInfo::precision() const
+{ return d->prec; }
+
+/*!
+ Returns the field's default value or an empty QVariant 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.
+*/
+QVariant QSqlFieldInfo::defaultValue() const
+{ return d->defValue; }
+
+/*!
+ Returns the name of the field in the SQL table.
+*/
+QString QSqlFieldInfo::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 QSqlFieldInfo::typeID() const
+{ return d->typeID; }
+
+/*!
+ Returns TRUE if the field should be included in auto-generated
+ SQL statments, e.g. in QSqlCursor; otherwise returns FALSE.
+
+ \sa setGenerated()
+*/
+bool QSqlFieldInfo::isGenerated() const
+{ return d->generated; }
+
+/*!
+ Returns TRUE if trailing whitespace should be removed from
+ character fields; otherwise returns FALSE.
+
+ \sa setTrim()
+*/
+bool QSqlFieldInfo::isTrim() const
+{ return d->trim; }
+
+/*!
+ Returns TRUE if the field is calculated; otherwise returns FALSE.
+
+ \sa setCalculated()
+*/
+bool QSqlFieldInfo::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 QSqlFieldInfo::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 QSqlCursor).
+
+ \sa isGenerated()
+*/
+void QSqlFieldInfo::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
+ QSqlCursor and overriding QSqlCursor::calculateField().
+
+ \sa isCalculated()
+*/
+void QSqlFieldInfo::setCalculated( bool calc )
+{ d->calculated = calc; }
+
+#endif
diff --git a/src/sql/qsqlfield.h b/src/sql/qsqlfield.h
new file mode 100644
index 0000000..b9e312b
--- /dev/null
+++ b/src/sql/qsqlfield.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Definition of QSqlField class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLFIELD_H
+#define QSQLFIELD_H
+
+#ifndef QT_H
+#include "qstring.h"
+#include "qvariant.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlFieldPrivate
+{
+public:
+ QVariant::Type type;
+};
+
+class QM_EXPORT_SQL QSqlField
+{
+public:
+ QSqlField( const QString& fieldName = QString::null, QVariant::Type type = QVariant::Invalid );
+ QSqlField( const QSqlField& other );
+ QSqlField& operator=( const QSqlField& other );
+ bool operator==(const QSqlField& other) const;
+ virtual ~QSqlField();
+
+ virtual void setValue( const QVariant& value );
+ virtual QVariant value() const;
+ virtual void setName( const QString& name );
+ QString name() const;
+ virtual void setNull();
+ bool isNull() const;
+ virtual void setReadOnly( bool readOnly );
+ bool isReadOnly() const;
+ void clear( bool nullify = TRUE );
+ QVariant::Type type() const;
+
+private:
+ QString nm;
+ QVariant val;
+ uint ro: 1;
+ uint nul: 1;
+ QSqlFieldPrivate* d;
+};
+
+inline QVariant QSqlField::value() const
+{ return val; }
+
+inline QString QSqlField::name() const
+{ return nm; }
+
+inline bool QSqlField::isNull() const
+{ return nul; }
+
+inline bool QSqlField::isReadOnly() const
+{ return ro; }
+
+inline QVariant::Type QSqlField::type() const
+{ return d->type; }
+
+
+/******************************************/
+/******* QSqlFieldInfo Class ******/
+/******************************************/
+
+struct QSqlFieldInfoPrivate;
+
+class QM_EXPORT_SQL QSqlFieldInfo
+{
+public:
+ QSqlFieldInfo( const QString& name = QString::null,
+ QVariant::Type typ = QVariant::Invalid,
+ int required = -1,
+ int len = -1,
+ int prec = -1,
+ const QVariant& defValue = QVariant(),
+ int sqlType = 0,
+ bool generated = TRUE,
+ bool trim = FALSE,
+ bool calculated = FALSE );
+ QSqlFieldInfo( const QSqlFieldInfo & other );
+ QSqlFieldInfo( const QSqlField & other, bool generated = TRUE );
+ virtual ~QSqlFieldInfo();
+ QSqlFieldInfo& operator=( const QSqlFieldInfo& other );
+ bool operator==( const QSqlFieldInfo& f ) const;
+
+ QSqlField toField() const;
+ int isRequired() const;
+ QVariant::Type type() const;
+ int length() const;
+ int precision() const;
+ QVariant defaultValue() const;
+ QString 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:
+ QSqlFieldInfoPrivate* d;
+};
+
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqlform.cpp b/src/sql/qsqlform.cpp
new file mode 100644
index 0000000..255c5d7
--- /dev/null
+++ b/src/sql/qsqlform.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Implementation of QSqlForm class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QSqlFormPrivate
+{
+public:
+ QSqlFormPrivate() : propertyMap( 0 ), buf( 0 ), dirty( FALSE ) {}
+ ~QSqlFormPrivate() { if ( propertyMap ) delete propertyMap; }
+ QStringList fld;
+ QDict<QWidget> wgt;
+ QMap< QWidget *, QSqlField * > map;
+ QSqlPropertyMap * propertyMap;
+ QSqlRecord* buf;
+ bool dirty;
+};
+
+/*!
+ \class QSqlForm
+ \brief The QSqlForm class creates and manages data entry forms
+ tied to SQL databases.
+
+ \ingroup database
+ \mainclass
+ \module sql
+
+ Typical use of a QSqlForm 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 QSqlForm.
+ \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 QSqlForm does not access the database directly, but
+ most often via QSqlFields which are part of a QSqlCursor. A
+ QSqlCursor::insert(), QSqlCursor::update() or QSqlCursor::del()
+ call is needed to actually write values to the database.
+
+ Some sample code to initialize a form successfully:
+
+ \code
+ QLineEdit myEditor( this );
+ QSqlForm myForm( this );
+ QSqlCursor 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 QSqlPropertyMap. The form
+ uses this object to get or set the value of a widget.
+
+ Note that \link designer-manual.book Qt Designer\endlink provides
+ a visual means of creating data-aware forms.
+
+ \sa installPropertyMap(), QSqlPropertyMap
+*/
+
+
+/*!
+ Constructs a QSqlForm with parent \a parent and called \a name.
+*/
+QSqlForm::QSqlForm( QObject * parent, const char * name )
+ : QObject( parent, name )
+{
+ d = new QSqlFormPrivate();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+QSqlForm::~QSqlForm()
+{
+ delete d;
+}
+
+/*!
+ Installs a custom QSqlPropertyMap. This is useful if you plan to
+ create your own custom editor widgets.
+
+ QSqlForm takes ownership of \a pmap, so \a pmap is deleted when
+ QSqlForm goes out of scope.
+
+ \sa QDataTable::installEditorFactory()
+*/
+void QSqlForm::installPropertyMap( QSqlPropertyMap * 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 QSqlForm::setRecord( QSqlRecord* 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 QSqlForm::insert( QWidget * widget, const QString& field )
+{
+ d->dirty = TRUE;
+ d->wgt.insert( field, widget );
+ d->fld += field;
+}
+
+/*!
+ \overload
+
+ Removes \a field from the form.
+*/
+
+void QSqlForm::remove( const QString& 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 QSqlForm::insert( QWidget * widget, QSqlField * field )
+{
+ d->map[widget] = field;
+}
+
+/*!
+ Removes a \a widget, and hence the field it's mapped to, from the
+ form.
+*/
+
+void QSqlForm::remove( QWidget * 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 QSqlForm::clearValues( bool nullify )
+{
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ for( it = d->map.begin(); it != d->map.end(); ++it ){
+ QSqlField* f = (*it);
+ if ( f )
+ f->clear( nullify );
+ }
+ readFields();
+}
+
+/*!
+ Removes every widget, and the fields they're mapped to, from the form.
+*/
+void QSqlForm::clear()
+{
+ d->dirty = TRUE;
+ d->fld.clear();
+ clearMap();
+}
+
+/*!
+ Returns the number of widgets in the form.
+*/
+uint QSqlForm::count() const
+{
+ return (uint)d->map.count();
+}
+
+/*!
+ Returns the \a{i}-th widget in the form. Useful for traversing
+ the widgets in the form.
+*/
+QWidget * QSqlForm::widget( uint i ) const
+{
+ QMap< QWidget *, QSqlField * >::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.
+*/
+QWidget * QSqlForm::fieldToWidget( QSqlField * field ) const
+{
+ QMap< QWidget *, QSqlField * >::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.
+*/
+QSqlField * QSqlForm::widgetToField( QWidget * 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 QSqlForm::readFields()
+{
+ sync();
+ QSqlField * f;
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ QSqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ QSqlPropertyMap::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 QSqlCursor::insert(), QSqlCursor::update()
+ or QSqlCursor::del() as appropriate.
+*/
+void QSqlForm::writeFields()
+{
+ sync();
+ QSqlField * f;
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ QSqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ QSqlPropertyMap::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 QSqlForm::readField( QWidget * widget )
+{
+ sync();
+ QSqlField * field = 0;
+ QSqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ QSqlPropertyMap::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 QSqlForm::writeField( QWidget * widget )
+{
+ sync();
+ QSqlField * field = 0;
+ QSqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ QSqlPropertyMap::defaultMap() : d->propertyMap;
+ field = widgetToField( widget );
+ if( field )
+ field->setValue( pmap->property( widget ) );
+}
+
+/*! \internal
+*/
+
+void QSqlForm::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 QSqlForm::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 0000000..5ab070e
--- /dev/null
+++ b/src/sql/qsqlform.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Definition of QSqlForm class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLFORM_H
+#define QSQLFORM_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qmap.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL_FORM
+
+class QSqlField;
+class QSqlRecord;
+class QSqlEditorFactory;
+class QSqlPropertyMap;
+class QWidget;
+class QSqlFormPrivate;
+
+class QM_EXPORT_SQL QSqlForm : public QObject
+{
+ Q_OBJECT
+public:
+ QSqlForm( QObject * parent = 0, const char * name = 0 );
+ ~QSqlForm();
+
+ virtual void insert( QWidget * widget, const QString& field );
+ virtual void remove( const QString& field );
+ uint count() const;
+
+ QWidget * widget( uint i ) const;
+ QSqlField * widgetToField( QWidget * widget ) const;
+ QWidget * fieldToWidget( QSqlField * field ) const;
+
+ void installPropertyMap( QSqlPropertyMap * map );
+
+ virtual void setRecord( QSqlRecord* buf );
+
+public slots:
+ virtual void readField( QWidget * widget );
+ virtual void writeField( QWidget * widget );
+ virtual void readFields();
+ virtual void writeFields();
+
+ virtual void clear();
+ virtual void clearValues( bool nullify = FALSE );
+
+protected:
+ virtual void insert( QWidget * widget, QSqlField * field );
+ virtual void remove( QWidget * widget );
+ void clearMap();
+
+private:
+ virtual void sync();
+ QSqlFormPrivate* d;
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ QSqlForm( const QSqlForm & );
+ QSqlForm &operator=( const QSqlForm & );
+#endif
+};
+
+#endif // QT_NO_SQL
+#endif // QSQLFORM_H
diff --git a/src/sql/qsqlindex.cpp b/src/sql/qsqlindex.cpp
new file mode 100644
index 0000000..f5bdace
--- /dev/null
+++ b/src/sql/qsqlindex.cpp
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** Implementation of QSqlIndex class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlindex.h"
+
+#ifndef QT_NO_SQL
+
+#include "qsqlcursor.h"
+
+/*!
+ \class QSqlIndex qsqlindex.h
+ \brief The QSqlIndex class provides functions to manipulate and
+ describe QSqlCursor and QSqlDatabase indexes.
+
+ \ingroup database
+ \module sql
+
+ This class is used to describe and manipulate QSqlCursor and
+ QSqlDatabase 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 QSqlCursor object.
+
+ Normally, QSqlIndex objects are created by \l QSqlDatabase or
+ QSqlCursor.
+*/
+
+/*!
+ Constructs an empty index using the cursor name \a cursorname and
+ index name \a name.
+*/
+
+QSqlIndex::QSqlIndex( const QString& cursorname, const QString& name )
+ : QSqlRecord(), cursor(cursorname), nm(name)
+{
+
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QSqlIndex::QSqlIndex( const QSqlIndex& other )
+ : QSqlRecord(other), cursor(other.cursor), nm(other.nm), sorts(other.sorts)
+{
+}
+
+/*!
+ Sets the index equal to \a other.
+*/
+
+QSqlIndex& QSqlIndex::operator=( const QSqlIndex& other )
+{
+ cursor = other.cursor;
+ nm = other.nm;
+ sorts = other.sorts;
+ QSqlRecord::operator=( other );
+ return *this;
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlIndex::~QSqlIndex()
+{
+
+}
+
+/*!
+ Sets the name of the index to \a name.
+*/
+
+void QSqlIndex::setName( const QString& name )
+{
+ nm = name;
+}
+
+/*!
+ \fn QString QSqlIndex::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 QSqlIndex::append( const QSqlField& 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 QSqlIndex::append( const QSqlField& field, bool desc )
+{
+ sorts.append( desc );
+ QSqlRecord::append( field );
+}
+
+
+/*!
+ Returns TRUE if field \a i in the index is sorted in descending
+ order; otherwise returns FALSE.
+*/
+
+bool QSqlIndex::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 QSqlIndex::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.
+*/
+
+QString QSqlIndex::toString( const QString& prefix, const QString& sep, bool verbose ) const
+{
+ QString 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
+ QStringList list = myIndex.toStringList();
+ QStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+*/
+QStringList QSqlIndex::toStringList( const QString& prefix, bool verbose ) const
+{
+ QStringList 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.
+*/
+
+QString QSqlIndex::createField( int i, const QString& prefix, bool verbose ) const
+{
+ QString f;
+ if ( !prefix.isEmpty() )
+ f += prefix + ".";
+ f += field( i )->name();
+ if ( verbose )
+ f += " " + QString( ( 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()
+*/
+
+QSqlIndex QSqlIndex::fromStringList( const QStringList& l, const QSqlCursor* cursor )
+{
+ QSqlIndex newSort;
+ for ( uint i = 0; i < l.count(); ++i ) {
+ QString 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 QSqlField* field = cursor->field( f.simplifyWhiteSpace() );
+ if ( field )
+ newSort.append( *field, desc );
+ else
+ qWarning( "QSqlIndex::fromStringList: unknown field: '%s'", f.latin1());
+ }
+ return newSort;
+}
+
+/*!
+ \fn QString QSqlIndex::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 QSqlIndex::setCursorName( const QString& cursorName )
+{
+ cursor = cursorName;
+}
+
+#endif
diff --git a/src/sql/qsqlindex.h b/src/sql/qsqlindex.h
new file mode 100644
index 0000000..d4d6b05
--- /dev/null
+++ b/src/sql/qsqlindex.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Definition of QSqlIndex class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLINDEX_H
+#define QSQLINDEX_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 QM_EXPORT_SQL
+#define QM_TEMPLATE_EXTERN_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#define QM_TEMPLATE_EXTERN_SQL Q_TEMPLATE_EXTERN
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlCursor;
+
+class QM_EXPORT_SQL QSqlIndex : public QSqlRecord
+{
+public:
+ QSqlIndex( const QString& cursorName = QString::null, const QString& name = QString::null );
+ QSqlIndex( const QSqlIndex& other );
+ ~QSqlIndex();
+ QSqlIndex& operator=( const QSqlIndex& other );
+ virtual void setCursorName( const QString& cursorName );
+ QString cursorName() const { return cursor; }
+ virtual void setName( const QString& name );
+ QString name() const { return nm; }
+
+ void append( const QSqlField& field );
+ virtual void append( const QSqlField& field, bool desc );
+
+ bool isDescending( int i ) const;
+ virtual void setDescending( int i, bool desc );
+
+ QString toString( const QString& prefix = QString::null,
+ const QString& sep = ",",
+ bool verbose = TRUE ) const;
+ QStringList toStringList( const QString& prefix = QString::null,
+ bool verbose = TRUE ) const;
+
+ static QSqlIndex fromStringList( const QStringList& l, const QSqlCursor* cursor );
+
+private:
+ QString createField( int i, const QString& prefix, bool verbose ) const;
+ QString cursor;
+ QString nm;
+ QValueList<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 0000000..68360dc
--- /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 Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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 QSqlCursorManagerPrivate
+{
+public:
+ QSqlCursorManagerPrivate()
+ : cur( 0 ), autoDelete( FALSE )
+ {}
+
+ QString ftr;
+ QStringList srt;
+ QSqlCursor* cur;
+ bool autoDelete;
+};
+
+/*!
+ \class QSqlCursorManager qsqlmanager_p.h
+ \brief The QSqlCursorManager 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.
+
+*/
+
+QSqlCursorManager::QSqlCursorManager()
+{
+ d = new QSqlCursorManagerPrivate;
+}
+
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+QSqlCursorManager::~QSqlCursorManager()
+{
+ 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 QSqlCursorManager::setSort( const QSqlIndex& sort )
+{
+ setSort( sort.toStringList() );
+}
+
+/*! \internal
+
+ Sets the manager's sort to the stringlist \a sort. To apply the
+ new sort, use refresh().
+
+ */
+
+void QSqlCursorManager::setSort( const QStringList& sort )
+{
+ d->srt = sort;
+}
+
+/*! \internal
+
+ Returns the current sort, or an empty stringlist if there is none.
+
+*/
+
+QStringList QSqlCursorManager::sort() const
+{
+ return d->srt;
+}
+
+/*! \internal
+
+ Sets the manager's filter to the string \a filter. To apply the
+ new filter, use refresh().
+
+*/
+
+void QSqlCursorManager::setFilter( const QString& filter )
+{
+ d->ftr = filter;
+}
+
+/*! \internal
+
+ Returns the current filter, or an empty string if there is none.
+
+*/
+
+QString QSqlCursorManager::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 QSqlCursorManager::setAutoDelete( bool enable )
+{
+ d->autoDelete = enable;
+}
+
+
+/*! \internal
+
+ Returns TRUE if auto-deletion is enabled, otherwise FALSE.
+
+ \sa setAutoDelete()
+
+*/
+
+bool QSqlCursorManager::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 QSqlCursorManager::setCursor( QSqlCursor* 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()
+
+*/
+
+QSqlCursor* QSqlCursorManager::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 QSqlCursorManager::refresh()
+{
+ QSqlCursor* cur = cursor();
+ if ( !cur )
+ return FALSE;
+ QString currentFilter = d->ftr;
+ QStringList currentSort = d->srt;
+ QSqlIndex newSort = QSqlIndex::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 QSqlCursor* cur, const QSqlRecord* buf,
+ const QSqlIndex& idx )
+{
+ bool indexEquals = FALSE;
+ for ( uint i = 0; i < idx.count(); ++i ) {
+ const QString 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 QSqlRecord* buf1, const QSqlRecord* buf2,
+ const QSqlIndex& idx )
+{
+ int cmp = 0;
+
+ int i = 0;
+ const QString fn( idx.field(i)->name() );
+ const QSqlField* f1 = buf1->field( fn );
+
+ if ( f1 ) {
+ switch ( f1->type() ) { // ### more types?
+ case QVariant::String:
+ case QVariant::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 QString& msg, QSqlRecord* cursor )
+{
+ qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ qDebug( "%s", msg.latin1() );
+ for ( uint j = 0; j < cursor->count(); ++j ) {
+ qDebug( "%s", (cursor->field(j)->name() + " type:"
+ + QString(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
+ QSqlCursor* myCursor = myManager.cursor();
+ ...
+ QSqlRecord* 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 QSqlCursorManager::findBuffer( const QSqlIndex& idx, int atHint )
+{
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug("QSqlCursorManager::findBuffer:");
+#endif
+ QSqlCursor* cur = cursor();
+ if ( !cur )
+ return FALSE;
+ if ( !cur->isActive() )
+ return FALSE;
+ if ( !idx.count() ) {
+ if ( cur->at() == QSql::BeforeFirst )
+ cur->next();
+ return FALSE;
+ }
+ QSqlRecord* 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 = QMAX( 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( QSqlDriver::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
+ QApplication::setOverrideCursor( Qt::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
+ QApplication::restoreOverrideCursor();
+#endif
+ }
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Done, result:" + QString::number( indexEquals ) );
+#endif
+ return indexEquals;
+}
+
+#ifndef QT_NO_SQL_FORM
+
+class QSqlFormManagerPrivate
+{
+public:
+ QSqlFormManagerPrivate() : frm(0), rcd(0) {}
+ QSqlForm* frm;
+ QSqlRecord* rcd;
+};
+
+
+/*! \internal
+
+ Creates a form manager.
+
+*/
+
+QSqlFormManager::QSqlFormManager()
+{
+ d = new QSqlFormManagerPrivate();
+}
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+QSqlFormManager::~QSqlFormManager()
+{
+ delete d;
+}
+
+/*! \internal
+
+ Clears the default form values. If there is no default form,
+ nothing happens,
+
+*/
+
+void QSqlFormManager::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 QSqlFormManager::setForm( QSqlForm* 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()
+
+*/
+
+QSqlForm* QSqlFormManager::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 QSqlFormManager::setRecord( QSqlRecord* 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()
+*/
+
+QSqlRecord* QSqlFormManager::record()
+{
+ return d->rcd;
+}
+
+
+/*! \internal
+
+ Causes the default form to read its fields . If there is no
+ default form, nothing happens.
+
+ \sa setForm()
+
+*/
+
+void QSqlFormManager::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 QSqlFormManager::writeFields()
+{
+ if ( d->frm ) {
+ d->frm->writeFields();
+ }
+}
+
+#endif // QT_NO_SQL_FORM
+
+class QDataManagerPrivate
+{
+public:
+ QDataManagerPrivate()
+ : mode( QSql::None ), autoEd( TRUE ), confEdits( 3 ),
+ confCancs( FALSE ) {}
+ QSql::Op mode;
+ bool autoEd;
+ QBitArray confEdits;
+ bool confCancs;
+
+};
+
+/*!
+ \class QDataManager qsqlmanager_p.h
+ \ingroup database
+
+ \brief The QDataManager class is an internal class for implementing
+ the data-aware widgets.
+
+ \internal
+
+ QDataManager is a strictly internal class that acts as a base class
+ for other data-aware widgets.
+
+*/
+
+
+/*! \internal
+
+ Constructs an empty data handler.
+
+*/
+
+QDataManager::QDataManager()
+{
+ d = new QDataManagerPrivate();
+}
+
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+QDataManager::~QDataManager()
+{
+ 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 QDataManager::handleError( QWidget* parent, const QSqlError& e )
+{
+#ifndef QT_NO_MESSAGEBOX
+ if (e.driverText().isEmpty() && e.databaseText().isEmpty()) {
+ QMessageBox::warning ( parent, "Warning", "An error occurred while accessing the database");
+ } else {
+ QMessageBox::warning ( parent, "Warning", e.driverText() + "\n" + e.databaseText(),
+ 0, 0 );
+ }
+#endif // QT_NO_MESSAGEBOX
+}
+
+
+/*! \internal
+
+ Sets the internal mode to \a m.
+
+*/
+
+void QDataManager::setMode( QSql::Op m )
+{
+ d->mode = m;
+}
+
+
+/*! \internal
+
+ Returns the current mode.
+
+*/
+
+QSql::Op QDataManager::mode() const
+{
+ return d->mode;
+}
+
+
+/*! \internal
+
+ Sets the auto-edit mode to \a auto.
+
+*/
+
+void QDataManager::setAutoEdit( bool autoEdit )
+{
+ d->autoEd = autoEdit;
+}
+
+
+
+/*! \internal
+
+ Returns TRUE if auto-edit mode is enabled; otherwise returns FALSE.
+
+*/
+
+bool QDataManager::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 QDataManager::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 QDataManager::setConfirmInsert( bool confirm )
+{
+ d->confEdits[ QSql::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 QDataManager::setConfirmUpdate( bool confirm )
+{
+ d->confEdits[ QSql::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 QDataManager::setConfirmDelete( bool confirm )
+{
+ d->confEdits[ QSql::Delete ] = confirm;
+}
+
+/*! \internal
+
+ Returns TRUE if the table confirms all edit operations (inserts,
+ updates and deletes), otherwise returns FALSE.
+*/
+
+bool QDataManager::confirmEdits() const
+{
+ return ( confirmInsert() && confirmUpdate() && confirmDelete() );
+}
+
+/*! \internal
+
+ Returns TRUE if the table confirms inserts, otherwise returns
+ FALSE.
+*/
+
+bool QDataManager::confirmInsert() const
+{
+ return d->confEdits[ QSql::Insert ];
+}
+
+/*! \internal
+
+ Returns TRUE if the table confirms updates, otherwise returns
+ FALSE.
+*/
+
+bool QDataManager::confirmUpdate() const
+{
+ return d->confEdits[ QSql::Update ];
+}
+
+/*! \internal
+
+ Returns TRUE if the table confirms deletes, otherwise returns
+ FALSE.
+*/
+
+bool QDataManager::confirmDelete() const
+{
+ return d->confEdits[ QSql::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 QDataManager::setConfirmCancels( bool confirm )
+{
+ d->confCancs = confirm;
+}
+
+/*! \internal
+
+ Returns TRUE if the table confirms cancels, otherwise returns FALSE.
+*/
+
+bool QDataManager::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.
+
+*/
+
+QSql::Confirm QDataManager::confirmEdit( QWidget* parent, QSql::Op m )
+{
+ int ans = 2;
+ if ( m == QSql::Delete ) {
+#ifndef QT_NO_MESSAGEBOX
+ ans = QMessageBox::information( parent,
+ qApp->translate( "QSql", "Delete" ),
+ qApp->translate( "QSql", "Delete this record?" ),
+ qApp->translate( "QSql", "Yes" ),
+ qApp->translate( "QSql", "No" ),
+ QString::null, 0, 1 );
+#else
+ ans = QSql::No;
+#endif // QT_NO_MESSAGEBOX
+ } else if ( m != QSql::None ) {
+ QString caption;
+ if ( m == QSql::Insert ) {
+ caption = qApp->translate( "QSql", "Insert" );
+ } else { // QSql::Update
+ caption = qApp->translate( "QSql", "Update" );
+ }
+#ifndef QT_NO_MESSAGEBOX
+ ans = QMessageBox::information( parent, caption,
+ qApp->translate( "QSql", "Save edits?" ),
+ qApp->translate( "QSql", "Yes" ),
+ qApp->translate( "QSql", "No" ),
+ qApp->translate( "QSql", "Cancel" ),
+ 0, 2 );
+#else
+ ans = QSql::No;
+#endif // QT_NO_MESSAGEBOX
+ }
+
+ switch ( ans ) {
+ case 0:
+ return QSql::Yes;
+ case 1:
+ return QSql::No;
+ default:
+ return QSql::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.
+
+
+*/
+
+QSql::Confirm QDataManager::confirmCancel( QWidget* parent, QSql::Op )
+{
+#ifndef QT_NO_MESSAGEBOX
+ switch ( QMessageBox::information( parent,
+ qApp->translate( "QSql", "Confirm" ),
+ qApp->translate( "QSql", "Cancel your edits?" ),
+ qApp->translate( "QSql", "Yes" ),
+ qApp->translate( "QSql", "No" ),
+ QString::null, 0, 1 ) ) {
+ case 0:
+ return QSql::Yes;
+ case 1:
+ return QSql::No;
+ default:
+ return QSql::Cancel;
+ }
+#else
+ return QSql::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 0000000..6df8858
--- /dev/null
+++ b/src/sql/qsqlmanager_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Definition of QSqlManager class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLMANAGER_P_H
+#define QSQLMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlCursor;
+class QSqlForm;
+class QSqlCursorManagerPrivate;
+
+class QM_EXPORT_SQL QSqlCursorManager
+{
+public:
+ QSqlCursorManager();
+ virtual ~QSqlCursorManager();
+
+ virtual void setSort( const QSqlIndex& sort );
+ virtual void setSort( const QStringList& sort );
+ QStringList sort() const;
+ virtual void setFilter( const QString& filter );
+ QString filter() const;
+ virtual void setCursor( QSqlCursor* cursor, bool autoDelete = FALSE );
+ QSqlCursor* cursor() const;
+
+ virtual void setAutoDelete( bool enable );
+ bool autoDelete() const;
+
+ virtual bool refresh();
+ virtual bool findBuffer( const QSqlIndex& idx, int atHint = 0 );
+
+private:
+ QSqlCursorManagerPrivate* d;
+};
+
+#ifndef QT_NO_SQL_FORM
+
+class QSqlFormManagerPrivate;
+
+class QM_EXPORT_SQL QSqlFormManager
+{
+public:
+ QSqlFormManager();
+ virtual ~QSqlFormManager();
+
+ virtual void setForm( QSqlForm* form );
+ QSqlForm* form();
+ virtual void setRecord( QSqlRecord* record );
+ QSqlRecord* record();
+
+ virtual void clearValues();
+ virtual void readFields();
+ virtual void writeFields();
+
+private:
+ QSqlFormManagerPrivate* d;
+};
+
+#endif
+
+class QWidget;
+class QDataManagerPrivate;
+
+class QM_EXPORT_SQL QDataManager
+{
+public:
+ QDataManager();
+ virtual ~QDataManager();
+
+ virtual void setMode( QSql::Op m );
+ QSql::Op mode() const;
+ virtual void setAutoEdit( bool autoEdit );
+ bool autoEdit() const;
+
+ virtual void handleError( QWidget* parent, const QSqlError& error );
+ virtual QSql::Confirm confirmEdit( QWidget* parent, QSql::Op m );
+ virtual QSql::Confirm confirmCancel( QWidget* parent, QSql::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:
+ QDataManagerPrivate* d;
+};
+
+
+#endif
+#endif
diff --git a/src/sql/qsqlpropertymap.cpp b/src/sql/qsqlpropertymap.cpp
new file mode 100644
index 0000000..3c99a1c
--- /dev/null
+++ b/src/sql/qsqlpropertymap.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Definition of QSqlPropertyMap class
+**
+** Created : 2000-11-20
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlpropertymap.h"
+
+#ifndef QT_NO_SQL_FORM
+
+#include "qwidget.h"
+#include "qcleanuphandler.h"
+#include "qmetaobject.h"
+#include "qmap.h"
+
+class QSqlPropertyMapPrivate
+{
+public:
+ QSqlPropertyMapPrivate() {}
+ QMap< QString, QString > propertyMap;
+};
+
+/*!
+ \class QSqlPropertyMap qsqlpropertymap.h
+ \brief The QSqlPropertyMap class is used to map widgets to SQL fields.
+
+ \ingroup database
+ \module sql
+
+ The SQL module uses Qt \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 QLineEdit can be used to edit text strings and
+ other data types in QDataTables or QSqlForms. Several properties
+ are defined in QLineEdit, but only the \e text property is used to
+ insert and extract text from a QLineEdit. Both QDataTable and
+ QSqlForm use the global QSqlPropertyMap 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 QDataTable or
+ QSqlForm, you must install your own QSqlPropertyMap for that table
+ or form. Example:
+
+ \code
+ QSqlPropertyMap *myMap = new QSqlPropertyMap();
+ QSqlForm *myForm = new QSqlForm( this );
+ MyEditor myEditor( this );
+
+ // Set the QSqlForm's record buffer to the update buffer of
+ // a pre-existing QSqlCursor 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 QSqlPropertyMap that is used by
+ default. (Bear in mind that QSqlPropertyMap takes ownership of the
+ new default map.)
+
+ \code
+ QSqlPropertyMap *myMap = new QSqlPropertyMap;
+
+ myMap->insert( "MyEditor", "content" );
+ QSqlPropertyMap::installDefaultMap( myMap );
+ ...
+ \endcode
+
+ \sa QDataTable, QSqlForm, QSqlEditorFactory
+*/
+
+/*!
+
+Constructs a QSqlPropertyMap.
+
+The default property mappings used by Qt widgets are:
+\table
+\header \i Widgets \i Property
+\row \i \l QCheckBox,
+ \l QRadioButton
+ \i checked
+\row \i \l QComboBox,
+ \l QListBox
+ \i currentItem
+\row \i \l QDateEdit
+ \i date
+\row \i \l QDateTimeEdit
+ \i dateTime
+\row \i \l QTextBrowser
+ \i source
+\row \i \l QButton,
+ \l QDial,
+ \l QLabel,
+ \l QLineEdit,
+ \l QMultiLineEdit,
+ \l QPushButton,
+ \l QTextEdit,
+ \i text
+\row \i \l QTimeEdit
+ \i time
+\row \i \l QLCDNumber,
+ \l QScrollBar
+ \l QSlider,
+ \l QSpinBox
+ \i value
+\endtable
+*/
+
+QSqlPropertyMap::QSqlPropertyMap()
+{
+ d = new QSqlPropertyMapPrivate();
+ const struct MapData {
+ const char *classname;
+ const char *property;
+ } mapData[] = {
+ { "QButton", "text" },
+ { "QCheckBox", "checked" },
+ { "QRadioButton", "checked" },
+ { "QComboBox", "currentItem" },
+ { "QDateEdit", "date" },
+ { "QDateTimeEdit", "dateTime" },
+ { "QDial", "value" },
+ { "QLabel", "text" },
+ { "QLCDNumber", "value" },
+ { "QLineEdit", "text" },
+ { "QListBox", "currentItem" },
+ { "QMultiLineEdit", "text" },
+ { "QPushButton", "text" },
+ { "QScrollBar", "value" },
+ { "QSlider", "value" },
+ { "QSpinBox", "value" },
+ { "QTextBrowser", "source" },
+ { "QTextEdit", "text" },
+ { "QTextView", "text" },
+ { "QTimeEdit", "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 QSqlPropertyMap.
+
+ Note that if the QSqlPropertyMap is installed with
+ installPropertyMap() the object it was installed into, e.g. the
+ QSqlForm, takes ownership and will delete the QSqlPropertyMap when
+ necessary.
+*/
+QSqlPropertyMap::~QSqlPropertyMap()
+{
+ delete d;
+}
+
+/*!
+ Returns the mapped property of \a widget as a QVariant.
+*/
+QVariant QSqlPropertyMap::property( QWidget * widget )
+{
+ if( !widget ) return QVariant();
+ const QMetaObject* mo = widget->metaObject();
+ while ( mo && !d->propertyMap.contains( QString( mo->className() ) ) )
+ mo = mo->superClass();
+
+ if ( !mo ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlPropertyMap::property: %s does not exist", widget->metaObject()->className() );
+#endif
+ return QVariant();
+ }
+ return widget->property( d->propertyMap[ mo->className() ] );
+}
+
+/*!
+ Sets the property of \a widget to \a value.
+*/
+void QSqlPropertyMap::setProperty( QWidget * widget, const QVariant & value )
+{
+ if( !widget ) return;
+
+ QMetaObject* mo = widget->metaObject();
+ while ( mo && !d->propertyMap.contains( QString( mo->className() ) ) )
+ mo = mo->superClass();
+ if ( !mo ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlPropertyMap::setProperty: %s not handled by QSqlPropertyMap", 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 QSqlPropertyMap::insert( const QString & classname,
+ const QString & property )
+{
+ d->propertyMap[ classname ] = property;
+}
+
+/*!
+ Removes \a classname from the map.
+*/
+void QSqlPropertyMap::remove( const QString & classname )
+{
+ d->propertyMap.remove( classname );
+}
+
+static QSqlPropertyMap * defaultmap = 0;
+static QCleanupHandler< QSqlPropertyMap > qsql_cleanup_property_map;
+
+/*!
+ Returns the application global QSqlPropertyMap.
+*/
+QSqlPropertyMap * QSqlPropertyMap::defaultMap()
+{
+ if( defaultmap == 0 ){
+ defaultmap = new QSqlPropertyMap();
+ qsql_cleanup_property_map.add( &defaultmap );
+ }
+ return defaultmap;
+}
+
+/*!
+ Replaces the global default property map with \a map. All
+ QDataTable and QSqlForm instantiations will use this new map for
+ inserting and extracting values to and from editors.
+ \e{QSqlPropertyMap takes ownership of \a map, and destroys it
+ when it is no longer needed.}
+*/
+void QSqlPropertyMap::installDefaultMap( QSqlPropertyMap * 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 0000000..46d14ad
--- /dev/null
+++ b/src/sql/qsqlpropertymap.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Definition of QSqlPropertyMap class
+**
+** Created : 2000-11-20
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLPROPERTYMAP_H
+#define QSQLPROPERTYMAP_H
+
+#ifndef QT_H
+#include "qvariant.h"
+#include "qstring.h"
+#endif // QT_H
+
+#ifndef QT_NO_SQL_FORM
+
+class QWidget;
+class QSqlPropertyMapPrivate;
+
+class Q_EXPORT QSqlPropertyMap {
+public:
+ QSqlPropertyMap();
+ virtual ~QSqlPropertyMap();
+
+ QVariant property( QWidget * widget );
+ virtual void setProperty( QWidget * widget, const QVariant & value );
+
+ void insert( const QString & classname, const QString & property );
+ void remove( const QString & classname );
+
+ static QSqlPropertyMap * defaultMap();
+ static void installDefaultMap( QSqlPropertyMap * map );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ QSqlPropertyMap( const QSqlPropertyMap & );
+ QSqlPropertyMap &operator=( const QSqlPropertyMap & );
+#endif
+ QSqlPropertyMapPrivate* d;
+
+};
+
+#endif // QT_NO_SQL_FORM
+#endif // QSQLPROPERTYMAP_H
diff --git a/src/sql/qsqlquery.cpp b/src/sql/qsqlquery.cpp
new file mode 100644
index 0000000..fec2343
--- /dev/null
+++ b/src/sql/qsqlquery.cpp
@@ -0,0 +1,1215 @@
+/****************************************************************************
+**
+** Implementation of QSqlQuery class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "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
+*/
+QSqlResultShared::QSqlResultShared( QSqlResult* result ): sqlResult(result)
+{
+ if ( result )
+ connect( result->driver(), SIGNAL(destroyed()), this, SLOT(slotResultDestroyed()) );
+}
+
+/*!
+\internal
+*/
+QSqlResultShared::~QSqlResultShared()
+{
+ delete sqlResult;
+}
+
+/*!
+\internal
+
+In case a plugin gets unloaded the pointer to the sqlResult gets invalid
+*/
+void QSqlResultShared::slotResultDestroyed()
+{
+ delete sqlResult;
+ sqlResult = 0;
+}
+
+/*!
+ \class QSqlQuery qsqlquery.h
+ \brief The QSqlQuery class provides a means of executing and
+ manipulating SQL statements.
+
+ \ingroup database
+ \mainclass
+ \module sql
+
+ QSqlQuery encapsulates the functionality involved in creating,
+ navigating and retrieving data from SQL queries which are executed
+ on a \l QSqlDatabase. 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 QSqlQuery::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
+ QVariants.
+
+ For example:
+
+ \code
+ QSqlQuery query( "SELECT name FROM customer" );
+ while ( query.next() ) {
+ QString 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 QSqlDatabase::record().
+ For the sake of efficiency there are no functions to access a field
+ by name. (The \l QSqlCursor class provides a higher-level interface
+ with field access by name and automatic SQL generation.)
+
+ QSqlQuery supports prepared query execution and the binding of
+ parameter values to placeholders. Some databases don't support
+ these features, so for them Qt emulates the required
+ functionality. For example, the Oracle and ODBC drivers have
+ proper prepared query support, and Qt makes use of it; but for
+ databases that don't have this support, Qt 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 requires 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. Qt
+ 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
+ QSqlQuery 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
+ QSqlQuery 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 QSqlDriver::hasFeature() using QSqlDriver::PreparedQueries
+ as argument for driver feature.
+
+ <b>Binding values using positional placeholders #1</b>
+ \code
+ QSqlQuery 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
+ QSqlQuery query;
+ query.prepare( "call AsciiToInt(?, ?)" );
+ query.bindValue( 0, "A" );
+ query.bindValue( 1, 0, QSql::Out );
+ query.exec();
+ int i = query.boundValue( 1 ).toInt(); // i is 65.
+ \endcode
+
+ \sa QSqlDatabase QSqlCursor QVariant
+*/
+
+/*!
+ Creates a QSqlQuery object which uses the QSqlResult \a r to
+ communicate with a database.
+*/
+
+QSqlQuery::QSqlQuery( QSqlResult * r )
+{
+ d = new QSqlResultShared( r );
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlQuery::~QSqlQuery()
+{
+ if (d->deref()) {
+ delete d;
+ }
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QSqlQuery::QSqlQuery( const QSqlQuery& other )
+ : d(other.d)
+{
+ d->ref();
+}
+
+/*!
+ Creates a QSqlQuery 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 QSqlDatabase
+*/
+QSqlQuery::QSqlQuery( const QString& query, QSqlDatabase* db )
+{
+ init( query, db );
+}
+
+/*!
+ Creates a QSqlQuery object using the database \a db. If \a db is
+ 0, the application's default database is used.
+
+ \sa QSqlDatabase
+*/
+
+QSqlQuery::QSqlQuery( QSqlDatabase* db )
+{
+ init( QString::null, db );
+}
+
+/*! \internal
+*/
+
+void QSqlQuery::init( const QString& query, QSqlDatabase* db )
+{
+ d = new QSqlResultShared( 0 );
+ QSqlDatabase* database = db;
+ if ( !database )
+ database = QSqlDatabase::database( QSqlDatabase::defaultConnection, FALSE );
+ if ( database )
+ *this = database->driver()->createQuery();
+ if ( !query.isNull() )
+ exec( query );
+}
+
+/*!
+ Assigns \a other to the query.
+*/
+
+QSqlQuery& QSqlQuery::operator=( const QSqlQuery& 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 QSqlQuery::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 QSqlQuery::exec ( const QString& query )
+{
+ if ( !d->sqlResult )
+ return FALSE;
+ if ( d->sqlResult->extension() && driver()->hasFeature( QSqlDriver::PreparedQueries ) )
+ d->sqlResult->extension()->clear();
+ d->sqlResult->setActive( FALSE );
+ d->sqlResult->setLastError( QSqlError() );
+ d->sqlResult->setAt( QSql::BeforeFirst );
+ if ( !driver() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::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("QSqlQuery::exec: database not open" );
+#endif
+ return FALSE;
+ }
+ if ( query.isNull() || query.length() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::exec: empty query" );
+#endif
+ return FALSE;
+ }
+#ifdef QT_DEBUG_SQL
+ qDebug( "\n QSqlQuery: " + 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 QVariant 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()
+*/
+
+QVariant QSqlQuery::value( int i ) const
+{
+ if ( !d->sqlResult )
+ return QVariant();
+ if ( isActive() && isValid() && ( i > QSql::BeforeFirst ) ) {
+ return d->sqlResult->data( i );
+ } else {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QSqlQuery::value: not positioned on a valid record" );
+#endif
+ }
+ return QVariant();
+}
+
+/*!
+ Returns the current internal position of the query. The first
+ record is at position zero. If the position is invalid, a
+ QSql::Location will be returned indicating the invalid position.
+
+ \sa prev() next() first() last() seek() isActive() isValid()
+*/
+
+int QSqlQuery::at() const
+{
+ if ( !d->sqlResult )
+ return QSql::BeforeFirst;
+ return d->sqlResult->at();
+}
+
+/*!
+ Returns the text of the current query being used, or QString::null
+ if there is no current query text.
+
+ \sa executedQuery()
+*/
+
+QString QSqlQuery::lastQuery() const
+{
+ if ( !d->sqlResult )
+ return QString::null;
+ return d->sqlResult->lastQuery();
+}
+
+/*!
+ Returns the database driver associated with the query.
+*/
+
+const QSqlDriver* QSqlQuery::driver() const
+{
+ if ( !d->sqlResult )
+ return 0;
+ return d->sqlResult->driver();
+}
+
+/*!
+ Returns the result associated with the query.
+*/
+
+const QSqlResult* QSqlQuery::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 QSqlQuery::seek( int i, bool relative )
+{
+ if ( !isSelect() || !isActive() )
+ return FALSE;
+ beforeSeek();
+ checkDetach();
+ int actualIdx;
+ if ( !relative ) { // arbitrary seek
+ if ( i < 0 ) {
+ d->sqlResult->setAt( QSql::BeforeFirst );
+ afterSeek();
+ return FALSE;
+ }
+ actualIdx = i;
+ } else {
+ switch ( at() ) { // relative seek
+ case QSql::BeforeFirst:
+ if ( i > 0 )
+ actualIdx = i;
+ else {
+ afterSeek();
+ return FALSE;
+ }
+ break;
+ case QSql::AfterLast:
+ if ( i < 0 ) {
+ d->sqlResult->fetchLast();
+ actualIdx = at() + i;
+ } else {
+ afterSeek();
+ return FALSE;
+ }
+ break;
+ default:
+ if ( ( at() + i ) < 0 ) {
+ d->sqlResult->setAt( QSql::BeforeFirst );
+ afterSeek();
+ return FALSE;
+ }
+ actualIdx = at() + i;
+ break;
+ }
+ }
+ // let drivers optimize
+ if ( isForwardOnly() && actualIdx < at() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::seek: cannot seek backwards in a forward only query" );
+#endif
+ afterSeek();
+ return FALSE;
+ }
+ if ( actualIdx == ( at() + 1 ) && at() != QSql::BeforeFirst ) {
+ if ( !d->sqlResult->fetchNext() ) {
+ d->sqlResult->setAt( QSql::AfterLast );
+ afterSeek();
+ return FALSE;
+ }
+ afterSeek();
+ return TRUE;
+ }
+ if ( actualIdx == ( at() - 1 ) ) {
+ if ( !d->sqlResult->fetchPrev() ) {
+ d->sqlResult->setAt( QSql::BeforeFirst );
+ afterSeek();
+ return FALSE;
+ }
+ afterSeek();
+ return TRUE;
+ }
+ if ( !d->sqlResult->fetch( actualIdx ) ) {
+ d->sqlResult->setAt( QSql::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 QSqlQuery::next()
+{
+ if ( !isSelect() || !isActive() )
+ return FALSE;
+ beforeSeek();
+ checkDetach();
+ bool b = FALSE;
+ switch ( at() ) {
+ case QSql::BeforeFirst:
+ b = d->sqlResult->fetchFirst();
+ afterSeek();
+ return b;
+ case QSql::AfterLast:
+ afterSeek();
+ return FALSE;
+ default:
+ if ( !d->sqlResult->fetchNext() ) {
+ d->sqlResult->setAt( QSql::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 QSqlQuery::prev()
+{
+ if ( !isSelect() || !isActive() )
+ return FALSE;
+ if ( isForwardOnly() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::seek: cannot seek backwards in a forward only query" );
+#endif
+ return FALSE;
+ }
+
+ beforeSeek();
+ checkDetach();
+ bool b = FALSE;
+ switch ( at() ) {
+ case QSql::BeforeFirst:
+ afterSeek();
+ return FALSE;
+ case QSql::AfterLast:
+ b = d->sqlResult->fetchLast();
+ afterSeek();
+ return b;
+ default:
+ if ( !d->sqlResult->fetchPrev() ) {
+ d->sqlResult->setAt( QSql::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 QSqlQuery::first()
+{
+ if ( !isSelect() || !isActive() )
+ return FALSE;
+ if ( isForwardOnly() && at() > QSql::BeforeFirst ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::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 QSqlQuery::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() QSqlDriver::hasFeature()
+*/
+int QSqlQuery::size() const
+{
+ if ( !d->sqlResult )
+ return -1;
+ if ( isActive() && d->sqlResult->driver()->hasFeature( QSqlDriver::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() QSqlDriver::hasFeature()
+*/
+
+int QSqlQuery::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 QSqlError
+*/
+
+QSqlError QSqlQuery::lastError() const
+{
+ if ( !d->sqlResult )
+ return QSqlError();
+ return d->sqlResult->lastError();
+}
+
+/*!
+ Returns TRUE if the query is currently positioned on a valid
+ record; otherwise returns FALSE.
+*/
+
+bool QSqlQuery::isValid() const
+{
+ if ( !d->sqlResult )
+ return FALSE;
+ return d->sqlResult->isValid();
+}
+
+/*!
+ Returns TRUE if the query is currently active; otherwise returns
+ FALSE.
+*/
+
+bool QSqlQuery::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 QSqlQuery::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 QSqlQuery::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
+ QDataTable, since they must to be able to scroll backward as well
+ as forward.
+
+ \sa isForwardOnly(), next(), seek()
+*/
+void QSqlQuery::setForwardOnly( bool forward )
+{
+ if ( d->sqlResult )
+ d->sqlResult->setForwardOnly( forward );
+}
+
+/*!
+ \internal
+*/
+
+void QSqlQuery::deref()
+{
+ if ( d->deref() ) {
+ delete d;
+ d = 0;
+ }
+}
+
+/*!
+ \internal
+*/
+
+bool QSqlQuery::checkDetach()
+{
+ if ( d->count > 1 && d->sqlResult ) {
+ QString 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 QSqlQuery::beforeSeek()
+{
+
+}
+
+
+/*!
+ Protected virtual function called after the internal record
+ pointer is moved to a new record. The default implementation does
+ nothing.
+*/
+
+void QSqlQuery::afterSeek()
+{
+
+}
+
+// XXX: Hack to keep BCI - remove in 4.0. QSqlExtension should be
+// removed, and the prepare(), exec() etc. fu's should be
+// made virtual members of QSqlQuery/QSqlResult
+
+/*!
+ 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 QSqlQuery::prepare( const QString& query )
+{
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return FALSE;
+ d->sqlResult->setActive( FALSE );
+ d->sqlResult->setLastError( QSqlError() );
+ d->sqlResult->setAt( QSql::BeforeFirst );
+ d->sqlResult->extension()->clear();
+ if ( !driver() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::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("QSqlQuery::prepare: database not open" );
+#endif
+ return FALSE;
+ }
+ if ( query.isNull() || query.length() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("QSqlQuery::prepare: empty query" );
+#endif
+ return FALSE;
+ }
+#ifdef QT_DEBUG_SQL
+ qDebug( "\n QSqlQuery: " + query );
+#endif
+ QString q = query;
+ QRegExp rx(QString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)"));
+ if ( driver()->hasFeature( QSqlDriver::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( QSqlDriver::NamedPlaceholders ) ) {
+ QRegExp rx(QString::fromLatin1("'[^']*'|\\?"));
+ while ( (i = rx.search( q, i )) != -1 ) {
+ if ( rx.cap(0) == "?" ) {
+ q = q.replace( i, 1, ":f" + QString::number(cnt) );
+ cnt++;
+ }
+ i += rx.matchedLength();
+ }
+ } else if ( driver()->hasFeature( QSqlDriver::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 QSqlQuery::exec()
+{
+ bool ret;
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return FALSE;
+ if ( driver()->hasFeature( QSqlDriver::PreparedQueries ) ) {
+ ret = d->sqlResult->extension()->exec();
+ } else {
+ // fake preparation - just replace the placeholders..
+ QString query = d->sqlResult->lastQuery();
+ if ( d->sqlResult->extension()->bindMethod() == QSqlExtension::BindByName ) {
+ int i;
+ QVariant val;
+ QString 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;
+ QSqlField 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 {
+ QMap<int, QString>::ConstIterator it;
+ QString 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 ) {
+ QSqlField 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..
+ QString 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 QSql::Out or \c QSql::InOut, the placeholder will be
+ overwritten with data from the database after the exec() call.
+
+ \sa addBindValue(), prepare(), exec()
+*/
+void QSqlQuery::bindValue( const QString& placeholder, const QVariant& val, QSql::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 QSql::Out or \c QSql::InOut, the placeholder will be
+ overwritten with data from the database after the exec() call.
+
+ \sa addBindValue(), prepare(), exec()
+*/
+void QSqlQuery::bindValue( int pos, const QVariant& val, QSql::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 QSql::Out or \c QSql::InOut, the placeholder will
+ be overwritten with data from the database after the exec() call.
+
+ \sa bindValue(), prepare(), exec()
+*/
+void QSqlQuery::addBindValue( const QVariant& val, QSql::ParameterType type )
+{
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return;
+ d->sqlResult->extension()->addBindValue( val, type );
+}
+
+
+/*!
+ \overload
+
+ Binds the placeholder with type \c QSql::In.
+*/
+void QSqlQuery::bindValue( const QString& placeholder, const QVariant& val )
+{
+ bindValue( placeholder, val, QSql::In );
+}
+
+/*!
+ \overload
+
+ Binds the placeholder at position \a pos with type \c QSql::In.
+*/
+void QSqlQuery::bindValue( int pos, const QVariant& val )
+{
+ bindValue( pos, val, QSql::In );
+}
+
+/*!
+ \overload
+
+ Binds the placeholder with type \c QSql::In.
+*/
+void QSqlQuery::addBindValue( const QVariant& val )
+{
+ addBindValue( val, QSql::In );
+}
+
+/*!
+ Returns the value for the \a placeholder.
+*/
+QVariant QSqlQuery::boundValue( const QString& placeholder ) const
+{
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return QVariant();
+ return d->sqlResult->extension()->boundValue( placeholder );
+}
+
+/*!
+ \overload
+
+ Returns the value for the placeholder at position \a pos.
+*/
+QVariant QSqlQuery::boundValue( int pos ) const
+{
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return QVariant();
+ return d->sqlResult->extension()->boundValue( pos );
+}
+
+/*!
+ Returns a map of the bound values.
+
+ The bound values can be examined in the following way:
+ \code
+ QSqlQuery query;
+ ...
+ // Examine the bound values - bound using named binding
+ QMap<QString, QVariant>::ConstIterator it;
+ QMap<QString, QVariant> 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
+ QValueList<QVariant>::ConstIterator it;
+ QValueList<QVariant> list = query.boundValues().values();
+ int i = 0;
+ for ( it = list.begin(); it != list.end(); ++it )
+ qWarning( "Placeholder pos: %d, Value: " + (*it).toString(), i++ );
+ ...
+
+ \endcode
+*/
+QMap<QString,QVariant> QSqlQuery::boundValues() const
+{
+ if ( !d->sqlResult || !d->sqlResult->extension() )
+ return QMap<QString,QVariant>();
+ 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()
+*/
+QString QSqlQuery::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 0000000..cf176d8
--- /dev/null
+++ b/src/sql/qsqlquery.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Definition of QSqlQuery class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLQUERY_H
+#define QSQLQUERY_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 QSqlDriver;
+class QSqlResult;
+class QSqlDatabase;
+
+class Q_EXPORT QSqlResultShared : public QObject, public QShared
+{
+ Q_OBJECT
+public:
+ QSqlResultShared( QSqlResult* result );
+ virtual ~QSqlResultShared();
+ QSqlResult* sqlResult;
+ QString executedQuery;
+private slots:
+ void slotResultDestroyed();
+};
+
+class Q_EXPORT QSqlQuery
+{
+public:
+ QSqlQuery( QSqlResult * r );
+ QSqlQuery( const QString& query = QString::null, QSqlDatabase* db = 0 );
+ Q_EXPLICIT QSqlQuery( QSqlDatabase* db );
+ QSqlQuery( const QSqlQuery& other );
+ QSqlQuery& operator=( const QSqlQuery& other );
+ virtual ~QSqlQuery();
+
+ bool isValid() const;
+ bool isActive() const;
+ bool isNull( int field ) const;
+ int at() const;
+ QString lastQuery() const;
+ int numRowsAffected() const;
+ QSqlError lastError() const;
+ bool isSelect() const;
+ int size() const;
+ const QSqlDriver* driver() const;
+ const QSqlResult* result() const;
+ bool isForwardOnly() const;
+ void setForwardOnly( bool forward );
+
+ virtual bool exec ( const QString& query );
+ virtual QVariant 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 QString& query );
+ void bindValue( const QString& placeholder, const QVariant& val );
+ void bindValue( int pos, const QVariant& val );
+ void addBindValue( const QVariant& val );
+ // remove these overloads in 4.0
+ void bindValue( const QString& placeholder, const QVariant& val, QSql::ParameterType type );
+ void bindValue( int pos, const QVariant& val, QSql::ParameterType type );
+ void addBindValue( const QVariant& val, QSql::ParameterType type );
+ QVariant boundValue( const QString& placeholder ) const;
+ QVariant boundValue( int pos ) const;
+ QMap<QString, QVariant> boundValues() const;
+ QString executedQuery() const;
+
+protected:
+ virtual void beforeSeek();
+ virtual void afterSeek();
+
+private:
+ void init( const QString& query, QSqlDatabase* db );
+ void deref();
+ bool checkDetach();
+ QSqlResultShared* d;
+};
+
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqlrecord.cpp b/src/sql/qsqlrecord.cpp
new file mode 100644
index 0000000..bc6a25f
--- /dev/null
+++ b/src/sql/qsqlrecord.cpp
@@ -0,0 +1,774 @@
+/****************************************************************************
+**
+** Implementation of QSqlRecord class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlrecord.h"
+
+#ifndef QT_NO_SQL
+
+#include "qregexp.h"
+#include "qvaluevector.h"
+#include "qshared.h"
+#include "qnamespace.h"
+
+class QSqlRecordPrivate
+{
+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)
+ QSqlField field;
+ bool nogen;
+ };
+
+ QSqlRecordPrivate(): cnt(0)
+ {
+ }
+ QSqlRecordPrivate( const QSqlRecordPrivate& other )
+ {
+ *this = other;
+ }
+ ~QSqlRecordPrivate() {};
+ QSqlRecordPrivate& operator=( const QSqlRecordPrivate& other )
+ {
+ fi = other.fi;
+ cnt = other.cnt;
+ return *this;
+ }
+ void append( const QSqlField& field )
+ {
+ info i;
+ i.field = field;
+ fi.append( i );
+ cnt++;
+ }
+ void insert( int pos, const QSqlField& 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:
+ QValueVector< info > fi;
+ uint cnt;
+};
+
+QSqlRecordShared::~QSqlRecordShared()
+{
+ if ( d )
+ delete d;
+}
+
+/*!
+ \class QSqlRecord qsqlfield.h
+ \brief The QSqlRecord class encapsulates a database record, i.e. a
+ set of database fields.
+
+ \ingroup database
+ \module sql
+
+ The QSqlRecord class encapsulates the functionality and
+ characteristics of a database record (usually a table or view within
+ the database). QSqlRecords support adding and removing fields as
+ well as setting and retrieving field values.
+
+ QSqlRecord is implicitly shared. This means you can make copies of
+ the record in time O(1). If multiple QSqlRecord 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 QSqlRecordInfo
+*/
+
+
+/*!
+ Constructs an empty record.
+*/
+
+QSqlRecord::QSqlRecord()
+{
+ sh = new QSqlRecordShared( new QSqlRecordPrivate() );
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QSqlRecord::QSqlRecord( const QSqlRecord& other )
+ : sh( other.sh )
+{
+ sh->ref();
+}
+
+/*!
+ Sets the record equal to \a other.
+*/
+
+QSqlRecord& QSqlRecord::operator=( const QSqlRecord& other )
+{
+ other.sh->ref();
+ deref();
+ sh = other.sh;
+ return *this;
+}
+
+/*! \internal
+*/
+
+void QSqlRecord::deref()
+{
+ if ( sh->deref() ) {
+ delete sh;
+ sh = 0;
+ }
+}
+
+/*! \internal
+*/
+
+bool QSqlRecord::checkDetach()
+{
+ if ( sh->count > 1 ) {
+ sh->deref();
+ sh = new QSqlRecordShared( new QSqlRecordPrivate( *sh->d ) );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlRecord::~QSqlRecord()
+{
+ 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{QSqlQuery}s. When working
+ with a QSqlCursor the \link QSqlCursor::value() value(const
+ QString&)\endlink overload which uses field names is more
+ appropriate.
+*/
+
+QVariant QSqlRecord::value( int i ) const
+{
+ const QSqlField * f = field(i);
+
+ if( f )
+ return f->value();
+ return QVariant();
+}
+
+/*!
+ \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.
+*/
+
+QVariant QSqlRecord::value( const QString& name ) const
+{
+ const QSqlField * f = field( name );
+
+ if( f )
+ return f->value();
+ return QVariant();
+}
+
+/*!
+ Returns the name of the field at position \a i. If the field does
+ not exist, QString::null is returned.
+*/
+
+QString QSqlRecord::fieldName( int i ) const
+{
+ const QSqlField* f = field( i );
+ if ( f )
+ return f->name();
+ return QString::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 QSqlRecord::position( const QString& name ) const
+{
+ for ( uint i = 0; i < count(); ++i ) {
+ if ( fieldName(i).upper() == name.upper() )
+ return i;
+ }
+#ifdef QT_CHECK_RANGE
+ qWarning( "QSqlRecord::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.
+*/
+
+QSqlField* QSqlRecord::field( int i )
+{
+ checkDetach();
+ if ( !sh->d->contains( i ) ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QSqlRecord::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.
+*/
+
+QSqlField* QSqlRecord::field( const QString& name )
+{
+ checkDetach();
+ if ( !sh->d->contains( position( name ) ) )
+ return 0;
+ return &sh->d->fieldInfo( position( name ) )->field;
+}
+
+
+/*!
+ \overload
+*/
+
+const QSqlField* QSqlRecord::field( int i ) const
+{
+ if ( !sh->d->contains( i ) ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QSqlRecord::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 QSqlField* QSqlRecord::field( const QString& 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 QSqlRecord::append( const QSqlField& 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 QSqlRecord::insert( int pos, const QSqlField& 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 QSqlRecord::remove( int pos )
+{
+ checkDetach();
+ sh->d->remove( pos );
+}
+
+/*!
+ Removes all the record's fields.
+
+ \sa clearValues()
+*/
+
+void QSqlRecord::clear()
+{
+ checkDetach();
+ sh->d->clear();
+}
+
+/*!
+ Returns TRUE if there are no fields in the record; otherwise
+ returns FALSE.
+*/
+
+bool QSqlRecord::isEmpty() const
+{
+ return sh->d->isEmpty();
+}
+
+
+/*!
+ Returns TRUE if there is a field in the record called \a name;
+ otherwise returns FALSE.
+*/
+
+bool QSqlRecord::contains( const QString& 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 QSqlRecord::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 QSqlCursor.
+
+ \sa isGenerated()
+*/
+
+void QSqlRecord::setGenerated( const QString& name, bool generated )
+{
+ setGenerated( position( name ), generated );
+}
+
+/*!
+ \overload
+
+ Sets the generated flag for the field \a i to \a generated.
+
+ \sa isGenerated()
+*/
+
+void QSqlRecord::setGenerated( int i, bool generated )
+{
+ checkDetach();
+ if ( !field( i ) )
+ return;
+ sh->d->fieldInfo( i )->nogen = !generated;
+}
+
+/*!
+ \internal
+ ### Remove in 4.0
+*/
+bool QSqlRecord::isNull( int i )
+{
+ checkDetach();
+ QSqlField* f = field( i );
+ if ( f ) {
+ return f->isNull();
+ }
+ return TRUE;
+}
+
+/*!
+ \internal
+ ### Remove in 4.0
+*/
+bool QSqlRecord::isNull( const QString& 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 QSqlRecord::isNull( int i ) const
+{
+ const QSqlField* 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 QSqlRecord::isNull( const QString& name ) const
+{
+ return isNull( position( name ) );
+}
+
+/*!
+ Sets the value of field \a i to NULL. If the field does not exist,
+ nothing happens.
+*/
+void QSqlRecord::setNull( int i )
+{
+ checkDetach();
+ QSqlField* 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 QSqlRecord::setNull( const QString& 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 QSqlRecord::isGenerated( const QString& 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 QSqlRecord::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\>"
+*/
+
+QString QSqlRecord::toString( const QString& prefix, const QString& sep ) const
+{
+ QString 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\>"
+*/
+
+QStringList QSqlRecord::toStringList( const QString& prefix ) const
+{
+ QStringList s;
+ for ( uint i = 0; i < count(); ++i ) {
+ if ( isGenerated( field(i)->name() ) )
+ s += createField( i, prefix );
+ }
+ return s;
+}
+
+/*! \internal
+*/
+
+QString QSqlRecord::createField( int i, const QString& prefix ) const
+{
+ QString f;
+ if ( !prefix.isEmpty() )
+ f = prefix + ".";
+ f += field( i )->name();
+ return f;
+}
+
+/*!
+ Returns the number of fields in the record.
+*/
+
+uint QSqlRecord::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 QSqlRecord::setValue( int i, const QVariant& val )
+{
+ checkDetach();
+ QSqlField* 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 QSqlRecord::setValue( const QString& name, const QVariant& val )
+{
+ setValue( position( name ), val );
+}
+
+
+/******************************************/
+/******* QSqlRecordInfo Impl ******/
+/******************************************/
+
+/*!
+ \class QSqlRecordInfo qsqlrecord.h
+ \brief The QSqlRecordInfo class encapsulates a set of database field meta data.
+
+ \ingroup database
+ \module sql
+
+ This class is a QValueList 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 QSqlFieldInfo record for a named
+ field.
+
+ \sa QValueList, QSqlFieldInfo
+*/
+
+
+/*!
+ Constructs a QSqlRecordInfo object based on the fields in the
+ QSqlRecord \a other.
+*/
+QSqlRecordInfo::QSqlRecordInfo( const QSqlRecord& other )
+{
+ for ( uint i = 0; i < other.count(); ++i ) {
+ push_back( QSqlFieldInfo( *(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.
+*/
+QSqlRecordInfo::size_type QSqlRecordInfo::contains( const QString& fieldName ) const
+{
+ size_type i = 0;
+ QString fName = fieldName.upper();
+ for( const_iterator it = begin(); it != end(); ++it ) {
+ if ( (*it).name().upper() == fName ) {
+ ++i;
+ }
+ }
+ return i;
+}
+
+/*!
+ Returns a QSqlFieldInfo object for the first field in the record
+ which has the field name \a fieldName. If no matching field is
+ found then an empty QSqlFieldInfo object is returned.
+*/
+QSqlFieldInfo QSqlRecordInfo::find( const QString& fieldName ) const
+{
+ QString fName = fieldName.upper();
+ for( const_iterator it = begin(); it != end(); ++it ) {
+ if ( (*it).name().upper() == fName ) {
+ return *it;
+ }
+ }
+ return QSqlFieldInfo();
+}
+
+/*!
+ Returns an empty QSqlRecord based on the field information
+ in this QSqlRecordInfo.
+*/
+QSqlRecord QSqlRecordInfo::toRecord() const
+{
+ QSqlRecord buf;
+ for( const_iterator it = begin(); it != end(); ++it ) {
+ buf.append( (*it).toField() );
+ }
+ return buf;
+}
+
+/*!
+ \fn QSqlRecordInfo::QSqlRecordInfo()
+
+ Constructs an empty record info object
+*/
+
+/*!
+ \fn QSqlRecordInfo::QSqlRecordInfo( const QSqlFieldInfoList& 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 0000000..0a7d898
--- /dev/null
+++ b/src/sql/qsqlrecord.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Definition of QSqlRecord class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLRECORD_H
+#define QSQLRECORD_H
+
+#ifndef QT_H
+#include "qstring.h"
+#include "qstringlist.h"
+#include "qvariant.h"
+#include "qsqlfield.h"
+#endif // QT_H
+
+#ifndef QT_NO_SQL
+
+class QSqlRecordPrivate;
+
+class QSqlRecordShared : public QShared
+{
+public:
+ QSqlRecordShared( QSqlRecordPrivate* sqlRecordPrivate )
+ : d( sqlRecordPrivate )
+ {}
+ virtual ~QSqlRecordShared();
+ QSqlRecordPrivate* d;
+};
+
+class Q_EXPORT QSqlRecord
+{
+public:
+ QSqlRecord();
+ QSqlRecord( const QSqlRecord& other );
+ QSqlRecord& operator=( const QSqlRecord& other );
+ virtual ~QSqlRecord();
+ virtual QVariant value( int i ) const;
+ virtual QVariant value( const QString& name ) const;
+ virtual void setValue( int i, const QVariant& val );
+ virtual void setValue( const QString& name, const QVariant& val );
+ bool isGenerated( int i ) const;
+ bool isGenerated( const QString& name ) const;
+ virtual void setGenerated( const QString& name, bool generated );
+ virtual void setGenerated( int i, bool generated );
+ virtual void setNull( int i );
+ virtual void setNull( const QString& name );
+ bool isNull( int i ); // remove in 4.0
+ bool isNull( const QString& name ); // remove in 4.0
+ bool isNull( int i ) const;
+ bool isNull( const QString& name ) const;
+
+ int position( const QString& name ) const;
+ QString fieldName( int i ) const;
+ QSqlField* field( int i );
+ QSqlField* field( const QString& name );
+ const QSqlField* field( int i ) const;
+ const QSqlField* field( const QString& name ) const;
+
+ virtual void append( const QSqlField& field );
+ virtual void insert( int pos, const QSqlField& field );
+ virtual void remove( int pos );
+
+ bool isEmpty() const;
+ bool contains( const QString& name ) const;
+ virtual void clear();
+ virtual void clearValues( bool nullify = FALSE );
+ uint count() const;
+ virtual QString toString( const QString& prefix = QString::null,
+ const QString& sep = "," ) const;
+ virtual QStringList toStringList( const QString& prefix = QString::null ) const;
+
+private:
+ QString createField( int i, const QString& prefix ) const;
+ void deref();
+ bool checkDetach();
+ QSqlRecordShared* sh;
+};
+
+/******************************************/
+/******* QSqlRecordInfo Class ******/
+/******************************************/
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+Q_TEMPLATE_EXTERN template class Q_EXPORT QValueList<QSqlFieldInfo>;
+// MOC_SKIP_END
+#endif
+
+typedef QValueList<QSqlFieldInfo> QSqlFieldInfoList;
+
+class Q_EXPORT QSqlRecordInfo: public QSqlFieldInfoList
+{
+public:
+ QSqlRecordInfo(): QSqlFieldInfoList() {}
+ QSqlRecordInfo( const QSqlFieldInfoList& other ): QSqlFieldInfoList( other ) {}
+ QSqlRecordInfo( const QSqlRecord& other );
+
+ size_type contains( const QString& fieldName ) const;
+ QSqlFieldInfo find( const QString& fieldName ) const;
+ QSqlRecord toRecord() const;
+
+};
+
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqlresult.cpp b/src/sql/qsqlresult.cpp
new file mode 100644
index 0000000..1ec963f
--- /dev/null
+++ b/src/sql/qsqlresult.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Implementation of QSqlResult class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlresult.h"
+#include "private/qsqlextension_p.h"
+
+#ifndef QT_NO_SQL
+
+class QSqlResultPrivate
+{
+public:
+ const QSqlDriver* sqldriver;
+ int idx;
+ QString sql;
+ bool active;
+ bool isSel;
+ QSqlError error;
+ QSqlExtension * ext;
+};
+
+/*!
+ \class QSqlResult
+ \brief The QSqlResult class provides an abstract interface for
+ accessing data from SQL databases.
+
+ \ingroup database
+ \module sql
+
+ Normally you would use QSqlQuery instead of QSqlResult since QSqlQuery
+ provides a generic wrapper for database-specific implementations of
+ QSqlResult.
+
+ \sa QSql
+*/
+
+
+/*!
+ Protected constructor which creates a QSqlResult using database \a
+ db. The object is initialized to an inactive state.
+*/
+
+QSqlResult::QSqlResult( const QSqlDriver * db ): forwardOnly( FALSE )
+{
+ d = new QSqlResultPrivate();
+ d->sqldriver = db;
+ d->idx = QSql::BeforeFirst;
+ d->isSel = FALSE;
+ d->active = FALSE;
+ d->ext = new QSqlExtension();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QSqlResult::~QSqlResult()
+{
+ 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 QSqlResult::setQuery( const QString& query )
+{
+ d->sql = query;
+}
+
+/*!
+ Returns the current SQL query text, or QString::null if there is none.
+*/
+
+QString QSqlResult::lastQuery() const
+{
+ return d->sql;
+}
+
+/*!
+ Returns the current (zero-based) position of the result.
+*/
+
+int QSqlResult::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 QSqlResult::isValid() const
+{
+ return ( d->idx != QSql::BeforeFirst && \
+ d->idx != QSql::AfterLast ) ? TRUE : FALSE;
+}
+
+/*!
+ \fn bool QSqlResult::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 QSqlResult::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 QSqlResult::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 QSqlResult::setSelect( bool s )
+{
+ d->isSel = s;
+}
+
+/*!
+ Returns TRUE if the current result is from a SELECT statement;
+ otherwise returns FALSE.
+*/
+
+bool QSqlResult::isSelect() const
+{
+ return d->isSel;
+}
+
+/*!
+ Returns the driver associated with the result.
+*/
+
+const QSqlDriver* QSqlResult::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 QSqlResult::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 QSqlResult::setLastError( const QSqlError& e )
+{
+ d->error = e;
+}
+
+
+/*!
+ Returns the last error associated with the result.
+*/
+
+QSqlError QSqlResult::lastError() const
+{
+ return d->error;
+}
+
+/*!
+ \fn int QSqlResult::size()
+
+ Returns the size of the result or -1 if it cannot be determined.
+*/
+
+/*!
+ \fn int QSqlResult::numRowsAffected()
+
+ Returns the number of rows affected by the last query executed.
+*/
+
+/*!
+ \fn QVariant QSqlResult::data( int i )
+
+ Returns the data for field \a i (zero-based) as a QVariant. 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 QVariant() if it cannot be determined.
+*/
+
+/*!
+ \fn bool QSqlResult::reset( const QString& 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 QSqlResult::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 QSqlResult::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 QSqlResult::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 QSqlResult::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 QSqlResult::fetchPrev()
+{
+ return fetch( at() - 1 );
+}
+
+/*!
+ Returns TRUE if you can only scroll forward through a result set;
+ otherwise returns FALSE.
+*/
+bool QSqlResult::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 QSqlResult::setForwardOnly( bool forward )
+{
+ forwardOnly = forward;
+}
+
+// XXX BCI HACK - remove in 4.0
+/*! \internal */
+void QSqlResult::setExtension( QSqlExtension * ext )
+{
+ if ( d->ext )
+ delete d->ext;
+ d->ext = ext;
+}
+
+/*! \internal */
+QSqlExtension * QSqlResult::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 0000000..c0f2ce5
--- /dev/null
+++ b/src/sql/qsqlresult.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Definition of QSqlResult class
+**
+** Created : 2000-11-03
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLRESULT_H
+#define QSQLRESULT_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 QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlDriver;
+class QSql;
+class QSqlResultPrivate;
+class QSqlExtension;
+
+class QM_EXPORT_SQL QSqlResult
+{
+friend class QSqlQuery;
+friend class QSqlResultShared;
+public:
+ virtual ~QSqlResult();
+
+ // BCI HACK - remove in 4.0
+ void setExtension( QSqlExtension * ext );
+ QSqlExtension * extension();
+
+protected:
+ QSqlResult(const QSqlDriver * db );
+ int at() const;
+ QString lastQuery() const;
+ QSqlError lastError() const;
+ bool isValid() const;
+ bool isActive() const;
+ bool isSelect() const;
+ bool isForwardOnly() const;
+ const QSqlDriver* driver() const;
+ virtual void setAt( int at );
+ virtual void setActive( bool a );
+ virtual void setLastError( const QSqlError& e );
+ virtual void setQuery( const QString& query );
+ virtual void setSelect( bool s );
+ virtual void setForwardOnly( bool forward );
+
+ virtual QVariant data( int i ) = 0;
+ virtual bool isNull( int i ) = 0;
+ virtual bool reset ( const QString& 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:
+ QSqlResultPrivate* d;
+ bool forwardOnly;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ QSqlResult( const QSqlResult & );
+ QSqlResult &operator=( const QSqlResult & );
+#endif
+};
+
+#endif // QT_NO_SQL
+#endif
diff --git a/src/sql/qsqlselectcursor.cpp b/src/sql/qsqlselectcursor.cpp
new file mode 100644
index 0000000..eeb318c
--- /dev/null
+++ b/src/sql/qsqlselectcursor.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Definition of QSqlSelectCursor class
+**
+** Created : 2002-11-13
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsqlselectcursor.h"
+#include "qsqldriver.h"
+
+#ifndef QT_NO_SQL
+
+class QSqlSelectCursorPrivate
+{
+public:
+ QSqlSelectCursorPrivate() : populated( FALSE ) {}
+ QString query;
+ bool populated : 1;
+};
+
+/*!
+ \class QSqlSelectCursor qsqlselectcursor.h
+ \brief The QSqlSelectCursor class provides browsing of general SQL
+ SELECT statements.
+
+ \ingroup database
+ \module sql
+
+ QSqlSelectCursor is a convenience class that makes it possible to
+ display result sets from general SQL \c SELECT statements in
+ data-aware Qt widgets. QSqlSelectCursor 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
+ QSqlSelectCursor::exec() function.
+
+ Example:
+ \code
+ ...
+ QSqlSelectCursor* cur = new QSqlSelectCursor( "SELECT id, firstname, lastname FROM author" );
+ QDataTable* table = new QDataTable( 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.
+ */
+QSqlSelectCursor::QSqlSelectCursor( const QString& query, QSqlDatabase* db )
+ : QSqlCursor( QString::null, FALSE, db )
+{
+ d = new QSqlSelectCursorPrivate;
+ d->query = query;
+ QSqlCursor::setMode( ReadOnly );
+ if ( !query.isNull() )
+ exec( query );
+}
+
+/*! Constructs a copy of \a other */
+QSqlSelectCursor::QSqlSelectCursor( const QSqlSelectCursor& other )
+ : QSqlCursor( other )
+{
+ d = new QSqlSelectCursorPrivate;
+ d->query = other.d->query;
+ d->populated = other.d->populated;
+}
+
+/*! Destroys the object and frees any allocated resources */
+QSqlSelectCursor::~QSqlSelectCursor()
+{
+ delete d;
+}
+
+/*! \reimp */
+bool QSqlSelectCursor::exec( const QString& query )
+{
+ d->query = query;
+ bool ret = QSqlCursor::exec( query );
+ if ( ret ) {
+ QSqlCursor::clear();
+ populateCursor();
+ }
+ return ret;
+}
+
+/*! \fn bool QSqlSelectCursor::select()
+ \reimp
+*/
+
+/*! \reimp */
+bool QSqlSelectCursor::select( const QString&, const QSqlIndex& )
+{
+ bool ret = QSqlCursor::exec( d->query );
+ if ( ret && !d->populated )
+ populateCursor();
+ return ret;
+}
+
+/*! \internal */
+void QSqlSelectCursor::populateCursor()
+{
+ QSqlRecordInfo inf = driver()->recordInfo( *(QSqlQuery*)this );
+ for ( QSqlRecordInfo::const_iterator it = inf.begin(); it != inf.end(); ++it )
+ QSqlCursor::append( *it );
+ d->populated = TRUE;
+}
+
+/*! \fn QSqlIndex QSqlSelectCursor::primaryIndex( bool ) const
+ \reimp
+*/
+
+/*! \fn QSqlIndex QSqlSelectCursor::index( const QStringList& ) const
+ \reimp
+*/
+
+/*! \fn QSqlIndex QSqlSelectCursor::index( const QString& ) const
+ \reimp
+*/
+
+/*! \fn QSqlIndex QSqlSelectCursor::index( const char* ) const
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setPrimaryIndex( const QSqlIndex& )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::append( const QSqlFieldInfo& )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::insert( int, const QSqlFieldInfo& )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::remove( int )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::clear()
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setGenerated( const QString&, bool )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setGenerated( int, bool )
+ \reimp
+*/
+
+/*! \fn QSqlRecord* QSqlSelectCursor::editBuffer( bool )
+ \reimp
+*/
+
+/*! \fn QSqlRecord* QSqlSelectCursor::primeInsert()
+ \reimp
+*/
+
+/*! \fn QSqlRecord* QSqlSelectCursor::primeUpdate()
+ \reimp
+*/
+
+/*! \fn QSqlRecord* QSqlSelectCursor::primeDelete()
+ \reimp
+*/
+
+/*! \fn int QSqlSelectCursor::insert( bool )
+ \reimp
+*/
+
+/*! \fn int QSqlSelectCursor::update( bool )
+ \reimp
+*/
+
+/*! \fn int QSqlSelectCursor::del( bool )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setMode( int )
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setSort( const QSqlIndex& )
+ \reimp
+*/
+
+/*! \fn QSqlIndex QSqlSelectCursor::sort() const
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setFilter( const QString& )
+ \reimp
+*/
+
+/*! \fn QString QSqlSelectCursor::filter() const
+ \reimp
+*/
+
+/*! \fn void QSqlSelectCursor::setName( const QString&, bool )
+ \reimp
+*/
+
+/*! \fn QString QSqlSelectCursor::name() const
+ \reimp
+*/
+
+/*! \fn QString QSqlSelectCursor::toString( const QString&, const QString& ) const
+ \reimp
+*/
+#endif // QT_NO_SQL
diff --git a/src/sql/qsqlselectcursor.h b/src/sql/qsqlselectcursor.h
new file mode 100644
index 0000000..2204b61
--- /dev/null
+++ b/src/sql/qsqlselectcursor.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Definition of QSqlSelectCursor class
+**
+** Created : 2002-11-13
+**
+** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef QSQLSELECTCURSOR_H
+#define QSQLSELECTCURSOR_H
+
+#ifndef QT_H
+#include "qsqlcursor.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_SQL ) || defined( QT_LICENSE_PROFESSIONAL )
+#define QM_EXPORT_SQL
+#else
+#define QM_EXPORT_SQL Q_EXPORT
+#endif
+
+#ifndef QT_NO_SQL
+
+class QSqlSelectCursorPrivate;
+
+class QM_EXPORT_SQL QSqlSelectCursor : public QSqlCursor
+{
+public:
+ QSqlSelectCursor( const QString& query = QString::null, QSqlDatabase* db = 0 );
+ QSqlSelectCursor( const QSqlSelectCursor& other );
+ ~QSqlSelectCursor();
+ bool exec( const QString& query );
+ bool select() { return QSqlCursor::select(); }
+
+protected:
+ QSqlIndex primaryIndex( bool = TRUE ) const { return QSqlIndex(); }
+ QSqlIndex index( const QStringList& ) const { return QSqlIndex(); }
+ QSqlIndex index( const QString& ) const { return QSqlIndex(); }
+ QSqlIndex index( const char* ) const { return QSqlIndex(); }
+ void setPrimaryIndex( const QSqlIndex& ) {}
+ void append( const QSqlFieldInfo& ) {}
+ void insert( int, const QSqlFieldInfo& ) {}
+ void remove( int ) {}
+ void clear() {}
+ void setGenerated( const QString&, bool ) {}
+ void setGenerated( int, bool ) {}
+ QSqlRecord* editBuffer( bool = FALSE ) { return 0; }
+ QSqlRecord* primeInsert() { return 0; }
+ QSqlRecord* primeUpdate() { return 0; }
+ QSqlRecord* 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 QSqlIndex& ) {}
+ QSqlIndex sort() const { return QSqlIndex(); }
+ void setFilter( const QString& ) {}
+ QString filter() const { return QString::null; }
+ void setName( const QString&, bool = TRUE ) {}
+ QString name() const { return QString::null; }
+ QString toString( const QString& = QString::null, const QString& = "," ) const { return QString::null; }
+ bool select( const QString &, const QSqlIndex& = QSqlIndex() );
+
+private:
+ void populateCursor();
+
+ QSqlSelectCursorPrivate * d;
+};
+
+#endif // QT_NO_SQL
+#endif // QSQLSELECTCURSOR_H
diff --git a/src/sql/qt_sql.pri b/src/sql/qt_sql.pri
new file mode 100644
index 0000000..5533c6f
--- /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
+ }
+}
+