diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/sql/kernel | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/sql/kernel')
-rw-r--r-- | src/sql/kernel/kernel.pri | 24 | ||||
-rw-r--r-- | src/sql/kernel/qsql.h | 113 | ||||
-rw-r--r-- | src/sql/kernel/qsqlcachedresult.cpp | 297 | ||||
-rw-r--r-- | src/sql/kernel/qsqlcachedresult_p.h | 99 | ||||
-rw-r--r-- | src/sql/kernel/qsqldatabase.cpp | 1487 | ||||
-rw-r--r-- | src/sql/kernel/qsqldatabase.h | 159 | ||||
-rw-r--r-- | src/sql/kernel/qsqldriver.cpp | 803 | ||||
-rw-r--r-- | src/sql/kernel/qsqldriver.h | 151 | ||||
-rw-r--r-- | src/sql/kernel/qsqldriverplugin.cpp | 108 | ||||
-rw-r--r-- | src/sql/kernel/qsqldriverplugin.h | 81 | ||||
-rw-r--r-- | src/sql/kernel/qsqlerror.cpp | 253 | ||||
-rw-r--r-- | src/sql/kernel/qsqlerror.h | 104 | ||||
-rw-r--r-- | src/sql/kernel/qsqlfield.cpp | 557 | ||||
-rw-r--r-- | src/sql/kernel/qsqlfield.h | 119 | ||||
-rw-r--r-- | src/sql/kernel/qsqlindex.cpp | 256 | ||||
-rw-r--r-- | src/sql/kernel/qsqlindex.h | 92 | ||||
-rw-r--r-- | src/sql/kernel/qsqlnulldriver_p.h | 114 | ||||
-rw-r--r-- | src/sql/kernel/qsqlquery.cpp | 1220 | ||||
-rw-r--r-- | src/sql/kernel/qsqlquery.h | 130 | ||||
-rw-r--r-- | src/sql/kernel/qsqlrecord.cpp | 605 | ||||
-rw-r--r-- | src/sql/kernel/qsqlrecord.h | 123 | ||||
-rw-r--r-- | src/sql/kernel/qsqlresult.cpp | 1013 | ||||
-rw-r--r-- | src/sql/kernel/qsqlresult.h | 152 |
23 files changed, 8060 insertions, 0 deletions
diff --git a/src/sql/kernel/kernel.pri b/src/sql/kernel/kernel.pri new file mode 100644 index 0000000..c6fe404 --- /dev/null +++ b/src/sql/kernel/kernel.pri @@ -0,0 +1,24 @@ +HEADERS += kernel/qsql.h \ + kernel/qsqlquery.h \ + kernel/qsqldatabase.h \ + kernel/qsqlfield.h \ + kernel/qsqlrecord.h \ + kernel/qsqldriver.h \ + kernel/qsqlnulldriver_p.h \ + kernel/qsqldriverplugin.h \ + kernel/qsqlerror.h \ + kernel/qsqlresult.h \ + kernel/qsqlcachedresult_p.h \ + kernel/qsqlindex.h + +SOURCES += kernel/qsqlquery.cpp \ + kernel/qsqldatabase.cpp \ + kernel/qsqlfield.cpp \ + kernel/qsqlrecord.cpp \ + kernel/qsqldriver.cpp \ + kernel/qsqldriverplugin.cpp \ + kernel/qsqlerror.cpp \ + kernel/qsqlresult.cpp \ + kernel/qsqlindex.cpp \ + kernel/qsqlcachedresult.cpp + diff --git a/src/sql/kernel/qsql.h b/src/sql/kernel/qsql.h new file mode 100644 index 0000000..0e3388a --- /dev/null +++ b/src/sql/kernel/qsql.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQL_H +#define QSQL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +namespace QSql +{ + enum Location + { + BeforeFirstRow = -1, + AfterLastRow = -2 +#ifdef QT3_SUPPORT + , BeforeFirst = BeforeFirstRow, + AfterLast = AfterLastRow +#endif + }; + + enum ParamTypeFlag + { + In = 0x00000001, + Out = 0x00000002, + InOut = In | Out, + Binary = 0x00000004 + }; + Q_DECLARE_FLAGS(ParamType, ParamTypeFlag) + + enum TableType + { + Tables = 0x01, + SystemTables = 0x02, + Views = 0x04, + AllTables = 0xff + }; + + enum NumericalPrecisionPolicy + { + LowPrecisionInt32 = 0x01, + LowPrecisionInt64 = 0x02, + LowPrecisionDouble = 0x04, + + HighPrecision = 0 + }; + +#ifdef QT3_SUPPORT + enum Op { + None = -1, + Insert = 0, + Update = 1, + Delete = 2 + }; + + enum Confirm { + Cancel = -1, + No = 0, + Yes = 1 + }; +#endif +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSql::ParamType) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQL_H diff --git a/src/sql/kernel/qsqlcachedresult.cpp b/src/sql/kernel/qsqlcachedresult.cpp new file mode 100644 index 0000000..e85229f --- /dev/null +++ b/src/sql/kernel/qsqlcachedresult.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qsqlcachedresult_p.h" + +#include <qvariant.h> +#include <qdatetime.h> +#include <qvector.h> + +QT_BEGIN_NAMESPACE + +/* + QSqlCachedResult is a convenience class for databases that only allow + forward only fetching. It will cache all the results so we can iterate + backwards over the results again. + + All you need to do is to inherit from QSqlCachedResult and reimplement + gotoNext(). gotoNext() will have a reference to the internal cache and + will give you an index where you can start filling in your data. Special + case: If the user actually wants a forward-only query, idx will be -1 + to indicate that we are not interested in the actual values. +*/ + +static const uint initial_cache_size = 128; + +class QSqlCachedResultPrivate +{ +public: + QSqlCachedResultPrivate(); + bool canSeek(int i) const; + inline int cacheCount() const; + void init(int count, bool fo); + void cleanup(); + int nextIndex(); + void revertLast(); + + QSqlCachedResult::ValueCache cache; + int rowCacheEnd; + int colCount; + bool forwardOnly; + bool atEnd; +}; + +QSqlCachedResultPrivate::QSqlCachedResultPrivate(): + rowCacheEnd(0), colCount(0), forwardOnly(false), atEnd(false) +{ +} + +void QSqlCachedResultPrivate::cleanup() +{ + cache.clear(); + forwardOnly = false; + atEnd = false; + colCount = 0; + rowCacheEnd = 0; +} + +void QSqlCachedResultPrivate::init(int count, bool fo) +{ + Q_ASSERT(count); + cleanup(); + forwardOnly = fo; + colCount = count; + if (fo) { + cache.resize(count); + rowCacheEnd = count; + } else { + cache.resize(initial_cache_size * count); + } +} + +int QSqlCachedResultPrivate::nextIndex() +{ + if (forwardOnly) + return 0; + int newIdx = rowCacheEnd; + if (newIdx + colCount > cache.size()) + cache.resize(qMin(cache.size() * 2, cache.size() + 10000)); + rowCacheEnd += colCount; + + return newIdx; +} + +bool QSqlCachedResultPrivate::canSeek(int i) const +{ + if (forwardOnly || i < 0) + return false; + return rowCacheEnd >= (i + 1) * colCount; +} + +void QSqlCachedResultPrivate::revertLast() +{ + if (forwardOnly) + return; + rowCacheEnd -= colCount; +} + +inline int QSqlCachedResultPrivate::cacheCount() const +{ + Q_ASSERT(!forwardOnly); + Q_ASSERT(colCount); + return rowCacheEnd / colCount; +} + +////////////// + +QSqlCachedResult::QSqlCachedResult(const QSqlDriver * db): QSqlResult (db) +{ + d = new QSqlCachedResultPrivate(); +} + +QSqlCachedResult::~QSqlCachedResult() +{ + delete d; +} + +void QSqlCachedResult::init(int colCount) +{ + d->init(colCount, isForwardOnly()); +} + +bool QSqlCachedResult::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::AfterLastRow) + return false; + while(at() < i - 1) { + if (!gotoNext(d->cache, -1)) + return false; + setAt(at() + 1); + } + if (!gotoNext(d->cache, 0)) + return false; + setAt(at() + 1); + return true; + } + if (d->canSeek(i)) { + setAt(i); + return true; + } + if (d->rowCacheEnd > 0) + setAt(d->cacheCount()); + while (at() < i + 1) { + if (!cacheNext()) + return false; + } + setAt(i); + + return true; +} + +bool QSqlCachedResult::fetchNext() +{ + if (d->canSeek(at() + 1)) { + setAt(at() + 1); + return true; + } + return cacheNext(); +} + +bool QSqlCachedResult::fetchPrevious() +{ + return fetch(at() - 1); +} + +bool QSqlCachedResult::fetchFirst() +{ + if (d->forwardOnly && at() != QSql::BeforeFirstRow) { + return false; + } + if (d->canSeek(0)) { + setAt(0); + return true; + } + return cacheNext(); +} + +bool QSqlCachedResult::fetchLast() +{ + if (d->atEnd) { + if (d->forwardOnly) + return false; + else + return fetch(d->cacheCount() - 1); + } + + int i = at(); + while (fetchNext()) + ++i; /* brute force */ + if (d->forwardOnly && at() == QSql::AfterLastRow) { + setAt(i); + return true; + } else { + return fetch(i); + } +} + +QVariant QSqlCachedResult::data(int i) +{ + int idx = d->forwardOnly ? i : at() * d->colCount + i; + if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd) + return QVariant(); + + return d->cache.at(idx); +} + +bool QSqlCachedResult::isNull(int i) +{ + int idx = d->forwardOnly ? i : at() * d->colCount + i; + if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd) + return true; + + return d->cache.at(idx).isNull(); +} + +void QSqlCachedResult::cleanup() +{ + setAt(QSql::BeforeFirstRow); + setActive(false); + d->cleanup(); +} + +void QSqlCachedResult::clearValues() +{ + setAt(QSql::BeforeFirstRow); + d->rowCacheEnd = 0; + d->atEnd = false; +} + +bool QSqlCachedResult::cacheNext() +{ + if (d->atEnd) + return false; + + if (!gotoNext(d->cache, d->nextIndex())) { + d->revertLast(); + d->atEnd = true; + return false; + } + setAt(at() + 1); + return true; +} + +int QSqlCachedResult::colCount() const +{ + return d->colCount; +} + +QSqlCachedResult::ValueCache &QSqlCachedResult::cache() +{ + return d->cache; +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlcachedresult_p.h b/src/sql/kernel/qsqlcachedresult_p.h new file mode 100644 index 0000000..8a4869c --- /dev/null +++ b/src/sql/kernel/qsqlcachedresult_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLCACHEDRESULT_P_H +#define QSQLCACHEDRESULT_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. +// + +#include "QtSql/qsqlresult.h" + +QT_BEGIN_NAMESPACE + +class QVariant; +template <typename T> class QVector; + +class QSqlCachedResultPrivate; + +class Q_SQL_EXPORT QSqlCachedResult: public QSqlResult +{ +public: + virtual ~QSqlCachedResult(); + + typedef QVector<QVariant> ValueCache; + +protected: + QSqlCachedResult(const QSqlDriver * db); + + void init(int colCount); + void cleanup(); + void clearValues(); + + virtual bool gotoNext(ValueCache &values, int index) = 0; + + QVariant data(int i); + bool isNull(int i); + bool fetch(int i); + bool fetchNext(); + bool fetchPrevious(); + bool fetchFirst(); + bool fetchLast(); + + int colCount() const; + ValueCache &cache(); + +private: + bool cacheNext(); + QSqlCachedResultPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QSQLCACHEDRESULT_P_H diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp new file mode 100644 index 0000000..6232452 --- /dev/null +++ b/src/sql/kernel/qsqldatabase.cpp @@ -0,0 +1,1487 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqldatabase.h" +#include "qsqlquery.h" + +#ifdef Q_OS_WIN32 +// Conflicting declarations of LPCBYTE in sqlfront.h and winscard.h +#define _WINSCARD_H_ +#endif + +#ifdef QT_SQL_PSQL +#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_SQLITE2 +#include "../drivers/sqlite2/qsql_sqlite2.h" +#endif +#ifdef QT_SQL_IBASE +#undef SQL_FLOAT // avoid clash with ODBC +#undef SQL_DOUBLE +#undef SQL_TIMESTAMP +#undef SQL_TYPE_TIME +#undef SQL_TYPE_DATE +#undef SQL_DATE +#define SCHAR IBASE_SCHAR // avoid clash with ODBC (older versions of ibase.h with Firebird) +#include "../drivers/ibase/qsql_ibase.h" +#undef SCHAR +#endif + +#include "qdebug.h" +#include "qcoreapplication.h" +#include "qreadwritelock.h" +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "qsqldriverplugin.h" +#include "qsqlindex.h" +#include "private/qfactoryloader_p.h" +#include "private/qsqlnulldriver_p.h" +#include "qmutex.h" +#include "qhash.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QSqlDriverFactoryInterface_iid, + QLatin1String("/sqldrivers"))) +#endif + +QT_STATIC_CONST_IMPL char *QSqlDatabase::defaultConnection = "qt_sql_default_connection"; + +typedef QHash<QString, QSqlDriverCreatorBase*> DriverDict; + +class QConnectionDict: public QHash<QString, QSqlDatabase> +{ +public: + inline bool contains_ts(const QString &key) + { + QReadLocker locker(&lock); + return contains(key); + } + inline QStringList keys_ts() const + { + QReadLocker locker(&lock); + return keys(); + } + + mutable QReadWriteLock lock; +}; +Q_GLOBAL_STATIC(QConnectionDict, dbDict) + +class QSqlDatabasePrivate +{ +public: + QSqlDatabasePrivate(QSqlDriver *dr = 0): + driver(dr), + port(-1) + { + ref = 1; + } + QSqlDatabasePrivate(const QSqlDatabasePrivate &other); + ~QSqlDatabasePrivate(); + void init(const QString& type); + void copy(const QSqlDatabasePrivate *other); + void disable(); + + QAtomicInt ref; + QSqlDriver* driver; + QString dbname; + QString uname; + QString pword; + QString hname; + QString drvName; + int port; + QString connOptions; + QString connName; + + static QSqlDatabasePrivate *shared_null(); + static QSqlDatabase database(const QString& name, bool open); + static void addDatabase(const QSqlDatabase &db, const QString & name); + static void removeDatabase(const QString& name); + static void invalidateDb(const QSqlDatabase &db, const QString &name); + static DriverDict &driverDict(); + static void cleanConnections(); +}; + +QSqlDatabasePrivate::QSqlDatabasePrivate(const QSqlDatabasePrivate &other) +{ + ref = 1; + dbname = other.dbname; + uname = other.uname; + pword = other.pword; + hname = other.hname; + drvName = other.drvName; + port = other.port; + connOptions = other.connOptions; + driver = other.driver; +} + +QSqlDatabasePrivate::~QSqlDatabasePrivate() +{ + if (driver != shared_null()->driver) + delete driver; +} + +void QSqlDatabasePrivate::cleanConnections() +{ + QConnectionDict *dict = dbDict(); + Q_ASSERT(dict); + QWriteLocker locker(&dict->lock); + + QConnectionDict::iterator it = dict->begin(); + while (it != dict->end()) { + invalidateDb(it.value(), it.key()); + ++it; + } + dict->clear(); +} + +static bool qDriverDictInit = false; +static void cleanDriverDict() +{ + qDeleteAll(QSqlDatabasePrivate::driverDict()); + QSqlDatabasePrivate::driverDict().clear(); + QSqlDatabasePrivate::cleanConnections(); + qDriverDictInit = false; +} + +DriverDict &QSqlDatabasePrivate::driverDict() +{ + static DriverDict dict; + if (!qDriverDictInit) { + qDriverDictInit = true; + qAddPostRoutine(cleanDriverDict); + } + return dict; +} + +QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null() +{ + static QSqlNullDriver dr; + static QSqlDatabasePrivate n(&dr); + return &n; +} + +void QSqlDatabasePrivate::invalidateDb(const QSqlDatabase &db, const QString &name) +{ + if (db.d->ref != 1) { + qWarning("QSqlDatabasePrivate::removeDatabase: connection '%s' is still in use, " + "all queries will cease to work.", name.toLocal8Bit().constData()); + db.d->disable(); + db.d->connName.clear(); + } +} + +void QSqlDatabasePrivate::removeDatabase(const QString &name) +{ + QConnectionDict *dict = dbDict(); + Q_ASSERT(dict); + QWriteLocker locker(&dict->lock); + + if (!dict->contains(name)) + return; + + invalidateDb(dict->take(name), name); +} + +void QSqlDatabasePrivate::addDatabase(const QSqlDatabase &db, const QString &name) +{ + QConnectionDict *dict = dbDict(); + Q_ASSERT(dict); + QWriteLocker locker(&dict->lock); + + if (dict->contains(name)) { + invalidateDb(dict->take(name), name); + qWarning("QSqlDatabasePrivate::addDatabase: duplicate connection name '%s', old " + "connection removed.", name.toLocal8Bit().data()); + } + dict->insert(name, db); + db.d->connName = name; +} + +/*! \internal +*/ +QSqlDatabase QSqlDatabasePrivate::database(const QString& name, bool open) +{ + const QConnectionDict *dict = dbDict(); + Q_ASSERT(dict); + + dict->lock.lockForRead(); + QSqlDatabase db = dict->value(name); + dict->lock.unlock(); + if (db.isValid() && !db.isOpen() && open) { + if (!db.open()) + qWarning() << "QSqlDatabasePrivate::database: unable to open database:" << db.lastError().text(); + + } + return db; +} + + +/*! \internal + Copies the connection data from \a other. +*/ +void QSqlDatabasePrivate::copy(const QSqlDatabasePrivate *other) +{ + dbname = other->dbname; + uname = other->uname; + pword = other->pword; + hname = other->hname; + drvName = other->drvName; + port = other->port; + connOptions = other->connOptions; +} + +void QSqlDatabasePrivate::disable() +{ + if (driver != shared_null()->driver) { + delete driver; + driver = shared_null()->driver; + } +} + +/*! + \class QSqlDriverCreatorBase + \brief The QSqlDriverCreatorBase class is the base class for + SQL driver factories. + + \ingroup database + \inmodule QtSql + + Reimplement createObject() to return an instance of the specific + QSqlDriver subclass that you want to provide. + + See QSqlDatabase::registerSqlDriver() for details. + + \sa QSqlDriverCreator +*/ + +/*! + \fn QSqlDriverCreatorBase::~QSqlDriverCreatorBase() + + Destroys the SQL driver creator object. +*/ + +/*! + \fn QSqlDriver *QSqlDriverCreatorBase::createObject() const + + Reimplement this function to returns a new instance of a + QSqlDriver subclass. +*/ + +/*! + \class QSqlDriverCreator + \brief The QSqlDriverCreator class is a template class that + provides a SQL driver factory for a specific driver type. + + \ingroup database + \inmodule QtSql + + QSqlDriverCreator<T> instantiates objects of type T, where T is a + QSqlDriver subclass. + + See QSqlDatabase::registerSqlDriver() for details. +*/ + +/*! + \fn QSqlDriver *QSqlDriverCreator::createObject() const + \reimp +*/ + +/*! + \class QSqlDatabase + \brief The QSqlDatabase class represents a connection to + a database. + + \ingroup database + \mainclass + \inmodule QtSql + + The QSqlDatabase class provides an interface for accessing a + database through a connection. An instance of QSqlDatabase + represents the connection. The connection provides access to the + database via one of the \l{SQL Database Drivers#Supported + Databases} {supported database drivers}, which are derived from + QSqlDriver. Alternatively, you can subclass your own database + driver from QSqlDriver. See \l{How to Write Your Own Database + Driver} for more information. + + Create a connection (i.e., an instance of QSqlDatabase) by calling + one of the static addDatabase() functions, where you specify + \l{SQL Database Drivers#Supported Databases} {the driver or type + of driver} to use (i.e., what kind of database will you access?) + and a connection name. A connection is known by its own name, + \e{not} by the name of the database it connects to. You can have + multiple connections to one database. QSqlDatabase also supports + the concept of a \e{default} connection, which is the unnamed + connection. To create the default connection, don't pass the + connection name argument when you call addDatabase(). + Subsequently, when you call any static member function that takes + the connection name argument, if you don't pass the connection + name argument, the default connection is assumed. The following + snippet shows how to create and open a default connection to a + MySQL database: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 0 + + Once the QSqlDatabase object has been created, set the connection + parameters with setDatabaseName(), setUserName(), setPassword(), + setHostName(), setPort(), and setConnectOptions(). Then call + open() to activate the physical connection to the database. The + connection is not usable until you open it. + + The connection defined above will be the \e{default} connection, + because we didn't give a connection name to \l{QSqlDatabase::} + {addDatabase()}. Subsequently, you can get the default connection + by calling database() without the connection name argument: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 1 + + QSqlDatabase is a value class. Changes made to a database + connection via one instance of QSqlDatabase will affect other + instances of QSqlDatabase that represent the same connection. Use + cloneDatabase() to create an independent database connection based + on an existing one. + + If you create multiple database connections, specify a unique + connection name for each one, when you call addDatabase(). Use + database() with a connection name to get that connection. Use + removeDatabase() with a connection name to remove a connection. + QSqlDatabase outputs a warning if you try to remove a connection + referenced by other QSqlDatabase objects. Use contains() to see if + a given connection name is in the list of connections. + + Once a connection is established, you can call tables() to get the + list of tables in the database, call primaryIndex() to get a + table's primary index, and call record() to get meta-information + about a table's fields (e.g., field names). + + \note QSqlDatabase::exec() is deprecated. Use QSqlQuery::exec() + instead. + + If the driver supports transactions, use transaction() to start a + transaction, and commit() or rollback() to complete it. Use + \l{QSqlDriver::} {hasFeature()} to ask if the driver supports + transactions. \note When using transactions, you must start the + transaction before you create your query. + + If an error occurrs, lastError() will return information about it. + + Get the names of the available SQL drivers with drivers(). Check + for the presence of a particular driver with isDriverAvailable(). + If you have created your own custom driver, you must register it + with registerSqlDriver(). + + \sa QSqlDriver, QSqlQuery, {QtSql Module}, {Threads and the SQL Module} +*/ + +/*! \fn QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName) + \threadsafe + + Adds a database to the list of database connections using the + driver \a type and the connection name \a connectionName. If + there already exists a database connection called \a + connectionName, that connection is removed. + + The database connection is referred to by \a connectionName. The + newly added database connection is returned. + + If \a connectionName is not specified, the new connection becomes + the default connection for the application, and subsequent calls + to database() without the connection name argument will return the + default connection. If a \a connectionName is provided here, use + database(\a connectionName) to retrieve the connection. + + \warning If you add a connection with the same name as an existing + connection, the new connection replaces the old one. If you call + this function more than once without specifying \a connectionName, + the default connection will be the one replaced. + + Before using the connection, it must be initialized. e.g., call + some or all of setDatabaseName(), setUserName(), setPassword(), + setHostName(), setPort(), and setConnectOptions(), and, finally, + open(). + + \sa database() removeDatabase() {Threads and the SQL Module} +*/ +QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName) +{ + QSqlDatabase db(type); + QSqlDatabasePrivate::addDatabase(db, connectionName); + return db; +} + +/*! + \threadsafe + + 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, an invalid + connection is returned. + + \sa isOpen() {Threads and the SQL Module} +*/ + +QSqlDatabase QSqlDatabase::database(const QString& connectionName, bool open) +{ + return QSqlDatabasePrivate::database(connectionName, open); +} + +/*! + \threadsafe + + 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. + + Example: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 0 + + The correct way to do it: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 1 + + To remove the default connection, which may have been created with a + call to addDatabase() not specifying a connection name, you can + retrieve the default connection name by calling connectionName() on + the database returned by database(). Note that if a default database + hasn't been created an invalid database will be returned. + + \sa database() connectionName() {Threads and the SQL Module} +*/ + +void QSqlDatabase::removeDatabase(const QString& connectionName) +{ + QSqlDatabasePrivate::removeDatabase(connectionName); +} + +/*! + Returns a list of all the available database drivers. + + \sa registerSqlDriver() +*/ + +QStringList QSqlDatabase::drivers() +{ + QStringList list; + +#ifdef QT_SQL_PSQL + list << QLatin1String("QPSQL7"); + list << QLatin1String("QPSQL"); +#endif +#ifdef QT_SQL_MYSQL + list << QLatin1String("QMYSQL3"); + list << QLatin1String("QMYSQL"); +#endif +#ifdef QT_SQL_ODBC + list << QLatin1String("QODBC3"); + list << QLatin1String("QODBC"); +#endif +#ifdef QT_SQL_OCI + list << QLatin1String("QOCI8"); + list << QLatin1String("QOCI"); +#endif +#ifdef QT_SQL_TDS + list << QLatin1String("QTDS7"); + list << QLatin1String("QTDS"); +#endif +#ifdef QT_SQL_DB2 + list << QLatin1String("QDB2"); +#endif +#ifdef QT_SQL_SQLITE + list << QLatin1String("QSQLITE"); +#endif +#ifdef QT_SQL_SQLITE2 + list << QLatin1String("QSQLITE2"); +#endif +#ifdef QT_SQL_IBASE + list << QLatin1String("QIBASE"); +#endif + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (QFactoryLoader *fl = loader()) { + QStringList keys = fl->keys(); + for (QStringList::const_iterator i = keys.constBegin(); i != keys.constEnd(); ++i) { + if (!list.contains(*i)) + list << *i; + } + } +#endif + + DriverDict dict = QSqlDatabasePrivate::driverDict(); + for (DriverDict::const_iterator i = dict.constBegin(); i != dict.constEnd(); ++i) { + if (!list.contains(i.key())) + list << i.key(); + } + + return list; +} + +/*! + 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: + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 2 + + QSqlDatabase takes ownership of the \a creator pointer, so you + mustn't delete it yourself. + + \sa drivers() +*/ +void QSqlDatabase::registerSqlDriver(const QString& name, QSqlDriverCreatorBase *creator) +{ + delete QSqlDatabasePrivate::driverDict().take(name); + if (creator) + QSqlDatabasePrivate::driverDict().insert(name, creator); +} + +/*! + \threadsafe + + Returns true if the list of database connections contains \a + connectionName; otherwise returns false. + + \sa connectionNames(), database(), {Threads and the SQL Module} +*/ + +bool QSqlDatabase::contains(const QString& connectionName) +{ + return dbDict()->contains_ts(connectionName); +} + +/*! + \threadsafe + + Returns a list containing the names of all connections. + + \sa contains(), database(), {Threads and the SQL Module} +*/ +QStringList QSqlDatabase::connectionNames() +{ + return dbDict()->keys_ts(); +} + +/*! + \overload + + Creates a QSqlDatabase connection that uses the driver referred + to by \a type. If the \a type is not recognized, the database + connection will have no functionality. + + The currently available driver types are: + + \table + \header \i Driver Type \i Description + \row \i QDB2 \i IBM DB2 + \row \i QIBASE \i Borland InterBase Driver + \row \i QMYSQL \i MySQL Driver + \row \i QOCI \i Oracle Call Interface Driver + \row \i QODBC \i ODBC Driver (includes Microsoft SQL Server) + \row \i QPSQL \i PostgreSQL Driver + \row \i QSQLITE \i SQLite version 3 or above + \row \i QSQLITE2 \i SQLite version 2 + \row \i QTDS \i Sybase Adaptive Server + \endtable + + Additional third party drivers, including your own custom + drivers, can be loaded dynamically. + + \sa {SQL Database Drivers}, registerSqlDriver(), drivers() +*/ + +QSqlDatabase::QSqlDatabase(const QString &type) +{ + d = new QSqlDatabasePrivate(); + d->init(type); +} + +/*! + \overload + + Creates a database connection using the given \a driver. +*/ + +QSqlDatabase::QSqlDatabase(QSqlDriver *driver) +{ + d = new QSqlDatabasePrivate(driver); +} + +/*! + Creates an empty, invalid QSqlDatabase object. Use addDatabase(), + removeDatabase(), and database() to get valid QSqlDatabase + objects. +*/ +QSqlDatabase::QSqlDatabase() +{ + d = QSqlDatabasePrivate::shared_null(); + d->ref.ref(); +} + +/*! + Creates a copy of \a other. +*/ +QSqlDatabase::QSqlDatabase(const QSqlDatabase &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Assigns \a other to this object. +*/ +QSqlDatabase &QSqlDatabase::operator=(const QSqlDatabase &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + \internal + + Create the actual driver instance \a type. +*/ + +void QSqlDatabasePrivate::init(const QString &type) +{ + drvName = type; + + if (!driver) { +#ifdef QT_SQL_PSQL + if (type == QLatin1String("QPSQL") || type == QLatin1String("QPSQL7")) + driver = new QPSQLDriver(); +#endif +#ifdef QT_SQL_MYSQL + if (type == QLatin1String("QMYSQL") || type == QLatin1String("QMYSQL3")) + driver = new QMYSQLDriver(); +#endif +#ifdef QT_SQL_ODBC + if (type == QLatin1String("QODBC") || type == QLatin1String("QODBC3")) + driver = new QODBCDriver(); +#endif +#ifdef QT_SQL_OCI + if (type == QLatin1String("QOCI") || type == QLatin1String("QOCI8")) + driver = new QOCIDriver(); +#endif +#ifdef QT_SQL_TDS + if (type == QLatin1String("QTDS") || type == QLatin1String("QTDS7")) + driver = new QTDSDriver(); +#endif +#ifdef QT_SQL_DB2 + if (type == QLatin1String("QDB2")) + driver = new QDB2Driver(); +#endif +#ifdef QT_SQL_SQLITE + if (type == QLatin1String("QSQLITE")) + driver = new QSQLiteDriver(); +#endif +#ifdef QT_SQL_SQLITE2 + if (type == QLatin1String("QSQLITE2")) + driver = new QSQLite2Driver(); +#endif +#ifdef QT_SQL_IBASE + if (type == QLatin1String("QIBASE")) + driver = new QIBaseDriver(); +#endif + } + + if (!driver) { + DriverDict dict = QSqlDatabasePrivate::driverDict(); + for (DriverDict::const_iterator it = dict.constBegin(); + it != dict.constEnd() && !driver; ++it) { + if (type == it.key()) { + driver = ((QSqlDriverCreatorBase*)(*it))->createObject(); + } + } + } + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (!driver && loader()) { + if (QSqlDriverFactoryInterface *factory = qobject_cast<QSqlDriverFactoryInterface*>(loader()->instance(type))) + driver = factory->create(type); + } +#endif // QT_NO_LIBRARY + + if (!driver) { + qWarning("QSqlDatabase: %s driver not loaded", type.toLatin1().data()); + qWarning("QSqlDatabase: available drivers: %s", + QSqlDatabase::drivers().join(QLatin1String(" ")).toLatin1().data()); + if (QCoreApplication::instance() == 0) + qWarning("QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins"); + driver = shared_null()->driver; + } +} + +/*! + Destroys the object and frees any allocated resources. + + If this is the last QSqlDatabase object that uses a certain + database connection, the is automatically closed. + + \sa close() +*/ + +QSqlDatabase::~QSqlDatabase() +{ + if (!d->ref.deref()) { + close(); + delete d; + } +} + +/*! + Executes a SQL statement on the database and returns a QSqlQuery + object. Use lastError() to retrieve error information. If \a + query is empty, 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->createResult()); + if (!query.isEmpty()) { + 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 lastError(). + + \sa lastError() setDatabaseName() setUserName() setPassword() + \sa setHostName() setPort() setConnectOptions() +*/ + +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 the + connection and it 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, and + invalidating any existing QSqlQuery objects that are used with the + database. + + This will also affect copies of this QSqlDatabase object. + + \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 \c{true} if the operation succeeded. + Otherwise it returns \c{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 and a transaction() has been started. Returns \c{true} + if the operation succeeded. Otherwise it returns \c{false}. + + \note For some databases, the commit will fail and return \c{false} + if there is an \l{QSqlQuery::isActive()} {active query} using the + database for a \c{SELECT}. Make the query \l{QSqlQuery::isActive()} + {inactive} before doing the commit. + + Call lastError() to get information about errors. + + \sa QSqlQuery::isActive() QSqlDriver::hasFeature() rollback() +*/ +bool QSqlDatabase::commit() +{ + if (!d->driver->hasFeature(QSqlDriver::Transactions)) + return false; + return d->driver->commitTransaction(); +} + +/*! + Rolls back a transaction on the database, if the driver supports + transactions and a transaction() has been started. Returns \c{true} + if the operation succeeded. Otherwise it returns \c{false}. + + \note For some databases, the rollback will fail and return + \c{false} if there is an \l{QSqlQuery::isActive()} {active query} + using the database for a \c{SELECT}. Make the query + \l{QSqlQuery::isActive()} {inactive} before doing the rollback. + + Call lastError() to get information about errors. + + \sa QSqlQuery::isActive() QSqlDriver::hasFeature() commit() +*/ +bool QSqlDatabase::rollback() +{ + if (!d->driver->hasFeature(QSqlDriver::Transactions)) + return false; + return d->driver->rollbackTransaction(); +} + +/*! + Sets the connection's database name to \a name. To have effect, + the database name must be set \e{before} the connection is + \l{open()} {opened}. Alternatively, you can close() the + connection, set the database name, and call open() again. \note + The \e{database name} is not the \e{connection name}. The + connection name must be passed to addDatabase() at connection + object create time. + + For the QOCI (Oracle) driver, the database name is the TNS + Service Name. + + For the QODBC driver, the \a name can either be a DSN, a DSN + filename (in which case the file must have a \c .dsn extension), + or a connection string. + + For example, Microsoft Access users can use the following + connection string to open an \c .mdb file directly, instead of + having to create a DSN entry in the ODBC manager: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 3 + + There is no default value. + + \sa databaseName() setUserName() setPassword() setHostName() + \sa setPort() setConnectOptions() open() +*/ + +void QSqlDatabase::setDatabaseName(const QString& name) +{ + if (isValid()) + d->dbname = name; +} + +/*! + Sets the connection's user name to \a name. To have effect, the + user name must be set \e{before} the connection is \l{open()} + {opened}. Alternatively, you can close() the connection, set the + user name, and call open() again. + + There is no default value. + + \sa userName() setDatabaseName() setPassword() setHostName() + \sa setPort() setConnectOptions() open() +*/ + +void QSqlDatabase::setUserName(const QString& name) +{ + if (isValid()) + d->uname = name; +} + +/*! + Sets the connection's password to \a password. To have effect, the + password must be set \e{before} the connection is \l{open()} + {opened}. Alternatively, you can close() the connection, set the + password, and call open() again. + + 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 behavior. + + \sa password() setUserName() setDatabaseName() setHostName() + \sa setPort() setConnectOptions() open() +*/ + +void QSqlDatabase::setPassword(const QString& password) +{ + if (isValid()) + d->pword = password; +} + +/*! + Sets the connection's host name to \a host. To have effect, the + host name must be set \e{before} the connection is \l{open()} + {opened}. Alternatively, you can close() the connection, set the + host name, and call open() again. + + There is no default value. + + \sa hostName() setUserName() setPassword() setDatabaseName() + \sa setPort() setConnectOptions() open() +*/ + +void QSqlDatabase::setHostName(const QString& host) +{ + if (isValid()) + d->hname = host; +} + +/*! + Sets the connection's port number to \a port. To have effect, the + port number must be set \e{before} the connection is \l{open()} + {opened}. Alternatively, you can close() the connection, set the + port number, and call open() again.. + + There is no default value. + + \sa port() setUserName() setPassword() setHostName() + \sa setDatabaseName() setConnectOptions() open() +*/ + +void QSqlDatabase::setPort(int port) +{ + if (isValid()) + d->port = port; +} + +/*! + Returns the connection's database name, which may be empty. + \note The database name is not the connection name. + + \sa setDatabaseName() +*/ +QString QSqlDatabase::databaseName() const +{ + return d->dbname; +} + +/*! + Returns the connection's user name; it may be empty. + + \sa setUserName() +*/ +QString QSqlDatabase::userName() const +{ + return d->uname; +} + +/*! + Returns the connection's password. If the password was not set + with setPassword(), and if the password was given in the open() + call, or if no password was used, an empty string is returned. +*/ +QString QSqlDatabase::password() const +{ + return d->pword; +} + +/*! + Returns the connection's host name; it may be empty. + + \sa setHostName() +*/ +QString QSqlDatabase::hostName() const +{ + return d->hname; +} + +/*! + Returns the connection's driver name. + + \sa addDatabase(), driver() +*/ +QString QSqlDatabase::driverName() const +{ + return d->drvName; +} + +/*! + Returns the connection's port number. The value is undefined if + the port number has not been set. + + \sa setPort() +*/ +int QSqlDatabase::port() const +{ + return d->port; +} + +/*! + Returns the database driver used to access the database + connection. + + \sa addDatabase() drivers() +*/ + +QSqlDriver* QSqlDatabase::driver() const +{ + return d->driver; +} + +/*! + Returns information about the last error that occurred on the + database. + + Failures that occur in conjunction with an individual query are + reported by QSqlQuery::lastError(). + + \sa QSqlError, QSqlQuery::lastError() +*/ + +QSqlError QSqlDatabase::lastError() const +{ + return d->driver->lastError(); +} + + +/*! + Returns a list of the database's tables, system tables and views, + as specified by the parameter \a type. + + \sa primaryIndex(), record() +*/ + +QStringList QSqlDatabase::tables(QSql::TableType type) const +{ + return d->driver->tables(type); +} + +/*! + Returns the primary index for table \a tablename. If no primary + index exists an empty QSqlIndex is returned. + + \sa tables(), record() +*/ + +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. +*/ + +QSqlRecord QSqlDatabase::record(const QString& tablename) const +{ + return d->driver->record(tablename); +} + + +/*! + Sets database-specific \a options. This must be done before the + connection is opened or it has no effect (or you can close() the + connection, call this function and open() the connection again). + + The format of the \a options string is a semicolon 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 + \i SQL_ATTR_CONNECTION_POOLING + \i SQL_ATTR_ODBC_VERSION + \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 + \i UNIX_SOCKET + \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 + \list + \i OCI_ATTR_PREFETCH_ROWS + \i OCI_ATTR_PREFETCH_MEMORY + \endlist + + \i + \e none + + \header \i SQLite \i Interbase + \row + + \i + \list + \i QSQLITE_BUSY_TIMEOUT + \endlist + + \i + \list + \i ISC_DPB_LC_CTYPE + \i ISC_DPB_SQL_ROLE_NAME + \endlist + + \endtable + + Examples: + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 4 + + Refer to the client library documentation for more information + about the different options. + + \sa connectOptions() +*/ + +void QSqlDatabase::setConnectOptions(const QString &options) +{ + if (isValid()) + d->connOptions = options; +} + +/*! + Returns the connection options string used for this connection. + The string may be empty. + + \sa setConnectOptions() + */ +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) +{ + return drivers().contains(name); +} + +/*! \fn QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver* driver, const QString& connectionName) + + This overload is useful when you want to create a database + connection with a \l{QSqlDriver} {driver} you instantiated + yourself. It might be your own database driver, or you might just + need to instantiate one of the Qt drivers yourself. If you do + this, it is recommended that you include the driver code in your + application. For example, you can create a PostgreSQL connection + with your own QPSQL driver like this: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 5 + \codeline + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 6 + + 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. + + \note 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. + + Remember that you must link your application against the database + client library. Make sure the client library is in your linker's + search path, and add lines like these to your \c{.pro} file: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 7 + + The method described works for all the supplied drivers. The only + difference will be in the driver constructor arguments. Here is a + table of the drivers included with Qt, their source code files, + and their constructor arguments: + + \table + \header \i Driver \i Class name \i Constructor arguments \i File to include + \row + \i QPSQL + \i QPSQLDriver + \i PGconn *connection + \i \c qsql_psql.cpp + \row + \i QMYSQL + \i QMYSQLDriver + \i MYSQL *connection + \i \c qsql_mysql.cpp + \row + \i QOCI + \i QOCIDriver + \i OCIEnv *environment, OCISvcCtx *serviceContext + \i \c qsql_oci.cpp + \row + \i QODBC + \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 QTDS + \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 + + The host name (or service name) is needed when constructing the + QTDSDriver for creating new connections for internal queries. This + is to prevent blocking when several QSqlQuery objects are used + simultaneously. + + \warning Adding a database connection with the same connection + name as an existing connection, causes the existing connection to + be replaced by the new one. + + \warning The SQL framework takes ownership of the \a driver. It + must not be deleted. To remove the connection, use + removeDatabase(). + + \sa drivers() +*/ +QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver* driver, const QString& connectionName) +{ + QSqlDatabase db(driver); + QSqlDatabasePrivate::addDatabase(db, connectionName); + return db; +} + +/*! + Returns true if the QSqlDatabase has a valid driver. + + Example: + \snippet doc/src/snippets/code/src_sql_kernel_qsqldatabase.cpp 8 +*/ +bool QSqlDatabase::isValid() const +{ + return d->driver && d->driver != d->shared_null()->driver; +} + +#ifdef QT3_SUPPORT +/*! + Use query.record() instead. +*/ +QSqlRecord QSqlDatabase::record(const QSqlQuery& query) const +{ return query.record(); } + +/*! + Use query.record() instead. +*/ +QSqlRecord QSqlDatabase::recordInfo(const QSqlQuery& query) const +{ return query.record(); } + +/*! + \fn QSqlRecord QSqlDatabase::recordInfo(const QString& tablename) const + + Use record() instead. +*/ +#endif + +/*! + Clones the database connection \a other and and stores it as \a + connectionName. All the settings from the original database, e.g. + databaseName(), hostName(), etc., are copied across. Does nothing + if \a other is an invalid database. Returns the newly created + database connection. + + \note The new connection has not been opened. Before using the new + connection, you must call open(). +*/ +QSqlDatabase QSqlDatabase::cloneDatabase(const QSqlDatabase &other, const QString &connectionName) +{ + if (!other.isValid()) + return QSqlDatabase(); + + QSqlDatabase db(other.driverName()); + db.d->copy(other.d); + QSqlDatabasePrivate::addDatabase(db, connectionName); + return db; +} + +/*! + \since 4.4 + + Returns the connection name, which may be empty. \note The + connection name is not the \l{databaseName()} {database name}. + + \sa addDatabase() +*/ +QString QSqlDatabase::connectionName() const +{ + return d->connName; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QSqlDatabase &d) +{ + if (!d.isValid()) { + dbg.nospace() << "QSqlDatabase(invalid)"; + return dbg.space(); + } + + dbg.nospace() << "QSqlDatabase(driver=\"" << d.driverName() << "\", database=\"" + << d.databaseName() << "\", host=\"" << d.hostName() << "\", port=" << d.port() + << ", user=\"" << d.userName() << "\", open=" << d.isOpen() << ")"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqldatabase.h b/src/sql/kernel/qsqldatabase.h new file mode 100644 index 0000000..ca6f0b0 --- /dev/null +++ b/src/sql/kernel/qsqldatabase.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLDATABASE_H +#define QSQLDATABASE_H + +#include <QtCore/qstring.h> +#include <QtSql/qsql.h> +#ifdef QT3_SUPPORT +#include <QtSql/qsqlrecord.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QSqlError; +class QSqlDriver; +class QSqlIndex; +class QSqlRecord; +class QSqlQuery; +class QSqlDatabasePrivate; + +class Q_SQL_EXPORT QSqlDriverCreatorBase +{ +public: + virtual ~QSqlDriverCreatorBase() {} + virtual QSqlDriver *createObject() const = 0; +}; + +template <class T> +class QSqlDriverCreator : public QSqlDriverCreatorBase +{ +public: + QSqlDriver *createObject() const { return new T; } +}; + +class Q_SQL_EXPORT QSqlDatabase +{ +public: + QSqlDatabase(); + QSqlDatabase(const QSqlDatabase &other); + ~QSqlDatabase(); + + QSqlDatabase &operator=(const QSqlDatabase &other); + + bool open(); + bool open(const QString& user, const QString& password); + void close(); + bool isOpen() const; + bool isOpenError() const; + QStringList tables(QSql::TableType type = QSql::Tables) const; + QSqlIndex primaryIndex(const QString& tablename) const; + QSqlRecord record(const QString& tablename) const; +#ifdef QT3_SUPPORT + QT3_SUPPORT QSqlRecord record(const QSqlQuery& query) const; + inline QT3_SUPPORT QSqlRecord recordInfo(const QString& tablename) const + { return record(tablename); } + QT3_SUPPORT QSqlRecord recordInfo(const QSqlQuery& query) const; +#endif + QSqlQuery exec(const QString& query = QString()) const; + QSqlError lastError() const; + bool isValid() const; + + bool transaction(); + bool commit(); + bool rollback(); + + void setDatabaseName(const QString& name); + void setUserName(const QString& name); + void setPassword(const QString& password); + void setHostName(const QString& host); + void setPort(int p); + void setConnectOptions(const QString& options = QString()); + QString databaseName() const; + QString userName() const; + QString password() const; + QString hostName() const; + QString driverName() const; + int port() const; + QString connectOptions() const; + QString connectionName() const; + + QSqlDriver* driver() const; + + QT_STATIC_CONST char *defaultConnection; + + static QSqlDatabase addDatabase(const QString& type, + const QString& connectionName = QLatin1String(defaultConnection)); + static QSqlDatabase addDatabase(QSqlDriver* driver, + const QString& connectionName = QLatin1String(defaultConnection)); + static QSqlDatabase cloneDatabase(const QSqlDatabase &other, const QString& connectionName); + static QSqlDatabase database(const QString& connectionName = QLatin1String(defaultConnection), + bool open = true); + static void removeDatabase(const QString& connectionName); + static bool contains(const QString& connectionName = QLatin1String(defaultConnection)); + static QStringList drivers(); + static QStringList connectionNames(); + static void registerSqlDriver(const QString &name, QSqlDriverCreatorBase *creator); + static bool isDriverAvailable(const QString &name); + +protected: + explicit QSqlDatabase(const QString& type); + explicit QSqlDatabase(QSqlDriver* driver); + +private: + friend class QSqlDatabasePrivate; + QSqlDatabasePrivate *d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_SQL_EXPORT QDebug operator<<(QDebug, const QSqlDatabase &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLDATABASE_H diff --git a/src/sql/kernel/qsqldriver.cpp b/src/sql/kernel/qsqldriver.cpp new file mode 100644 index 0000000..ddebe45 --- /dev/null +++ b/src/sql/kernel/qsqldriver.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqldriver.h" + +#include "qdatetime.h" +#include "qsqlerror.h" +#include "qsqlfield.h" +#include "qsqlindex.h" +#include "private/qobject_p.h" + +QT_BEGIN_NAMESPACE + +class QSqlDriverPrivate : public QObjectPrivate +{ +public: + QSqlDriverPrivate(); + virtual ~QSqlDriverPrivate(); + +public: + // @CHECK: this member is never used. It was named q, which expanded to q_func(). + QSqlDriver *q_func(); + uint isOpen : 1; + uint isOpenError : 1; + QSqlError error; +}; + +inline QSqlDriverPrivate::QSqlDriverPrivate() + : QObjectPrivate(), isOpen(false), isOpenError(false) +{ +} + +QSqlDriverPrivate::~QSqlDriverPrivate() +{ +} + +/*! + \class QSqlDriver + \brief The QSqlDriver class is an abstract base class for accessing + specific SQL databases. + + \ingroup database + \inmodule QtSql + + This class should not be used directly. Use QSqlDatabase instead. + + If you want to create your own SQL drivers, you can subclass this + class and reimplement its pure virtual functions and those + virtual functions that you need. See \l{How to Write Your Own + Database Driver} for more information. + + \sa QSqlDatabase, QSqlResult +*/ + +/*! + Constructs a new driver with the given \a parent. +*/ + +QSqlDriver::QSqlDriver(QObject *parent) + : QObject(*new QSqlDriverPrivate, parent) +{ +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QSqlDriver::~QSqlDriver() +{ +} + +/*! + \since 4.4 + + \fn QSqlDriver::notification(const QString &name) + + This signal is emitted when the database posts an event notification + that the driver subscribes to. \a name identifies the event notification. + + \sa subscribeToNotification() +*/ + +/*! + \fn bool QSqlDriver::open(const QString &db, const QString &user, const QString& password, + const QString &host, int port, const QString &options) + + Derived classes must reimplement this pure virtual function to + 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 options. + + The function must return true on success and false on failure. + + \sa setOpen() +*/ + +/*! + \fn bool QSqlDriver::close() + + Derived classes must reimplement this pure virtual function in + order to close the database connection. Return true on success, + false on failure. + + \sa open(), setOpen() +*/ + +/*! + \fn QSqlResult *QSqlDriver::createResult() const + + Creates an empty SQL result on the database. Derived classes must + reimplement this function and return a QSqlResult object + appropriate for their database to the caller. +*/ + +/*! + Returns true if the database connection is open; otherwise returns + false. +*/ + +bool QSqlDriver::isOpen() const +{ + return d_func()->isOpen; +} + +/*! + Returns true if the there was an error opening the database + connection; otherwise returns false. +*/ + +bool QSqlDriver::isOpenError() const +{ + return d_func()->isOpenError; +} + +/*! + \enum QSqlDriver::DriverFeature + + This enum contains a list of features a driver might 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 the use of named placeholders. + \value PositionalPlaceholders Whether the driver supports the use of positional placeholders. + \value LastInsertId Whether the driver supports returning the Id of the last touched row. + \value BatchOperations Whether the driver supports batched operations, see QSqlQuery::execBatch() + \value SimpleLocking Whether the driver disallows a write lock on a table while other queries have a read lock on it. + \value LowPrecisionNumbers Whether the driver allows fetching numerical values with low precision. + \value EventNotifications Whether the driver supports database event notifications. + \value FinishQuery Whether the driver can do any low-level resource cleanup when QSqlQuery::finish() is called. + \value MultipleResultSets Whether the driver can access multiple result sets returned from batched statements or stored procedures. + + More information about supported features can be found in the + \l{sql-driver.html}{Qt SQL driver} documentation. + + \sa hasFeature() +*/ + +/*! + \enum QSqlDriver::StatementType + + This enum contains a list of SQL statement (or clause) types the + driver can create. + + \value WhereStatement An SQL \c WHERE statement (e.g., \c{WHERE f = 5}). + \value SelectStatement An SQL \c SELECT statement (e.g., \c{SELECT f FROM t}). + \value UpdateStatement An SQL \c UPDATE statement (e.g., \c{UPDATE TABLE t set f = 1}). + \value InsertStatement An SQL \c INSERT statement (e.g., \c{INSERT INTO t (f) values (1)}). + \value DeleteStatement An SQL \c DELETE statement (e.g., \c{DELETE FROM t}). + + \sa sqlStatement() +*/ + +/*! + \enum QSqlDriver::IdentifierType + + This enum contains a list of SQL identifier types. + + \value FieldName A SQL field name + \value TableName A SQL table name +*/ + +/*! + \fn bool QSqlDriver::hasFeature(DriverFeature feature) const + + Returns true if the driver supports feature \a feature; otherwise + returns false. + + Note that some databases need to be open() before this can be + determined. + + \sa DriverFeature +*/ + +/*! + This function sets the open state of the database to \a open. + Derived classes can use this function to report the status of + open(). + + \sa open(), setOpenError() +*/ + +void QSqlDriver::setOpen(bool open) +{ + d_func()->isOpen = open; +} + +/*! + This function sets the open error state of the database to \a + error. Derived classes can use this function to report the status + of open(). Note that if \a error is true the open state of the + database is set to closed (i.e., isOpen() returns false). + + \sa open(), setOpen() +*/ + +void QSqlDriver::setOpenError(bool error) +{ + d_func()->isOpenError = error; + if (error) + d_func()->isOpen = false; +} + +/*! + This function is called to begin a transaction. If successful, + return true, otherwise return false. The default implementation + does nothing and returns false. + + \sa commitTransaction(), rollbackTransaction() +*/ + +bool QSqlDriver::beginTransaction() +{ + return false; +} + +/*! + This function is called to commit a transaction. If successful, + return true, otherwise return false. The default implementation + does nothing and returns false. + + \sa beginTransaction(), rollbackTransaction() +*/ + +bool QSqlDriver::commitTransaction() +{ + return false; +} + +/*! + This function is called to rollback a transaction. If successful, + return true, otherwise return false. The default implementation + does nothing and returns false. + + \sa beginTransaction(), commitTransaction() +*/ + +bool QSqlDriver::rollbackTransaction() +{ + return false; +} + +/*! + This function is used to set the value of the last error, \a error, + that occurred on the database. + + \sa lastError() +*/ + +void QSqlDriver::setLastError(const QSqlError &error) +{ + d_func()->error = error; +} + +/*! + Returns a QSqlError object which contains information about the + last error that occurred on the database. +*/ + +QSqlError QSqlDriver::lastError() const +{ + return d_func()->error; +} + +/*! + Returns a list of the names of the 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 + backward compatibility. +*/ + +QStringList QSqlDriver::tables(QSql::TableType) 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 & /* tableName */) const +{ + return QSqlRecord(); +} + +/*! + Returns the \a identifier escaped according to the database rules. + \a identifier can either be a table name or field name, dependent + on \a type. + + The default implementation does nothing. + */ +QString QSqlDriver::escapeIdentifier(const QString &identifier, IdentifierType) const +{ + return identifier; +} + +/*! + Returns a SQL statement of type \a type for the table \a tableName + with the values from \a rec. If \a preparedStatement is true, the + string will contain placeholders instead of values. + + This method can be used to manipulate tables without having to worry + about database-dependent SQL dialects. For non-prepared statements, + the values will be properly escaped. +*/ +QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName, + const QSqlRecord &rec, bool preparedStatement) const +{ + int i; + QString s; + s.reserve(128); + switch (type) { + case SelectStatement: + for (i = 0; i < rec.count(); ++i) { + if (rec.isGenerated(i)) + s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1String(", ")); + } + if (s.isEmpty()) + return s; + s.chop(2); + s.prepend(QLatin1String("SELECT ")).append(QLatin1String(" FROM ")).append(escapeIdentifier(tableName, TableName)); + break; + case WhereStatement: + if (preparedStatement) { + for (int i = 0; i < rec.count(); ++i) + s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append( + QLatin1String(" = ? AND ")); + } else { + for (i = 0; i < rec.count(); ++i) { + s.append(escapeIdentifier(rec.fieldName(i), FieldName)); + QString val = formatValue(rec.field(i)); + if (val == QLatin1String("NULL")) + s.append(QLatin1String(" IS NULL")); + else + s.append(QLatin1String(" = ")).append(val); + s.append(QLatin1String(" AND ")); + } + } + if (!s.isEmpty()) { + s.prepend(QLatin1String("WHERE ")); + s.chop(5); // remove tailing AND + } + break; + case UpdateStatement: + s.append(QLatin1String("UPDATE ")).append(escapeIdentifier(tableName, TableName)).append( + QLatin1String(" SET ")); + for (i = 0; i < rec.count(); ++i) { + if (!rec.isGenerated(i) || !rec.value(i).isValid()) + continue; + s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1Char('=')); + if (preparedStatement) + s.append(QLatin1Char('?')); + else + s.append(formatValue(rec.field(i))); + s.append(QLatin1String(", ")); + } + if (s.endsWith(QLatin1String(", "))) + s.chop(2); + else + s.clear(); + break; + case DeleteStatement: + s.append(QLatin1String("DELETE FROM ")).append(escapeIdentifier(tableName, TableName)); + break; + case InsertStatement: { + s.append(QLatin1String("INSERT INTO ")).append(escapeIdentifier(tableName, TableName)).append(QLatin1String(" (")); + QString vals; + for (i = 0; i < rec.count(); ++i) { + if (!rec.isGenerated(i) || !rec.value(i).isValid()) + continue; + s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1String(", ")); + if (preparedStatement) + vals.append(QLatin1String("?")); + else + vals.append(formatValue(rec.field(i))); + vals.append(QLatin1String(", ")); + } + if (vals.isEmpty()) { + s.clear(); + } else { + vals.chop(2); // remove trailing comma + s[s.length() - 2] = QLatin1Char(')'); + s.append(QLatin1String("VALUES (")).append(vals).append(QLatin1String(")")); + } + break; } + } + return s; +} + +/*! + 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 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, "NULL" is returned. + + \i If \a field is \link QByteArray bytearray\endlink data, and the + driver can edit binary fields, the value is formatted as a + hexadecimal string. + + \i For any other field type, toString() is called on its value + and the result of this is returned. + + \endlist + + \sa QVariant::toString() + +*/ +QString QSqlDriver::formatValue(const QSqlField &field, bool trimStrings) const +{ + const QLatin1String nullTxt("NULL"); + + QString r; + if (field.isNull()) + r = nullTxt; + else { + switch (field.type()) { + case QVariant::Int: + case QVariant::UInt: + if (field.value().type() == QVariant::Bool) + r = field.value().toBool() ? QLatin1String("1") : QLatin1String("0"); + else + r = field.value().toString(); + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: + if (field.value().toDate().isValid()) + r = QLatin1Char('\'') + field.value().toDate().toString(Qt::ISODate) + + QLatin1Char('\''); + else + r = nullTxt; + break; + case QVariant::Time: + if (field.value().toTime().isValid()) + r = QLatin1Char('\'') + field.value().toTime().toString(Qt::ISODate) + + QLatin1Char('\''); + else + r = nullTxt; + break; + case QVariant::DateTime: + if (field.value().toDateTime().isValid()) + r = QLatin1Char('\'') + + field.value().toDateTime().toString(Qt::ISODate) + QLatin1Char('\''); + else + r = nullTxt; + break; +#endif + case QVariant::String: + case QVariant::Char: + { + QString result = field.value().toString(); + if (trimStrings) { + int end = result.length(); + while (end && result.at(end-1).isSpace()) /* skip white space from end */ + end--; + result.truncate(end); + } + /* escape the "'" character */ + result.replace(QLatin1Char('\''), QLatin1String("''")); + r = QLatin1Char('\'') + result + QLatin1Char('\''); + break; + } + case QVariant::Bool: + if (field.value().toBool()) + r = QLatin1String("1"); + else + r = QLatin1String("0"); + break; + case QVariant::ByteArray : { + if (hasFeature(BLOB)) { + QByteArray ba = field.value().toByteArray(); + QString res; + static const char hexchars[] = "0123456789abcdef"; + for (int i = 0; i < ba.size(); ++i) { + uchar s = (uchar) ba[i]; + res += QLatin1Char(hexchars[s >> 4]); + res += QLatin1Char(hexchars[s & 0x0f]); + } + r = QLatin1Char('\'') + res + QLatin1Char('\''); + break; + } + } + default: + r = field.value().toString(); + break; + } + } + return r; +} + +/*! + Returns the low-level database handle wrapped in a QVariant or an + invalid variant if there is no handle. + + \warning Use this with uttermost care and only if you know what you're doing. + + \warning The handle returned here can become a stale pointer if the connection + is modified (for example, if you close the connection). + + \warning The handle can be NULL if the connection is not open yet. + + The handle returned here is database-dependent, you should query the type + name of the variant before accessing it. + + This example retrieves the handle for a connection to sqlite: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldriver.cpp 0 + + This snippet returns the handle for PostgreSQL or MySQL: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqldriver.cpp 1 + + \sa QSqlResult::handle() +*/ +QVariant QSqlDriver::handle() const +{ + return QVariant(); +} + +/*! + \fn QSqlRecord QSqlDriver::record(const QSqlQuery& query) const + + Use query.record() instead. +*/ + +/*! + \fn QSqlRecord QSqlDriver::recordInfo(const QString& tablename) const + + Use record() instead. +*/ + +/*! + \fn QSqlRecord QSqlDriver::recordInfo(const QSqlQuery& query) const + + Use query.record() instead. +*/ + +/*! + \fn QString QSqlDriver::nullText() const + + sqlStatement() is now used to generate SQL. Use tr("NULL") for example, instead. +*/ + +/*! + \fn QString QSqlDriver::formatValue(const QSqlField *field, bool trimStrings) const + + Use the other formatValue() overload instead. +*/ + +/*! + This function is called to subscribe to event notifications from the database. + \a name identifies the event notification. + + If successful, return true, otherwise return false. + + The database must be open when this function is called. When the database is closed + by calling close() all subscribed event notifications are automatically unsubscribed. + Note that calling open() on an already open database may implicitly cause close() to + be called, which will cause the driver to unsubscribe from all event notifications. + + When an event notification identified by \a name is posted by the database the + notification() signal is emitted. + + \warning Because of binary compatibility constraints, this function is not virtual. + If you want to provide event notification support in your own QSqlDriver subclass, + reimplement the subscribeToNotificationImplementation() slot in your subclass instead. + The subscribeToNotification() function will dynamically detect the slot and call it. + + \since 4.4 + \sa unsubscribeFromNotification() subscribedToNotifications() QSqlDriver::hasFeature() +*/ +bool QSqlDriver::subscribeToNotification(const QString &name) +{ + bool result; + QMetaObject::invokeMethod(const_cast<QSqlDriver *>(this), + "subscribeToNotificationImplementation", Qt::DirectConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(QString, name)); + return result; +} + +/*! + This function is called to unsubscribe from event notifications from the database. + \a name identifies the event notification. + + If successful, return true, otherwise return false. + + The database must be open when this function is called. All subscribed event + notifications are automatically unsubscribed from when the close() function is called. + + After calling \e this function the notification() signal will no longer be emitted + when an event notification identified by \a name is posted by the database. + + \warning Because of binary compatibility constraints, this function is not virtual. + If you want to provide event notification support in your own QSqlDriver subclass, + reimplement the unsubscribeFromNotificationImplementation() slot in your subclass instead. + The unsubscribeFromNotification() function will dynamically detect the slot and call it. + + \since 4.4 + \sa subscribeToNotification() subscribedToNotifications() +*/ +bool QSqlDriver::unsubscribeFromNotification(const QString &name) +{ + bool result; + QMetaObject::invokeMethod(const_cast<QSqlDriver *>(this), + "unsubscribeFromNotificationImplementation", Qt::DirectConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(QString, name)); + return result; +} + +/*! + Returns a list of the names of the event notifications that are currently subscribed to. + + \warning Because of binary compatibility constraints, this function is not virtual. + If you want to provide event notification support in your own QSqlDriver subclass, + reimplement the subscribedToNotificationsImplementation() slot in your subclass instead. + The subscribedToNotifications() function will dynamically detect the slot and call it. + + \since 4.4 + \sa subscribeToNotification() unsubscribeFromNotification() +*/ +QStringList QSqlDriver::subscribedToNotifications() const +{ + QStringList result; + QMetaObject::invokeMethod(const_cast<QSqlDriver *>(this), + "subscribedToNotificationsImplementation", Qt::DirectConnection, + Q_RETURN_ARG(QStringList, result)); + return result; +} + +/*! + This slot is called to subscribe to event notifications from the database. + \a name identifies the event notification. + + If successful, return true, otherwise return false. + + The database must be open when this \e slot is called. When the database is closed + by calling close() all subscribed event notifications are automatically unsubscribed. + Note that calling open() on an already open database may implicitly cause close() to + be called, which will cause the driver to unsubscribe from all event notifications. + + When an event notification identified by \a name is posted by the database the + notification() signal is emitted. + + Reimplement this slot to provide your own QSqlDriver subclass with event notification + support; because of binary compatibility constraints, the subscribeToNotification() + function (introduced in Qt 4.4) is not virtual. Instead, subscribeToNotification() + will dynamically detect and call \e this slot. The default implementation does nothing + and returns false. + + \since 4.4 + \sa subscribeToNotification() +*/ +bool QSqlDriver::subscribeToNotificationImplementation(const QString &name) +{ + Q_UNUSED(name); + return false; +} + +/*! + This slot is called to unsubscribe from event notifications from the database. + \a name identifies the event notification. + + If successful, return true, otherwise return false. + + The database must be open when \e this slot is called. All subscribed event + notifications are automatically unsubscribed from when the close() function is called. + + After calling \e this slot the notification() signal will no longer be emitted + when an event notification identified by \a name is posted by the database. + + Reimplement this slot to provide your own QSqlDriver subclass with event notification + support; because of binary compatibility constraints, the unsubscribeFromNotification() + function (introduced in Qt 4.4) is not virtual. Instead, unsubscribeFromNotification() + will dynamically detect and call \e this slot. The default implementation does nothing + and returns false. + + \since 4.4 + \sa unsubscribeFromNotification() +*/ +bool QSqlDriver::unsubscribeFromNotificationImplementation(const QString &name) +{ + Q_UNUSED(name); + return false; +} + +/*! + Returns a list of the names of the event notifications that are currently subscribed to. + + Reimplement this slot to provide your own QSqlDriver subclass with event notification + support; because of binary compatibility constraints, the subscribedToNotifications() + function (introduced in Qt 4.4) is not virtual. Instead, subscribedToNotifications() + will dynamically detect and call \e this slot. The default implementation simply + returns an empty QStringList. + + \since 4.4 + \sa subscribedToNotifications() +*/ +QStringList QSqlDriver::subscribedToNotificationsImplementation() const +{ + return QStringList(); +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqldriver.h b/src/sql/kernel/qsqldriver.h new file mode 100644 index 0000000..e763719 --- /dev/null +++ b/src/sql/kernel/qsqldriver.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLDRIVER_H +#define QSQLDRIVER_H + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtSql/qsql.h> +#ifdef QT3_SUPPORT +#include <QtSql/qsqlquery.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QSqlDatabase; +class QSqlDriverPrivate; +class QSqlError; +class QSqlField; +class QSqlIndex; +class QSqlRecord; +class QSqlResult; +class QVariant; + +class Q_SQL_EXPORT QSqlDriver : public QObject +{ + friend class QSqlDatabase; + Q_OBJECT + Q_DECLARE_PRIVATE(QSqlDriver) + +public: + enum DriverFeature { Transactions, QuerySize, BLOB, Unicode, PreparedQueries, + NamedPlaceholders, PositionalPlaceholders, LastInsertId, + BatchOperations, SimpleLocking, LowPrecisionNumbers, + EventNotifications, FinishQuery, MultipleResultSets }; + + enum StatementType { WhereStatement, SelectStatement, UpdateStatement, + InsertStatement, DeleteStatement }; + + enum IdentifierType { FieldName, TableName }; + + explicit QSqlDriver(QObject *parent=0); + ~QSqlDriver(); + virtual bool isOpen() const; + bool isOpenError() const; + + virtual bool beginTransaction(); + virtual bool commitTransaction(); + virtual bool rollbackTransaction(); + virtual QStringList tables(QSql::TableType tableType) const; + virtual QSqlIndex primaryIndex(const QString &tableName) const; + virtual QSqlRecord record(const QString &tableName) const; +#ifdef QT3_SUPPORT + inline QT3_SUPPORT QSqlRecord record(const QSqlQuery& query) const + { return query.record(); } + inline QT3_SUPPORT QSqlRecord recordInfo(const QString& tablename) const + { return record(tablename); } + inline QT3_SUPPORT QSqlRecord recordInfo(const QSqlQuery& query) const + { return query.record(); } + inline QT3_SUPPORT QString nullText() const { return QLatin1String("NULL"); } + inline QT3_SUPPORT QString formatValue(const QSqlField *field, bool trimStrings = false) const + { return field ? formatValue(*field, trimStrings) : QString(); } +#endif + virtual QString formatValue(const QSqlField& field, bool trimStrings = false) const; + + virtual QString escapeIdentifier(const QString &identifier, IdentifierType type) const; + virtual QString sqlStatement(StatementType type, const QString &tableName, + const QSqlRecord &rec, bool preparedStatement) const; + + QSqlError lastError() const; + + virtual QVariant handle() const; + virtual bool hasFeature(DriverFeature f) const = 0; + virtual void close() = 0; + virtual QSqlResult *createResult() const = 0; + + virtual bool open(const QString& db, + const QString& user = QString(), + const QString& password = QString(), + const QString& host = QString(), + int port = -1, + const QString& connOpts = QString()) = 0; + bool subscribeToNotification(const QString &name); // ### Qt 5: make virtual + bool unsubscribeFromNotification(const QString &name); // ### Qt 5: make virtual + QStringList subscribedToNotifications() const; // ### Qt 5: make virtual + +Q_SIGNALS: + void notification(const QString &name); + +protected: + virtual void setOpen(bool o); + virtual void setOpenError(bool e); + virtual void setLastError(const QSqlError& e); + +protected Q_SLOTS: + bool subscribeToNotificationImplementation(const QString &name); // ### Qt 5: eliminate, see subscribeToNotification() + bool unsubscribeFromNotificationImplementation(const QString &name); // ### Qt 5: eliminate, see unsubscribeFromNotification() + QStringList subscribedToNotificationsImplementation() const; // ### Qt 5: eliminate, see subscribedNotifications() + +private: + Q_DISABLE_COPY(QSqlDriver) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLDRIVER_H diff --git a/src/sql/kernel/qsqldriverplugin.cpp b/src/sql/kernel/qsqldriverplugin.cpp new file mode 100644 index 0000000..b3de2cd --- /dev/null +++ b/src/sql/kernel/qsqldriverplugin.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqldriverplugin.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSqlDriverPlugin + \brief The QSqlDriverPlugin class provides an abstract base for custom QSqlDriver plugins. + + \ingroup plugins + \inmodule QtSql + + 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 Q_EXPORT_PLUGIN2() macro. See the SQL + plugins that come with Qt for example implementations (in the + \c{plugins/src/sqldrivers} subdirectory of the source + distribution). + + \sa {How to Create Qt 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 called \a + key. The driver key is usually the class name of the required + driver. Keys are case sensitive. + + \sa keys() +*/ + +/*! + Constructs a SQL driver plugin and sets the parent to \a parent. + This is invoked automatically by the Q_EXPORT_PLUGIN2() macro. +*/ + +QSqlDriverPlugin::QSqlDriverPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + 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() +{ +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqldriverplugin.h b/src/sql/kernel/qsqldriverplugin.h new file mode 100644 index 0000000..1150360 --- /dev/null +++ b/src/sql/kernel/qsqldriverplugin.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLDRIVERPLUGIN_H +#define QSQLDRIVERPLUGIN_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QSqlDriver; + +struct Q_SQL_EXPORT QSqlDriverFactoryInterface : public QFactoryInterface +{ + virtual QSqlDriver *create(const QString &name) = 0; +}; + +#define QSqlDriverFactoryInterface_iid "com.trolltech.Qt.QSqlDriverFactoryInterface" +Q_DECLARE_INTERFACE(QSqlDriverFactoryInterface, QSqlDriverFactoryInterface_iid) + +class Q_SQL_EXPORT QSqlDriverPlugin : public QObject, public QSqlDriverFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QSqlDriverFactoryInterface:QFactoryInterface) +public: + explicit QSqlDriverPlugin(QObject *parent = 0); + ~QSqlDriverPlugin(); + + virtual QStringList keys() const = 0; + virtual QSqlDriver *create(const QString &key) = 0; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLDRIVERPLUGIN_H diff --git a/src/sql/kernel/qsqlerror.cpp b/src/sql/kernel/qsqlerror.cpp new file mode 100644 index 0000000..14fc050 --- /dev/null +++ b/src/sql/kernel/qsqlerror.cpp @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqlerror.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QSqlError &s) +{ + dbg.nospace() << "QSqlError(" << s.number() << ", " << s.driverText() << + ", " << s.databaseText() << ")"; + return dbg.space(); +} +#endif + +/*! + \class QSqlError + \brief The QSqlError class provides SQL database error information. + + \ingroup database + \inmodule QtSql + + A QSqlError object can provide database-specific error data, + including the driverText() and databaseText() messages (or both + concatenated together as text()), and the error number() and + type(). The functions all have setters so that you can create and + return QSqlError objects from your own classes, for example from + your own SQL drivers. + + \sa QSqlDatabase::lastError(), QSqlQuery::lastError() +*/ + +/*! + \enum QSqlError::ErrorType + + This enum type describes the context in which the error occurred, e.g., a connection error, a statement error, etc. + + \value NoError No error occurred. + \value ConnectionError Connection error. + \value StatementError SQL statement syntax error. + \value TransactionError Transaction failed error. + \value UnknownError Unknown error. + + \omitvalue None + \omitvalue Connection + \omitvalue Statement + \omitvalue Transaction + \omitvalue Unknown +*/ + +/*! + 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, ErrorType 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) +{ +} + +/*! + Assigns the \a other error's values to this error. +*/ + +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. It may also be empty. + + \sa setDriverText() databaseText() text() +*/ +QString QSqlError::driverText() const +{ + return driverError; +} + +/*! + Sets the driver error text to the value of \a driverText. + + \sa driverText() setDatabaseText() text() +*/ + +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; it may be empty. + + \sa setDatabaseText() driverText() text() +*/ + +QString QSqlError::databaseText() const +{ + return databaseError; +} + +/*! + Sets the database error text to the value of \a databaseText. + + \sa databaseText() setDriverText() text() +*/ + +void QSqlError::setDatabaseText(const QString& databaseText) +{ + databaseError = databaseText; +} + +/*! + Returns the error type, or -1 if the type cannot be determined. + + \sa setType() +*/ + +QSqlError::ErrorType QSqlError::type() const +{ + return errorType; +} + +/*! + Sets the error type to the value of \a type. + + \sa type() +*/ + +void QSqlError::setType(ErrorType type) +{ + errorType = type; +} + +/*! + Returns the database-specific error number, or -1 if it cannot be + determined. + + \sa setNumber() +*/ + +int QSqlError::number() const +{ + return errorNumber; +} + +/*! + Sets the database-specific error number to \a number. + + \sa number() +*/ + +void QSqlError::setNumber(int number) +{ + errorNumber = number; +} + +/*! + This is a convenience function that returns databaseText() and + driverText() concatenated into a single string. + + \sa driverText() databaseText() +*/ + +QString QSqlError::text() const +{ + QString result = databaseError; + if (!databaseError.endsWith(QLatin1String("\n"))) + result += QLatin1Char(' '); + result += driverError; + return result; +} + +/*! + Returns true if an error is set, otherwise false. + + Example: + \snippet doc/src/snippets/code/src_sql_kernel_qsqlerror.cpp 0 + + \sa type() +*/ +bool QSqlError::isValid() const +{ + return errorType != NoError; +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlerror.h b/src/sql/kernel/qsqlerror.h new file mode 100644 index 0000000..9e09c27 --- /dev/null +++ b/src/sql/kernel/qsqlerror.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLERROR_H +#define QSQLERROR_H + +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class Q_SQL_EXPORT QSqlError +{ +public: + enum ErrorType { + NoError, + ConnectionError, + StatementError, + TransactionError, + UnknownError +#ifdef QT3_SUPPORT + , None = NoError, + Connection = ConnectionError, + Statement = StatementError, + Transaction = TransactionError, + Unknown = UnknownError +#endif + }; + QSqlError( const QString& driverText = QString(), + const QString& databaseText = QString(), + ErrorType type = NoError, + int number = -1); + QSqlError(const QSqlError& other); + QSqlError& operator=(const QSqlError& other); + ~QSqlError(); + + QString driverText() const; + void setDriverText(const QString& driverText); + QString databaseText() const; + void setDatabaseText(const QString& databaseText); + ErrorType type() const; + void setType(ErrorType type); + int number() const; + void setNumber(int number); + QString text() const; + bool isValid() const; + +private: + QString driverError; + QString databaseError; + ErrorType errorType; + int errorNumber; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_SQL_EXPORT QDebug operator<<(QDebug, const QSqlError &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLERROR_H diff --git a/src/sql/kernel/qsqlfield.cpp b/src/sql/kernel/qsqlfield.cpp new file mode 100644 index 0000000..8a808b6 --- /dev/null +++ b/src/sql/kernel/qsqlfield.cpp @@ -0,0 +1,557 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqlfield.h" +#include "qatomic.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +class QSqlFieldPrivate +{ +public: + QSqlFieldPrivate(const QString &name, + QVariant::Type type) : + ref(1), nm(name), ro(false), type(type), req(QSqlField::Unknown), + len(-1), prec(-1), tp(-1), gen(true), autoval(false) + { + } + + QSqlFieldPrivate(const QSqlFieldPrivate &other) + : ref(1), + nm(other.nm), + ro(other.ro), + type(other.type), + req(other.req), + len(other.len), + prec(other.prec), + def(other.def), + tp(other.tp), + gen(other.gen), + autoval(other.autoval) + {} + + bool operator==(const QSqlFieldPrivate& other) const + { + return (nm == other.nm + && ro == other.ro + && type == other.type + && req == other.req + && len == other.len + && prec == other.prec + && def == other.def + && gen == other.gen + && autoval == other.autoval); + } + + QAtomicInt ref; + QString nm; + uint ro: 1; + QVariant::Type type; + QSqlField::RequiredStatus req; + int len; + int prec; + QVariant def; + int tp; + uint gen: 1; + uint autoval: 1; +}; + + +/*! + \class QSqlField + \brief The QSqlField class manipulates the fields in SQL database tables + and views. + + \ingroup database + \ingroup shared + \inmodule QtSql + + 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: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 2 + + However, the field will attempt to cast certain data types to the + field data type where possible: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 3 + + QSqlField objects are rarely created explicitly in application + code. They are usually accessed indirectly through \l{QSqlRecord}s + that already contain a list of fields. For example: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 4 + \dots + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 5 + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 6 + + A QSqlField object can provide some meta-data about the field, for + example, its name(), variant type(), length(), precision(), + defaultValue(), typeID(), and its requiredStatus(), + isGenerated() and isReadOnly(). The field's data can be + checked to see if it isNull(), and its value() retrieved. When + editing the data can be set with setValue() or set to NULL with + clear(). + + \sa QSqlRecord +*/ + +/*! + \enum QSqlField::RequiredStatus + + Specifies whether the field is required or optional. + + \value Required The field must be specified when inserting records. + \value Optional The fields doesn't have to be specified when inserting records. + \value Unknown The database driver couldn't determine whether the field is required or + optional. + + \sa requiredStatus() +*/ + +/*! + Constructs an empty field called \a fieldName of variant type \a + type. + + \sa setRequiredStatus() setLength() setPrecision() setDefaultValue() setGenerated() setReadOnly() +*/ +QSqlField::QSqlField(const QString& fieldName, QVariant::Type type) +{ + d = new QSqlFieldPrivate(fieldName, type); +} + +/*! + Constructs a copy of \a other. +*/ + +QSqlField::QSqlField(const QSqlField& other) +{ + d = other.d; + d->ref.ref(); + val = other.val; +} + +/*! + Sets the field equal to \a other. +*/ + +QSqlField& QSqlField::operator=(const QSqlField& other) +{ + qAtomicAssign(d, other.d); + val = other.val; + return *this; +} + + +/*! \fn bool QSqlField::operator!=(const QSqlField &other) const + Returns true if the field is unequal to \a other; otherwise returns + false. +*/ + +/*! + Returns true if the field is equal to \a other; otherwise returns + false. +*/ +bool QSqlField::operator==(const QSqlField& other) const +{ + return ((d == other.d || *d == *other.d) + && val == other.val); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QSqlField::~QSqlField() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Sets the required status of this field to \a required. + + \sa requiredStatus() setType() setLength() setPrecision() setDefaultValue() setGenerated() setReadOnly() +*/ +void QSqlField::setRequiredStatus(RequiredStatus required) +{ + detach(); + d->req = required; +} + +/*! \fn void QSqlField::setRequired(bool required) + + Sets the required status of this field to \l Required if \a + required is true; otherwise sets it to \l Optional. + + \sa setRequiredStatus(), requiredStatus() +*/ + +/*! + Sets the field's length to \a fieldLength. For strings this is the + maximum number of characters the string can hold; the meaning + varies for other types. + + \sa length() setType() setRequiredStatus() setPrecision() setDefaultValue() setGenerated() setReadOnly() +*/ +void QSqlField::setLength(int fieldLength) +{ + detach(); + d->len = fieldLength; +} + +/*! + Sets the field's \a precision. This only affects numeric fields. + + \sa precision() setType() setRequiredStatus() setLength() setDefaultValue() setGenerated() setReadOnly() +*/ +void QSqlField::setPrecision(int precision) +{ + detach(); + d->prec = precision; +} + +/*! + Sets the default value used for this field to \a value. + + \sa defaultValue() value() setType() setRequiredStatus() setLength() setPrecision() setGenerated() setReadOnly() +*/ +void QSqlField::setDefaultValue(const QVariant &value) +{ + detach(); + d->def = value; +} + +/*! + \internal +*/ +void QSqlField::setSqlType(int type) +{ + detach(); + d->tp = type; +} + +/*! + Sets the generated state. If \a gen is false, no SQL will + be generated for this field; otherwise, Qt classes such as + QSqlQueryModel and QSqlTableModel will generate SQL for this + field. + + \sa isGenerated() setType() setRequiredStatus() setLength() setPrecision() setDefaultValue() setReadOnly() +*/ +void QSqlField::setGenerated(bool gen) +{ + detach(); + d->gen = gen; +} + + +/*! + 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. + + To set the value to NULL, use clear(). + + \sa value() isReadOnly() defaultValue() +*/ + +void QSqlField::setValue(const QVariant& value) +{ + if (isReadOnly()) + return; + val = value; +} + +/*! + Clears the value of the field and sets it to NULL. + If the field is read-only, nothing happens. + + \sa setValue() isReadOnly() requiredStatus() +*/ + +void QSqlField::clear() +{ + if (isReadOnly()) + return; + val = QVariant(type()); +} + +/*! + Sets the name of the field to \a name. + + \sa name() +*/ + +void QSqlField::setName(const QString& name) +{ + detach(); + d->nm = name; +} + +/*! + Sets the read only flag of the field's value to \a readOnly. A + read-only field cannot have its value set with setValue() and + cannot be cleared to NULL with clear(). +*/ +void QSqlField::setReadOnly(bool readOnly) +{ + detach(); + d->ro = readOnly; +} + +/*! + \fn QVariant QSqlField::value() const + + Returns the value of the field as a QVariant. + + Use isNull() to check if the field's value is NULL. +*/ + +/*! + Returns the name of the field. + + \sa setName() +*/ +QString QSqlField::name() const +{ + return d->nm; +} + +/*! + 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. + + \sa setType() +*/ +QVariant::Type QSqlField::type() const +{ + return d->type; +} + +/*! + Set's the field's variant type to \a type. + + \sa type() setRequiredStatus() setLength() setPrecision() setDefaultValue() setGenerated() setReadOnly() +*/ +void QSqlField::setType(QVariant::Type type) +{ + detach(); + d->type = type; +} + + +/*! + Returns true if the field's value is read-only; otherwise returns + false. + + \sa setReadOnly() type() requiredStatus() length() precision() defaultValue() isGenerated() +*/ +bool QSqlField::isReadOnly() const +{ return d->ro; } + +/*! + Returns true if the field's value is NULL; otherwise returns + false. + + \sa value() +*/ +bool QSqlField::isNull() const +{ return val.isNull(); } + +/*! \internal +*/ +void QSqlField::detach() +{ + qAtomicDetach(d); +} + +/*! + Returns true if this is a required field; otherwise returns false. + An \c INSERT will fail if a required field does not have a value. + + \sa setRequiredStatus() type() length() precision() defaultValue() isGenerated() +*/ +QSqlField::RequiredStatus QSqlField::requiredStatus() const +{ + return d->req; +} + +/*! + Returns the field's length. + + If the returned value is negative, it means that the information + is not available from the database. + + \sa setLength() type() requiredStatus() precision() defaultValue() isGenerated() +*/ +int QSqlField::length() const +{ + return d->len; +} + +/*! + Returns the field's precision; this is only meaningful for numeric + types. + + If the returned value is negative, it means that the information + is not available from the database. + + \sa setPrecision() type() requiredStatus() length() defaultValue() isGenerated() +*/ +int QSqlField::precision() const +{ + return d->prec; +} + +/*! + Returns the field's default value (which may be NULL). + + \sa setDefaultValue() type() requiredStatus() length() precision() isGenerated() +*/ +QVariant QSqlField::defaultValue() const +{ + return d->def; +} + +/*! + \internal + + Returns the type ID for the field. + + If the returned value is negative, it means that the information + is not available from the database. +*/ +int QSqlField::typeID() const +{ + return d->tp; +} + +/*! + Returns true if the field is generated; otherwise returns + false. + + \sa setGenerated() type() requiredStatus() length() precision() defaultValue() +*/ +bool QSqlField::isGenerated() const +{ + return d->gen; +} + +/*! + Returns true if the field's variant type is valid; otherwise + returns false. +*/ +bool QSqlField::isValid() const +{ + return d->type != QVariant::Invalid; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QSqlField &f) +{ +#ifndef Q_BROKEN_DEBUG_STREAM + dbg.nospace() << "QSqlField(" << f.name() << ", " << QVariant::typeToName(f.type()); + if (f.length() >= 0) + dbg.nospace() << ", length: " << f.length(); + if (f.precision() >= 0) + dbg.nospace() << ", precision: " << f.precision(); + if (f.requiredStatus() != QSqlField::Unknown) + dbg.nospace() << ", required: " + << (f.requiredStatus() == QSqlField::Required ? "yes" : "no"); + dbg.nospace() << ", generated: " << (f.isGenerated() ? "yes" : "no"); + if (f.typeID() >= 0) + dbg.nospace() << ", typeID: " << f.typeID(); + if (!f.defaultValue().isNull()) + dbg.nospace() << ", auto-value: \"" << f.defaultValue() << "\""; + dbg.nospace() << ")"; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QSqlField to QDebug"); + return dbg; + Q_UNUSED(f); +#endif +} +#endif + +/*! + \fn void QSqlField::setNull() + + Use clear() instead. +*/ + +/*! + Returns true if the value is auto-generated by the database, + for example auto-increment primary key values. + + \sa setAutoValue() +*/ +bool QSqlField::isAutoValue() const +{ + return d->autoval; +} + +/*! + Marks the field as an auto-generated value if \a autoVal + is true. + + \sa isAutoValue() + */ +void QSqlField::setAutoValue(bool autoVal) +{ + detach(); + d->autoval = autoVal; +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlfield.h b/src/sql/kernel/qsqlfield.h new file mode 100644 index 0000000..58b3616 --- /dev/null +++ b/src/sql/kernel/qsqlfield.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLFIELD_H +#define QSQLFIELD_H + +#include <QtCore/qvariant.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QSqlFieldPrivate; + +class Q_SQL_EXPORT QSqlField +{ +public: + enum RequiredStatus { Unknown = -1, Optional = 0, Required = 1 }; + + QSqlField(const QString& fieldName = QString(), + QVariant::Type type = QVariant::Invalid); + + QSqlField(const QSqlField& other); + QSqlField& operator=(const QSqlField& other); + bool operator==(const QSqlField& other) const; + inline bool operator!=(const QSqlField &other) const { return !operator==(other); } + ~QSqlField(); + + void setValue(const QVariant& value); + inline QVariant value() const + { return val; } + void setName(const QString& name); + QString name() const; + bool isNull() const; + void setReadOnly(bool readOnly); + bool isReadOnly() const; + void clear(); + QVariant::Type type() const; + bool isAutoValue() const; + + void setType(QVariant::Type type); + void setRequiredStatus(RequiredStatus status); + inline void setRequired(bool required) + { setRequiredStatus(required ? Required : Optional); } + void setLength(int fieldLength); + void setPrecision(int precision); + void setDefaultValue(const QVariant &value); + void setSqlType(int type); + void setGenerated(bool gen); + void setAutoValue(bool autoVal); + + RequiredStatus requiredStatus() const; + int length() const; + int precision() const; + QVariant defaultValue() const; + int typeID() const; + bool isGenerated() const; + bool isValid() const; + +#ifdef QT3_SUPPORT + inline QT3_SUPPORT void setNull() { clear(); } +#endif + +private: + void detach(); + QVariant val; + QSqlFieldPrivate* d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_SQL_EXPORT QDebug operator<<(QDebug, const QSqlField &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLFIELD_H diff --git a/src/sql/kernel/qsqlindex.cpp b/src/sql/kernel/qsqlindex.cpp new file mode 100644 index 0000000..02cdfb3 --- /dev/null +++ b/src/sql/kernel/qsqlindex.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqlindex.h" + +#include "qsqlfield.h" +#include "qstringlist.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSqlIndex + \brief The QSqlIndex class provides functions to manipulate and + describe database indexes. + + \ingroup database + \inmodule QtSql + + An \e 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. +*/ + +/*! + Constructs an empty index using the cursor name \a cursorname and + index name \a name. +*/ + +QSqlIndex::QSqlIndex(const QString& cursorname, const QString& name) + : 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 (i >= 0 && i < sorts.size()) + 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 (i >= 0 && i < sorts.size()) + sorts[i] = desc; +} + +#ifdef QT3_SUPPORT + +/*! + 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 (int i = 0; i < count(); ++i) { + if(comma) + s += sep + QLatin1Char(' '); + s += createField(i, prefix, verbose); + comma = true; + } + return s; +} + +/*! + 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. + \snippet doc/src/snippets/code/src_sql_kernel_qsqlindex.cpp 0 + +*/ +QStringList QSqlIndex::toStringList(const QString& prefix, bool verbose) const +{ + QStringList s; + for (int i = 0; i < count(); ++i) + s += createField(i, prefix, verbose); + return s; +} +#endif + +/*! \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 + QLatin1Char('.'); + f += field(i).name(); + if (verbose) + f += QLatin1Char(' ') + QString((isDescending(i) + ? QLatin1String("DESC") : QLatin1String("ASC"))); + return f; +} + +/*! + \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; +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlindex.h b/src/sql/kernel/qsqlindex.h new file mode 100644 index 0000000..82c0fb9 --- /dev/null +++ b/src/sql/kernel/qsqlindex.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLINDEX_H +#define QSQLINDEX_H + +#include <QtSql/qsqlrecord.h> +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class Q_SQL_EXPORT QSqlIndex : public QSqlRecord +{ +public: + QSqlIndex(const QString &cursorName = QString(), const QString &name = QString()); + QSqlIndex(const QSqlIndex &other); + ~QSqlIndex(); + QSqlIndex &operator=(const QSqlIndex &other); + void setCursorName(const QString &cursorName); + inline QString cursorName() const { return cursor; } + void setName(const QString& name); + inline QString name() const { return nm; } + + void append(const QSqlField &field); + void append(const QSqlField &field, bool desc); + + bool isDescending(int i) const; + void setDescending(int i, bool desc); + +#ifdef QT3_SUPPORT + QT3_SUPPORT QString toString(const QString &prefix = QString(), + const QString &sep = QLatin1String(","), + bool verbose = true) const; + QT3_SUPPORT QStringList toStringList(const QString& prefix = QString(), + bool verbose = true) const; +#endif + +private: + QString createField(int i, const QString& prefix, bool verbose) const; + QString cursor; + QString nm; + QList<bool> sorts; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLINDEX_H diff --git a/src/sql/kernel/qsqlnulldriver_p.h b/src/sql/kernel/qsqlnulldriver_p.h new file mode 100644 index 0000000..e899a0b --- /dev/null +++ b/src/sql/kernel/qsqlnulldriver_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLNULLDRIVER_P_H +#define QSQLNULLDRIVER_P_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. +// + +#include "QtCore/qvariant.h" +#include "QtSql/qsqldriver.h" +#include "QtSql/qsqlerror.h" +#include "QtSql/qsqlresult.h" + +QT_BEGIN_NAMESPACE + +class QSqlNullResult : public QSqlResult +{ +public: + inline explicit QSqlNullResult(const QSqlDriver* d): QSqlResult(d) + { QSqlResult::setLastError( + QSqlError(QLatin1String("Driver not loaded"), QLatin1String("Driver not loaded"), QSqlError::ConnectionError)); } +protected: + inline QVariant data(int) { return QVariant(); } + inline bool reset (const QString&) { return false; } + inline bool fetch(int) { return false; } + inline bool fetchFirst() { return false; } + inline bool fetchLast() { return false; } + inline bool isNull(int) { return false; } + inline int size() { return -1; } + inline int numRowsAffected() { return 0; } + + inline void setAt(int) {} + inline void setActive(bool) {} + inline void setLastError(const QSqlError&) {} + inline void setQuery(const QString&) {} + inline void setSelect(bool) {} + inline void setForwardOnly(bool) {} + + inline bool exec() { return false; } + inline bool prepare(const QString&) { return false; } + inline bool savePrepare(const QString&) { return false; } + inline void bindValue(int, const QVariant&, QSql::ParamType) {} + inline void bindValue(const QString&, const QVariant&, QSql::ParamType) {} +}; + +class QSqlNullDriver : public QSqlDriver +{ +public: + inline QSqlNullDriver(): QSqlDriver() + { QSqlDriver::setLastError( + QSqlError(QLatin1String("Driver not loaded"), QLatin1String("Driver not loaded"), QSqlError::ConnectionError)); } + inline bool hasFeature(DriverFeature) const { return false; } + inline bool open(const QString &, const QString & , const QString & , + const QString &, int, const QString&) + { return false; } + inline void close() {} + inline QSqlResult *createResult() const { return new QSqlNullResult(this); } + +protected: + inline void setOpen(bool) {} + inline void setOpenError(bool) {} + inline void setLastError(const QSqlError&) {} +}; + +QT_END_NAMESPACE + +#endif // QSQLNULLDRIVER_P_H diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp new file mode 100644 index 0000000..e6729a5 --- /dev/null +++ b/src/sql/kernel/qsqlquery.cpp @@ -0,0 +1,1220 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqlquery.h" + +//#define QT_DEBUG_SQL + +#include "qatomic.h" +#include "qsqlrecord.h" +#include "qsqlresult.h" +#include "qsqldriver.h" +#include "qsqldatabase.h" +#include "private/qsqlnulldriver_p.h" +#include "qvector.h" +#include "qmap.h" + +QT_BEGIN_NAMESPACE + +class QSqlQueryPrivate +{ +public: + QSqlQueryPrivate(QSqlResult* result); + ~QSqlQueryPrivate(); + QAtomicInt ref; + QSqlResult* sqlResult; + QSql::NumericalPrecisionPolicy precisionPolicy; + + static QSqlQueryPrivate* shared_null(); +}; + +Q_GLOBAL_STATIC_WITH_ARGS(QSqlQueryPrivate, nullQueryPrivate, (0)) +Q_GLOBAL_STATIC(QSqlNullDriver, nullDriver) +Q_GLOBAL_STATIC_WITH_ARGS(QSqlNullResult, nullResult, (nullDriver())) + +QSqlQueryPrivate* QSqlQueryPrivate::shared_null() +{ + QSqlQueryPrivate *null = nullQueryPrivate(); + null->ref.ref(); + return null; +} + +/*! +\internal +*/ +QSqlQueryPrivate::QSqlQueryPrivate(QSqlResult* result) + : ref(1), sqlResult(result), precisionPolicy(QSql::HighPrecision) +{ + if (!sqlResult) + sqlResult = nullResult(); +} + +QSqlQueryPrivate::~QSqlQueryPrivate() +{ + QSqlResult *nr = nullResult(); + if (!nr || sqlResult == nr) + return; + delete sqlResult; +} + +/*! + \class QSqlQuery + \brief The QSqlQuery class provides a means of executing and + manipulating SQL statements. + + \ingroup database + \ingroup shared + \mainclass + \inmodule QtSql + + 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, such as \c SELECT, \c + INSERT, \c UPDATE and \c DELETE, as well as DDL (data definition + language) statements, such as \c{CREATE} \c{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 so that 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. + + For some databases, if an active query that is a \c{SELECT} + statement exists when you call \l{QSqlDatabase::}{commit()} or + \l{QSqlDatabase::}{rollback()}, the commit or rollback will + fail. See isActive() for details. + + \target QSqlQuery examples + + Navigating records is performed with the following functions: + + \list + \o next() + \o previous() + \o first() + \o last() + \o seek() + \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., by using + next()), you can use setForwardOnly(), which will save a + significant amount of memory overhead and improve performance on + some databases. 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: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 7 + + To access the data returned by a query, use value(int). Each + field in the data returned by a \c SELECT statement is accessed + by passing the field's position in the statement, starting from + 0. This makes using \c{SELECT *} queries inadvisable because the + order of the fields returned is indeterminate. + + For the sake of efficiency, there are no functions to access a + field by name (unless you use prepared queries with names, as + explained below). To convert a field name into an index, use + record().\l{QSqlRecord::indexOf()}{indexOf()}, for example: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 8 + + QSqlQuery supports prepared query execution and the binding of + parameter values to placeholders. Some databases don't support + these features, so for those, 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. Use numRowsAffected() to find out how many rows + were affected by a non-\c SELECT query, and size() to find how + many were retrieved by a \c SELECT. + + Oracle databases identify placeholders by using a colon-name + syntax, e.g \c{:name}. ODBC simply uses \c ? characters. Qt + supports both syntaxes, with the restriction that you can't mix + them in the same query. + + You can retrieve the values of all the fields in a single variable + (a map) using boundValues(). + + \section1 Approaches to Binding Values + + Below we present the same example using each of the four + different binding approaches, as well as one example of binding + values to a stored procedure. + + \bold{Named binding using named placeholders:} + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 9 + + \bold{Positional binding using named placeholders:} + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 10 + + \bold{Binding values using positional placeholders (version 1):} + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 11 + + \bold{Binding values using positional placeholders (version 2):} + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 12 + + \bold{Binding values to a stored procedure:} + + 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. + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 13 + + Note that unbound parameters will retain their values. + + Stored procedures that uses the return statement to return values, + or return multiple result sets, are not fully supported. For specific + details see \l{SQL Database Drivers}. + + \warning You must load the SQL driver and open the connection before a + QSqlQuery is created. Also, the connection must remain open while the + query exists; otherwise, the behavior of QSqlQuery is undefined. + + \sa QSqlDatabase, QSqlQueryModel, QSqlTableModel, QVariant +*/ + +/*! + Constructs a QSqlQuery object which uses the QSqlResult \a result + to communicate with a database. +*/ + +QSqlQuery::QSqlQuery(QSqlResult *result) +{ + d = new QSqlQueryPrivate(result); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QSqlQuery::~QSqlQuery() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Constructs a copy of \a other. +*/ + +QSqlQuery::QSqlQuery(const QSqlQuery& other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + \internal +*/ +static void qInit(QSqlQuery *q, const QString& query, QSqlDatabase db) +{ + QSqlDatabase database = db; + if (!database.isValid()) + database = QSqlDatabase::database(QLatin1String(QSqlDatabase::defaultConnection), false); + if (database.isValid()) { + *q = QSqlQuery(database.driver()->createResult()); + } + if (!query.isEmpty()) + q->exec(query); +} + +/*! + Constructs a QSqlQuery object using the SQL \a query and the + database \a db. If \a db is not specified, the application's + default database is used. If \a query is not an empty string, it + will be executed. + + \sa QSqlDatabase +*/ +QSqlQuery::QSqlQuery(const QString& query, QSqlDatabase db) +{ + d = QSqlQueryPrivate::shared_null(); + qInit(this, query, db); +} + +/*! + Constructs a QSqlQuery object using the database \a db. + + \sa QSqlDatabase +*/ + +QSqlQuery::QSqlQuery(QSqlDatabase db) +{ + d = QSqlQueryPrivate::shared_null(); + qInit(this, QString(), db); +} + + +/*! + Assigns \a other to this object. +*/ + +QSqlQuery& QSqlQuery::operator=(const QSqlQuery& other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Returns true if the query is \l{isActive()}{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->isActive() && d->sqlResult->isValid()) + return d->sqlResult->isNull(field); + return true; +} + +/*! + + Executes the SQL in \a query. Returns true and sets the query state + to \l{isActive()}{active} if the query was successful; otherwise + returns false. 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 (for example, using next()). + + Note that the last error for this query is reset when exec() is + called. + + Example: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 34 + + \sa isActive(), isValid(), next(), previous(), first(), last(), + seek() +*/ + +bool QSqlQuery::exec(const QString& query) +{ + if (d->ref != 1) { + bool fo = isForwardOnly(); + *this = QSqlQuery(driver()->createResult()); + d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy); + setForwardOnly(fo); + } else { + d->sqlResult->clear(); + d->sqlResult->setActive(false); + d->sqlResult->setLastError(QSqlError()); + d->sqlResult->setAt(QSql::BeforeFirstRow); + d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy); + } + d->sqlResult->setQuery(query.trimmed()); + if (!driver()->isOpen() || driver()->isOpenError()) { + qWarning("QSqlQuery::exec: database not open"); + return false; + } + if (query.isEmpty()) { + qWarning("QSqlQuery::exec: empty query"); + return false; + } +#ifdef QT_DEBUG_SQL + qDebug("\n QSqlQuery: %s", query.toLocal8Bit().constData()); +#endif + return d->sqlResult->reset(query); +} + +/*! + Returns the value of field \a index in the current record. + + The fields are numbered from left to right using the text of the + \c SELECT statement, e.g. in + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlquery.cpp 0 + + 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 index does not + exist, if the query is inactive, or if the query is positioned on + an invalid record. + + \sa previous() next() first() last() seek() isActive() isValid() +*/ + +QVariant QSqlQuery::value(int index) const +{ + if (isActive() && isValid() && (index > QSql::BeforeFirstRow)) + return d->sqlResult->data(index); + qWarning("QSqlQuery::value: not positioned on a valid record"); + return QVariant(); +} + +/*! + Returns the current internal position of the query. The first + record is at position zero. If the position is invalid, the + function returns QSql::BeforeFirstRow or + QSql::AfterLastRow, which are special negative values. + + \sa previous() next() first() last() seek() isActive() isValid() +*/ + +int QSqlQuery::at() const +{ + return d->sqlResult->at(); +} + +/*! + Returns the text of the current query being used, or an empty + string if there is no current query text. + + \sa executedQuery() +*/ + +QString QSqlQuery::lastQuery() const +{ + return d->sqlResult->lastQuery(); +} + +/*! + Returns the database driver associated with the query. +*/ + +const QSqlDriver *QSqlQuery::driver() const +{ + return d->sqlResult->driver(); +} + +/*! + Returns the result associated with the query. +*/ + +const QSqlResult* QSqlQuery::result() const +{ + return d->sqlResult; +} + +/*! + Retrieves the record at position \a index, 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 \l{isActive()} + {active} state and isSelect() must return true before calling this + function. + + If \a relative is false (the default), the following rules apply: + + \list + + \o If \a index is negative, the result is positioned before the + first record and false is returned. + + \o Otherwise, an attempt is made to move to the record at position + \a index. If the record at position \a index 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 + + \o If the result is currently positioned before the first record or + on the first record, and \a index is negative, there is no change, + and false is returned. + + \o If the result is currently located after the last record, and \a + index is positive, there is no change, and false is returned. + + \o If the result is currently located somewhere in the middle, and + the relative offset \a index moves the result below zero, the result + is positioned before the first record and false is returned. + + \o Otherwise, an attempt is made to move to the record \a index + records ahead of the current record (or \a index records behind the + current record if \a index is negative). If the record at offset \a + index could not be retrieved, the result is positioned after the + last record if \a index >= 0, (or before the first record if \a + index is negative), and false is returned. If the record is + successfully retrieved, true is returned. + + \endlist + + \sa next() previous() first() last() at() isActive() isValid() +*/ +bool QSqlQuery::seek(int index, bool relative) +{ + if (!isSelect() || !isActive()) + return false; + int actualIdx; + if (!relative) { // arbitrary seek + if (index < 0) { + d->sqlResult->setAt(QSql::BeforeFirstRow); + return false; + } + actualIdx = index; + } else { + switch (at()) { // relative seek + case QSql::BeforeFirstRow: + if (index > 0) + actualIdx = index; + else { + return false; + } + break; + case QSql::AfterLastRow: + if (index < 0) { + d->sqlResult->fetchLast(); + actualIdx = at() + index; + } else { + return false; + } + break; + default: + if ((at() + index) < 0) { + d->sqlResult->setAt(QSql::BeforeFirstRow); + return false; + } + actualIdx = at() + index; + break; + } + } + // let drivers optimize + if (isForwardOnly() && actualIdx < at()) { + qWarning("QSqlQuery::seek: cannot seek backwards in a forward only query"); + return false; + } + if (actualIdx == (at() + 1) && at() != QSql::BeforeFirstRow) { + if (!d->sqlResult->fetchNext()) { + d->sqlResult->setAt(QSql::AfterLastRow); + return false; + } + return true; + } + if (actualIdx == (at() - 1)) { + if (!d->sqlResult->fetchPrevious()) { + d->sqlResult->setAt(QSql::BeforeFirstRow); + return false; + } + return true; + } + if (!d->sqlResult->fetch(actualIdx)) { + d->sqlResult->setAt(QSql::AfterLastRow); + return false; + } + 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 + the \l{isActive()}{active} state and isSelect() must return true + before calling this function or it will do nothing and return false. + + The following rules apply: + + \list + + \o 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. + + \o If the result is currently located after the last record, there + is no change and false is returned. + + \o 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 previous() first() last() seek() at() isActive() isValid() +*/ +bool QSqlQuery::next() +{ + if (!isSelect() || !isActive()) + return false; + bool b = false; + switch (at()) { + case QSql::BeforeFirstRow: + b = d->sqlResult->fetchFirst(); + return b; + case QSql::AfterLastRow: + return false; + default: + if (!d->sqlResult->fetchNext()) { + d->sqlResult->setAt(QSql::AfterLastRow); + return false; + } + 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 the \l{isActive()}{active} state and isSelect() must + return true before calling this function or it will do nothing and + return false. + + The following rules apply: + + \list + + \o If the result is currently located before the first record, there + is no change and false is returned. + + \o If the result is currently located after the last record, an + attempt is made to retrieve the last record. + + \o 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::previous() +{ + if (!isSelect() || !isActive()) + return false; + if (isForwardOnly()) { + qWarning("QSqlQuery::seek: cannot seek backwards in a forward only query"); + return false; + } + + bool b = false; + switch (at()) { + case QSql::BeforeFirstRow: + return false; + case QSql::AfterLastRow: + b = d->sqlResult->fetchLast(); + return b; + default: + if (!d->sqlResult->fetchPrevious()) { + d->sqlResult->setAt(QSql::BeforeFirstRow); + return false; + } + 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 the \l{isActive()}{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() previous() last() seek() at() isActive() isValid() + */ +bool QSqlQuery::first() +{ + if (!isSelect() || !isActive()) + return false; + if (isForwardOnly() && at() > QSql::BeforeFirstRow) { + qWarning("QSqlQuery::seek: cannot seek backwards in a forward only query"); + return false; + } + bool b = false; + b = d->sqlResult->fetchFirst(); + 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 + the \l{isActive()}{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() previous() first() seek() at() isActive() isValid() +*/ + +bool QSqlQuery::last() +{ + if (!isSelect() || !isActive()) + return false; + bool b = false; + b = d->sqlResult->fetchLast(); + 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-\c SELECT + statement, use numRowsAffected(). + + \sa isActive() numRowsAffected() QSqlDriver::hasFeature() +*/ +int QSqlQuery::size() const +{ + 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; use size() instead. If the query + is not \l{isActive()}{active}, -1 is returned. + + \sa size() QSqlDriver::hasFeature() +*/ + +int QSqlQuery::numRowsAffected() const +{ + if (isActive()) + return d->sqlResult->numRowsAffected(); + return -1; +} + +/*! + Returns error information about the last error (if any) that + occurred with this query. + + \sa QSqlError, QSqlDatabase::lastError() +*/ + +QSqlError QSqlQuery::lastError() const +{ + return d->sqlResult->lastError(); +} + +/*! + Returns true if the query is currently positioned on a valid + record; otherwise returns false. +*/ + +bool QSqlQuery::isValid() const +{ + return d->sqlResult->isValid(); +} + +/*! + + Returns true if the query is \e{active}. An active QSqlQuery is one + that has been \l{QSqlQuery::exec()} {exec()'d} successfully but not + yet finished with. When you are finished with an active query, you + can make make the query inactive by calling finish() or clear(), or + you can delete the QSqlQuery instance. + + \note Of particular interest is an active query that is a \c{SELECT} + statement. For some databases that support transactions, an active + query that is a \c{SELECT} statement can cause a \l{QSqlDatabase::} + {commit()} or a \l{QSqlDatabase::} {rollback()} to fail, so before + committing or rolling back, you should make your active \c{SELECT} + statement query inactive using one of the ways listed above. + + \sa isSelect() + */ +bool QSqlQuery::isActive() const +{ + return d->sqlResult->isActive(); +} + +/*! + Returns true if the current query is a \c SELECT statement; + otherwise returns false. +*/ + +bool QSqlQuery::isSelect() const +{ + return d->sqlResult->isSelect(); +} + +/*! + Returns true if you can only scroll forward through a result set; + otherwise returns false. + + \sa setForwardOnly(), next() +*/ +bool QSqlQuery::isForwardOnly() const +{ + return d->sqlResult->isForwardOnly(); +} + +/*! + Sets forward only mode to \a forward. If \a forward is true, only + next() and seek() with positive values, are allowed for navigating + the results. + + Forward only mode can be (depending on the driver) more memory + efficient since results do not need to be cached. It will also + improve performance on some databases. For this to be true, you must + call \c setForwardMode() before the query is prepared or executed. + Note that the constructor that takes a query and a database may + execute the query. + + Forward only mode is off by default. + + \sa isForwardOnly(), next(), seek() +*/ +void QSqlQuery::setForwardOnly(bool forward) +{ + d->sqlResult->setForwardOnly(forward); +} + +/*! + Returns a QSqlRecord containing the field information for the + current query. If the query points to a valid row (isValid() returns + true), the record is populated with the row's values. An empty + record is returned when there is no active query (isActive() returns + false). + + To retrieve values from a query, value() should be used since + its index-based lookup is faster. + + In the following example, a \c{SELECT * FROM} query is executed. + Since the order of the columns is not defined, QSqlRecord::indexOf() + is used to obtain the index of a column. + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlquery.cpp 1 + + \sa value() +*/ +QSqlRecord QSqlQuery::record() const +{ + QSqlRecord rec = d->sqlResult->record(); + + if (isValid()) { + for (int i = 0; i < rec.count(); ++i) + rec.setValue(i, value(i)); + } + return rec; +} + +/*! + Clears the result set and releases any resources held by the + query. Sets the query state to inactive. You should rarely if ever + need to call this function. +*/ +void QSqlQuery::clear() +{ + *this = QSqlQuery(driver()->createResult()); +} + +/*! + Prepares the SQL query \a query for execution. Returns true if the + query is prepared successfully; otherwise returns false. + + The query may contain placeholders for binding values. Both Oracle + style colon-name (e.g., \c{:surname}), and ODBC style (\c{?}) + placeholders are supported; but they cannot be mixed in the same + query. See the \l{QSqlQuery examples}{Detailed Description} for + examples. + + Portability note: Some databases choose to delay preparing a query + until it is executed the first time. In this case, preparing a + syntactically wrong query succeeds, but every consecutive exec() + will fail. + + Example: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 9 + + \sa exec(), bindValue(), addBindValue() +*/ +bool QSqlQuery::prepare(const QString& query) +{ + if (d->ref != 1) { + bool fo = isForwardOnly(); + *this = QSqlQuery(driver()->createResult()); + setForwardOnly(fo); + d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy); + } else { + d->sqlResult->setActive(false); + d->sqlResult->setLastError(QSqlError()); + d->sqlResult->setAt(QSql::BeforeFirstRow); + d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy); + } + if (!driver()) { + qWarning("QSqlQuery::prepare: no driver"); + return false; + } + if (!driver()->isOpen() || driver()->isOpenError()) { + qWarning("QSqlQuery::prepare: database not open"); + return false; + } + if (query.isEmpty()) { + qWarning("QSqlQuery::prepare: empty query"); + return false; + } +#ifdef QT_DEBUG_SQL + qDebug("\n QSqlQuery::prepare: %s", query.toLocal8Bit().constData()); +#endif + return d->sqlResult->savePrepare(query); +} + +/*! + Executes a previously prepared SQL query. Returns true if the query + executed successfully; otherwise returns false. + + Note that the last error for this query is reset when exec() is + called. + + \sa prepare() bindValue() addBindValue() boundValue() boundValues() +*/ +bool QSqlQuery::exec() +{ + d->sqlResult->resetBindCount(); + + if (d->sqlResult->lastError().isValid()) + d->sqlResult->setLastError(QSqlError()); + + return d->sqlResult->exec(); +} + +/*! \enum QSqlQuery::BatchExecutionMode + + \value ValuesAsRows - Updates multiple rows. Treats every entry in a QVariantList as a value for updating the next row. + \value ValuesAsColumns - Updates a single row. Treats every entry in a QVariantList as a single value of an array type. +*/ + +/*! + \since 4.2 + + Executes a previously prepared SQL query in a batch. All the bound + parameters have to be lists of variants. If the database doesn't + support batch executions, the driver will simulate it using + conventional exec() calls. + + Returns true if the query is executed successfully; otherwise + returns false. + + Example: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlquery.cpp 2 + + The example above inserts four new rows into \c myTable: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlquery.cpp 3 + + To bind NULL values, a null QVariant of the relevant type has to be + added to the bound QVariantList; for example, \c + {QVariant(QVariant::String)} should be used if you are using + strings. + + \note Every bound QVariantList must contain the same amount of + variants. + + \note The type of the QVariants in a list must not change. For + example, you cannot mix integer and string variants within a + QVariantList. + + The \a mode parameter indicates how the bound QVariantList will be + interpreted. If \a mode is \c ValuesAsRows, every variant within + the QVariantList will be interpreted as a value for a new row. \c + ValuesAsColumns is a special case for the Oracle driver. In this + mode, every entry within a QVariantList will be interpreted as + array-value for an IN or OUT value within a stored procedure. Note + that this will only work if the IN or OUT value is a table-type + consisting of only one column of a basic type, for example \c{TYPE + myType IS TABLE OF VARCHAR(64) INDEX BY BINARY_INTEGER;} + + \sa prepare(), bindValue(), addBindValue() +*/ +bool QSqlQuery::execBatch(BatchExecutionMode mode) +{ + return d->sqlResult->execBatch(mode == ValuesAsColumns); +} + +/*! + 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 + paramType is QSql::Out or QSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + To bind a NULL value, use a null QVariant; for example, use + \c {QVariant(QVariant::String)} if you are binding a string. + + \sa addBindValue(), prepare(), exec(), boundValue() boundValues() +*/ +void QSqlQuery::bindValue(const QString& placeholder, const QVariant& val, + QSql::ParamType paramType +) +{ + d->sqlResult->bindValue(placeholder, val, paramType); +} + +/*! + 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 + paramType is QSql::Out or QSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. +*/ +void QSqlQuery::bindValue(int pos, const QVariant& val, QSql::ParamType paramType) +{ + d->sqlResult->bindValue(pos, val, paramType); +} + +/*! + 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 paramType is QSql::Out or QSql::InOut, the placeholder will be + overwritten with data from the database after the exec() call. + + To bind a NULL value, use a null QVariant; for example, use \c + {QVariant(QVariant::String)} if you are binding a string. + + \sa bindValue(), prepare(), exec(), boundValue() boundValues() +*/ +void QSqlQuery::addBindValue(const QVariant& val, QSql::ParamType paramType) +{ + d->sqlResult->addBindValue(val, paramType); +} + +/*! + Returns the value for the \a placeholder. + + \sa boundValues() bindValue() addBindValue() +*/ +QVariant QSqlQuery::boundValue(const QString& placeholder) const +{ + return d->sqlResult->boundValue(placeholder); +} + +/*! + Returns the value for the placeholder at position \a pos. +*/ +QVariant QSqlQuery::boundValue(int pos) const +{ + return d->sqlResult->boundValue(pos); +} + +/*! + Returns a map of the bound values. + + With named binding, the bound values can be examined in the + following ways: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 14 + + With positional binding, the code becomes: + + \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 15 + + \sa boundValue() bindValue() addBindValue() +*/ +QMap<QString,QVariant> QSqlQuery::boundValues() const +{ + QMap<QString,QVariant> map; + + const QVector<QVariant> values(d->sqlResult->boundValues()); + for (int i = 0; i < values.count(); ++i) + map[d->sqlResult->boundValueName(i)] = values.at(i); + return map; +} + +/*! + Returns the last query that was successfully executed. + + In most cases this function returns the same string 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. It is mostly useful for debugging purposes. + + \sa lastQuery() +*/ +QString QSqlQuery::executedQuery() const +{ + return d->sqlResult->executedQuery(); +} + +/*! + \fn bool QSqlQuery::prev() + + Use previous() instead. +*/ + +/*! + Returns the object ID of the most recent inserted row if the + database supports it. An invalid QVariant will be returned if the + query did not insert any value or if the database does not report + the id back. If more than one row was touched by the insert, the + behavior is undefined. + + For MySQL databases the row's auto-increment field will be returned. + + \note For this function to work in PSQL, the table table must + contain OIDs, which may not have been created by default. Check the + \c default_with_oids configuration variable to be sure. + + \sa QSqlDriver::hasFeature() +*/ +QVariant QSqlQuery::lastInsertId() const +{ + return d->sqlResult->lastInsertId(); +} + +/*! + + Instruct the database driver to return numerical values with a + precision specified by \a precisionPolicy. + + The Oracle driver, for example, retrieves numerical values as + strings by default to prevent the loss of precision. If the high + precision doesn't matter, use this method to increase execution + speed by bypassing string conversions. + + Note: Drivers that don't support fetching numerical values with low + precision will ignore the precision policy. You can use + QSqlDriver::hasFeature() to find out whether a driver supports this + feature. + + Note: Setting the precision policy doesn't affect the currently + active query. Call \l{exec()}{exec(QString)} or prepare() in order + to activate the policy. + + \sa QSql::NumericalPrecisionPolicy, numericalPrecisionPolicy() +*/ +void QSqlQuery::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy) +{ + d->precisionPolicy = precisionPolicy; +} + +/*! + Returns the current precision policy. + + \sa QSql::NumericalPrecisionPolicy, setNumericalPrecisionPolicy() +*/ +QSql::NumericalPrecisionPolicy QSqlQuery::numericalPrecisionPolicy() const +{ + return d->precisionPolicy; +} + +/*! + \since 4.3.2 + + Instruct the database driver that no more data will be fetched from + this query until it is re-executed. There is normally no need to + call this function, but it may be helpful in order to free resources + such as locks or cursors if you intend to re-use the query at a + later time. + + Sets the query to inactive. Bound values retain their values. + + \sa prepare() exec() isActive() +*/ +void QSqlQuery::finish() +{ + if (isActive()) { + d->sqlResult->setLastError(QSqlError()); + d->sqlResult->setAt(QSql::BeforeFirstRow); + d->sqlResult->detachFromResultSet(); + d->sqlResult->setActive(false); + } +} + +/*! + \since 4.4 + + Discards the current result set and navigates to the next if available. + + Some databases are capable of returning multiple result sets for + stored procedures or SQL batches (a query strings that contains + multiple statements). If multiple result sets are available after + executing a query this function can be used to navigate to the next + result set(s). + + If a new result set is available this function will return true. + The query will be repositioned on an \e invalid record in the new + result set and must be navigated to a valid record before data + values can be retrieved. If a new result set isn't available the + function returns false and the the query is set to inactive. In any + case the old result set will be discarded. + + When one of the statements is a non-select statement a count of + affected rows may be available instead of a result set. + + Note that some databases, i.e. Microsoft SQL Server, requires + non-scrollable cursors when working with multiple result sets. Some + databases may execute all statements at once while others may delay + the execution until the result set is actually accessed, and some + databases may have restrictions on which statements are allowed to + be used in a SQL batch. + + \sa QSqlDriver::hasFeature() setForwardOnly() next() isSelect() numRowsAffected() isActive() lastError() +*/ +bool QSqlQuery::nextResult() +{ + if (isActive()) + return d->sqlResult->nextResult(); + return false; +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlquery.h b/src/sql/kernel/qsqlquery.h new file mode 100644 index 0000000..e042fbd --- /dev/null +++ b/src/sql/kernel/qsqlquery.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLQUERY_H +#define QSQLQUERY_H + +#include <QtSql/qsql.h> +#include <QtSql/qsqldatabase.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QVariant; +class QSqlDriver; +class QSqlError; +class QSqlResult; +class QSqlRecord; +template <class Key, class T> class QMap; +class QSqlQueryPrivate; + +class Q_SQL_EXPORT QSqlQuery +{ +public: + QSqlQuery(QSqlResult *r); + QSqlQuery(const QString& query = QString(), QSqlDatabase db = QSqlDatabase()); + explicit QSqlQuery(QSqlDatabase db); + QSqlQuery(const QSqlQuery& other); + QSqlQuery& operator=(const QSqlQuery& other); + ~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; + QSqlRecord record() const; + + void setForwardOnly(bool forward); + bool exec(const QString& query); + QVariant value(int i) const; + + void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy); + QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const; + + bool seek(int i, bool relative = false); + bool next(); + bool previous(); +#ifdef QT3_SUPPORT + inline QT3_SUPPORT bool prev() { return previous(); } +#endif + bool first(); + bool last(); + + void clear(); + + // prepared query support + bool exec(); + enum BatchExecutionMode { ValuesAsRows, ValuesAsColumns }; + bool execBatch(BatchExecutionMode mode = ValuesAsRows); + bool prepare(const QString& query); + void bindValue(const QString& placeholder, const QVariant& val, + QSql::ParamType type = QSql::In); + void bindValue(int pos, const QVariant& val, QSql::ParamType type = QSql::In); + void addBindValue(const QVariant& val, QSql::ParamType type = QSql::In); + QVariant boundValue(const QString& placeholder) const; + QVariant boundValue(int pos) const; + QMap<QString, QVariant> boundValues() const; + QString executedQuery() const; + QVariant lastInsertId() const; + void finish(); + bool nextResult(); + +private: + QSqlQueryPrivate* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLQUERY_H diff --git a/src/sql/kernel/qsqlrecord.cpp b/src/sql/kernel/qsqlrecord.cpp new file mode 100644 index 0000000..0162664 --- /dev/null +++ b/src/sql/kernel/qsqlrecord.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsqlrecord.h" + +#include "qdebug.h" +#include "qstringlist.h" +#include "qatomic.h" +#include "qsqlfield.h" +#include "qstring.h" +#include "qvector.h" + +QT_BEGIN_NAMESPACE + +class QSqlRecordPrivate +{ +public: + QSqlRecordPrivate(); + QSqlRecordPrivate(const QSqlRecordPrivate &other); + + inline bool contains(int index) { return index >= 0 && index < fields.count(); } + QString createField(int index, const QString &prefix) const; + + QVector<QSqlField> fields; + QAtomicInt ref; +}; + +QSqlRecordPrivate::QSqlRecordPrivate() +{ + ref = 1; +} + +QSqlRecordPrivate::QSqlRecordPrivate(const QSqlRecordPrivate &other): fields(other.fields) +{ + ref = 1; +} + +/*! \internal + Just for compat +*/ +QString QSqlRecordPrivate::createField(int index, const QString &prefix) const +{ + QString f; + if (!prefix.isEmpty()) + f = prefix + QLatin1Char('.'); + f += fields.at(index).name(); + return f; +} + +/*! + \class QSqlRecord + \brief The QSqlRecord class encapsulates a database record. + + \ingroup database + \ingroup shared + \inmodule QtSql + + The QSqlRecord class encapsulates the functionality and + characteristics of a database record (usually a row in a table or + view within the database). QSqlRecord supports adding and + removing fields as well as setting and retrieving field values. + + The values of a record's fields' can be set by name or position + with setValue(); if you want to set a field to null use + setNull(). To find the position of a field by name use indexOf(), + and to find the name of a field at a particular position use + fieldName(). Use field() to retrieve a QSqlField object for a + given field. Use contains() to see if the record contains a + particular field name. + + When queries are generated to be executed on the database only + those fields for which isGenerated() is true are included in the + generated SQL. + + A record can have fields added with append() or insert(), replaced + with replace(), and removed with remove(). All the fields can be + removed with clear(). The number of fields is given by count(); + all their values can be cleared (to null) using clearValues(). + + \sa QSqlField, QSqlQuery::record() +*/ + + +/*! + Constructs an empty record. + + \sa isEmpty(), append(), insert() +*/ + +QSqlRecord::QSqlRecord() +{ + d = new QSqlRecordPrivate(); +} + +/*! + Constructs a copy of \a other. + + QSqlRecord is \l{implicitly shared}. This means you can make copies + of a record in \l{constant time}. +*/ + +QSqlRecord::QSqlRecord(const QSqlRecord& other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Sets the record equal to \a other. + + QSqlRecord is \l{implicitly shared}. This means you can make copies + of a record in \l{constant time}. +*/ + +QSqlRecord& QSqlRecord::operator=(const QSqlRecord& other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QSqlRecord::~QSqlRecord() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \fn bool QSqlRecord::operator!=(const QSqlRecord &other) const + + Returns true if this object is not identical to \a other; + otherwise returns false. + + \sa operator==() +*/ + +/*! + Returns true if this object is identical to \a other (i.e., has + the same fields in the same order); otherwise returns false. + + \sa operator!=() +*/ +bool QSqlRecord::operator==(const QSqlRecord &other) const +{ + return d->fields == other.d->fields; +} + +/*! + Returns the value of the field located at position \a index in + the record. If \a index is out of bounds, an invalid QVariant + is returned. + + \sa fieldName() isNull() +*/ + +QVariant QSqlRecord::value(int index) const +{ + return d->fields.value(index).value(); +} + +/*! + \overload + + Returns the value of the field called \a name in the record. If + field \a name does not exist an invalid variant is returned. + + \sa indexOf() +*/ + +QVariant QSqlRecord::value(const QString& name) const +{ + return value(indexOf(name)); +} + +/*! + Returns the name of the field at position \a index. If the field + does not exist, an empty string is returned. + + \sa indexOf() +*/ + +QString QSqlRecord::fieldName(int index) const +{ + return d->fields.value(index).name(); +} + +/*! + 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. + + \sa fieldName() +*/ + +int QSqlRecord::indexOf(const QString& name) const +{ + QString nm = name.toUpper(); + for (int i = 0; i < count(); ++i) { + if (d->fields.at(i).name().toUpper() == nm) // TODO: case-insensitive comparison + return i; + } + return -1; +} + +#ifdef QT3_SUPPORT +/*! + \obsolete + Use field() instead +*/ +const QSqlField* QSqlRecord::fieldPtr(int index) const +{ + if (!d->contains(index)) + return 0; + + return &d->fields.at(index); +} + +/*! + \obsolete + Use field() instead +*/ + +const QSqlField* QSqlRecord::fieldPtr(const QString& name) const +{ + int i = indexOf(name); + if (!d->contains(i)) + return 0; + + return &d->fields.at(i); +} +#endif //QT3_SUPPORT + +/*! + Returns the field at position \a index. If the position is out of + range, an empty field is returned. + */ +QSqlField QSqlRecord::field(int index) const +{ + return d->fields.value(index); +} + +/*! \overload + Returns the field called \a name. + */ +QSqlField QSqlRecord::field(const QString &name) const +{ + return field(indexOf(name)); +} + + +/*! + Append a copy of field \a field to the end of the record. + + \sa insert() replace() remove() +*/ + +void QSqlRecord::append(const QSqlField& field) +{ + detach(); + d->fields.append(field); +} + +/*! + Inserts the field \a field at position \a pos in the record. + + \sa append() replace() remove() + */ +void QSqlRecord::insert(int pos, const QSqlField& field) +{ + detach(); + d->fields.insert(pos, field); +} + +/*! + Replaces the field at position \a pos with the given \a field. If + \a pos is out of range, nothing happens. + + \sa append() insert() remove() +*/ + +void QSqlRecord::replace(int pos, const QSqlField& field) +{ + if (!d->contains(pos)) + return; + + detach(); + d->fields[pos] = field; +} + +/*! + Removes the field at position \a pos. If \a pos is out of range, + nothing happens. + + \sa append() insert() replace() +*/ + +void QSqlRecord::remove(int pos) +{ + if (!d->contains(pos)) + return; + + detach(); + d->fields.remove(pos); +} + +/*! + Removes all the record's fields. + + \sa clearValues() isEmpty() +*/ + +void QSqlRecord::clear() +{ + detach(); + d->fields.clear(); +} + +/*! + Returns true if there are no fields in the record; otherwise + returns false. + + \sa append() insert() clear() +*/ + +bool QSqlRecord::isEmpty() const +{ + return d->fields.isEmpty(); +} + + +/*! + Returns true if there is a field in the record called \a name; + otherwise returns false. +*/ + +bool QSqlRecord::contains(const QString& name) const +{ + return indexOf(name) >= 0; +} + +/*! + Clears the value of all fields in the record and sets each field + to null. + + \sa setValue() +*/ + +void QSqlRecord::clearValues() +{ + detach(); + int count = d->fields.count(); + for (int i = 0; i < count; ++i) + d->fields[i].clear(); +} + +/*! + 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 by QSqlQueryModel for example. + + \sa isGenerated() +*/ + +void QSqlRecord::setGenerated(const QString& name, bool generated) +{ + setGenerated(indexOf(name), generated); +} + +/*! + \overload + + Sets the generated flag for the field \a index to \a generated. + + \sa isGenerated() +*/ + +void QSqlRecord::setGenerated(int index, bool generated) +{ + if (!d->contains(index)) + return; + detach(); + d->fields[index].setGenerated(generated); +} + +/*! + \overload + + Returns true if the field \a index is null or if there is no field at + position \a index; otherwise returns false. +*/ +bool QSqlRecord::isNull(int index) const +{ + return d->fields.value(index).isNull(); +} + +/*! + Returns true if the field called \a name is null or if there is no + field called \a name; otherwise returns false. + + \sa setNull() +*/ +bool QSqlRecord::isNull(const QString& name) const +{ + return isNull(indexOf(name)); +} + +/*! + Sets the value of field \a index to null. If the field does not exist, + nothing happens. + + \sa setValue() +*/ +void QSqlRecord::setNull(int index) +{ + if (!d->contains(index)) + return; + detach(); + d->fields[index].clear(); +} + +/*! + \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(indexOf(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(indexOf(name)); +} + +/*! \overload + + Returns true if the record has a field at position \a index and this + field is to be generated (the default); otherwise returns false. + + \sa setGenerated() +*/ +bool QSqlRecord::isGenerated(int index) const +{ + return d->fields.value(index).isGenerated(); +} + +#ifdef QT3_SUPPORT +/*! + Returns a list of all the record's field names as a string + separated by \a sep. + + In the unlikely event that you used this function in Qt 3, you + can simulate it using the rest of the QSqlRecord public API. +*/ + +QString QSqlRecord::toString(const QString& prefix, const QString& sep) const +{ + QString pflist; + bool comma = false; + for (int i = 0; i < count(); ++i) { + if (!d->fields.value(i).isGenerated()) { + if (comma) + pflist += sep + QLatin1Char(' '); + pflist += d->createField(i, prefix); + comma = true; + } + } + return pflist; +} + +/*! + Returns a list of all the record's field names, each having the + prefix \a prefix. + + In the unlikely event that you used this function in Qt 3, you + can simulate it using the rest of the QSqlRecord public API. +*/ + +QStringList QSqlRecord::toStringList(const QString& prefix) const +{ + QStringList s; + for (int i = 0; i < count(); ++i) { + if (!d->fields.value(i).isGenerated()) + s += d->createField(i, prefix); + } + return s; +} +#endif // QT3_SUPPORT + +/*! + Returns the number of fields in the record. + + \sa isEmpty() +*/ + +int QSqlRecord::count() const +{ + return d->fields.count(); +} + +/*! + Sets the value of the field at position \a index to \a val. If the + field does not exist, nothing happens. + + \sa setNull() +*/ + +void QSqlRecord::setValue(int index, const QVariant& val) +{ + if (!d->contains(index)) + return; + detach(); + d->fields[index].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(indexOf(name), val); +} + + +/*! \internal +*/ +void QSqlRecord::detach() +{ + qAtomicDetach(d); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QSqlRecord &r) +{ + dbg << "QSqlRecord(" << r.count() << ")"; + for (int i = 0; i < r.count(); ++i) + dbg << '\n' << QString::fromLatin1("%1:").arg(i, 2) << r.field(i) << r.value(i).toString(); + return dbg; +} +#endif + +/*! + \fn int QSqlRecord::position(const QString& name) const + + Use indexOf() instead. +*/ + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlrecord.h b/src/sql/kernel/qsqlrecord.h new file mode 100644 index 0000000..239ecdd --- /dev/null +++ b/src/sql/kernel/qsqlrecord.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLRECORD_H +#define QSQLRECORD_H + +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QSqlField; +class QStringList; +class QVariant; +class QSqlRecordPrivate; + +class Q_SQL_EXPORT QSqlRecord +{ +public: + QSqlRecord(); + QSqlRecord(const QSqlRecord& other); + QSqlRecord& operator=(const QSqlRecord& other); + ~QSqlRecord(); + + bool operator==(const QSqlRecord &other) const; + inline bool operator!=(const QSqlRecord &other) const { return !operator==(other); } + + 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); + + void setNull(int i); + void setNull(const QString& name); + bool isNull(int i) const; + bool isNull(const QString& name) const; + + int indexOf(const QString &name) const; + QString fieldName(int i) const; + + QSqlField field(int i) const; + QSqlField field(const QString &name) const; + + bool isGenerated(int i) const; + bool isGenerated(const QString& name) const; + void setGenerated(const QString& name, bool generated); + void setGenerated(int i, bool generated); + +#ifdef QT3_SUPPORT + QT3_SUPPORT const QSqlField* fieldPtr(int i) const; + QT3_SUPPORT const QSqlField* fieldPtr(const QString& name) const; + inline QT3_SUPPORT int position(const QString& name) const { return indexOf(name); } + QT3_SUPPORT QString toString(const QString& prefix = QString(), + const QString& sep = QLatin1String(",")) const; + QT3_SUPPORT QStringList toStringList(const QString& prefix = QString()) const; +#endif + + void append(const QSqlField& field); + void replace(int pos, const QSqlField& field); + void insert(int pos, const QSqlField& field); + void remove(int pos); + + bool isEmpty() const; + bool contains(const QString& name) const; + void clear(); + void clearValues(); + int count() const; + +private: + void detach(); + QSqlRecordPrivate* d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_SQL_EXPORT QDebug operator<<(QDebug, const QSqlRecord &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLRECORD_H diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp new file mode 100644 index 0000000..180bfc7 --- /dev/null +++ b/src/sql/kernel/qsqlresult.cpp @@ -0,0 +1,1013 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvariant.h" +#include "qhash.h" +#include "qregexp.h" +#include "qsqlerror.h" +#include "qsqlfield.h" +#include "qsqlrecord.h" +#include "qsqlresult.h" +#include "qvector.h" +#include "qsqldriver.h" + +QT_BEGIN_NAMESPACE + +struct QHolder { + QHolder(const QString& hldr = QString(), int index = -1): holderName(hldr), holderPos(index) {} + bool operator==(const QHolder& h) const { return h.holderPos == holderPos && h.holderName == holderName; } + bool operator!=(const QHolder& h) const { return h.holderPos != holderPos || h.holderName != holderName; } + QString holderName; + int holderPos; +}; + +class QSqlResultPrivate +{ +public: + QSqlResultPrivate(QSqlResult* d) + : q(d), sqldriver(0), idx(QSql::BeforeFirstRow), active(false), + isSel(false), forwardOnly(false), bindCount(0), binds(QSqlResult::PositionalBinding) + {} + + void clearValues() + { + values.clear(); + bindCount = 0; + } + + void resetBindCount() + { + bindCount = 0; + } + + void clearIndex() + { + indexes.clear(); + holders.clear(); + types.clear(); + } + + void clear() + { + clearValues(); + clearIndex();; + } + + QString positionalToNamedBinding(); + QString namedToPositionalBinding(); + QString holderAt(int index) const; + +public: + QSqlResult* q; + const QSqlDriver* sqldriver; + int idx; + QString sql; + bool active; + bool isSel; + QSqlError error; + bool forwardOnly; + + int bindCount; + QSqlResult::BindingSyntax binds; + + QString executedQuery; + QHash<int, QSql::ParamType> types; + QVector<QVariant> values; + typedef QHash<QString, int> IndexMap; + IndexMap indexes; + + typedef QVector<QHolder> QHolderVector; + QHolderVector holders; +}; + +QString QSqlResultPrivate::holderAt(int index) const +{ + return indexes.key(index); +} + +// return a unique id for bound names +static QString qFieldSerial(int i) +{ + ushort arr[] = { ':', 'f', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ushort *ptr = &arr[1]; + + while (i > 0) { + *(++ptr) = 'a' + i % 16; + i >>= 4; + } + + return QString::fromUtf16(arr, int(ptr - arr) + 1); +} + +static bool qIsAlnum(QChar ch) +{ + uint u = uint(ch.unicode()); + // matches [a-zA-Z0-9_] + return u - 'a' < 26 || u - 'A' < 26 || u - '0' < 10 || u == '_'; +} + +QString QSqlResultPrivate::positionalToNamedBinding() +{ + int n = sql.size(); + + QString result; + result.reserve(n * 5 / 4); + bool inQuote = false; + int count = 0; + + for (int i = 0; i < n; ++i) { + QChar ch = sql.at(i); + if (ch == QLatin1Char('?') && !inQuote) { + result += qFieldSerial(count++); + } else { + if (ch == QLatin1Char('\'')) + inQuote = !inQuote; + result += ch; + } + } + result.squeeze(); + return result; +} + +QString QSqlResultPrivate::namedToPositionalBinding() +{ + int n = sql.size(); + + QString result; + result.reserve(n); + bool inQuote = false; + int count = 0; + int i = 0; + + while (i < n) { + QChar ch = sql.at(i); + if (ch == QLatin1Char(':') && !inQuote + && (i == 0 || sql.at(i - 1) != QLatin1Char(':')) + && (i < n - 1 && qIsAlnum(sql.at(i + 1)))) { + int pos = i + 2; + while (pos < n && qIsAlnum(sql.at(pos))) + ++pos; + indexes[sql.mid(i, pos - i)] = count++; + result += QLatin1Char('?'); + i = pos; + } else { + if (ch == QLatin1Char('\'')) + inQuote = !inQuote; + result += ch; + ++i; + } + } + result.squeeze(); + return result; +} + +/*! + \class QSqlResult + \brief The QSqlResult class provides an abstract interface for + accessing data from specific SQL databases. + + \ingroup database + \inmodule QtSql + + Normally, you would use QSqlQuery instead of QSqlResult, since + QSqlQuery provides a generic wrapper for database-specific + implementations of QSqlResult. + + If you are implementing your own SQL driver (by subclassing + QSqlDriver), you will need to provide your own QSqlResult + subclass that implements all the pure virtual functions and other + virtual functions that you need. + + \sa QSqlDriver +*/ + +/*! + \enum QSqlResult::BindingSyntax + + This enum type specifies the different syntaxes for specifying + placeholders in prepared queries. + + \value PositionalBinding Use the ODBC-style positional syntax, with "?" as placeholders. + \value NamedBinding Use the Oracle-style syntax with named placeholders (e.g., ":id") + \omitvalue BindByPosition + \omitvalue BindByName + + \sa bindingSyntax() +*/ + +/*! + \enum QSqlResult::VirtualHookOperation + \internal +*/ + +/*! + Creates a QSqlResult using database driver \a db. The object is + initialized to an inactive state. + + \sa isActive(), driver() +*/ + +QSqlResult::QSqlResult(const QSqlDriver *db) +{ + d = new QSqlResultPrivate(this); + d->sqldriver = db; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QSqlResult::~QSqlResult() +{ + delete d; +} + +/*! + Sets the current query for the result to \a query. You must call + reset() to execute the query on the database. + + \sa reset(), lastQuery() +*/ + +void QSqlResult::setQuery(const QString& query) +{ + d->sql = query; +} + +/*! + Returns the current SQL query text, or an empty string if there + isn't one. + + \sa setQuery() +*/ + +QString QSqlResult::lastQuery() const +{ + return d->sql; +} + +/*! + Returns the current (zero-based) row position of the result. May + return the special values QSql::BeforeFirstRow or + QSql::AfterLastRow. + + \sa setAt(), isValid() +*/ +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. + + \sa at() +*/ + +bool QSqlResult::isValid() const +{ + return d->idx != QSql::BeforeFirstRow && d->idx != QSql::AfterLastRow; +} + +/*! + \fn bool QSqlResult::isNull(int index) + + Returns true if the field at position \a index in the current row + 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; +} + +/*! + This function is provided for derived classes to set the + internal (zero-based) row position to \a index. + + \sa at() +*/ + +void QSqlResult::setAt(int index) +{ + d->idx = index; +} + + +/*! + This function is provided for derived classes to indicate whether + or not the current statement is a SQL \c SELECT statement. The \a + select parameter should be true if the statement is a \c SELECT + statement; otherwise it should be false. + + \sa isSelect() +*/ + +void QSqlResult::setSelect(bool select) +{ + d->isSel = select; +} + +/*! + Returns true if the current result is from a \c SELECT statement; + otherwise returns false. + + \sa setSelect() +*/ + +bool QSqlResult::isSelect() const +{ + return d->isSel; +} + +/*! + Returns the driver associated with the result. This is the object + that was passed to the constructor. +*/ + +const QSqlDriver *QSqlResult::driver() const +{ + return d->sqldriver; +} + + +/*! + This function is provided for derived classes to set the internal + active state to \a active. + + \sa isActive() +*/ + +void QSqlResult::setActive(bool active) +{ + if (active && d->executedQuery.isEmpty()) + d->executedQuery = d->sql; + + d->active = active; +} + +/*! + This function is provided for derived classes to set the last + error to \a error. + + \sa lastError() +*/ + +void QSqlResult::setLastError(const QSqlError &error) +{ + d->error = error; +} + + +/*! + Returns the last error associated with the result. +*/ + +QSqlError QSqlResult::lastError() const +{ + return d->error; +} + +/*! + \fn int QSqlResult::size() + + Returns the size of the \c SELECT result, or -1 if it cannot be + determined or if the query is not a \c SELECT statement. + + \sa numRowsAffected() +*/ + +/*! + \fn int QSqlResult::numRowsAffected() + + Returns the number of rows affected by the last query executed, or + -1 if it cannot be determined or if the query is a \c SELECT + statement. + + \sa size() +*/ + +/*! + \fn QVariant QSqlResult::data(int index) + + Returns the data for field \a index in the current row 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 index is + non-negative. Derived classes must reimplement this function and + return the value of field \a index, 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 only called 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. + + \sa setQuery() +*/ + +/*! + \fn bool QSqlResult::fetch(int index) + + Positions the result to an arbitrary (zero-based) row \a index. + + 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 row \a index, and call setAt() with an appropriate + value. Return true to indicate success, or false to signify + failure. + + \sa isActive(), fetchFirst(), fetchLast(), fetchNext(), fetchPrevious() +*/ + +/*! + \fn bool QSqlResult::fetchFirst() + + Positions the result to the first record (row 0) 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. + + \sa fetch(), fetchLast() +*/ + +/*! + \fn bool QSqlResult::fetchLast() + + Positions the result to the last record (last row) 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. + + \sa fetch(), fetchFirst() +*/ + +/*! + Positions the result to the next available record (row) 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. + + \sa fetch(), fetchPrevious() +*/ + +bool QSqlResult::fetchNext() +{ + return fetch(at() + 1); +} + +/*! + Positions the result to the previous record (row) 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::fetchPrevious() +{ + return fetch(at() - 1); +} + +/*! + Returns true if you can only scroll forward through the result + set; otherwise returns false. + + \sa setForwardOnly() +*/ +bool QSqlResult::isForwardOnly() const +{ + return d->forwardOnly; +} + +/*! + Sets forward only mode to \a forward. If \a forward is true, only + fetchNext() is allowed for navigating the results. Forward only + mode needs much less memory since results do not have to be + cached. By default, this feature is disabled. + + \sa isForwardOnly(), fetchNext() +*/ +void QSqlResult::setForwardOnly(bool forward) +{ + d->forwardOnly = forward; +} + +/*! + Prepares the given \a query, using the underlying database + functionality where possible. Returns true if the query is + prepared successfully; otherwise returns false. + + \sa prepare() +*/ +bool QSqlResult::savePrepare(const QString& query) +{ + if (!driver()) + return false; + d->clear(); + d->sql = query; + if (!driver()->hasFeature(QSqlDriver::PreparedQueries)) + return prepare(query); + + if (driver()->hasFeature(QSqlDriver::NamedPlaceholders)) { + // parse the query to memorize parameter location + d->namedToPositionalBinding(); + d->executedQuery = d->positionalToNamedBinding(); + } else { + d->executedQuery = d->namedToPositionalBinding(); + } + return prepare(d->executedQuery); +} + +/*! + Prepares the given \a query for execution; the query will normally + use placeholders so that it can be executed repeatedly. Returns + true if the query is prepared successfully; otherwise returns false. + + \sa exec() +*/ +bool QSqlResult::prepare(const QString& query) +{ + int n = query.size(); + + bool inQuote = false; + int i = 0; + + while (i < n) { + QChar ch = query.at(i); + if (ch == QLatin1Char(':') && !inQuote + && (i == 0 || query.at(i - 1) != QLatin1Char(':')) + && (i < n - 1 && qIsAlnum(query.at(i + 1)))) { + int pos = i + 2; + while (pos < n && qIsAlnum(query.at(pos))) + ++pos; + + d->holders.append(QHolder(query.mid(i, pos - i), i)); + i = pos; + } else { + if (ch == QLatin1Char('\'')) + inQuote = !inQuote; + ++i; + } + } + d->sql = query; + return true; // fake prepares should always succeed +} + +/*! + Executes the query, returning true if successful; otherwise returns + false. + + \sa prepare() +*/ +bool QSqlResult::exec() +{ + bool ret; + // fake preparation - just replace the placeholders.. + QString query = lastQuery(); + if (d->binds == NamedBinding) { + int i; + QVariant val; + QString holder; + for (i = d->holders.count() - 1; i >= 0; --i) { + holder = d->holders.at(i).holderName; + val = d->values.value(d->indexes.value(holder)); + QSqlField f(QLatin1String(""), val.type()); + f.setValue(val); + query = query.replace(d->holders.at(i).holderPos, + holder.length(), driver()->formatValue(f)); + } + } else { + QString val; + int i = 0; + int idx = 0; + for (idx = 0; idx < d->values.count(); ++idx) { + i = query.indexOf(QLatin1Char('?'), i); + if (i == -1) + continue; + QVariant var = d->values.value(idx); + QSqlField f(QLatin1String(""), var.type()); + if (var.isNull()) + f.clear(); + else + f.setValue(var); + val = driver()->formatValue(f); + query = query.replace(i, 1, driver()->formatValue(f)); + i += val.length(); + } + } + + // have to retain the original query with placeholders + QString orig = lastQuery(); + ret = reset(query); + d->executedQuery = query; + setQuery(orig); + d->resetBindCount(); + return ret; +} + +/*! + Binds the value \a val of parameter type \a paramType to position \a index + in the current record (row). + + \sa addBindValue() +*/ +void QSqlResult::bindValue(int index, const QVariant& val, QSql::ParamType paramType) +{ + d->binds = PositionalBinding; + d->indexes[qFieldSerial(index)] = index; + if (d->values.count() <= index) + d->values.resize(index + 1); + d->values[index] = val; + if (paramType != QSql::In || !d->types.isEmpty()) + d->types[index] = paramType; +} + +/*! + \overload + + Binds the value \a val of parameter type \a paramType to the \a + placeholder name in the current record (row). + + Note that binding an undefined placeholder will result in undefined behavior. +*/ +void QSqlResult::bindValue(const QString& placeholder, const QVariant& val, + QSql::ParamType paramType) +{ + d->binds = NamedBinding; + // if the index has already been set when doing emulated named + // bindings - don't reset it + int idx = d->indexes.value(placeholder, -1); + if (idx >= 0) { + if (d->values.count() <= idx) + d->values.resize(idx + 1); + d->values[idx] = val; + } else { + d->values.append(val); + idx = d->values.count() - 1; + d->indexes[placeholder] = idx; + } + + if (paramType != QSql::In || !d->types.isEmpty()) + d->types[idx] = paramType; +} + +/*! + Binds the value \a val of parameter type \a paramType to the next + available position in the current record (row). + + \sa bindValue() +*/ +void QSqlResult::addBindValue(const QVariant& val, QSql::ParamType paramType) +{ + d->binds = PositionalBinding; + bindValue(d->bindCount, val, paramType); + ++d->bindCount; +} + +/*! + Returns the value bound at position \a index in the current record + (row). + + \sa bindValue(), boundValues() +*/ +QVariant QSqlResult::boundValue(int index) const +{ + return d->values.value(index); +} + +/*! + \overload + + Returns the value bound by the given \a placeholder name in the + current record (row). + + \sa bindValueType() +*/ +QVariant QSqlResult::boundValue(const QString& placeholder) const +{ + int idx = d->indexes.value(placeholder, -1); + return d->values.value(idx); +} + +/*! + Returns the parameter type for the value bound at position \a index. + + \sa boundValue() +*/ +QSql::ParamType QSqlResult::bindValueType(int index) const +{ + return d->types.value(index, QSql::In); +} + +/*! + \overload + + Returns the parameter type for the value bound with the given \a + placeholder name. +*/ +QSql::ParamType QSqlResult::bindValueType(const QString& placeholder) const +{ + return d->types.value(d->indexes.value(placeholder, -1), QSql::In); +} + +/*! + Returns the number of bound values in the result. + + \sa boundValues() +*/ +int QSqlResult::boundValueCount() const +{ + return d->values.count(); +} + +/*! + Returns a vector of the result's bound values for the current + record (row). + + \sa boundValueCount() +*/ +QVector<QVariant>& QSqlResult::boundValues() const +{ + return d->values; +} + +/*! + Returns the binding syntax used by prepared queries. +*/ +QSqlResult::BindingSyntax QSqlResult::bindingSyntax() const +{ + return d->binds; +} + +/*! + Clears the entire result set and releases any associated + resources. +*/ +void QSqlResult::clear() +{ + d->clear(); +} + +/*! + Returns the query that was actually executed. This may differ from + the query that was passed, for example if bound values were used + with a prepared query and the underlying database doesn't support + prepared queries. + + \sa exec(), setQuery() +*/ +QString QSqlResult::executedQuery() const +{ + return d->executedQuery; +} + +void QSqlResult::resetBindCount() +{ + d->resetBindCount(); +} + +/*! + Returns the name of the bound value at position \a index in the + current record (row). + + \sa boundValue() +*/ +QString QSqlResult::boundValueName(int index) const +{ + return d->holderAt(index); +} + +/*! + Returns true if at least one of the query's bound values is a \c + QSql::Out or a QSql::InOut; otherwise returns false. + + \sa bindValueType() +*/ +bool QSqlResult::hasOutValues() const +{ + if (d->types.isEmpty()) + return false; + QHash<int, QSql::ParamType>::ConstIterator it; + for (it = d->types.constBegin(); it != d->types.constEnd(); ++it) { + if (it.value() != QSql::In) + return true; + } + return false; +} + +/*! + Returns the current record if the query is active; otherwise + returns an empty QSqlRecord. + + The default implementation always returns an empty QSqlRecord. + + \sa isActive() +*/ +QSqlRecord QSqlResult::record() const +{ + return QSqlRecord(); +} + +/*! + Returns the object ID of the most recent inserted row if the + database supports it. + An invalid QVariant will be returned if the query did not + insert any value or if the database does not report the id back. + If more than one row was touched by the insert, the behavior is + undefined. + + Note that for Oracle databases the row's ROWID will be returned, + while for MySQL databases the row's auto-increment field will + be returned. + + \sa QSqlDriver::hasFeature() +*/ +QVariant QSqlResult::lastInsertId() const +{ + return QVariant(); +} + +/*! \internal +*/ +void QSqlResult::virtual_hook(int, void *) +{ + Q_ASSERT(false); +} + +/*! \internal + \since 4.2 + + Executes a prepared query in batch mode if the driver supports it, + otherwise emulates a batch execution using bindValue() and exec(). + QSqlDriver::hasFeature() can be used to find out whether a driver + supports batch execution. + + Batch execution can be faster for large amounts of data since it + reduces network roundtrips. + + For batch executions, bound values have to be provided as lists + of variants (QVariantList). + + Each list must contain values of the same type. All lists must + contain equal amount of values (rows). + + NULL values are passed in as typed QVariants, for example + \c {QVariant(QVariant::Int)} for an integer NULL value. + + Example: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlresult.cpp 0 + + Here, we insert two rows into a SQL table, with each row containing three values. + + \sa exec(), QSqlDriver::hasFeature() +*/ +bool QSqlResult::execBatch(bool arrayBind) +{ + if (driver()->hasFeature(QSqlDriver::BatchOperations)) { + virtual_hook(BatchOperation, &arrayBind); + d->resetBindCount(); + return d->error.type() == QSqlError::NoError; + } else { + QVector<QVariant> values = d->values; + if (values.count() == 0) + return false; + for (int i = 0; i < values.at(0).toList().count(); ++i) { + for (int j = 0; j < values.count(); ++j) + bindValue(j, values.at(j).toList().at(i), QSql::In); + if (!exec()) + return false; + } + return true; + } + return false; +} + +/*! \internal + */ +void QSqlResult::detachFromResultSet() +{ + if (driver()->hasFeature(QSqlDriver::FinishQuery) + || driver()->hasFeature(QSqlDriver::SimpleLocking)) + virtual_hook(DetachFromResultSet, 0); +} + +/*! \internal + */ +void QSqlResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy) +{ + if (driver()->hasFeature(QSqlDriver::LowPrecisionNumbers)) + virtual_hook(SetNumericalPrecision, &policy); +} + +/*! \internal +*/ +bool QSqlResult::nextResult() +{ + if (driver()->hasFeature(QSqlDriver::MultipleResultSets)) { + bool result = false; + virtual_hook(NextResult, &result); + return result; + } + return false; +} + +/*! + Returns the low-level database handle for this result set + wrapped in a QVariant or an invalid QVariant if there is no handle. + + \warning Use this with uttermost care and only if you know what you're doing. + + \warning The handle returned here can become a stale pointer if the result + is modified (for example, if you clear it). + + \warning The handle can be NULL if the result was not executed yet. + + The handle returned here is database-dependent, you should query the type + name of the variant before accessing it. + + This example retrieves the handle for a sqlite result: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlresult.cpp 1 + + This snippet returns the handle for PostgreSQL or MySQL: + + \snippet doc/src/snippets/code/src_sql_kernel_qsqlresult.cpp 2 + + \sa QSqlDriver::handle() +*/ +QVariant QSqlResult::handle() const +{ + return QVariant(); +} + +QT_END_NAMESPACE diff --git a/src/sql/kernel/qsqlresult.h b/src/sql/kernel/qsqlresult.h new file mode 100644 index 0000000..0a3d8b9 --- /dev/null +++ b/src/sql/kernel/qsqlresult.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQLRESULT_H +#define QSQLRESULT_H + +#include <QtCore/qvariant.h> +#include <QtCore/qvector.h> +#include <QtSql/qsql.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Sql) + +class QString; +class QSqlRecord; +template <typename T> class QVector; +class QVariant; +class QSqlDriver; +class QSqlError; +class QSqlResultPrivate; + +class Q_SQL_EXPORT QSqlResult +{ + friend class QSqlQuery; + friend class QSqlTableModelPrivate; + friend class QSqlResultPrivate; + +public: + virtual ~QSqlResult(); + virtual QVariant handle() const; + +protected: + enum BindingSyntax { + PositionalBinding, + NamedBinding +#ifdef QT3_SUPPORT + , BindByPosition = PositionalBinding, + BindByName = NamedBinding +#endif + }; + + explicit 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); + + // prepared query support + virtual bool exec(); + virtual bool prepare(const QString& query); + virtual bool savePrepare(const QString& sqlquery); + virtual void bindValue(int pos, const QVariant& val, QSql::ParamType type); + virtual void bindValue(const QString& placeholder, const QVariant& val, + QSql::ParamType type); + void addBindValue(const QVariant& val, QSql::ParamType type); + QVariant boundValue(const QString& placeholder) const; + QVariant boundValue(int pos) const; + QSql::ParamType bindValueType(const QString& placeholder) const; + QSql::ParamType bindValueType(int pos) const; + int boundValueCount() const; + QVector<QVariant>& boundValues() const; + QString executedQuery() const; + QString boundValueName(int pos) const; + void clear(); + bool hasOutValues() const; + + BindingSyntax bindingSyntax() const; + + 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 fetchPrevious(); + virtual bool fetchFirst() = 0; + virtual bool fetchLast() = 0; + virtual int size() = 0; + virtual int numRowsAffected() = 0; + virtual QSqlRecord record() const; + virtual QVariant lastInsertId() const; + + enum VirtualHookOperation { BatchOperation, DetachFromResultSet, SetNumericalPrecision, NextResult }; + virtual void virtual_hook(int id, void *data); + bool execBatch(bool arrayBind = false); + void detachFromResultSet(); + void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy); + bool nextResult(); + +private: + QSqlResultPrivate* d; + void resetBindCount(); // HACK + +private: + Q_DISABLE_COPY(QSqlResult) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQLRESULT_H |