From 50f6cf6e4852ff39a79bde074204b498ee9ce21d Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 16:51:20 +0100 Subject: QSqlTableModel: remove unnecessary parameter from setSubmitted() Change-Id: I6d23788163ffd6ba7a8f01ed40910d861ff92703 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 571c28f515..075e32ff6e 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -684,7 +684,7 @@ bool QSqlTableModel::submitAll() Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); break; } - it.value().setSubmitted(true); + it.value().setSubmitted(); } return select(); -- cgit v1.2.3 From 463bd32fe5120d3954d93b6792050ac47796467f Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 22:50:40 +0100 Subject: QSqlTableModel: mirror database values in change cache Between submitting and the next select, these values will be more up-to-date than those that could be obtained from the query. This will be useful for constructing primary values and reverting changes made after submitting. Change-Id: I8317617f3e7043ad0b79b333731c55fb88aef171 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 075e32ff6e..d31e4bc335 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -510,7 +510,7 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in if (row.op() == QSqlTableModelPrivate::None) row = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update, - d->rec); + record(index.row())); row.setValue(index.column(), value); emit dataChanged(index, index); @@ -1004,7 +1004,7 @@ bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) // so fake this by adjusting row --row; } else { - d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete); + d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); if (d->strategy == OnManualSubmit) emit headerDataChanged(Qt::Vertical, idx, idx); } @@ -1235,7 +1235,7 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &values) QSqlTableModelPrivate::ModifiedRow &mrow = d->cache[row]; if (mrow.op() == QSqlTableModelPrivate::None) mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update, - d->rec); + record(row)); Map::const_iterator i = map.constBegin(); const Map::const_iterator e = map.constEnd(); -- cgit v1.2.3 From 698e620aad68d702c32b62e8114fe12a7b3e2ca5 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 23:45:44 +0100 Subject: QSqlTableModel: derive primary values from database values Primary values are used to map a row in the model to a row in the database table. It is critically important between submitting a change and the following select (which refreshes the query) to have updated primary values. Otherwise, if the change affected the primary values, additional changes before select will misbehave. Change-Id: I5d08dd70ac5d3f06cd9d3186a439f4c80a037c2d Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index d31e4bc335..a2999b30c4 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -201,16 +201,7 @@ bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement, QSqlRecord QSqlTableModelPrivate::primaryValues(int row) const { - Q_Q(const QSqlTableModel); - if (cache.value(row).op() == Insert) - return QSqlRecord(); - - QSqlRecord values(primaryIndex.isEmpty() ? rec : primaryIndex); - - for (int i = 0; i < values.count(); ++i) - values.setValue(i, q->QSqlQueryModel::data(createIndex(row, rec.indexOf(values.fieldName(i))), Qt::EditRole)); - - return values; + return cache.value(row).primaryValues(primaryIndex.isEmpty() ? rec : primaryIndex); } /*! -- cgit v1.2.3 From ea6d1fde1b5ce53691cc9b9154bd992a16be035f Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 15 Feb 2012 02:07:59 +0100 Subject: QSqlTableModel::data(): use cached values when available Simplify logic. If the record is in the cache, even untouched values should be there. This is also necessary for getting the most up-to-date values between submitting and the next select. Change-Id: I8578d96229797ce9fb0d07fe456301358f2be071 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index a2999b30c4..24668dd946 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -402,29 +402,9 @@ QVariant QSqlTableModel::data(const QModelIndex &index, int role) const if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::EditRole)) return QVariant(); - if (d->cache.contains(index.row())) { - const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row()); - - switch (d->strategy) { - case OnFieldChange: - case OnRowChange: - if (row.op() == QSqlTableModelPrivate::Insert) { - if (index.column() < 0 || index.column() >= row.rec().count()) - return QVariant(); - return row.rec().value(index.column()); - } else if (row.op() == QSqlTableModelPrivate::Update) { - if (row.rec().isGenerated(index.column())) - return row.rec().value(index.column()); - } - break; - case OnManualSubmit: - if (row.op() == QSqlTableModelPrivate::Insert - || (row.op() != QSqlTableModelPrivate::None - && row.rec().isGenerated(index.column()))) - return row.rec().value(index.column()); - break; - } - } + const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(index.row()); + if (mrow.op() != QSqlTableModelPrivate::None) + return mrow.rec().value(index.column()); return QSqlQueryModel::data(index, role); } -- cgit v1.2.3 From b979956ec46093e5668c2b264f9b68da3cbb0326 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 15 Feb 2012 09:56:23 +0100 Subject: QSqlTableModel: handle changes between submit and select Once an insert has been submitted, the cached record behaves like an update. For row bookkeeping, we still have to remember that it was originally inserted and is not in the query rows. Between submitting a delete and selecting, we remove the values from the deleted record. This causes a blank row to be displayed. Read-only flag is set for cells in deleted row. Reverting between submit and select means going back to the last submitted values. When removing rows, it's better to process from highest row numbers to lowest. This avoids complications with higher rows shifting down when lower rows are removed. Change-Id: I8752fa11f7a1b88f2a71b9e03a020ac37e62487f Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 52 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 24668dd946..9ade5f14d5 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -87,7 +87,7 @@ int QSqlTableModelPrivate::insertCount(int maxRow) const for (; i != e && (maxRow < 0 || i.key() <= maxRow); ++i) { - if (i.value().op() == Insert) + if (i.value().insert()) ++cnt; } @@ -122,19 +122,17 @@ void QSqlTableModelPrivate::revertCachedRow(int row) Q_Q(QSqlTableModel); ModifiedRow r = cache.value(row); - // cannot revert a committed change - if (r.submitted()) - return; - 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)); + if (!r.submitted()) { + cache[row].revert(); + emit q->dataChanged(q->createIndex(row, 0), + q->createIndex(row, q->columnCount() - 1)); + } break; case QSqlTableModelPrivate::Insert: { QMap::Iterator it = cache.find(row); @@ -373,7 +371,7 @@ bool QSqlTableModel::select() while (it != d->cache.constBegin()) { --it; // rows must be accounted for - if (it.value().op() == QSqlTableModelPrivate::Insert) { + if (it.value().insert()) { beginRemoveRows(QModelIndex(), it.key(), it.key()); it = d->cache.erase(it); endRemoveRows(); @@ -470,11 +468,14 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount()) return false; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(index.row()).op() != QSqlTableModelPrivate::Insert) { - d->cache.clear(); + revertAll(); } else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(index.row())) { submit(); - d->cache.clear(); + revertAll(); } QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()]; @@ -759,8 +760,10 @@ void QSqlTableModel::revertAll() { Q_D(QSqlTableModel); - while (!d->cache.isEmpty()) - revertRow(d->cache.constBegin().key()); + const QList rows(d->cache.keys()); + for (int i = rows.size() - 1; i >= 0; --i) { + revertRow(rows.value(i)); + } } /*! @@ -967,15 +970,17 @@ bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) else if (!count) return true; - for (int i = 0; i < count; ++i) { - int idx = row + i; - if (d->cache.value(idx).op() == QSqlTableModelPrivate::Insert) { + // Iterate backwards so we don't have to worry about removed rows causing + // higher cache entries to shift downwards. + for (int idx = row + count - 1; idx >= row; --idx) { + QSqlTableModelPrivate::ModifiedRow& mrow = d->cache[idx]; + if (mrow.op() == QSqlTableModelPrivate::Insert) { revertRow(idx); - // Reverting a row means all the other cache entries have been adjusted downwards - // so fake this by adjusting row - --row; } else { - d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + if (mrow.op() == QSqlTableModelPrivate::None) + mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + else + mrow.setOp(QSqlTableModelPrivate::Delete); if (d->strategy == OnManualSubmit) emit headerDataChanged(Qt::Vertical, idx, idx); } @@ -1158,6 +1163,8 @@ Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const return 0; if (d->rec.field(index.column()).isReadOnly()) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } @@ -1186,8 +1193,11 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &values) if (row >= rowCount()) return false; + if (d->cache.value(row).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(row).op() != QSqlTableModelPrivate::Insert) - d->cache.clear(); + revertAll(); else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(row)) submit(); -- cgit v1.2.3 From 291e2c7d5416af4d16dc0a6e60df7980ba745a3d Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Thu, 16 Feb 2012 02:38:02 +0100 Subject: QSqlTableModel: long live selectRow()! Change-Id: If26dbcc8a1e8ef1376ef7a688c946ce5270e5706 Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 9ade5f14d5..40230c3043 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -391,6 +391,49 @@ bool QSqlTableModel::select() return true; } +/*! + Refreshes \a row in the model with values from the database table row matching + on primary key values. Without a primary key, all column values must match. If + no matching row is found, the model will show an empty row. + + Returns true if successful; otherwise returns false. + + \sa select() +*/ +bool QSqlTableModel::selectRow(int row) +{ + Q_D(QSqlTableModel); + + if (row < 0 || row >= rowCount()) + return false; + + const int table_sort_col = d->sortColumn; + d->sortColumn = -1; + const QString table_filter = d->filter; + d->filter = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement, + d->tableName, + d->primaryValues(row), + false); + if (d->filter.startsWith(QLatin1String("WHERE "), Qt::CaseInsensitive)) + d->filter.remove(0, 6); + const QString stmt = selectStatement(); + d->sortColumn = table_sort_col; + d->filter = table_filter; + + QSqlQuery q(d->db); + q.setForwardOnly(true); + if (!q.exec(stmt)) + return false; + + bool exists = q.next(); + d->cache[row].refresh(exists, q.record()); + + emit headerDataChanged(Qt::Vertical, row, row); + emit dataChanged(createIndex(row, 0), createIndex(row, columnCount() - 1)); + + return true; +} + /*! \reimp */ -- cgit v1.2.3 From 888fed8065f708baeb6efa5f280c2f1aec3dee78 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 21 Feb 2012 09:22:26 +0100 Subject: QSqlTableModel: use selectRow() for field and row edit strategies Calling select refreshes the query data but disrupts view navigation. For OnFieldChange and OnRecordChange it makes sense to only select the row in question. This does not disturb view navigation. Assume disruption of view navigation is not a problem for OnManualSubmit because the user or application decides when submitAll is called. Task-number: QTBUG-2875 Change-Id: I1e5f68668fb9102f6296d67d543e80daa403f1c4 Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 40230c3043..aa7dc3dcf8 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -663,8 +663,8 @@ bool QSqlTableModel::deleteRowFromTable(int row) 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. + In OnManualSubmit, 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 @@ -677,6 +677,8 @@ bool QSqlTableModel::submitAll() { Q_D(QSqlTableModel); + bool success = true; + for (QSqlTableModelPrivate::CacheMap::Iterator it = d->cache.begin(); it != d->cache.constEnd(); ++it) { if (it.value().submitted()) @@ -684,25 +686,35 @@ bool QSqlTableModel::submitAll() switch (it.value().op()) { case QSqlTableModelPrivate::Insert: - if (!insertRowIntoTable(it.value().rec())) - return false; + success = insertRowIntoTable(it.value().rec()); break; case QSqlTableModelPrivate::Update: - if (!updateRowInTable(it.key(), it.value().rec())) - return false; + success = updateRowInTable(it.key(), it.value().rec()); break; case QSqlTableModelPrivate::Delete: - if (!deleteRowFromTable(it.key())) - return false; + success = deleteRowFromTable(it.key()); break; case QSqlTableModelPrivate::None: Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); break; } - it.value().setSubmitted(); + + if (success) { + it.value().setSubmitted(); + if (d->strategy != OnManualSubmit) + success = selectRow(it.key()); + } + + if (!success) + break; + } + + if (success) { + if (d->strategy == OnManualSubmit) + success = select(); } - return select(); + return success; } /*! @@ -719,8 +731,8 @@ bool QSqlTableModel::submitAll() 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. + Does not automatically repopulate the model. Submitted rows are + refreshed from the database on success. \sa revert(), revertRow(), submitAll(), revertAll(), lastError() */ -- cgit v1.2.3 From 79c0d9adbec05b0d6648e74952a862fa72b13c63 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 7 Mar 2012 00:24:08 +0100 Subject: QSqlTableModel::selectRow(): complete documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mention in changes and document Qt version (merci à dfaure). Follow-up to 291e2c7d5416af4d16dc0a6e60df7980ba745a3d. Change-Id: Ie5626e9cd268812c1173ca494ccd8d6bd9be2687 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/sql/models/qsqltablemodel.cpp') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index aa7dc3dcf8..d39df1d710 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -392,6 +392,8 @@ bool QSqlTableModel::select() } /*! + \since 5.0 + Refreshes \a row in the model with values from the database table row matching on primary key values. Without a primary key, all column values must match. If no matching row is found, the model will show an empty row. -- cgit v1.2.3