From aa07b48fb66d5b214a4afcea2db0eea62fefec8d Mon Sep 17 00:00:00 2001 From: Alexey Karandashev Date: Mon, 15 May 2023 16:08:50 +0200 Subject: [PATCH] This refactoring job is related to https://github.com/qgis/QGIS-Enhancement-Proposals/issues/272 and the original pull request for the AWS Redshift driver https://github.com/qgis/QGIS/pull/53018. - Move qgsConnectionPool_[connectionCreate|connectionDestroy|invalidateConnection|connectionIsValid|connectionToName] into their appropriate abstract classes and declare them as pure virual. - Implement the destructor and the functions above for all providers. - Refactor QgsPostgresConnPool[Group] to QgsSQLConnPool[Group], in order to reuse the template for future SQL based providers (AWS Redshift). --- src/core/providers/ogr/qgsogrconnpool.cpp | 43 +++++ src/core/providers/ogr/qgsogrconnpool.h | 76 +++----- src/core/providers/qgssqlconnpool.h | 124 +++++++++++++ src/core/qgsconnectionpool.h | 174 ++++++++++-------- src/providers/hana/qgshanaconnectionpool.cpp | 17 +- src/providers/hana/qgshanaconnectionpool.h | 67 ++++--- src/providers/oracle/qgsoracleconnpool.h | 52 +++--- src/providers/postgres/CMakeLists.txt | 3 +- .../postgres/qgspostgresconnpool.cpp | 44 ----- src/providers/postgres/qgspostgresconnpool.h | 57 +----- .../spatialite/qgsspatialiteconnpool.cpp | 29 +++ .../spatialite/qgsspatialiteconnpool.h | 41 ++--- 12 files changed, 407 insertions(+), 320 deletions(-) create mode 100644 src/core/providers/qgssqlconnpool.h delete mode 100644 src/providers/postgres/qgspostgresconnpool.cpp diff --git a/src/core/providers/ogr/qgsogrconnpool.cpp b/src/core/providers/ogr/qgsogrconnpool.cpp index 98c2d116237d..03c71dbb1a01 100644 --- a/src/core/providers/ogr/qgsogrconnpool.cpp +++ b/src/core/providers/ogr/qgsogrconnpool.cpp @@ -18,6 +18,43 @@ #include "qgslogger.h" + +void QgsOgrConnPoolGroup::connectionCreate( const QString &connectionInfo, QgsOgrConn *&connection ) +{ + connection = new QgsOgrConn; + + const QVariantMap parts = QgsOgrProviderMetadata().decodeUri( connectionInfo ); + const QString fullPath = parts.value( QStringLiteral( "vsiPrefix" ) ).toString() + + parts.value( QStringLiteral( "path" ) ).toString() + + parts.value( QStringLiteral( "vsiSuffix" ) ).toString(); + const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList(); + char **papszOpenOptions = nullptr; + for ( const QString &option : std::as_const( openOptions ) ) + { + papszOpenOptions = CSLAddString( papszOpenOptions, + option.toUtf8().constData() ); + } + connection->ds = QgsOgrProviderUtils::GDALOpenWrapper( fullPath.toUtf8().constData(), false, papszOpenOptions, nullptr ); + CSLDestroy( papszOpenOptions ); + connection->path = connectionInfo; + connection->valid = true; +} + +void QgsOgrConnPoolGroup::connectionDestroy( QgsOgrConn *connection ) +{ + destroyOgrConn( connection ); +} + +void QgsOgrConnPoolGroup::invalidateConnection( QgsOgrConn *connection ) +{ + connection->valid = false; +} + +bool QgsOgrConnPoolGroup::connectionIsValid( QgsOgrConn *connection ) +{ + return connection->valid; +} + QgsOgrConnPool *QgsOgrConnPool::sInstance = nullptr; // static public @@ -44,4 +81,10 @@ QgsOgrConnPool::~QgsOgrConnPool() QgsDebugCall; } +QString QgsOgrConnPool::connectionToName( QgsOgrConn *connection ) +{ + return connection->path; +} + + ///@endcond diff --git a/src/core/providers/ogr/qgsogrconnpool.h b/src/core/providers/ogr/qgsogrconnpool.h index aa0a6f57ed82..3458606f1b68 100644 --- a/src/core/providers/ogr/qgsogrconnpool.h +++ b/src/core/providers/ogr/qgsogrconnpool.h @@ -33,48 +33,6 @@ struct QgsOgrConn bool valid; }; -inline QString qgsConnectionPool_ConnectionToName( QgsOgrConn *c ) -{ - return c->path; -} - -inline void qgsConnectionPool_ConnectionCreate( const QString &connInfo, QgsOgrConn *&c ) -{ - c = new QgsOgrConn; - - const QVariantMap parts = QgsOgrProviderMetadata().decodeUri( connInfo ); - const QString fullPath = parts.value( QStringLiteral( "vsiPrefix" ) ).toString() - + parts.value( QStringLiteral( "path" ) ).toString() - + parts.value( QStringLiteral( "vsiSuffix" ) ).toString(); - const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList(); - char **papszOpenOptions = nullptr; - for ( const QString &option : openOptions ) - { - papszOpenOptions = CSLAddString( papszOpenOptions, - option.toUtf8().constData() ); - } - c->ds = QgsOgrProviderUtils::GDALOpenWrapper( fullPath.toUtf8().constData(), false, papszOpenOptions, nullptr ); - CSLDestroy( papszOpenOptions ); - c->path = connInfo; - c->valid = true; -} - -inline void qgsConnectionPool_ConnectionDestroy( QgsOgrConn *c ) -{ - QgsOgrProviderUtils::GDALCloseWrapper( c->ds ); - delete c; -} - -inline void qgsConnectionPool_InvalidateConnection( QgsOgrConn *c ) -{ - c->valid = false; -} - -inline bool qgsConnectionPool_ConnectionIsValid( QgsOgrConn *c ) -{ - return c->valid; -} - class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroup { Q_OBJECT @@ -92,6 +50,18 @@ class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroupds ); + delete connection; + } + protected slots: void handleConnectionExpired() { onConnectionExpired(); } void startExpirationTimer() { expirationTimer->start(); } @@ -113,7 +89,7 @@ class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroup { public: - + QString connectionToName( QgsOgrConn *connection ) override; // NOTE: first call to this function initializes the // singleton. // WARNING: concurrent call from multiple threads may result @@ -140,31 +116,31 @@ class QgsOgrConnPool : public QgsConnectionPoolref(); mMutex.unlock(); } /** * \brief Decrease the reference count on the connection pool for the specified connection. - * \param connInfo The connection string. + * \param connectionInfo The connection string. */ - void unref( const QString &connInfo ) + void unref( const QString &connectionInfo ) { mMutex.lock(); - T_Groups::iterator it = mGroups.find( connInfo ); + T_Groups::iterator it = mGroups.find( connectionInfo ); if ( it == mGroups.end() ) { mMutex.unlock(); diff --git a/src/core/providers/qgssqlconnpool.h b/src/core/providers/qgssqlconnpool.h new file mode 100644 index 000000000000..8ff31788ca3c --- /dev/null +++ b/src/core/providers/qgssqlconnpool.h @@ -0,0 +1,124 @@ +/*************************************************************************** + qgssqlconnpool.h + --------------------- + begin : January 2014 + copyright : (C) 2014 by Martin Dobias + email : wonder dot sk at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSQLCONNPOOL_H +#define QGSSQLCONNPOOL_H +#define SIP_NO_FILE + +#include "qgsconnectionpool.h" +#include "qgslogger.h" + +/** + * \ingroup core + * \brief Template class for SQL based provider connection pool group. + * \note not available in Python bindings. + * \since QGIS 3.32 + */ +template +class QgsSQLConnPoolGroup : public QgsConnectionPoolGroup +{ + public: + + /** + * The descendants inherit from QObject and pass a self reference. + */ + explicit QgsSQLConnPoolGroup( const QString &name, QObject *qobject ) : + QgsConnectionPoolGroup( name ) + { + QgsConnectionPoolGroup::initTimer( qobject ); + } + + using typename QgsConnectionPoolGroup::Item; + using QgsConnectionPoolGroup::connections; + ~QgsSQLConnPoolGroup() override + { + for ( const Item &item : std::as_const( connections ) ) + { + connectionDestroy( item.connection ); + } + } + void connectionCreate( const QString &connectionInfo, T *&connection ) override + { + connection = T::connectDb( connectionInfo, true, false ); + } + + void connectionDestroy( T *connection ) override + { + connection->unref(); // will delete itself + } + + void invalidateConnection( T * ) override {} + + bool connectionIsValid( T * ) override + { + return true; + } +}; + +/** + * \ingroup core + * \brief Template class for SQL based provider connection pool. + * \note not available in Python bindings. + * \since QGIS 3.32 + */ +template +class QgsSQLConnPool : public QgsConnectionPool +{ + public: + + /** + * \returns the instance singleton \a sInstance. + */ + static QgsSQLConnPool *instance() + { + if ( !sInstance ) + sInstance = new QgsSQLConnPool(); + return sInstance; + } + + /** + * Reset the global instance of the connection pool. + */ + static void cleanupInstance() + { + delete sInstance; + sInstance = nullptr; + } + + QString connectionToName( T *connection ) override + { + return connection->connInfo(); + } + + protected: + + /** + * Constructor/Destructor implementation for the sole purpose of debugging. + */ + QgsSQLConnPool() : QgsConnectionPool() + { + QgsDebugCall; + } + + ~QgsSQLConnPool() override + { + QgsDebugCall; + } + private: + static inline QgsSQLConnPool *sInstance = nullptr; +}; + + +#endif // QGSSQLCONNPOOL_H diff --git a/src/core/qgsconnectionpool.h b/src/core/qgsconnectionpool.h index 3ffc1e34203e..fcee426dea9b 100644 --- a/src/core/qgsconnectionpool.h +++ b/src/core/qgsconnectionpool.h @@ -40,14 +40,6 @@ * \ingroup core * \brief Template that stores data related to a connection to a single server or datasource. * - * It is assumed that following functions exist: - * - * - void qgsConnectionPool_ConnectionCreate(QString name, T& c) ... create a new connection - * - void qgsConnectionPool_ConnectionDestroy(T c) ... destroy the connection - * - QString qgsConnectionPool_ConnectionToName(T c) ... lookup connection's name (path) - * - void qgsConnectionPool_InvalidateConnection(T c) ... flag a connection as invalid - * - bool qgsConnectionPool_ConnectionIsValid(T c) ... return whether a connection is valid - * * Because of issues with templates and QObject's signals and slots, this class only provides helper functions for QObject-related * functionality - the place which uses the template is resonsible for: * @@ -66,23 +58,38 @@ class QgsConnectionPoolGroup struct Item { - T c; + T connection; QTime lastUsedTime; }; QgsConnectionPoolGroup( const QString &ci ) - : connInfo( ci ) - , sem( QgsApplication::instance()->maxConcurrentConnectionsPerPool() + CONN_POOL_SPARE_CONNECTIONS ) + : connectionInfo( ci ) + , semaphore( QgsApplication::instance()->maxConcurrentConnectionsPerPool() + CONN_POOL_SPARE_CONNECTIONS ) { } - ~QgsConnectionPoolGroup() - { - for ( const Item &item : std::as_const( conns ) ) - { - qgsConnectionPool_ConnectionDestroy( item.c ); - } - } + virtual ~QgsConnectionPoolGroup() = 0; + + /** + * Classes that implement this template must implement their own connection functions. + * Create a \a connection based on connection information \a connectionInfo. + */ + virtual void connectionCreate( const QString &connectionInfo, T &connection ) = 0; + + /** + * Destroy the \a connection. + */ + virtual void connectionDestroy( T connection ) = 0; + + /** + * Invalidate the \a connection. + */ + virtual void invalidateConnection( T connection ) = 0; + + /** + * \returns true if the \a connection is valid. + */ + virtual bool connectionIsValid( T connection ) = 0; //! QgsConnectionPoolGroup cannot be copied QgsConnectionPoolGroup( const QgsConnectionPoolGroup &other ) = delete; @@ -102,7 +109,7 @@ class QgsConnectionPoolGroup // we are going to acquire a resource - if no resource is available, we will block here if ( timeout >= 0 ) { - if ( !sem.tryAcquire( requiredFreeConnectionCount, timeout ) ) + if ( !semaphore.tryAcquire( requiredFreeConnectionCount, timeout ) ) return nullptr; } else @@ -111,66 +118,70 @@ class QgsConnectionPoolGroup // tryAcquire is broken on Qt > 5.8 with negative timeouts - see // https://bugreports.qt.io/browse/QTBUG-64413 // https://lists.osgeo.org/pipermail/qgis-developer/2017-November/050456.html - sem.acquire( requiredFreeConnectionCount ); + semaphore.acquire( requiredFreeConnectionCount ); } - sem.release( requiredFreeConnectionCount - 1 ); + semaphore.release( requiredFreeConnectionCount - 1 ); // quick (preferred) way - use cached connection { - QMutexLocker locker( &connMutex ); + QMutexLocker locker( &connectionMutex ); - if ( !conns.isEmpty() ) + if ( !connections.isEmpty() ) { - Item i = conns.pop(); - if ( !qgsConnectionPool_ConnectionIsValid( i.c ) ) + Item i = connections.pop(); + if ( !connectionIsValid( i.connection ) ) { - qgsConnectionPool_ConnectionDestroy( i.c ); - qgsConnectionPool_ConnectionCreate( connInfo, i.c ); + connectionDestroy( i.connection ); + connectionCreate( connectionInfo, i.connection ); } // no need to run if nothing can expire - if ( conns.isEmpty() ) + if ( connections.isEmpty() ) { // will call the slot directly or queue the call (if the object lives in a different thread) QMetaObject::invokeMethod( expirationTimer->parent(), "stopExpirationTimer" ); } - acquiredConns.append( i.c ); + acquiredConnections.append( i.connection ); - return i.c; + return i.connection; } } - T c; - qgsConnectionPool_ConnectionCreate( connInfo, c ); - if ( !c ) + T connection; + connectionCreate( connectionInfo, connection ); + if ( !connection ) { // we didn't get connection for some reason, so release the lock - sem.release(); + semaphore.release(); return nullptr; } - connMutex.lock(); - acquiredConns.append( c ); - connMutex.unlock(); - return c; + connectionMutex.lock(); + acquiredConnections.append( connection ); + connectionMutex.unlock(); + return connection; } - void release( T conn ) + /** + * Release the \a connection if it's valid, destroy it otherwise. + * Unlock the connection mutex and semaphore. + */ + void release( T connection ) { - connMutex.lock(); - acquiredConns.removeAll( conn ); - if ( !qgsConnectionPool_ConnectionIsValid( conn ) ) + connectionMutex.lock(); + acquiredConnections.removeAll( connection ); + if ( !connectionIsValid( connection ) ) { - qgsConnectionPool_ConnectionDestroy( conn ); + connectionDestroy( connection ); } else { Item i; - i.c = conn; + i.connection = connection; i.lastUsedTime = QTime::currentTime(); - conns.push( i ); + connections.push( i ); if ( !expirationTimer->isActive() ) { @@ -179,22 +190,22 @@ class QgsConnectionPoolGroup } } - connMutex.unlock(); + connectionMutex.unlock(); - sem.release(); // this can unlock a thread waiting in acquire() + semaphore.release(); // this can unlock a thread waiting in acquire() } void invalidateConnections() { - connMutex.lock(); - for ( const Item &i : std::as_const( conns ) ) + connectionMutex.lock(); + for ( const Item &i : std::as_const( connections ) ) { - qgsConnectionPool_ConnectionDestroy( i.c ); + connectionDestroy( i.connection ); } - conns.clear(); - for ( T c : std::as_const( acquiredConns ) ) - qgsConnectionPool_InvalidateConnection( c ); - connMutex.unlock(); + connections.clear(); + for ( T connection : std::as_const( acquiredConnections ) ) + invalidateConnection( connection ); + connectionMutex.unlock(); } protected: @@ -212,15 +223,15 @@ class QgsConnectionPoolGroup void onConnectionExpired() { - connMutex.lock(); + connectionMutex.lock(); QTime now = QTime::currentTime(); // what connections have expired? QList toDelete; - for ( int i = 0; i < conns.count(); ++i ) + for ( int i = 0; i < connections.count(); ++i ) { - if ( conns.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME ) + if ( connections.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME ) toDelete.append( i ); } @@ -228,27 +239,30 @@ class QgsConnectionPoolGroup for ( int j = toDelete.count() - 1; j >= 0; --j ) { int index = toDelete[j]; - qgsConnectionPool_ConnectionDestroy( conns[index].c ); - conns.remove( index ); + connectionDestroy( connections[index].connection ); + connections.remove( index ); } - if ( conns.isEmpty() ) + if ( connections.isEmpty() ) expirationTimer->stop(); - connMutex.unlock(); + connectionMutex.unlock(); } protected: - QString connInfo; - QStack conns; - QList acquiredConns; - QMutex connMutex; - QSemaphore sem; + QString connectionInfo; + QStack connections; + QList acquiredConnections; + QMutex connectionMutex; + QSemaphore semaphore; QTimer *expirationTimer = nullptr; }; +template +QgsConnectionPoolGroup::~QgsConnectionPoolGroup() {} + /** * \ingroup core @@ -285,6 +299,11 @@ class QgsConnectionPool mMutex.unlock(); } + /** + * \returns the name of the \a connection. + */ + virtual QString connectionToName( T connection ) = 0; + /** * Try to acquire a connection for a maximum of \a timeout milliseconds. * If \a timeout is a negative value the calling thread will be blocked @@ -295,13 +314,13 @@ class QgsConnectionPool * * \returns initialized connection or NULLPTR if unsuccessful */ - T acquireConnection( const QString &connInfo, int timeout = -1, bool requestMayBeNested = false, QgsFeedback *feedback = nullptr ) + T acquireConnection( const QString &connectionInfo, int timeout = -1, bool requestMayBeNested = false, QgsFeedback *feedback = nullptr ) { mMutex.lock(); - typename T_Groups::iterator it = mGroups.find( connInfo ); + typename T_Groups::iterator it = mGroups.find( connectionInfo ); if ( it == mGroups.end() ) { - it = mGroups.insert( connInfo, new T_Group( connInfo ) ); + it = mGroups.insert( connectionInfo, new T_Group( connectionInfo ) ); } T_Group *group = *it; mMutex.unlock(); @@ -313,8 +332,8 @@ class QgsConnectionPool while ( !feedback->isCanceled() ) { - if ( T conn = group->acquire( 300, requestMayBeNested ) ) - return conn; + if ( T connection = group->acquire( 300, requestMayBeNested ) ) + return connection; if ( timeout > 0 && timer.elapsed() >= timeout ) return nullptr; @@ -328,15 +347,15 @@ class QgsConnectionPool } //! Release an existing connection so it will get back into the pool and can be reused - void releaseConnection( T conn ) + void releaseConnection( T connection ) { mMutex.lock(); - typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) ); + typename T_Groups::iterator it = mGroups.find( connectionToName( connection ) ); Q_ASSERT( it != mGroups.end() ); T_Group *group = *it; mMutex.unlock(); - group->release( conn ); + group->release( connection ); } /** @@ -346,11 +365,11 @@ class QgsConnectionPool * invalidated when such datasets are changed to ensure the handles are * refreshed. See the OGR provider for an example where this is needed. */ - void invalidateConnections( const QString &connInfo ) + void invalidateConnections( const QString &connectionInfo ) { mMutex.lock(); - if ( mGroups.contains( connInfo ) ) - mGroups[connInfo]->invalidateConnections(); + if ( mGroups.contains( connectionInfo ) ) + mGroups[connectionInfo]->invalidateConnections(); mMutex.unlock(); } @@ -360,5 +379,4 @@ class QgsConnectionPool QMutex mMutex; }; - #endif // QGSCONNECTIONPOOL_H diff --git a/src/providers/hana/qgshanaconnectionpool.cpp b/src/providers/hana/qgshanaconnectionpool.cpp index 1f52751e8cae..052fcf263879 100644 --- a/src/providers/hana/qgshanaconnectionpool.cpp +++ b/src/providers/hana/qgshanaconnectionpool.cpp @@ -20,34 +20,29 @@ #include "qgshanautils.h" #include "qgslogger.h" -QgsHanaConnectionPoolGroup::QgsHanaConnectionPoolGroup( const QString &name ) - : QgsConnectionPoolGroup( name ) -{ - initTimer( this ); -} QBasicMutex QgsHanaConnectionPool::sMutex; std::shared_ptr QgsHanaConnectionPool::sInstance; -QgsHanaConnection *QgsHanaConnectionPool::getConnection( const QString &connInfo ) +QgsHanaConnection *QgsHanaConnectionPool::getConnection( const QString &connectionInfo ) { - std::shared_ptr instance; + std::shared_ptr instance; { QMutexLocker lock( &sMutex ); if ( !sInstance ) sInstance = std::shared_ptr( new QgsHanaConnectionPool() ); instance = sInstance; } - return instance->acquireConnection( connInfo ); + return instance->acquireConnection( connectionInfo ); } -void QgsHanaConnectionPool::returnConnection( QgsHanaConnection *conn ) +void QgsHanaConnectionPool::returnConnection( QgsHanaConnection *connection ) { QMutexLocker lock( &sMutex ); if ( sInstance ) - sInstance->releaseConnection( conn ); + sInstance->releaseConnection( connection ); else - qgsConnectionPool_ConnectionDestroy( conn ); + delete connection; } void QgsHanaConnectionPool::cleanupInstance() diff --git a/src/providers/hana/qgshanaconnectionpool.h b/src/providers/hana/qgshanaconnectionpool.h index 4032d4925140..20c7efedfb2c 100644 --- a/src/providers/hana/qgshanaconnectionpool.h +++ b/src/providers/hana/qgshanaconnectionpool.h @@ -23,39 +23,43 @@ #include #include -inline QString qgsConnectionPool_ConnectionToName( QgsHanaConnection *c ) -{ - return c->connInfo(); -} - -inline void qgsConnectionPool_ConnectionCreate( const QgsDataSourceUri &uri, QgsHanaConnection *&c ) -{ - c = QgsHanaConnection::createConnection( uri ); -} - -inline void qgsConnectionPool_ConnectionDestroy( QgsHanaConnection *c ) -{ - delete c; -} - -inline void qgsConnectionPool_InvalidateConnection( QgsHanaConnection *c ) -{ - Q_UNUSED( c ); -} - -inline bool qgsConnectionPool_ConnectionIsValid( QgsHanaConnection *c ) -{ - Q_UNUSED( c ); - return true; -} - class QgsHanaConnectionPoolGroup : public QObject, public QgsConnectionPoolGroup { Q_OBJECT public: - explicit QgsHanaConnectionPoolGroup( const QString &name ); + explicit QgsHanaConnectionPoolGroup( const QString &name ) + : QgsConnectionPoolGroup( name ) + { + initTimer( this ); + } + + ~QgsHanaConnectionPoolGroup() override + { + for ( const Item &item : std::as_const( connections ) ) + { + delete item.connection; + } + } + + void connectionCreate( const QString &uri, QgsHanaConnection *&c ) override + { + c = QgsHanaConnection::createConnection( uri ); + } + + void connectionDestroy( QgsHanaConnection *connection ) override + { + delete connection; + } + + void invalidateConnection( QgsHanaConnection * ) override {} + + + bool connectionIsValid( QgsHanaConnection * ) override + { + return true; + } protected slots: void handleConnectionExpired() { onConnectionExpired(); } @@ -70,10 +74,15 @@ class QgsHanaConnectionPool : public QgsConnectionPool { public: - static QgsHanaConnection *getConnection( const QString &connInfo ); - static void returnConnection( QgsHanaConnection *conn ); + static QgsHanaConnection *getConnection( const QString &connectionInfo ); + static void returnConnection( QgsHanaConnection *connection ); static void cleanupInstance(); + QString connectionToName( QgsHanaConnection *connection ) override + { + return connection->connInfo(); + } + protected: Q_DISABLE_COPY( QgsHanaConnectionPool ) diff --git a/src/providers/oracle/qgsoracleconnpool.h b/src/providers/oracle/qgsoracleconnpool.h index 739f53a31215..b69a2e4388d5 100644 --- a/src/providers/oracle/qgsoracleconnpool.h +++ b/src/providers/oracle/qgsoracleconnpool.h @@ -20,39 +20,32 @@ #include "qgsoracleconn.h" -inline QString qgsConnectionPool_ConnectionToName( QgsOracleConn *c ) -{ - return c->connInfo(); -} - -inline void qgsConnectionPool_ConnectionCreate( const QgsDataSourceUri &uri, QgsOracleConn *&c ) -{ - c = QgsOracleConn::connectDb( uri, false ); -} - -inline void qgsConnectionPool_ConnectionDestroy( QgsOracleConn *c ) -{ - c->disconnect(); // will delete itself -} - -inline void qgsConnectionPool_InvalidateConnection( QgsOracleConn *c ) -{ - Q_UNUSED( c ) -} - -inline bool qgsConnectionPool_ConnectionIsValid( QgsOracleConn *c ) -{ - Q_UNUSED( c ) - return true; -} - - class QgsOracleConnPoolGroup : public QObject, public QgsConnectionPoolGroup { Q_OBJECT public: explicit QgsOracleConnPoolGroup( QString name ) : QgsConnectionPoolGroup( name ) { initTimer( this ); } + ~QgsOracleConnPoolGroup() override + { + for ( const Item &item : std::as_const( connections ) ) + { + item.connection->disconnect(); // will delete itself + } + } + + void connectionCreate( const QString &connectionInfo, QgsOracleConn *&connection ) override + { + connection = QgsOracleConn::connectDb( connectionInfo, false ); + } + + void connectionDestroy( QgsOracleConn *connection ) override + { + connection->disconnect(); // will delete itself + } + + void invalidateConnection( QgsOracleConn * ) override {}; + bool connectionIsValid( QgsOracleConn * ) override { return true;}; protected slots: void handleConnectionExpired() { onConnectionExpired(); } @@ -72,6 +65,11 @@ class QgsOracleConnPool : public QgsConnectionPoolconnInfo(); + } + protected: Q_DISABLE_COPY( QgsOracleConnPool ) diff --git a/src/providers/postgres/CMakeLists.txt b/src/providers/postgres/CMakeLists.txt index b074d4d845d7..6237b6976e57 100644 --- a/src/providers/postgres/CMakeLists.txt +++ b/src/providers/postgres/CMakeLists.txt @@ -4,7 +4,6 @@ set(PG_SRCS qgspostgresprovider.cpp qgspostgresconn.cpp - qgspostgresconnpool.cpp qgspostgresdataitems.cpp qgspostgresfeatureiterator.cpp qgspostgresprojectstorage.cpp @@ -34,6 +33,7 @@ endif() set(PG_HDRS qgspostgresexpressioncompiler.h qgspostgresproviderconnection.h + qgspostgresconnpool.h ) ######################################################## @@ -94,7 +94,6 @@ set(PGRASTER_SRCS raster/qgspostgresrastershareddata.cpp raster/qgspostgresrasterutils.cpp qgspostgresconn.cpp - qgspostgresconnpool.cpp qgspostgresprovidermetadatautils.cpp ) diff --git a/src/providers/postgres/qgspostgresconnpool.cpp b/src/providers/postgres/qgspostgresconnpool.cpp deleted file mode 100644 index 44967d18bd81..000000000000 --- a/src/providers/postgres/qgspostgresconnpool.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************** - qgspostgresconnpool.cpp - --------------------- - begin : January 2014 - copyright : (C) 2014 by Martin Dobias - email : wonder dot sk at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgspostgresconnpool.h" -#include "qgspostgresconn.h" -#include "qgslogger.h" - -QgsPostgresConnPool *QgsPostgresConnPool::sInstance = nullptr; - -QgsPostgresConnPool *QgsPostgresConnPool::instance() -{ - if ( !sInstance ) - sInstance = new QgsPostgresConnPool(); - return sInstance; -} - -void QgsPostgresConnPool::cleanupInstance() -{ - delete sInstance; - sInstance = nullptr; -} - -QgsPostgresConnPool::QgsPostgresConnPool() : QgsConnectionPool() -{ - QgsDebugCall; -} - -QgsPostgresConnPool::~QgsPostgresConnPool() -{ - QgsDebugCall; -} - diff --git a/src/providers/postgres/qgspostgresconnpool.h b/src/providers/postgres/qgspostgresconnpool.h index d85531d60412..507f6a51a13b 100644 --- a/src/providers/postgres/qgspostgresconnpool.h +++ b/src/providers/postgres/qgspostgresconnpool.h @@ -18,69 +18,26 @@ #include "qgsconnectionpool.h" #include "qgspostgresconn.h" +#include "qgssqlconnpool.h" -inline QString qgsConnectionPool_ConnectionToName( QgsPostgresConn *c ) -{ - return c->connInfo(); -} - -inline void qgsConnectionPool_ConnectionCreate( const QString &connInfo, QgsPostgresConn *&c ) -{ - c = QgsPostgresConn::connectDb( connInfo, true, false ); -} - -inline void qgsConnectionPool_ConnectionDestroy( QgsPostgresConn *c ) -{ - c->unref(); // will delete itself -} - -inline void qgsConnectionPool_InvalidateConnection( QgsPostgresConn *c ) -{ - Q_UNUSED( c ) -} - -inline bool qgsConnectionPool_ConnectionIsValid( QgsPostgresConn *c ) -{ - Q_UNUSED( c ) - return true; -} - - -class QgsPostgresConnPoolGroup : public QObject, public QgsConnectionPoolGroup +// QObject currently does not support templating (due to the macros Q_OBJECT, slots), +// hence this class explicitly specializes QgsSQLConnPoolGroup. +class QgsPostgresConnPoolGroup : public QObject, public QgsSQLConnPoolGroup { Q_OBJECT - public: - explicit QgsPostgresConnPoolGroup( const QString &name ) : QgsConnectionPoolGroup( name ) { initTimer( this ); } - + explicit QgsPostgresConnPoolGroup( const QString &name ) : QgsSQLConnPoolGroup( name, this ) {} protected slots: void handleConnectionExpired() { onConnectionExpired(); } void startExpirationTimer() { expirationTimer->start(); } void stopExpirationTimer() { expirationTimer->stop(); } - protected: Q_DISABLE_COPY( QgsPostgresConnPoolGroup ) - }; -//! PostgreSQL connection pool - singleton -class QgsPostgresConnPool : public QgsConnectionPool -{ - public: - static QgsPostgresConnPool *instance(); - - static void cleanupInstance(); - - protected: - Q_DISABLE_COPY( QgsPostgresConnPool ) - - private: - QgsPostgresConnPool(); - ~QgsPostgresConnPool() override; - - static QgsPostgresConnPool *sInstance; -}; +//! PostgreSQL connection pool - singleton +using QgsPostgresConnPool = QgsSQLConnPool; #endif // QGSPOSTGRESCONNPOOL_H diff --git a/src/providers/spatialite/qgsspatialiteconnpool.cpp b/src/providers/spatialite/qgsspatialiteconnpool.cpp index dfb6fa6fcec5..b4d073767ef9 100644 --- a/src/providers/spatialite/qgsspatialiteconnpool.cpp +++ b/src/providers/spatialite/qgsspatialiteconnpool.cpp @@ -18,6 +18,30 @@ #include "qgsspatialiteconnpool.h" +void QgsSpatiaLiteConnPoolGroup::connectionCreate( const QString &connectionInfo, QgsSqliteHandle *&connection ) +{ + connection = QgsSqliteHandle::openDb( connectionInfo, false ); +} + +void QgsSpatiaLiteConnPoolGroup::connectionDestroy( QgsSqliteHandle *connection ) +{ + QgsSqliteHandle::closeDb( connection ); // will delete itself +} + +void QgsSpatiaLiteConnPoolGroup::invalidateConnection( QgsSqliteHandle *connection ) +{ + /* Invalidation is used in particular by the WFS provider that uses a */ + /* temporary SpatiaLite DB and want to delete it at some point. For that */ + /* it must invalidate all handles pointing to it */ + connection->invalidate(); +} + +bool QgsSpatiaLiteConnPoolGroup::connectionIsValid( QgsSqliteHandle *connection ) +{ + return connection->isValid(); +} + + QgsSpatiaLiteConnPool *QgsSpatiaLiteConnPool::sInstance = nullptr; QgsSpatiaLiteConnPool *QgsSpatiaLiteConnPool::instance() @@ -41,3 +65,8 @@ void QgsSpatiaLiteConnPool::cleanupInstance() delete sInstance; sInstance = nullptr; } + +QString QgsSpatiaLiteConnPool::connectionToName( QgsSqliteHandle *connection ) +{ + return connection->dbPath(); +} diff --git a/src/providers/spatialite/qgsspatialiteconnpool.h b/src/providers/spatialite/qgsspatialiteconnpool.h index be3dc6d3e9da..baa53e38f411 100644 --- a/src/providers/spatialite/qgsspatialiteconnpool.h +++ b/src/providers/spatialite/qgsspatialiteconnpool.h @@ -19,41 +19,23 @@ #include "qgsconnectionpool.h" #include "qgsspatialiteconnection.h" -inline QString qgsConnectionPool_ConnectionToName( QgsSqliteHandle *c ) -{ - return c->dbPath(); -} - -inline void qgsConnectionPool_ConnectionCreate( const QString &connInfo, QgsSqliteHandle *&c ) -{ - c = QgsSqliteHandle::openDb( connInfo, false ); -} - -inline void qgsConnectionPool_ConnectionDestroy( QgsSqliteHandle *c ) -{ - QgsSqliteHandle::closeDb( c ); // will delete itself -} - -inline void qgsConnectionPool_InvalidateConnection( QgsSqliteHandle *c ) -{ - /* Invalidation is used in particular by the WFS provider that uses a */ - /* temporary SpatiaLite DB and want to delete it at some point. For that */ - /* it must invalidate all handles pointing to it */ - c->invalidate(); -} - -inline bool qgsConnectionPool_ConnectionIsValid( QgsSqliteHandle *c ) -{ - return c->isValid(); -} - - class QgsSpatiaLiteConnPoolGroup : public QObject, public QgsConnectionPoolGroup { Q_OBJECT public: explicit QgsSpatiaLiteConnPoolGroup( const QString &name ) : QgsConnectionPoolGroup( name ) { initTimer( this ); } + ~QgsSpatiaLiteConnPoolGroup() override + { + for ( Item &item : connections ) + { + QgsSqliteHandle::closeDb( item.connection ); // will delete itself + } + } + void connectionCreate( const QString &connectionInfo, QgsSqliteHandle *&connection ) override; + void connectionDestroy( QgsSqliteHandle *connection ) override; + void invalidateConnection( QgsSqliteHandle *connection ) override; + bool connectionIsValid( QgsSqliteHandle *connection ) override; protected slots: void handleConnectionExpired() { onConnectionExpired(); } @@ -81,6 +63,7 @@ class QgsSpatiaLiteConnPool : public QgsConnectionPool