Initialize the QSqlQuery to be invalid when creating a sql model

When QSqlQueryModel or QSqlTableModel is created it will create a
QSqlQuery which defaults to using the default QSqlDatabase connection.

If this connection belongs to another thread then it will throw a
warning as this is not safe to use. Since the QSqlQuery is always
recreated when a query is set, the instance which is a member of
the class can effectively be invalid until a new one is set.

Task-number: QTBUG-69213
Change-Id: I68a5dd59fe62788f531d59a0680da11b118ee383
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Andy Shaw 2018-08-17 12:58:01 +02:00 committed by Christian Ehrlicher
parent fbb4befa33
commit 28702cb239
3 changed files with 49 additions and 2 deletions

View File

@ -75,7 +75,7 @@ public:
void initColOffsets(int size);
int columnInQuery(int modelColumn) const;
mutable QSqlQuery query;
mutable QSqlQuery query = { QSqlQuery(0) };
mutable QSqlError error;
QModelIndex bottom;
QSqlRecord rec;

View File

@ -93,7 +93,7 @@ public:
QSqlTableModel::EditStrategy strategy;
bool busyInsertingRows;
QSqlQuery editQuery;
QSqlQuery editQuery = { QSqlQuery(0) };
QSqlIndex primaryIndex;
QString tableName;
QString filter;

View File

@ -31,11 +31,29 @@
#include "../../kernel/qsqldatabase/tst_databases.h"
#include <QtSql>
#include <QtSql/private/qsqltablemodel_p.h>
#include <QThread>
const QString test(qTableName("test", __FILE__, QSqlDatabase())),
test2(qTableName("test2", __FILE__, QSqlDatabase())),
test3(qTableName("test3", __FILE__, QSqlDatabase()));
// In order to catch when the warning message occurs, indicating that the database belongs to another
// thread, we have to install our own message handler. To ensure that the test reporting still happens
// as before, we call the originating one.
//
// For now, this is only called inside the modelInAnotherThread() test
QtMessageHandler oldHandler = nullptr;
void sqlTableModelMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (type == QtWarningMsg &&
msg == "QSqlDatabasePrivate::database: requested database does not "
"belong to the calling thread.") {
QFAIL("Requested database does not belong to the calling thread.");
}
if (oldHandler)
oldHandler(type, context, msg);
}
class tst_QSqlTableModel : public QObject
{
@ -116,6 +134,7 @@ private slots:
void sqlite_bigTable_data() { generic_data("QSQLITE"); }
void sqlite_bigTable();
void modelInAnotherThread();
// bug specific tests
void insertRecordBeforeSelect_data() { generic_data(); }
@ -276,6 +295,10 @@ void tst_QSqlTableModel::init()
void tst_QSqlTableModel::cleanup()
{
recreateTestTables();
if (oldHandler) {
qInstallMessageHandler(oldHandler);
oldHandler = nullptr;
}
}
void tst_QSqlTableModel::select()
@ -2100,5 +2123,29 @@ void tst_QSqlTableModel::invalidFilterAndHeaderData()
QVERIFY(!v.isValid());
}
class SqlThread : public QThread
{
public:
SqlThread() : QThread() {}
void run()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "non-default-connection");
QSqlTableModel stm(nullptr, db);
isDone = true;
}
bool isDone = false;
};
void tst_QSqlTableModel::modelInAnotherThread()
{
oldHandler = qInstallMessageHandler(sqlTableModelMessageHandler);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
CHECK_DATABASE(db);
SqlThread t;
t.start();
QTRY_VERIFY(t.isDone);
QVERIFY(t.isFinished());
}
QTEST_MAIN(tst_QSqlTableModel)
#include "tst_qsqltablemodel.moc"