QSqlQueryModel: fix nested beginResetModel/endResetModel
Follow-up to 83c9ebbd6692cde99ee692e6549c591100f12545. Consider the case where calls to the reset methods on the same object are nested as in the following sequence: 1. beginResetModel() 2. beginResetModel() 3. endResetModel() 4. endResetModel() In such cases, only the outermost calls, i.e., 1) and 4), should emit signals. After 83c9ebbd6692cde99ee692e6549c591100f12545, 1) and 3) emitted the signals, which is wrong. This is corrected by keeping track of the nesting level. Such sequences can come about when a base class calls the begin/end methods between the calls made by the subclass. QSqlTableModel::select() is an example of this. Test included. Change-Id: Ia62b45cb1abaab00a32bb8357de4a958bcff83e5 Reviewed-by: Andy Shaw <andy.shaw@digia.com> Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
98c663acd9
commit
c194b7f345
@ -78,10 +78,10 @@ void QSqlQueryModelPrivate::prefetch(int limit)
|
|||||||
atEnd = true; // this is the end.
|
atEnd = true; // this is the end.
|
||||||
}
|
}
|
||||||
if (newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
|
if (newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
|
||||||
if (!resetting)
|
if (!nestedResetLevel)
|
||||||
q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
|
q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
|
||||||
bottom = newBottom;
|
bottom = newBottom;
|
||||||
if (!resetting)
|
if (!nestedResetLevel)
|
||||||
q->endInsertRows();
|
q->endInsertRows();
|
||||||
} else {
|
} else {
|
||||||
bottom = newBottom;
|
bottom = newBottom;
|
||||||
@ -215,10 +215,9 @@ bool QSqlQueryModel::canFetchMore(const QModelIndex &parent) const
|
|||||||
void QSqlQueryModel::beginResetModel()
|
void QSqlQueryModel::beginResetModel()
|
||||||
{
|
{
|
||||||
Q_D(QSqlQueryModel);
|
Q_D(QSqlQueryModel);
|
||||||
if (!d->resetting) {
|
if (!d->nestedResetLevel)
|
||||||
QAbstractTableModel::beginResetModel();
|
QAbstractTableModel::beginResetModel();
|
||||||
d->resetting = true;
|
++d->nestedResetLevel;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \internal
|
/*! \internal
|
||||||
@ -226,10 +225,9 @@ void QSqlQueryModel::beginResetModel()
|
|||||||
void QSqlQueryModel::endResetModel()
|
void QSqlQueryModel::endResetModel()
|
||||||
{
|
{
|
||||||
Q_D(QSqlQueryModel);
|
Q_D(QSqlQueryModel);
|
||||||
if (d->resetting) {
|
--d->nestedResetLevel;
|
||||||
d->resetting = false;
|
if (!d->nestedResetLevel)
|
||||||
QAbstractTableModel::endResetModel();
|
QAbstractTableModel::endResetModel();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \fn int QSqlQueryModel::rowCount(const QModelIndex &parent) const
|
/*! \fn int QSqlQueryModel::rowCount(const QModelIndex &parent) const
|
||||||
|
@ -67,7 +67,7 @@ class QSqlQueryModelPrivate: public QAbstractItemModelPrivate
|
|||||||
{
|
{
|
||||||
Q_DECLARE_PUBLIC(QSqlQueryModel)
|
Q_DECLARE_PUBLIC(QSqlQueryModel)
|
||||||
public:
|
public:
|
||||||
QSqlQueryModelPrivate() : atEnd(false), resetting(false) {}
|
QSqlQueryModelPrivate() : atEnd(false), nestedResetLevel(0) {}
|
||||||
~QSqlQueryModelPrivate();
|
~QSqlQueryModelPrivate();
|
||||||
|
|
||||||
void prefetch(int);
|
void prefetch(int);
|
||||||
@ -80,7 +80,7 @@ public:
|
|||||||
uint atEnd : 1;
|
uint atEnd : 1;
|
||||||
QVector<QHash<int, QVariant> > headers;
|
QVector<QHash<int, QVariant> > headers;
|
||||||
QVarLengthArray<int, 56> colOffsets; // used to calculate indexInQuery of columns
|
QVarLengthArray<int, 56> colOffsets; // used to calculate indexInQuery of columns
|
||||||
bool resetting;
|
int nestedResetLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
// helpers for building SQL expressions
|
// helpers for building SQL expressions
|
||||||
|
@ -91,6 +91,8 @@ private slots:
|
|||||||
void setQuerySignalEmission();
|
void setQuerySignalEmission();
|
||||||
void setQueryWithNoRowsInResultSet_data() { generic_data(); }
|
void setQueryWithNoRowsInResultSet_data() { generic_data(); }
|
||||||
void setQueryWithNoRowsInResultSet();
|
void setQueryWithNoRowsInResultSet();
|
||||||
|
void nestedResets_data() { generic_data(); }
|
||||||
|
void nestedResets();
|
||||||
|
|
||||||
void task_180617();
|
void task_180617();
|
||||||
void task_180617_data() { generic_data(); }
|
void task_180617_data() { generic_data(); }
|
||||||
@ -585,6 +587,61 @@ void tst_QSqlQueryModel::setQueryWithNoRowsInResultSet()
|
|||||||
QCOMPARE(modelRowsInsertedSpy.count(), 0);
|
QCOMPARE(modelRowsInsertedSpy.count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NestedResetsTest: public QSqlQueryModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
NestedResetsTest(QObject* parent = 0) : QSqlQueryModel(parent), gotAboutToBeReset(false), gotReset(false)
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(modelAboutToBeReset()), this, SLOT(modelAboutToBeResetSlot()));
|
||||||
|
connect(this, SIGNAL(modelReset()), this, SLOT(modelResetSlot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testme()
|
||||||
|
{
|
||||||
|
// Only the outermost beginResetModel/endResetModel should
|
||||||
|
// emit signals.
|
||||||
|
gotAboutToBeReset = gotReset = false;
|
||||||
|
beginResetModel();
|
||||||
|
QCOMPARE(gotAboutToBeReset, true);
|
||||||
|
QCOMPARE(gotReset, false);
|
||||||
|
|
||||||
|
gotAboutToBeReset = gotReset = false;
|
||||||
|
beginResetModel();
|
||||||
|
QCOMPARE(gotAboutToBeReset, false);
|
||||||
|
QCOMPARE(gotReset, false);
|
||||||
|
|
||||||
|
gotAboutToBeReset = gotReset = false;
|
||||||
|
endResetModel();
|
||||||
|
QCOMPARE(gotAboutToBeReset, false);
|
||||||
|
QCOMPARE(gotReset, false);
|
||||||
|
|
||||||
|
gotAboutToBeReset = gotReset = false;
|
||||||
|
endResetModel();
|
||||||
|
QCOMPARE(gotAboutToBeReset, false);
|
||||||
|
QCOMPARE(gotReset, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void modelAboutToBeResetSlot() { gotAboutToBeReset = true; }
|
||||||
|
void modelResetSlot() { gotReset = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool gotAboutToBeReset;
|
||||||
|
bool gotReset;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QSqlQueryModel::nestedResets()
|
||||||
|
{
|
||||||
|
QFETCH(QString, dbName);
|
||||||
|
QSqlDatabase db = QSqlDatabase::database(dbName);
|
||||||
|
CHECK_DATABASE(db);
|
||||||
|
|
||||||
|
NestedResetsTest t;
|
||||||
|
t.testme();
|
||||||
|
}
|
||||||
|
|
||||||
// For task 180617
|
// For task 180617
|
||||||
// According to the task, several specific duplicate SQL queries would cause
|
// According to the task, several specific duplicate SQL queries would cause
|
||||||
// multiple empty grid lines to be visible in the view
|
// multiple empty grid lines to be visible in the view
|
||||||
|
Loading…
x
Reference in New Issue
Block a user