My class DataTable is derived from QAbstractTableModel. It uses a QSqlTableModel object internally to fetch data from a db table. It represents a record for every row in the db (it does more but the record count is always the number of rows in the db table).
With MySql, my DataTable::rowCount() implementation just calls rowCount() on the QSqlTableModel, which works nicely.
Now with SQLite, Qt's SQLite driver returns a row count of 256 if there are more than 256 rows in the db table, so my DataTable class also returns 256 - which is wrong. The documentation tells me to call while (sql_model->canFetchMore()) sql_model->fetchMore();
. Calling fetchMore() right after the internal QSqlTableModel is created actually causes the following rowCount() call to return the correct value. But as soon as something is changed in the database (my class would call insertRow() or setData() on the QSqlTableModel), the next QSqlTableModel::rowCount() call will again return 256.
The database is only modified by my class, which in turn uses that particular QSqlTableModel object (or a view, which uses my DataTable as model, could update something). So there's no other process that could insert rows into the database.
So when should my DataTable class call fetchMore() for rowCount() to always return the actual row count?
I'm thinking my class should connect some of the signals emitted by QSqlTableModel to a slot that would call fetchMore(), although I'm not sure if that's the proper/reliable way to do it?
Update:
Here's some code to demonstrate the basic issue.
QSqlTableModel *model = new QSqlTableModel(0, database); //QSqlDatabase
model->setTable("tablename");
qDebug() << "0 row count" << model->rowCount(); //0 row count 0
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
qDebug() << "1 row count" << model->rowCount(); //1 row count 256
while (model->canFetchMore()) model->fetchMore();
qDebug() << "2 row count" << model->rowCount(); //2 row count 1520
//... other methods ...
model->setData(model->index(0, 0), "TEST");
model->submitAll();
qDebug() << "3 row count" << model->rowCount(); //3 row count 256
while (model->canFetchMore()) model->fetchMore();
qDebug() << "4 row count" << model->rowCount(); //4 row count 1520
After loading the sql model, rowCount() returns 256 (1), so fetchMore() has to be called. rowCount() then returns the actual row count.
Later, data is changed, after which rowCount() again returns 256 (3).
So it seems like fetchMore() has to be called after every write operation on the sql model. But rather than putting this while/canFetchMore()/fetchMore() loop at the end of every single method that modifies the model, I'm wondering if it would suffice to connect the beforeInsert(QSqlRecord&), beforeUpdate(int, QSqlRecord&) and beforeDelete(int) signals to a slot that would then call fetchAll()? Would this be reliable and appropriate?
Correction: Not before* signals (too early), but probably layoutChanged(), dataChanged(), rowsInserted() and rowsRemoved().
Update 2:
Note regarding SQL: I know that I could send a separate SELECT COUNT
SQL query to the database in theory, but this doesn't answer the question. As long as I can avoid SQL, I won't write SQL. In my mind, sending such an SQL query defies the purpose of an object-oriented QAbstractTableModel class. Plus rowCount() is const (should not send queries) and should be fast. Anyway, this won't fix rowCount().
I ended up connecting a slot that calls fetchMore() to the relevant signals (see above) AND asserting that everything has been fetched in rowCount():
assert(!sql_model->canFetchMore())
This is because rowCount() not being able to report the correct row count counts as failure state to me, hence the assertion. In other words, I'd rather want my application to crash than use an incorrect row count.
Just connecting it to the dataChanged() signal (as suggested in the first answer: I would probably try to use dataChanged signal.
) isn't sufficient. I have connected it to dataChanged(const QModelIndex&, const QModelIndex&)
, rowsInserted(const QModelIndex&, int, int)
, rowsRemoved(const QModelIndex&, int, int)
and layoutChanged()
.
Seems to work, the assertion has not failed yet.
If someone could specifically confirm this (or explain why it won't always work), I'd appreciate an answer.
The current answer does not fully answer the question (mentions dataChanged() signal but not the other signals), so I'm writing my own answer.
It's been a while and I believe I have covered all cases: I ended up connecting a slot that calls fetchMore() to the relevant signals AND asserting that everything has been fetched in my DataTable::rowCount() method: assert(!sql_model->canFetchMore())
(Of course rowCount() is a const method, so I couldn't fetch if nothing has been fetched yet, but that wouldn't be the job a getter anyway; the assertion is fine because canFetchMore() is also const.)
Signals: dataChanged(const QModelIndex&, const QModelIndex&), rowsInserted(const QModelIndex&, int, int), rowsRemoved(const QModelIndex&, int, int) and layoutChanged()
I'm using the assertion to make sure my model receives the correct row count or else the application would crash (which happens if not all of the mentioned signals are connected, for example layoutChanged()). This is important in my case, as an incorrect row count might lead to data loss in my case.
So far, the assertion hasn't failed, so I'm assuming this solves it.