diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index 81c0aeec038..fdbb16a5898 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -7,6 +7,7 @@ #include "qcoreapplication.h" #include "qreadwritelock.h" #include "qsqldriver.h" +#include "qsqldriver_p.h" #include "qsqldriverplugin.h" #include "qsqlindex.h" #include "QtCore/qapplicationstatic.h" @@ -282,6 +283,10 @@ void QSqlDatabasePrivate::disable() QSqlDriver. Alternatively, you can subclass your own database driver from QSqlDriver. See \l{How to Write Your Own Database Driver} for more information. + A QSqlDatabase instance must only be accessed by the thread it + was created in. Therefore you have to make sure to create them + in the correct context. Alternatively you can change the context + with QSqlDatabase::moveToThread(). Create a connection (i.e., an instance of QSqlDatabase) by calling one of the static addDatabase() functions, where you specify @@ -1333,6 +1338,50 @@ QSql::NumericalPrecisionPolicy QSqlDatabase::numericalPrecisionPolicy() const return d->precisionPolicy; } +/*! + \since 6.8 + + Changes the thread affinity for QSqlDatabase and its associated driver. + This function returns \c true when the function succeeds. Event processing + will continue in the \a targetThread. + + During this operation you have to make sure that there is no QSqlQuery + bound to this instance otherwise the QSqlDatabase will not be moved to + the given thread and the function returns \c false. + + Since the associated driver is derived from QObject, all constraints for + moving a QObject to another thread also apply to this function. + + \sa QObject::moveToThread(), {Threads and the SQL Module} +*/ +bool QSqlDatabase::moveToThread(QThread *targetThread) +{ + if (auto drv = driver()) { + if (drv != QSqlDatabasePrivate::shared_null()->driver) { + // two instances are alive - the one here and the one in dbDict() + if (d->ref.loadRelaxed() > 2) { + qWarning("QSqlDatabasePrivate::moveToThread: connection '%ls' is still in use " + "in the current thread.", qUtf16Printable(d->connName)); + return false; + } + return drv->moveToThread(targetThread); + } + } + return false; +} + +/*! + \since 6.8 + + Returns a pointer to the associated QThread instance. +*/ +QThread *QSqlDatabase::currentThread() const +{ + if (auto drv = driver()) + return drv->thread(); + return nullptr; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QSqlDatabase &d) diff --git a/src/sql/kernel/qsqldatabase.h b/src/sql/kernel/qsqldatabase.h index 10019c7d5c1..5059dbba83e 100644 --- a/src/sql/kernel/qsqldatabase.h +++ b/src/sql/kernel/qsqldatabase.h @@ -18,6 +18,7 @@ class QSqlIndex; class QSqlRecord; class QSqlQuery; class QSqlDatabasePrivate; +class QThread; class Q_SQL_EXPORT QSqlDriverCreatorBase { @@ -80,6 +81,8 @@ public: QString connectionName() const; void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy); QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const; + bool moveToThread(QThread *targetThread); + QThread *currentThread() const; QSqlDriver* driver() const; diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index 1b762abc687..19afacf6f91 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -104,6 +104,8 @@ private slots: void infinityAndNan(); void multipleThreads_data() { generic_data(); } void multipleThreads(); + void moveToThread_data() { generic_data(); } + void moveToThread(); void db2_valueCacheUpdate_data() { generic_data("QDB2"); } void db2_valueCacheUpdate(); @@ -2335,5 +2337,32 @@ void tst_QSqlDatabase::multipleThreads() QTRY_VERIFY(t.isFinished()); } +void tst_QSqlDatabase::moveToThread() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + auto clonedDb = QSqlDatabase::cloneDatabase(db, "clonedDb"); + auto mainThread = QThread::currentThread(); + CHECK_DATABASE(db); + QCOMPARE(db.currentThread(), mainThread); + QCOMPARE(clonedDb.currentThread(), mainThread); + std::unique_ptr t(QThread::create([&] { + db.moveToThread(mainThread); + QThread::currentThread()->exit(); + })); + db.moveToThread(t.get()); + QCOMPARE(db.currentThread(), t.get()); + QCOMPARE(clonedDb.currentThread(), mainThread); + t->start(); + QTRY_VERIFY(t->isRunning()); + QTRY_VERIFY(t->wait(30000)); + QCOMPARE(db.currentThread(), mainThread); + QCOMPARE(clonedDb.currentThread(), mainThread); + db = QSqlDatabase(); + clonedDb = QSqlDatabase(); + QSqlDatabase::removeDatabase("clonedDb"); +} + + QTEST_MAIN(tst_QSqlDatabase) #include "tst_qsqldatabase.moc"