From 9b361f0e90fb1c154a16e65ec087ad36d5cca9b4 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 5 Dec 2017 14:21:05 +0100 Subject: [PATCH] Return an invalid QSqlDatabase when accessing from another thread QSqlDatabase objects can only be used in the thread that the connection was opened for. So if the driver was created already then we check if the thread is correct. If it is not then we output a warning and return an invalid QSqlDatabase. [ChangeLog][QtSql][QSqlDatabase] QSqlDatabase::database() will return an invalid QSqlDatabase if the calling thread does not own the requested QSqlDatabase. Task-number: QTBUG-216 Change-Id: Ib5a25aa62129e3925f9819109af05961e5178bc5 Reviewed-by: Friedemann Kleint Reviewed-by: Edward Welbourne --- src/sql/kernel/qsqldatabase.cpp | 6 ++++ .../kernel/qsqldatabase/tst_qsqldatabase.cpp | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index b129499fe63..ded9bd72468 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -50,6 +50,7 @@ #include "private/qsqlnulldriver_p.h" #include "qmutex.h" #include "qhash.h" +#include "qthread.h" #include QT_BEGIN_NAMESPACE @@ -232,6 +233,11 @@ QSqlDatabase QSqlDatabasePrivate::database(const QString& name, bool open) dict->lock.lockForRead(); QSqlDatabase db = dict->value(name); dict->lock.unlock(); + if (db.driver() && db.driver()->thread() != QThread::currentThread()) { + qWarning("QSqlDatabasePrivate::database: requested database does not belong to the calling thread."); + return QSqlDatabase(); + } + if (db.isValid() && !db.isOpen() && open) { if (!db.open()) qWarning() << "QSqlDatabasePrivate::database: unable to open database:" << db.lastError().text(); diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index ec5afd2b5c6..427bc092a5c 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -126,6 +126,8 @@ private slots: void formatValueTrimStrings(); void precisionPolicy_data() { generic_data(); } void precisionPolicy(); + void multipleThreads_data() { generic_data(); } + void multipleThreads(); void db2_valueCacheUpdate_data() { generic_data("QDB2"); } void db2_valueCacheUpdate(); @@ -2317,5 +2319,38 @@ void tst_QSqlDatabase::cloneDatabase() } } +class DatabaseThreadObject : public QObject +{ + Q_OBJECT +public: + DatabaseThreadObject(const QString &name, QObject *parent = nullptr) : QObject(parent), dbName(name) + {} +public slots: + void ready() + { + QTest::ignoreMessage(QtWarningMsg, + "QSqlDatabasePrivate::database: requested database does not belong to the calling thread."); + QSqlDatabase db = QSqlDatabase::database(dbName); + QVERIFY(!db.isValid()); + QThread::currentThread()->exit(); + } +private: + QString dbName; +}; + +void tst_QSqlDatabase::multipleThreads() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + DatabaseThreadObject dto(dbName); + QThread t; + dto.moveToThread(&t); + connect(&t, &QThread::started, &dto, &DatabaseThreadObject::ready); + t.start(); + QTRY_VERIFY(t.isRunning()); + QTRY_VERIFY(t.isFinished()); +} + QTEST_MAIN(tst_QSqlDatabase) #include "tst_qsqldatabase.moc"