src/sql/models/qsqltablemodel.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sql/models/qsqltablemodel.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1340 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 "qsqltablemodel.h"
+
+#include "qsqldriver.h"
+#include "qsqlerror.h"
+#include "qsqlfield.h"
+#include "qsqlindex.h"
+#include "qsqlquery.h"
+#include "qsqlrecord.h"
+#include "qsqlresult.h"
+
+#include "qsqltablemodel_p.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+    Populates our record with values.
+*/
+QSqlRecord QSqlTableModelPrivate::record(const QVector<QVariant> &values) const
+{
+    QSqlRecord r = rec;
+    for (int i = 0; i < r.count() && i < values.count(); ++i)
+        r.setValue(i, values.at(i));
+    return r;
+}
+
+/*! \internal
+    Set a record for OnFieldChange and OnRowChange.
+*/
+bool QSqlTableModelPrivate::setRecord(int row, const QSqlRecord &record)
+{
+    Q_Q(QSqlTableModel);
+    bool isOk = true;
+
+    QSqlTableModel::EditStrategy oldStrategy = strategy;
+
+    // FieldChange strategy makes no sense when setting an entire row
+    if (strategy == QSqlTableModel::OnFieldChange)
+        strategy = QSqlTableModel::OnRowChange;
+    for (int i = 0; i < record.count(); ++i) {
+        int idx = nameToIndex(record.fieldName(i));
+        if (idx == -1)
+            continue;
+        QModelIndex cIndex = q->createIndex(row, idx);
+        QVariant value = record.value(i);
+        QVariant oldValue = q->data(cIndex);
+        if (oldValue.isNull() || oldValue != value)
+            isOk &= q->setData(cIndex, value, Qt::EditRole);
+    }
+    if (isOk && oldStrategy == QSqlTableModel::OnFieldChange)
+        q->submitAll();
+    strategy = oldStrategy;
+
+    return isOk;
+}
+
+int QSqlTableModelPrivate::nameToIndex(const QString &name) const
+{
+    QString fieldname = name;
+    if (db.driver()->isIdentifierEscaped(fieldname, QSqlDriver::FieldName))
+        fieldname = db.driver()->stripDelimiters(fieldname, QSqlDriver::FieldName);
+    return rec.indexOf(fieldname);
+}
+
+void QSqlTableModelPrivate::initRecordAndPrimaryIndex()
+{
+    rec = db.record(tableName);
+    primaryIndex = db.primaryIndex(tableName);
+}
+
+void QSqlTableModelPrivate::clear()
+{
+    editIndex = -1;
+    sortColumn = -1;
+    sortOrder = Qt::AscendingOrder;
+    tableName.clear();
+    editQuery.clear();
+    editBuffer.clear();
+    cache.clear();
+    primaryIndex.clear();
+    rec.clear();
+    filter.clear();
+}
+
+void QSqlTableModelPrivate::revertInsertedRow()
+{
+    Q_Q(QSqlTableModel);
+    if (insertIndex == -1)
+        return;
+
+    q->beginRemoveRows(QModelIndex(), insertIndex, insertIndex);
+    insertIndex = -1;
+    q->endRemoveRows();
+}
+
+void QSqlTableModelPrivate::clearEditBuffer()
+{
+    editBuffer = rec;
+}
+
+void QSqlTableModelPrivate::clearCache()
+{
+    cache.clear();
+}
+
+void QSqlTableModelPrivate::revertCachedRow(int row)
+{
+    Q_Q(QSqlTableModel);
+    ModifiedRow r = cache.value(row);
+    switch (r.op) {
+    case QSqlTableModelPrivate::None:
+        Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map");
+        return;
+    case QSqlTableModelPrivate::Update:
+    case QSqlTableModelPrivate::Delete:
+        cache.remove(row);
+        emit q->dataChanged(q->createIndex(row, 0),
+                            q->createIndex(row, q->columnCount() - 1));
+        break;
+    case QSqlTableModelPrivate::Insert: {
+            QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = cache.find(row);
+            if (it == cache.end())
+                return;
+            q->beginRemoveRows(QModelIndex(), row, row);
+            it = cache.erase(it);
+            while (it != cache.end()) {
+                int oldKey = it.key();
+                const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
+                cache.erase(it);
+                it = cache.insert(oldKey - 1, oldValue);
+                ++it;
+            }
+            q->endRemoveRows();
+        break; }
+    }
+}
+
+bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement,
+                                 const QSqlRecord &rec, const QSqlRecord &whereValues)
+{
+    if (stmt.isEmpty())
+        return false;
+
+    // lazy initialization of editQuery
+    if (editQuery.driver() != db.driver())
+        editQuery = QSqlQuery(db);
+
+    // workaround for In-Process databases - remove all read locks
+    // from the table to make sure the editQuery succeeds
+    if (db.driver()->hasFeature(QSqlDriver::SimpleLocking))
+        const_cast<QSqlResult *>(query.result())->detachFromResultSet();
+
+    if (prepStatement) {
+        if (editQuery.lastQuery() != stmt) {
+            if (!editQuery.prepare(stmt)) {
+                error = editQuery.lastError();
+                return false;
+            }
+        }
+        int i;
+        for (i = 0; i < rec.count(); ++i) {
+            if (rec.isGenerated(i) && rec.value(i).type() != QVariant::Invalid)
+                editQuery.addBindValue(rec.value(i));
+        }
+        for (i = 0; i < whereValues.count(); ++i) {
+            if (whereValues.isGenerated(i) && !whereValues.isNull(i))
+                editQuery.addBindValue(whereValues.value(i));
+        }
+
+        if (!editQuery.exec()) {
+            error = editQuery.lastError();
+            return false;
+        }
+    } else {
+        if (!editQuery.exec(stmt)) {
+            error = editQuery.lastError();
+            return false;
+        }
+    }
+    return true;
+}
+
+QSqlRecord QSqlTableModelPrivate::primaryValues(int row)
+{
+    QSqlRecord record;
+    if (!query.seek(row)) {
+        error = query.lastError();
+        return record;
+    }
+    if (primaryIndex.isEmpty()) {
+        record = rec;
+        for (int i = 0; i < record.count(); ++i)
+            record.setValue(i, query.value(i));
+    } else {
+        record = primaryIndex;
+        for (int i = 0; i < record.count(); ++i)
+            record.setValue(i, query.value(rec.indexOf(record.fieldName(i))));
+    }
+    return record;
+}
+
+/*!
+    \class QSqlTableModel
+    \brief The QSqlTableModel class provides an editable data model
+    for a single database table.
+
+    \ingroup database
+    \inmodule QtSql
+
+    QSqlTableModel is a high-level interface for reading and writing
+    database records from a single table. It is build on top of the
+    lower-level QSqlQuery and can be used to provide data to view
+    classes such as QTableView. For example:
+
+    \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 24
+
+    We set the SQL table's name and the edit strategy, then we set up
+    the labels displayed in the view header. The edit strategy
+    dictates when the changes done by the user in the view are
+    actually applied to the database. The possible values are \l
+    OnFieldChange, \l OnRowChange, and \l OnManualSubmit.
+
+    QSqlTableModel can also be used to access a database
+    programmatically, without binding it to a view:
+
+    \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 25
+
+    The code snippet above extracts the \c salary field from record 4 in
+    the result set of the query \c{SELECT * from employee}.
+
+    It is possible to set filters using setFilter(), or modify the
+    sort order using setSort(). At the end, you must call select() to
+    populate the model with data.
+
+    The \l{sql/tablemodel} example illustrates how to use
+    QSqlTableModel as the data source for a QTableView.
+
+    QSqlTableModel provides no direct support for foreign keys. Use
+    the QSqlRelationalTableModel and QSqlRelationalDelegate if you
+    want to resolve foreign keys.
+
+    \sa QSqlRelationalTableModel, QSqlQuery, {Model/View Programming},
+        {Table Model Example}, {Cached Table Example}
+*/
+
+/*!
+    \fn QSqlTableModel::beforeDelete(int row)
+
+    This signal is emitted by deleteRowFromTable() before the \a row
+    is deleted from the currently active database table.
+*/
+
+/*!
+    \fn void QSqlTableModel::primeInsert(int row, QSqlRecord &record)
+
+    This signal is emitted by insertRows(), when an insertion is
+    initiated in the given \a row of the currently active database
+    table. The \a record parameter can be written to (since it is a
+    reference), for example to populate some fields with default
+    values.
+*/
+
+/*!
+    \fn QSqlTableModel::beforeInsert(QSqlRecord &record)
+
+    This signal is emitted by insertRowIntoTable() before a new row is
+    inserted into the currently active database table. The values that
+    are about to be inserted are stored in \a record and can be
+    modified before they will be inserted.
+*/
+
+/*!
+    \fn QSqlTableModel::beforeUpdate(int row, QSqlRecord &record)
+
+    This signal is emitted by updateRowInTable() before the \a row is
+    updated in the currently active database table with the values
+    from \a record.
+
+    Note that only values that are marked as generated will be updated.
+    The generated flag can be set with \l QSqlRecord::setGenerated()
+    and checked with \l QSqlRecord::isGenerated().
+
+    \sa QSqlRecord::isGenerated()
+*/
+
+/*!
+    Creates an empty QSqlTableModel and sets the parent to \a parent
+    and the database connection to \a db. If \a db is not valid, the
+    default database connection will be used.
+
+    The default edit strategy is \l OnRowChange.
+*/
+QSqlTableModel::QSqlTableModel(QObject *parent, QSqlDatabase db)
+    : QSqlQueryModel(*new QSqlTableModelPrivate, parent)
+{
+    Q_D(QSqlTableModel);
+    d->db = db.isValid() ? db : QSqlDatabase::database();
+}
+
+/*!  \internal
+*/
+QSqlTableModel::QSqlTableModel(QSqlTableModelPrivate &dd, QObject *parent, QSqlDatabase db)
+    : QSqlQueryModel(dd, parent)
+{
+    Q_D(QSqlTableModel);
+    d->db = db.isValid() ? db : QSqlDatabase::database();
+}
+
+/*!
+    Destroys the object and frees any allocated resources.
+*/
+QSqlTableModel::~QSqlTableModel()
+{
+}
+
+/*!
+    Sets the database table on which the model operates to \a
+    tableName. Does not select data from the table, but fetches its
+    field information.
+
+    To populate the model with the table's data, call select().
+
+    Error information can be retrieved with \l lastError().
+
+    \sa select(), setFilter(), lastError()
+*/
+void QSqlTableModel::setTable(const QString &tableName)
+{
+    Q_D(QSqlTableModel);
+    clear();
+    d->tableName = tableName;
+    d->initRecordAndPrimaryIndex();
+    d->initColOffsets(d->rec.count());
+
+    if (d->rec.count() == 0)
+        d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(),
+                             QSqlError::StatementError);
+}
+
+/*!
+    Returns the name of the currently selected table.
+*/
+QString QSqlTableModel::tableName() const
+{
+    Q_D(const QSqlTableModel);
+    return d->tableName;
+}
+
+/*!
+    Populates the model with data from the table that was set via setTable(), using the
+    specified filter and sort condition, and returns true if successful; otherwise
+    returns false.
+
+    \sa setTable(), setFilter(), selectStatement()
+*/
+bool QSqlTableModel::select()
+{
+    Q_D(QSqlTableModel);
+    QString query = selectStatement();
+    if (query.isEmpty())
+        return false;
+
+    revertAll();
+    QSqlQuery qu(query, d->db);
+    setQuery(qu);
+
+    if (!qu.isActive()) {
+        // something went wrong - revert to non-select state
+        d->initRecordAndPrimaryIndex();
+        return false;
+    }
+    return true;
+}
+
+/*!
+    \reimp
+*/
+QVariant QSqlTableModel::data(const QModelIndex &index, int role) const
+{
+    Q_D(const QSqlTableModel);
+    if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::EditRole))
+        return QVariant();
+
+    QModelIndex item = indexInQuery(index);
+
+    switch (d->strategy) {
+    case OnFieldChange:
+    case OnRowChange:
+        if (index.row() == d->insertIndex) {
+            QVariant val;
+            if (item.column() < 0 || item.column() >= d->rec.count())
+                return val;
+            val = d->editBuffer.value(index.column());
+            if (val.type() == QVariant::Invalid)
+                val = QVariant(d->rec.field(item.column()).type());
+            return val;
+        }
+        if (d->editIndex == item.row()) {
+            QVariant var = d->editBuffer.value(item.column());
+            if (var.isValid())
+                return var;
+        }
+        break;
+    case OnManualSubmit: {
+        const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row());
+        const QVariant var = row.rec.value(item.column());
+        if (var.isValid() || row.op == QSqlTableModelPrivate::Insert)
+            return var;
+        break; }
+    }
+    return QSqlQueryModel::data(item, role);
+}
+
+/*!
+    \reimp
+*/
+QVariant QSqlTableModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    Q_D(const QSqlTableModel);
+    if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
+        switch (d->strategy) {
+        case OnFieldChange:
+        case OnRowChange:
+            if (d->insertIndex == section)
+                return QLatin1String("*");
+            break;
+        case OnManualSubmit:
+            QSqlTableModelPrivate::Op op = d->cache.value(section).op;
+            if (op == QSqlTableModelPrivate::Insert)
+                return QLatin1String("*");
+            else if (op == QSqlTableModelPrivate::Delete)
+                return QLatin1String("!");
+            break;
+        }
+    }
+    return QSqlQueryModel::headerData(section, orientation, role);
+}
+
+/*!
+    Returns true if the value at the index \a index is dirty, otherwise false.
+    Dirty values are values that were modified in the model
+    but not yet written into the database.
+
+    If \a index is invalid or points to a non-existing row, false is returned.
+*/
+bool QSqlTableModel::isDirty(const QModelIndex &index) const
+{
+    Q_D(const QSqlTableModel);
+    if (!index.isValid())
+        return false;
+
+    switch (d->strategy) {
+        case OnFieldChange:
+            return false;
+        case OnRowChange:
+            return index.row() == d->editIndex && d->editBuffer.value(index.column()).isValid();
+        case OnManualSubmit: {
+            const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row());
+            return row.op == QSqlTableModelPrivate::Insert
+                   || row.op == QSqlTableModelPrivate::Delete
+                   || (row.op == QSqlTableModelPrivate::Update
+                       && row.rec.value(index.column()).isValid());
+        }
+    }
+    return false;
+}
+
+/*!
+    Sets the data for the item \a index for the role \a role to \a
+    value. Depending on the edit strategy, the value might be applied
+    to the database at once or cached in the model.
+
+    Returns true if the value could be set or false on error, for
+    example if \a index is out of bounds.
+
+    \sa editStrategy(), data(), submit(), submitAll(), revertRow()
+*/
+bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+    Q_D(QSqlTableModel);
+    if (role != Qt::EditRole)
+        return QSqlQueryModel::setData(index, value, role);
+
+    if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
+        return false;
+
+    bool isOk = true;
+    switch (d->strategy) {
+    case OnFieldChange: {
+        if (index.row() == d->insertIndex) {
+            d->editBuffer.setValue(index.column(), value);
+            return true;
+        }
+        d->clearEditBuffer();
+        d->editBuffer.setValue(index.column(), value);
+        isOk = updateRowInTable(index.row(), d->editBuffer);
+        if (isOk)
+            select();
+        emit dataChanged(index, index);
+        break; }
+    case OnRowChange:
+        if (index.row() == d->insertIndex) {
+            d->editBuffer.setValue(index.column(), value);
+            return true;
+        }
+        if (d->editIndex != index.row()) {
+            if (d->editIndex != -1)
+                submit();
+            d->clearEditBuffer();
+        }
+        d->editBuffer.setValue(index.column(), value);
+        d->editIndex = index.row();
+        emit dataChanged(index, index);
+        break;
+    case OnManualSubmit: {
+        QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
+        if (row.op == QSqlTableModelPrivate::None) {
+            row.op = QSqlTableModelPrivate::Update;
+            row.rec = d->rec;
+            row.primaryValues = d->primaryValues(indexInQuery(index).row());
+        }
+        row.rec.setValue(index.column(), value);
+        emit dataChanged(index, index);
+        break; }
+    }
+    return isOk;
+}
+
+/*!
+    This function simply calls QSqlQueryModel::setQuery(\a query).
+    You should normally not call it on a QSqlTableModel. Instead, use
+    setTable(), setSort(), setFilter(), etc., to set up the query.
+
+    \sa selectStatement()
+*/
+void QSqlTableModel::setQuery(const QSqlQuery &query)
+{
+    QSqlQueryModel::setQuery(query);
+}
+
+/*!
+    Updates the given \a row in the currently active database table
+    with the specified \a values. Returns true if successful; otherwise
+    returns false.
+
+    This is a low-level method that operates directly on the database
+    and should not be called directly. Use setData() to update values.
+    The model will decide depending on its edit strategy when to modify
+    the database.
+
+    Note that only values that have the generated-flag set are updated.
+    The generated-flag can be set with QSqlRecord::setGenerated() and
+    tested with QSqlRecord::isGenerated().
+
+    \sa QSqlRecord::isGenerated(), setData()
+*/
+bool QSqlTableModel::updateRowInTable(int row, const QSqlRecord &values)
+{
+    Q_D(QSqlTableModel);
+    QSqlRecord rec(values);
+    emit beforeUpdate(row, rec);
+
+    const QSqlRecord whereValues = d->strategy == OnManualSubmit ? d->cache[row].primaryValues : d->primaryValues(row);
+    bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries);
+    QString stmt = d->db.driver()->sqlStatement(QSqlDriver::UpdateStatement, d->tableName,
+                                                rec, prepStatement);
+    QString where = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement, d->tableName,
+                                                 whereValues, prepStatement);
+
+    if (stmt.isEmpty() || where.isEmpty() || row < 0 || row >= rowCount()) {
+        d->error = QSqlError(QLatin1String("No Fields to update"), QString(),
+                                 QSqlError::StatementError);
+        return false;
+    }
+    stmt.append(QLatin1Char(' ')).append(where);
+
+    return d->exec(stmt, prepStatement, rec, whereValues);
+}
+
+
+/*!
+    Inserts the values \a values into the currently active database table.
+
+    This is a low-level method that operates directly on the database
+    and should not be called directly. Use insertRow() and setData()
+    to insert values. The model will decide depending on its edit strategy
+    when to modify the database.
+
+    Returns true if the values could be inserted, otherwise false.
+    Error information can be retrieved with \l lastError().
+
+    \sa lastError(), insertRow(), insertRows()
+*/
+bool QSqlTableModel::insertRowIntoTable(const QSqlRecord &values)
+{
+    Q_D(QSqlTableModel);
+    QSqlRecord rec = values;
+    emit beforeInsert(rec);
+
+    bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries);
+    QString stmt = d->db.driver()->sqlStatement(QSqlDriver::InsertStatement, d->tableName,
+                                                rec, prepStatement);
+
+    if (stmt.isEmpty()) {
+        d->error = QSqlError(QLatin1String("No Fields to update"), QString(),
+                                 QSqlError::StatementError);
+        return false;
+    }
+
+    return d->exec(stmt, prepStatement, rec);
+}
+
+/*!
+    Deletes the given \a row from the currently active database table.
+
+    This is a low-level method that operates directly on the database
+    and should not be called directly. Use removeRow() or removeRows()
+    to delete values. The model will decide depending on its edit strategy
+    when to modify the database.
+
+    Returns true if the row was deleted; otherwise returns false.
+
+    \sa removeRow(), removeRows()
+*/
+bool QSqlTableModel::deleteRowFromTable(int row)
+{
+    Q_D(QSqlTableModel);
+    emit beforeDelete(row);
+
+    const QSqlRecord whereValues = d->strategy == OnManualSubmit ? d->cache[row].primaryValues : d->primaryValues(row);
+    bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries);
+    QString stmt = d->db.driver()->sqlStatement(QSqlDriver::DeleteStatement,
+                                                d->tableName,
+                                                QSqlRecord(),
+                                                prepStatement);
+    QString where = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement,
+                                                 d->tableName,
+                                                 whereValues,
+                                                 prepStatement);
+
+    if (stmt.isEmpty() || where.isEmpty()) {
+        d->error = QSqlError(QLatin1String("Unable to delete row"), QString(),
+                             QSqlError::StatementError);
+        return false;
+    }
+    stmt.append(QLatin1Char(' ')).append(where);
+
+    return d->exec(stmt, prepStatement, whereValues);
+}
+
+/*!
+    Submits all pending changes and returns true on success.
+    Returns false on error, detailed error information can be
+    obtained with lastError().
+
+    On success the model will be repopulated. Any views 
+    presenting it will lose their selections.
+
+    Note: In OnManualSubmit mode, already submitted changes won't
+    be cleared from the cache when submitAll() fails. This allows
+    transactions to be rolled back and resubmitted again without
+    losing data.
+
+    \sa revertAll(), lastError()
+*/
+bool QSqlTableModel::submitAll()
+{
+    Q_D(QSqlTableModel);
+
+    switch (d->strategy) {
+    case OnFieldChange:
+        if (d->insertIndex == -1)
+            return true;
+        // else fall through
+    case OnRowChange:
+        if (d->editBuffer.isEmpty())
+            return true;
+        if (d->insertIndex != -1) {
+            if (!insertRowIntoTable(d->editBuffer))
+                return false;
+            d->bottom = d->bottom.sibling(d->bottom.row() + 1, d->bottom.column());
+        } else {
+            if (!updateRowInTable(d->editIndex, d->editBuffer))
+                return false;
+        }
+        d->clearEditBuffer();
+        d->editIndex = -1;
+        d->insertIndex = -1;
+        return select();
+    case OnManualSubmit:
+        for (QSqlTableModelPrivate::CacheMap::ConstIterator it = d->cache.constBegin();
+             it != d->cache.constEnd(); ++it) {
+            switch (it.value().op) {
+            case QSqlTableModelPrivate::Insert:
+                if (!insertRowIntoTable(it.value().rec))
+                    return false;
+                d->bottom = d->bottom.sibling(d->bottom.row() + 1, d->bottom.column());
+                break;
+            case QSqlTableModelPrivate::Update:
+                if (!updateRowInTable(it.key(), it.value().rec))
+                    return false;
+                break;
+            case QSqlTableModelPrivate::Delete:
+                if (!deleteRowFromTable(it.key()))
+                    return false;
+                break;
+            case QSqlTableModelPrivate::None:
+                Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation");
+                break;
+            }
+        }
+        d->clearCache();
+        return select();
+    }
+    return false;
+}
+
+/*!
+    This reimplemented slot is called by the item delegates when the
+    user stopped editing the current row.
+
+    Submits the currently edited row if the model's strategy is set
+    to OnRowChange or OnFieldChange. Does nothing for the OnManualSubmit
+    strategy.
+
+    Use submitAll() to submit all pending changes for the
+    OnManualSubmit strategy.
+
+    Returns true on success; otherwise returns false. Use lastError()
+    to query detailed error information.
+
+    On success the model will be repopulated. Any views 
+    presenting it will lose their selections.
+
+    \sa revert(), revertRow(), submitAll(), revertAll(), lastError()
+*/
+bool QSqlTableModel::submit()
+{
+    Q_D(QSqlTableModel);
+    if (d->strategy == OnRowChange || d->strategy == OnFieldChange)
+        return submitAll();
+    return true;
+}
+
+/*!
+    This reimplemented slot is called by the item delegates when the
+    user canceled editing the current row.
+
+    Reverts the changes if the model's strategy is set to
+    OnRowChange. Does nothing for the other edit strategies.
+
+    Use revertAll() to revert all pending changes for the
+    OnManualSubmit strategy or revertRow() to revert a specific row.
+
+    \sa submit(), submitAll(), revertRow(), revertAll()
+*/
+void QSqlTableModel::revert()
+{
+    Q_D(QSqlTableModel);
+    if (d->strategy == OnRowChange)
+        revertAll();
+}
+
+/*!
+    \enum QSqlTableModel::EditStrategy
+
+    This enum type describes which strategy to choose when editing values in the database.
+
+    \value OnFieldChange  All changes to the model will be applied immediately to the database.
+    \value OnRowChange  Changes to a row will be applied when the user selects a different row.
+    \value OnManualSubmit  All changes will be cached in the model until either submitAll()
+                           or revertAll() is called.
+
+    Note: To prevent inserting only partly initialized rows into the database,
+    \c OnFieldChange will behave like \c OnRowChange for newly inserted rows.
+
+    \sa setEditStrategy()
+*/
+
+
+/*!
+    Sets the strategy for editing values in the database to \a
+    strategy.
+
+    This will revert any pending changes.
+
+    \sa editStrategy(), revertAll()
+*/
+void QSqlTableModel::setEditStrategy(EditStrategy strategy)
+{
+    Q_D(QSqlTableModel);
+    revertAll();
+    d->strategy = strategy;
+}
+
+/*!
+    Returns the current edit strategy.
+
+    \sa setEditStrategy()
+*/
+QSqlTableModel::EditStrategy QSqlTableModel::editStrategy() const
+{
+    Q_D(const QSqlTableModel);
+    return d->strategy;
+}
+
+/*!
+    Reverts all pending changes.
+
+    \sa revert(), revertRow(), submitAll()
+*/
+void QSqlTableModel::revertAll()
+{
+    Q_D(QSqlTableModel);
+    switch (d->strategy) {
+    case OnFieldChange:
+        break;
+    case OnRowChange:
+        if (d->editIndex != -1)
+            revertRow(d->editIndex);
+        else if (d->insertIndex != -1)
+            revertRow(d->insertIndex);
+        break;
+    case OnManualSubmit:
+        while (!d->cache.isEmpty())
+            revertRow(d->cache.constBegin().key());
+        break;
+    }
+}
+
+/*!
+    Reverts all changes for the specified \a row.
+
+    \sa revert(), revertAll(), submit(), submitAll()
+*/
+void QSqlTableModel::revertRow(int row)
+{
+    if (row < 0)
+        return;
+
+    Q_D(QSqlTableModel);
+    switch (d->strategy) {
+    case OnFieldChange:
+        break;
+    case OnRowChange: {
+        if (d->editIndex == row) {
+            d->editBuffer.clear();
+            int oldIndex = d->editIndex;
+            d->editIndex = -1;
+            emit dataChanged(createIndex(oldIndex, 0), createIndex(oldIndex, columnCount()));
+        } else if (d->insertIndex == row) {
+            d->revertInsertedRow();
+        }
+        break; }
+    case OnManualSubmit:
+        d->revertCachedRow(row);
+        break;
+    }
+}
+
+/*!
+    Returns the primary key for the current table, or an empty
+    QSqlIndex if the table is not set or has no primary key.
+
+    \sa setTable(), setPrimaryKey(), QSqlDatabase::primaryIndex()
+*/
+QSqlIndex QSqlTableModel::primaryKey() const
+{
+    Q_D(const QSqlTableModel);
+    return d->primaryIndex;
+}
+
+/*!
+    Protected method that allows subclasses to set the primary key to
+    \a key.
+
+    Normally, the primary index is set automatically whenever you
+    call setTable().
+
+    \sa primaryKey(), QSqlDatabase::primaryIndex()
+*/
+void QSqlTableModel::setPrimaryKey(const QSqlIndex &key)
+{
+    Q_D(QSqlTableModel);
+    d->primaryIndex = key;
+}
+
+/*!
+    Returns a pointer to the used QSqlDatabase or 0 if no database was set.
+*/
+QSqlDatabase QSqlTableModel::database() const
+{
+    Q_D(const QSqlTableModel);
+     return d->db;
+}
+
+/*!
+    Sorts the data by \a column with the sort order \a order.
+    This will immediately select data, use setSort()
+    to set a sort order without populating the model with data.
+
+    \sa setSort(), select(), orderByClause()
+*/
+void QSqlTableModel::sort(int column, Qt::SortOrder order)
+{
+    setSort(column, order);
+    select();
+}
+
+/*!
+    Sets the sort order for \a column to \a order. This does not
+    affect the current data, to refresh the data using the new
+    sort order, call select().
+
+    \sa select(), orderByClause()
+*/
+void QSqlTableModel::setSort(int column, Qt::SortOrder order)
+{
+    Q_D(QSqlTableModel);
+    d->sortColumn = column;
+    d->sortOrder = order;
+}
+
+/*!
+    Returns an SQL \c{ORDER BY} clause based on the currently set
+    sort order.
+
+    \sa setSort(), selectStatement()
+*/
+QString QSqlTableModel::orderByClause() const
+{
+    Q_D(const QSqlTableModel);
+    QString s;
+    QSqlField f = d->rec.field(d->sortColumn);
+    if (!f.isValid())
+        return s;
+        
+    QString table = d->tableName;
+    //we can safely escape the field because it would have been obtained from the database
+    //and have the correct case
+    QString field = d->db.driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName);
+    s.append(QLatin1String("ORDER BY ")).append(table).append(QLatin1Char('.')).append(field);
+    s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC");
+
+    return s;
+}
+
+/*!
+    Returns the index of the field \a fieldName.
+*/
+int QSqlTableModel::fieldIndex(const QString &fieldName) const
+{
+    Q_D(const QSqlTableModel);
+    return d->rec.indexOf(fieldName);
+}
+
+/*!
+    Returns the SQL \c SELECT statement used internally to populate
+    the model. The statement includes the filter and the \c{ORDER BY}
+    clause.
+
+    \sa filter(), orderByClause()
+*/
+QString QSqlTableModel::selectStatement() const
+{
+    Q_D(const QSqlTableModel);
+    QString query;
+    if (d->tableName.isEmpty()) {
+        d->error = QSqlError(QLatin1String("No table name given"), QString(),
+                             QSqlError::StatementError);
+        return query;
+    }
+    if (d->rec.isEmpty()) {
+        d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(),
+                             QSqlError::StatementError);
+        return query;
+    }
+
+    query = d->db.driver()->sqlStatement(QSqlDriver::SelectStatement,
+                                         d->tableName,
+                                         d->rec,
+                                         false);
+    if (query.isEmpty()) {
+        d->error = QSqlError(QLatin1String("Unable to select fields from table ") + d->tableName,
+                             QString(), QSqlError::StatementError);
+        return query;
+    }
+    if (!d->filter.isEmpty())
+        query.append(QLatin1String(" WHERE ")).append(d->filter);
+    QString orderBy(orderByClause());
+    if (!orderBy.isEmpty())
+        query.append(QLatin1Char(' ')).append(orderBy);
+
+    return query;
+}
+
+/*!
+    Removes \a count columns from the \a parent model, starting at
+    index \a column.
+
+    Returns if the columns were successfully removed; otherwise
+    returns false.
+
+    \sa removeRows()
+*/
+bool QSqlTableModel::removeColumns(int column, int count, const QModelIndex &parent)
+{
+    Q_D(QSqlTableModel);
+    if (parent.isValid() || column < 0 || column + count > d->rec.count())
+        return false;
+    for (int i = 0; i < count; ++i)
+        d->rec.remove(column);
+    if (d->query.isActive())
+        return select();
+    return true;
+}
+
+/*!
+    Removes \a count rows starting at \a row. Since this model
+    does not support hierarchical structures, \a parent must be
+    an invalid model index.
+
+    Emits the beforeDelete() signal before a row is deleted. When
+    the edit strategy is OnManualSubmit signal emission is delayed
+    until submitAll() is called.
+
+    Returns true if all rows could be removed; otherwise returns
+    false. Detailed error information can be retrieved using
+    lastError().
+
+    \sa removeColumns(), insertRows()
+*/
+bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+    Q_D(QSqlTableModel);
+    if (parent.isValid() || row < 0 || count <= 0)
+        return false;
+
+    int i;
+    switch (d->strategy) {
+    case OnFieldChange:
+    case OnRowChange:
+        for (i = 0; i < count; ++i) {
+            if (row + i == d->insertIndex)
+                d->revertInsertedRow();
+            else if (!deleteRowFromTable(row + i))
+                return false;
+        }
+        select();
+        break;
+    case OnManualSubmit:
+        for (i = 0; i < count; ++i) {
+            int idx = row + i;
+            if (idx >= rowCount())
+                return false;
+            if (d->cache.value(idx).op == QSqlTableModelPrivate::Insert)
+                revertRow(idx);
+            else {
+                d->cache[idx].op = QSqlTableModelPrivate::Delete;
+                d->cache[idx].primaryValues = d->primaryValues(indexInQuery(createIndex(idx, 0)).row());
+                emit headerDataChanged(Qt::Vertical, idx, idx);
+            }
+        }
+        break;
+    }
+    return true;
+}
+
+/*!
+    Inserts \a count empty rows at position \a row. Note that \a
+    parent must be invalid, since this model does not support
+    parent-child relations.
+
+    Only one row at a time can be inserted when using the
+    OnFieldChange or OnRowChange update strategies.
+
+    The primeInsert() signal will be emitted for each new row.
+    Connect to it if you want to initialize the new row with default
+    values.
+
+    Returns false if the parameters are out of bounds; otherwise
+    returns true.
+
+    \sa primeInsert(), insertRecord()
+*/
+bool QSqlTableModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+    Q_D(QSqlTableModel);
+    if (row < 0 || count <= 0 || row > rowCount() || parent.isValid())
+        return false;
+
+    switch (d->strategy) {
+    case OnFieldChange:
+    case OnRowChange:
+        if (count != 1)
+            return false;
+        beginInsertRows(parent, row, row);
+        d->insertIndex = row;
+        // ### apply dangling changes...
+        d->clearEditBuffer();
+        emit primeInsert(row, d->editBuffer);
+        break;
+    case OnManualSubmit:
+        beginInsertRows(parent, row, row + count - 1);
+        if (!d->cache.isEmpty()) {
+            QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = d->cache.end();
+            while (it != d->cache.begin() && (--it).key() >= row) {
+                int oldKey = it.key();
+                const QSqlTableModelPrivate::ModifiedRow oldValue = it.value();
+                d->cache.erase(it);
+                it = d->cache.insert(oldKey + count, oldValue);
+            }
+        }
+
+        for (int i = 0; i < count; ++i) {
+            d->cache[row + i] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Insert,
+                                                                   d->rec);
+            emit primeInsert(row + i, d->cache[row + i].rec);
+        }
+        break;
+    }
+    endInsertRows();
+    return true;
+}
+
+/*!
+    Inserts the \a record after \a row. If \a row is negative, the
+    record will be appended to the end. Calls insertRows() and
+    setRecord() internally.
+
+    Returns true if the row could be inserted, otherwise false.
+
+    \sa insertRows(), removeRows()
+*/
+bool QSqlTableModel::insertRecord(int row, const QSqlRecord &record)
+{
+    Q_D(QSqlTableModel);
+    if (row < 0)
+        row = rowCount();
+    if (!insertRow(row, QModelIndex()))
+        return false;
+    if (!setRecord(row, record))
+        return false;
+    if (d->strategy == OnFieldChange || d->strategy == OnRowChange)
+        return submit();
+    return true;
+}
+
+/*! \reimp
+*/
+int QSqlTableModel::rowCount(const QModelIndex &parent) const
+{
+    Q_D(const QSqlTableModel);
+
+    if (parent.isValid())
+        return 0;
+
+    int rc = QSqlQueryModel::rowCount();
+    if (d->strategy == OnManualSubmit) {
+        for (QSqlTableModelPrivate::CacheMap::ConstIterator it = d->cache.constBegin();
+             it != d->cache.constEnd(); ++it) {
+             if (it.value().op == QSqlTableModelPrivate::Insert)
+                 ++rc;
+        }
+    } else if (d->insertIndex >= 0) {
+        ++rc;
+    }
+    return rc;
+}
+
+/*!
+    Returns the index of the value in the database result set for the
+    given \a item in the model.
+
+    The return value is identical to \a item if no columns or rows
+    have been inserted, removed, or moved around.
+
+    Returns an invalid model index if \a item is out of bounds or if
+    \a item does not point to a value in the result set.
+
+    \sa QSqlQueryModel::indexInQuery()
+*/
+QModelIndex QSqlTableModel::indexInQuery(const QModelIndex &item) const
+{
+    Q_D(const QSqlTableModel);
+    const QModelIndex it = QSqlQueryModel::indexInQuery(item);
+    if (d->strategy == OnManualSubmit) {
+        int rowOffset = 0;
+        QSqlTableModelPrivate::CacheMap::ConstIterator i = d->cache.constBegin();
+        while (i != d->cache.constEnd() && i.key() <= it.row()) {
+            if (i.value().op == QSqlTableModelPrivate::Insert)
+                ++rowOffset;
+            ++i;
+        }
+        return createIndex(it.row() - rowOffset, it.column(), it.internalPointer());
+    } else {
+        if (d->insertIndex >= 0 && it.row() >= d->insertIndex)
+            return createIndex(it.row() - 1, it.column(), it.internalPointer());
+    }
+    return it;
+}
+
+/*!
+    Returns the currently set filter.
+
+    \sa setFilter(), select()
+*/
+QString QSqlTableModel::filter() const
+{
+    Q_D(const QSqlTableModel);
+    return d->filter;
+}
+
+/*!
+    Sets the current filter to \a filter.
+
+    The filter is a SQL \c WHERE clause without the keyword \c WHERE
+    (for example, \c{name='Josephine')}.
+
+    If the model is already populated with data from a database,
+    the model re-selects it with the new filter. Otherwise, the filter
+    will be applied the next time select() is called.
+
+    \sa filter(), select(), selectStatement(), orderByClause()
+*/
+void QSqlTableModel::setFilter(const QString &filter)
+{
+    Q_D(QSqlTableModel);
+    d->filter = filter;
+    if (d->query.isActive())
+        select();
+}
+
+/*! \reimp
+*/
+void QSqlTableModel::clear()
+{
+    Q_D(QSqlTableModel);
+    d->clear();
+    QSqlQueryModel::clear();
+}
+
+/*! \reimp
+*/
+Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const
+{
+    Q_D(const QSqlTableModel);
+    if (index.internalPointer() || index.column() < 0 || index.column() >= d->rec.count()
+        || index.row() < 0)
+        return 0;
+    if (d->rec.field(index.column()).isReadOnly())
+        return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+    return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
+}
+
+/*!
+    Sets the values at the specified \a row to the values of \a
+    record. Returns true if all the values could be set; otherwise
+    returns false.
+
+    \sa record()
+*/
+bool QSqlTableModel::setRecord(int row, const QSqlRecord &record)
+{
+    Q_D(QSqlTableModel);
+    Q_ASSERT_X(row >= 0, "QSqlTableModel::setRecord()", "Cannot set a record to a row less than 0");
+    if (row >= rowCount())
+        return false;
+
+    bool isOk = true;
+    switch (d->strategy) {
+    case OnFieldChange:
+    case OnRowChange:
+        return d->setRecord(row, record);
+    case OnManualSubmit: {
+        QSqlTableModelPrivate::ModifiedRow &mrow = d->cache[row];
+        if (mrow.op == QSqlTableModelPrivate::None) {
+            mrow.op = QSqlTableModelPrivate::Update;
+            mrow.rec = d->rec;
+            mrow.primaryValues = d->primaryValues(indexInQuery(createIndex(row, 0)).row());
+        }
+        QString fieldName;
+        for (int i = 0; i < record.count(); ++i) {
+            fieldName = record.fieldName(i);
+            if (d->db.driver()->isIdentifierEscaped(fieldName, QSqlDriver::FieldName))
+                fieldName = d->db.driver()->stripDelimiters(fieldName, QSqlDriver::FieldName);
+            int idx = mrow.rec.indexOf(fieldName);
+            if (idx == -1)
+                isOk = false;
+            else
+                mrow.rec.setValue(idx, record.value(i));
+        }
+        return isOk; }
+    }
+    return false;
+}
+
+QT_END_NAMESPACE