summaryrefslogtreecommitdiffstats
path: root/src/sql/kernel/qsqlresult.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sql/kernel/qsqlresult.cpp')
-rw-r--r--src/sql/kernel/qsqlresult.cpp1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp
new file mode 100644
index 0000000000..e8dcc044d2
--- /dev/null
+++ b/src/sql/kernel/qsqlresult.cpp
@@ -0,0 +1,1040 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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"
+#include <QDebug>
+
+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), precisionPolicy(QSql::LowPrecisionDouble), 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;
+ QSql::NumericalPrecisionPolicy precisionPolicy;
+
+ 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(reinterpret_cast<const QChar *>(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;
+ if(db) {
+ setNumericalPrecisionPolicy(db->numericalPrecisionPolicy());
+ }
+}
+
+/*!
+ 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.
+
+ Setting forward only to false is a suggestion to the database engine,
+ which has the final say on whether a result set is forward only or
+ scrollable. isForwardOnly() will always return the correct status of
+ the result set.
+
+ \note Calling setForwardOnly after execution of the query will result
+ in unexpected results at best, and crashes at worst.
+
+ \sa isForwardOnly(), fetchNext(), QSqlQuery::setForwardOnly()
+*/
+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).
+
+ Values cannot be bound to multiple locations in the query, eg:
+ \code
+ INSERT INTO testtable (id, name, samename) VALUES (:id, :name, :name)
+ \endcode
+ Binding to name will bind to the first :name, but not the second.
+
+ \note Binding an undefined placeholder will result in undefined behavior.
+
+ \sa QSqlQuery::bindValue()
+*/
+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 *)
+{
+}
+
+/*! \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)
+{
+ d->precisionPolicy = policy;
+ virtual_hook(SetNumericalPrecision, &policy);
+}
+
+/*! \internal
+ */
+QSql::NumericalPrecisionPolicy QSqlResult::numericalPrecisionPolicy() const
+{
+ return d->precisionPolicy;
+}
+
+/*! \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