diff options
Diffstat (limited to 'src/sql/kernel/qsqldatabase.cpp')
-rw-r--r-- | src/sql/kernel/qsqldatabase.cpp | 299 |
1 files changed, 169 insertions, 130 deletions
diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index 8e896314eb..e44533291a 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -3,12 +3,14 @@ #include "qsqldatabase.h" #include "qsqlquery.h" -#include "qdebug.h" +#include "qloggingcategory.h" #include "qcoreapplication.h" #include "qreadwritelock.h" #include "qsqldriver.h" +#include "qsqldriver_p.h" #include "qsqldriverplugin.h" #include "qsqlindex.h" +#include "QtCore/qapplicationstatic.h" #include "private/qfactoryloader_p.h" #include "private/qsqlnulldriver_p.h" #include "qhash.h" @@ -16,39 +18,57 @@ QT_BEGIN_NAMESPACE +Q_STATIC_LOGGING_CATEGORY(lcSqlDb, "qt.sql.qsqldatabase") + using namespace Qt::StringLiterals; +#define CHECK_QCOREAPPLICATION \ + if (Q_UNLIKELY(!QCoreApplication::instance())) { \ + qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \ + return; \ + } +#define CHECK_QCOREAPPLICATION_RETVAL \ + if (Q_UNLIKELY(!QCoreApplication::instance())) { \ + qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \ + return {}; \ + } + Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QSqlDriverFactoryInterface_iid, "/sqldrivers"_L1)) const char *QSqlDatabase::defaultConnection = "qt_sql_default_connection"; -typedef QHash<QString, QSqlDriverCreatorBase*> DriverDict; - -class QConnectionDict: public QHash<QString, QSqlDatabase> -{ -public: - inline bool contains_ts(const QString &key) - { - QReadLocker locker(&lock); - return contains(key); - } - inline QStringList keys_ts() const +namespace { + struct QtSqlGlobals { - QReadLocker locker(&lock); - return keys(); - } - - mutable QReadWriteLock lock; -}; -Q_GLOBAL_STATIC(QConnectionDict, dbDict) + ~QtSqlGlobals(); + QSqlDatabase connection(const QString &key) const + { + QReadLocker locker(&lock); + return connections.value(key); + } + bool connectionExists(const QString &key) const + { + QReadLocker locker(&lock); + return connections.contains(key); + } + QStringList connectionNames() const + { + QReadLocker locker(&lock); + return connections.keys(); + } + mutable QReadWriteLock lock; + QHash<QString, QSqlDriverCreatorBase*> registeredDrivers; + QHash<QString, QSqlDatabase> connections; + }; +} +Q_APPLICATION_STATIC(QtSqlGlobals, s_sqlGlobals) class QSqlDatabasePrivate { public: - QSqlDatabasePrivate(QSqlDatabase *d, QSqlDriver *dr = nullptr): + QSqlDatabasePrivate(QSqlDriver *dr): ref(1), - q(d), driver(dr), port(-1) { @@ -61,7 +81,6 @@ public: void disable(); QAtomicInt ref; - QSqlDatabase *q; QSqlDriver* driver; QString dbname; QString uname; @@ -78,13 +97,10 @@ public: static void addDatabase(const QSqlDatabase &db, const QString & name); static void removeDatabase(const QString& name); static void invalidateDb(const QSqlDatabase &db, const QString &name, bool doWarn = true); - static DriverDict &driverDict(); - static void cleanConnections(); }; QSqlDatabasePrivate::QSqlDatabasePrivate(const QSqlDatabasePrivate &other) : ref(1) { - q = other.q; dbname = other.dbname; uname = other.uname; pword = other.pword; @@ -104,51 +120,25 @@ QSqlDatabasePrivate::~QSqlDatabasePrivate() delete driver; } -void QSqlDatabasePrivate::cleanConnections() +QtSqlGlobals::~QtSqlGlobals() { - QConnectionDict *dict = dbDict(); - Q_ASSERT(dict); - QWriteLocker locker(&dict->lock); - - QConnectionDict::iterator it = dict->begin(); - while (it != dict->end()) { - invalidateDb(it.value(), it.key(), false); - ++it; - } - dict->clear(); -} - -static bool qDriverDictInit = false; -static void cleanDriverDict() -{ - qDeleteAll(QSqlDatabasePrivate::driverDict()); - QSqlDatabasePrivate::driverDict().clear(); - QSqlDatabasePrivate::cleanConnections(); - qDriverDictInit = false; -} - -DriverDict &QSqlDatabasePrivate::driverDict() -{ - static DriverDict dict; - if (!qDriverDictInit) { - qDriverDictInit = true; - qAddPostRoutine(cleanDriverDict); - } - return dict; + qDeleteAll(registeredDrivers); + for (const auto &[k, v] : std::as_const(connections).asKeyValueRange()) + QSqlDatabasePrivate::invalidateDb(v, k, false); } QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null() { static QSqlNullDriver dr; - static QSqlDatabasePrivate n(nullptr, &dr); + static QSqlDatabasePrivate n(&dr); return &n; } void QSqlDatabasePrivate::invalidateDb(const QSqlDatabase &db, const QString &name, bool doWarn) { if (db.d->ref.loadRelaxed() != 1 && doWarn) { - qWarning("QSqlDatabasePrivate::removeDatabase: connection '%s' is still in use, " - "all queries will cease to work.", name.toLocal8Bit().constData()); + qCWarning(lcSqlDb, "QSqlDatabasePrivate::removeDatabase: connection '%ls' is still in use, " + "all queries will cease to work.", qUtf16Printable(name)); db.d->disable(); db.d->connName.clear(); } @@ -156,28 +146,28 @@ void QSqlDatabasePrivate::invalidateDb(const QSqlDatabase &db, const QString &na void QSqlDatabasePrivate::removeDatabase(const QString &name) { - QConnectionDict *dict = dbDict(); - Q_ASSERT(dict); - QWriteLocker locker(&dict->lock); + CHECK_QCOREAPPLICATION + QtSqlGlobals *sqlGlobals = s_sqlGlobals(); + QWriteLocker locker(&sqlGlobals->lock); - if (!dict->contains(name)) + if (!sqlGlobals->connections.contains(name)) return; - invalidateDb(dict->take(name), name); + invalidateDb(sqlGlobals->connections.take(name), name); } void QSqlDatabasePrivate::addDatabase(const QSqlDatabase &db, const QString &name) { - QConnectionDict *dict = dbDict(); - Q_ASSERT(dict); - QWriteLocker locker(&dict->lock); - - if (dict->contains(name)) { - invalidateDb(dict->take(name), name); - qWarning("QSqlDatabasePrivate::addDatabase: duplicate connection name '%s', old " - "connection removed.", name.toLocal8Bit().data()); + CHECK_QCOREAPPLICATION + QtSqlGlobals *sqlGlobals = s_sqlGlobals(); + QWriteLocker locker(&sqlGlobals->lock); + + if (sqlGlobals->connections.contains(name)) { + invalidateDb(sqlGlobals->connections.take(name), name); + qCWarning(lcSqlDb, "QSqlDatabasePrivate::addDatabase: duplicate connection name '%ls', old " + "connection removed.", qUtf16Printable(name)); } - dict->insert(name, db); + sqlGlobals->connections.insert(name, db); db.d->connName = name; } @@ -185,22 +175,18 @@ void QSqlDatabasePrivate::addDatabase(const QSqlDatabase &db, const QString &nam */ QSqlDatabase QSqlDatabasePrivate::database(const QString& name, bool open) { - const QConnectionDict *dict = dbDict(); - Q_ASSERT(dict); - - dict->lock.lockForRead(); - QSqlDatabase db = dict->value(name); - dict->lock.unlock(); + CHECK_QCOREAPPLICATION_RETVAL + QSqlDatabase db = s_sqlGlobals()->connection(name); if (!db.isValid()) return db; if (db.driver()->thread() != QThread::currentThread()) { - qWarning("QSqlDatabasePrivate::database: requested database does not belong to the calling thread."); + qCWarning(lcSqlDb, "QSqlDatabasePrivate::database: requested database does not belong to the calling thread."); return QSqlDatabase(); } if (open && !db.isOpen()) { if (!db.open()) - qWarning() << "QSqlDatabasePrivate::database: unable to open database:" << db.lastError().text(); + qCWarning(lcSqlDb) << "QSqlDatabasePrivate::database: unable to open database:" << db.lastError().text(); } return db; @@ -212,7 +198,6 @@ QSqlDatabase QSqlDatabasePrivate::database(const QString& name, bool open) */ void QSqlDatabasePrivate::copy(const QSqlDatabasePrivate *other) { - q = other->q; dbname = other->dbname; uname = other->uname; pword = other->pword; @@ -254,6 +239,8 @@ void QSqlDatabasePrivate::disable() Destroys the SQL driver creator object. */ +QSqlDriverCreatorBase::~QSqlDriverCreatorBase() + = default; /*! \fn QSqlDriver *QSqlDriverCreatorBase::createObject() const @@ -298,6 +285,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 @@ -389,9 +380,6 @@ void QSqlDatabasePrivate::disable() \li registers a custom-made driver \endtable - \note QSqlDatabase::exec() is deprecated. Use QSqlQuery::exec() - instead. - \note When using transactions, you must start the transaction before you create your query. @@ -495,6 +483,7 @@ void QSqlDatabase::removeDatabase(const QString& connectionName) QStringList QSqlDatabase::drivers() { + CHECK_QCOREAPPLICATION_RETVAL QStringList list; if (QFactoryLoader *fl = loader()) { @@ -507,7 +496,9 @@ QStringList QSqlDatabase::drivers() } } - const DriverDict dict = QSqlDatabasePrivate::driverDict(); + QtSqlGlobals *sqlGlobals = s_sqlGlobals(); + QReadLocker locker(&sqlGlobals->lock); + const auto &dict = sqlGlobals->registeredDrivers; for (const auto &[k, _] : dict.asKeyValueRange()) { if (!list.contains(k)) list << k; @@ -531,9 +522,12 @@ QStringList QSqlDatabase::drivers() */ void QSqlDatabase::registerSqlDriver(const QString& name, QSqlDriverCreatorBase *creator) { - delete QSqlDatabasePrivate::driverDict().take(name); + CHECK_QCOREAPPLICATION + QtSqlGlobals *sqlGlobals = s_sqlGlobals(); + QWriteLocker locker(&sqlGlobals->lock); + delete sqlGlobals->registeredDrivers.take(name); if (creator) - QSqlDatabasePrivate::driverDict().insert(name, creator); + sqlGlobals->registeredDrivers.insert(name, creator); } /*! @@ -547,7 +541,8 @@ void QSqlDatabase::registerSqlDriver(const QString& name, QSqlDriverCreatorBase bool QSqlDatabase::contains(const QString& connectionName) { - return dbDict()->contains_ts(connectionName); + CHECK_QCOREAPPLICATION_RETVAL + return s_sqlGlobals()->connectionExists(connectionName); } /*! @@ -559,7 +554,8 @@ bool QSqlDatabase::contains(const QString& connectionName) */ QStringList QSqlDatabase::connectionNames() { - return dbDict()->keys_ts(); + CHECK_QCOREAPPLICATION_RETVAL + return s_sqlGlobals()->connectionNames(); } /*! @@ -580,6 +576,7 @@ QStringList QSqlDatabase::connectionNames() \row \li QODBC \li ODBC Driver (includes Microsoft SQL Server) \row \li QPSQL \li PostgreSQL Driver \row \li QSQLITE \li SQLite version 3 or above + \row \li QMIMER \li Mimer SQL 11 or above \endtable Additional third party drivers, including your own custom @@ -589,8 +586,8 @@ QStringList QSqlDatabase::connectionNames() */ QSqlDatabase::QSqlDatabase(const QString &type) + : d(new QSqlDatabasePrivate(nullptr)) { - d = new QSqlDatabasePrivate(this); d->init(type); } @@ -601,8 +598,8 @@ QSqlDatabase::QSqlDatabase(const QString &type) */ QSqlDatabase::QSqlDatabase(QSqlDriver *driver) + : d(new QSqlDatabasePrivate(driver)) { - d = new QSqlDatabasePrivate(this, driver); } /*! @@ -611,8 +608,8 @@ QSqlDatabase::QSqlDatabase(QSqlDriver *driver) objects. */ QSqlDatabase::QSqlDatabase() + : d(QSqlDatabasePrivate::shared_null()) { - d = QSqlDatabasePrivate::shared_null(); d->ref.ref(); } @@ -642,27 +639,27 @@ QSqlDatabase &QSqlDatabase::operator=(const QSqlDatabase &other) void QSqlDatabasePrivate::init(const QString &type) { + CHECK_QCOREAPPLICATION drvName = type; if (!driver) { - DriverDict dict = QSqlDatabasePrivate::driverDict(); - for (DriverDict::const_iterator it = dict.constBegin(); - it != dict.constEnd() && !driver; ++it) { - if (type == it.key()) { - driver = ((QSqlDriverCreatorBase*)(*it))->createObject(); - } - } + QtSqlGlobals *sqlGlobals = s_sqlGlobals(); + QReadLocker locker(&sqlGlobals->lock); + const auto &dict = sqlGlobals->registeredDrivers; + auto it = dict.find(type); + if (it != dict.end()) + driver = it.value()->createObject(); } if (!driver && loader()) driver = qLoadPlugin<QSqlDriver, QSqlDriverPlugin>(loader(), type); if (!driver) { - qWarning("QSqlDatabase: %s driver not loaded", type.toLatin1().data()); - qWarning("QSqlDatabase: available drivers: %s", - QSqlDatabase::drivers().join(u' ').toLatin1().data()); + qCWarning(lcSqlDb, "QSqlDatabase: %ls driver not loaded", qUtf16Printable(type)); + qCWarning(lcSqlDb, "QSqlDatabase: available drivers: %ls", + qUtf16Printable(QSqlDatabase::drivers().join(u' '))); if (QCoreApplication::instance() == nullptr) - qWarning("QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins"); + qCWarning(lcSqlDb, "QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins"); driver = shared_null()->driver; } } @@ -691,8 +688,9 @@ QSqlDatabase::~QSqlDatabase() lastError() is not affected. \sa QSqlQuery, lastError() + \deprecated [6.6] Use QSqlQuery::exec() instead. */ - +#if QT_DEPRECATED_SINCE(6, 6) QSqlQuery QSqlDatabase::exec(const QString & query) const { QSqlQuery r(d->driver->createResult()); @@ -702,6 +700,7 @@ QSqlQuery QSqlDatabase::exec(const QString & query) const } return r; } +#endif /*! Opens the database connection using the current connection @@ -1137,6 +1136,7 @@ bool QSqlDatabase::isDriverAvailable(const QString& name) } /*! \fn QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver* driver, const QString& connectionName) + \overload This overload is useful when you want to create a database connection with a \l{QSqlDriver} {driver} you instantiated @@ -1205,6 +1205,11 @@ bool QSqlDatabase::isDriverAvailable(const QString& name) \li sqlite *connection \li \c qsql_sqlite.cpp \row + \li QMIMER + \li QMimerSQLDriver + \li MimerSession *connection + \li \c qsql_mimer.cpp + \row \li QIBASE \li QIBaseDriver \li isc_db_handle connection @@ -1248,6 +1253,8 @@ bool QSqlDatabase::isValid() const \note The new connection has not been opened. Before using the new connection, you must call open(). + + \reentrant */ QSqlDatabase QSqlDatabase::cloneDatabase(const QSqlDatabase &other, const QString &connectionName) { @@ -1279,24 +1286,11 @@ QSqlDatabase QSqlDatabase::cloneDatabase(const QSqlDatabase &other, const QStrin QSqlDatabase QSqlDatabase::cloneDatabase(const QString &other, const QString &connectionName) { - const QConnectionDict *dict = dbDict(); - Q_ASSERT(dict); - - dict->lock.lockForRead(); - QSqlDatabase otherDb = dict->value(other); - dict->lock.unlock(); - if (!otherDb.isValid()) - return QSqlDatabase(); - - QSqlDatabase db(otherDb.driverName()); - db.d->copy(otherDb.d); - QSqlDatabasePrivate::addDatabase(db, connectionName); - return db; + CHECK_QCOREAPPLICATION_RETVAL + return cloneDatabase(s_sqlGlobals()->connection(other), connectionName); } /*! - \since 4.4 - Returns the connection name, which may be empty. \note The connection name is not the \l{databaseName()} {database name}. @@ -1308,10 +1302,11 @@ QString QSqlDatabase::connectionName() const } /*! - \since 4.6 + \property QSqlDatabase::numericalPrecisionPolicy + \since 6.8 - Sets the default numerical precision policy used by queries created - on this database connection to \a precisionPolicy. + This property holds the default numerical precision policy used by + queries created on this database connection. Note: Drivers that don't support fetching numerical values with low precision will ignore the precision policy. You can use @@ -1321,9 +1316,12 @@ QString QSqlDatabase::connectionName() const Note: Setting the default precision policy to \a precisionPolicy doesn't affect any currently active queries. - \sa QSql::NumericalPrecisionPolicy, numericalPrecisionPolicy(), - QSqlQuery::setNumericalPrecisionPolicy(), QSqlQuery::numericalPrecisionPolicy() + \sa QSql::NumericalPrecisionPolicy, QSqlQuery::numericalPrecisionPolicy, + QSqlDriver::numericalPrecisionPolicy */ +/*! + Sets \l numericalPrecisionPolicy to \a precisionPolicy. + */ void QSqlDatabase::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy) { if (driver()) @@ -1332,12 +1330,7 @@ void QSqlDatabase::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy pr } /*! - \since 4.6 - - Returns the current default precision policy for the database connection. - - \sa QSql::NumericalPrecisionPolicy, setNumericalPrecisionPolicy(), - QSqlQuery::numericalPrecisionPolicy(), QSqlQuery::setNumericalPrecisionPolicy() + Returns the \l numericalPrecisionPolicy. */ QSql::NumericalPrecisionPolicy QSqlDatabase::numericalPrecisionPolicy() const { @@ -1347,6 +1340,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::thread() const +{ + if (auto drv = driver()) + return drv->thread(); + return nullptr; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QSqlDatabase &d) @@ -1367,3 +1404,5 @@ QDebug operator<<(QDebug dbg, const QSqlDatabase &d) #endif QT_END_NAMESPACE + +#include "moc_qsqldatabase.cpp" |