From 28702cb239b0dbc915ee3511768ff8e366e35e88 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 17 Aug 2018 12:58:01 +0200 Subject: [PATCH] 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 Reviewed-by: Edward Welbourne --- src/sql/models/qsqlquerymodel_p.h | 2 +- src/sql/models/qsqltablemodel_p.h | 2 +- .../qsqltablemodel/tst_qsqltablemodel.cpp | 47 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/sql/models/qsqlquerymodel_p.h b/src/sql/models/qsqlquerymodel_p.h index 76aaf00c88..d5ca2f89cb 100644 --- a/src/sql/models/qsqlquerymodel_p.h +++ b/src/sql/models/qsqlquerymodel_p.h @@ -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; diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index faa1b30803..bb568ab444 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -93,7 +93,7 @@ public: QSqlTableModel::EditStrategy strategy; bool busyInsertingRows; - QSqlQuery editQuery; + QSqlQuery editQuery = { QSqlQuery(0) }; QSqlIndex primaryIndex; QString tableName; QString filter; diff --git a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp index 430fa981d5..da31f437d9 100644 --- a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp +++ b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp @@ -31,11 +31,29 @@ #include "../../kernel/qsqldatabase/tst_databases.h" #include #include +#include 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"