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 <Friedemann.Kleint@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Andy Shaw 2017-12-05 14:21:05 +01:00
parent 2b0eb3fac3
commit 9b361f0e90
2 changed files with 41 additions and 0 deletions

View File

@ -50,6 +50,7 @@
#include "private/qsqlnulldriver_p.h"
#include "qmutex.h"
#include "qhash.h"
#include "qthread.h"
#include <stdlib.h>
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();

View File

@ -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"