SQL: Make QSqlDatabase::DriverDict creation thread-safe

Make the QSqlDatabase::DriverDict thread-safe and make sure it's
properly cleaned up on destruction.

Pick-to: 6.5 6.2 5.15
Fixes: QTBUG-112961
Change-Id: I1ff70e477579231754ef829fdede944d6042894d
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Christian Ehrlicher 2023-04-18 19:40:25 +02:00
parent 68f35a571a
commit 048a68c1e9
2 changed files with 18 additions and 15 deletions

View File

@ -9,6 +9,7 @@
#include "qsqldriver.h" #include "qsqldriver.h"
#include "qsqldriverplugin.h" #include "qsqldriverplugin.h"
#include "qsqlindex.h" #include "qsqlindex.h"
#include "QtCore/qapplicationstatic.h"
#include "private/qfactoryloader_p.h" #include "private/qfactoryloader_p.h"
#include "private/qsqlnulldriver_p.h" #include "private/qsqlnulldriver_p.h"
#include "qhash.h" #include "qhash.h"
@ -23,8 +24,6 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
const char *QSqlDatabase::defaultConnection = "qt_sql_default_connection"; const char *QSqlDatabase::defaultConnection = "qt_sql_default_connection";
typedef QHash<QString, QSqlDriverCreatorBase*> DriverDict;
class QConnectionDict: public QHash<QString, QSqlDatabase> class QConnectionDict: public QHash<QString, QSqlDatabase>
{ {
public: public:
@ -48,6 +47,14 @@ public:
}; };
Q_GLOBAL_STATIC(QConnectionDict, dbDict) Q_GLOBAL_STATIC(QConnectionDict, dbDict)
namespace {
struct DriverDict : public QHash<QString, QSqlDriverCreatorBase*>
{
~DriverDict();
};
}
Q_APPLICATION_STATIC(DriverDict, qtDriverDict)
class QSqlDatabasePrivate class QSqlDatabasePrivate
{ {
public: public:
@ -120,23 +127,15 @@ void QSqlDatabasePrivate::cleanConnections()
dict->clear(); dict->clear();
} }
static bool qDriverDictInit = false; DriverDict::~DriverDict()
static void cleanDriverDict()
{ {
qDeleteAll(QSqlDatabasePrivate::driverDict()); qDeleteAll(*this);
QSqlDatabasePrivate::driverDict().clear();
QSqlDatabasePrivate::cleanConnections(); QSqlDatabasePrivate::cleanConnections();
qDriverDictInit = false;
} }
DriverDict &QSqlDatabasePrivate::driverDict() DriverDict &QSqlDatabasePrivate::driverDict()
{ {
static DriverDict dict; return *qtDriverDict();
if (!qDriverDictInit) {
qDriverDictInit = true;
qAddPostRoutine(cleanDriverDict);
}
return dict;
} }
QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null() QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null()
@ -504,7 +503,7 @@ QStringList QSqlDatabase::drivers()
} }
QReadLocker locker(&dbDict()->lock); QReadLocker locker(&dbDict()->lock);
const DriverDict dict = QSqlDatabasePrivate::driverDict(); const DriverDict &dict = QSqlDatabasePrivate::driverDict();
for (const auto &[k, _] : dict.asKeyValueRange()) { for (const auto &[k, _] : dict.asKeyValueRange()) {
if (!list.contains(k)) if (!list.contains(k))
list << k; list << k;
@ -645,7 +644,7 @@ void QSqlDatabasePrivate::init(const QString &type)
if (!driver) { if (!driver) {
QReadLocker locker(&dbDict()->lock); QReadLocker locker(&dbDict()->lock);
DriverDict dict = QSqlDatabasePrivate::driverDict(); const DriverDict &dict = QSqlDatabasePrivate::driverDict();
for (DriverDict::const_iterator it = dict.constBegin(); for (DriverDict::const_iterator it = dict.constBegin();
it != dict.constEnd() && !driver; ++it) { it != dict.constEnd() && !driver; ++it) {
if (type == it.key()) { if (type == it.key()) {

View File

@ -121,6 +121,10 @@ void tst_QSql::open()
void tst_QSql::openInvalid() void tst_QSql::openInvalid()
{ {
int argc = 1;
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
QCoreApplication app(argc, argv, false);
QSqlDatabase db; QSqlDatabase db;
QVERIFY(!db.open()); QVERIFY(!db.open());