/qt-everywhere-opensource-src-4.7.1/src/sql/models/qsqltablemodel.cpp
C++ | 1340 lines | 776 code | 102 blank | 462 comment | 228 complexity | a02098c0c970cc1d4036db16c08c517f MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, BSD-3-Clause, GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, GPL-3.0, CC-BY-SA-4.0, Apache-2.0
- /****************************************************************************
- **
- ** Copyright (C) 2010 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$
- ** Commercial Usage
- ** Licensees holding valid Qt Commercial licenses may use this file in
- ** accordance with the Qt Commercial License Agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and Nokia.
- **
- ** 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.
- **
- ** 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 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() || lastError().isValid()) {
- // 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