From c3611c60a1ddc8fd31b6200c414a56a94f5872ad Mon Sep 17 00:00:00 2001 From: vcloarec Date: Thu, 13 Jul 2023 10:20:52 +0200 Subject: [PATCH] create postgres RO connection in the thread where the provider live --- .../providers/qgsdataprovider.sip.in | 1 + src/core/project/qgsproject.cpp | 2 ++ src/core/providers/qgsdataprovider.h | 1 + src/providers/postgres/qgspostgresprovider.cpp | 15 +++++++++++++-- src/providers/postgres/qgspostgresprovider.h | 2 +- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/python/core/auto_generated/providers/qgsdataprovider.sip.in b/python/core/auto_generated/providers/qgsdataprovider.sip.in index 5c80f8dc887..ecf3e1bd421 100644 --- a/python/core/auto_generated/providers/qgsdataprovider.sip.in +++ b/python/core/auto_generated/providers/qgsdataprovider.sip.in @@ -79,6 +79,7 @@ Abstract base class for spatial data provider implementations. SkipFullScan, ForceReadOnly, SkipCredentialsRequest, + ParallelThreadLoading, }; typedef QFlags ReadFlags; diff --git a/src/core/project/qgsproject.cpp b/src/core/project/qgsproject.cpp index d7f482637ac..4bc12466eee 100644 --- a/src/core/project/qgsproject.cpp +++ b/src/core/project/qgsproject.cpp @@ -1368,6 +1368,7 @@ void QgsProject::preloadProviders( const QVector ¶llelLayerNodes, // Requesting credential from worker thread could lead to deadlocks because the main thread is waiting for worker thread to fininsh layerToLoad.flags.setFlag( QgsDataProvider::SkipCredentialsRequest, true ); + layerToLoad.flags.setFlag( QgsDataProvider::ParallelThreadLoading, true ); layersToLoad.insert( layerToLoad.layerId, layerToLoad ); } @@ -1426,6 +1427,7 @@ void QgsProject::preloadProviders( const QVector ¶llelLayerNodes, const LayerToLoad &lay = it.value(); QgsDataProvider::ReadFlags providerFlags = lay.flags; providerFlags.setFlag( QgsDataProvider::SkipCredentialsRequest, false ); + providerFlags.setFlag( QgsDataProvider::ParallelThreadLoading, false ); QgsScopedRuntimeProfile profile( "Create data providers/" + lay.layerId, QStringLiteral( "projectload" ) ); std::unique_ptr provider( QgsProviderRegistry::instance()->createProvider( lay.provider, lay.dataSource, lay.options, providerFlags ) ); i++; diff --git a/src/core/providers/qgsdataprovider.h b/src/core/providers/qgsdataprovider.h index 96afebe70dd..946d707aced 100644 --- a/src/core/providers/qgsdataprovider.h +++ b/src/core/providers/qgsdataprovider.h @@ -128,6 +128,7 @@ class CORE_EXPORT QgsDataProvider : public QObject SkipFullScan = 1 << 4, //!< Skip expensive full scan on files (i.e. on delimited text) (since QGIS 3.24) ForceReadOnly = 1 << 5, //!< Open layer in a read-only mode (since QGIS 3.28) SkipCredentialsRequest = 1 << 6, //! Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block the thread creating the provider if it is not the main thread (since QGIS 3.32). + ParallelThreadLoading = 1 << 7, //! Provider is created in a parallel thread than the one where it will live (since QGIS 3.32.1). }; Q_DECLARE_FLAGS( ReadFlags, ReadFlag ) diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 875ab361b84..cd3f0fa0481 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -303,6 +303,11 @@ QgsPostgresProvider::QgsPostgresProvider( QString const &uri, const ProviderOpti mLayerMetadata.setType( QStringLiteral( "dataset" ) ); mLayerMetadata.setCrs( crs() ); + + // Constructor is called in another thread than the thread where the provider will live, + // so we disconnect the DB, connection will be done later in the provider thread when needed + if ( flags.testFlag( QgsDataProvider::ParallelThreadLoading ) ) + disconnectDb(); } QgsPostgresProvider::~QgsPostgresProvider() @@ -320,7 +325,13 @@ QgsAbstractFeatureSource *QgsPostgresProvider::featureSource() const QgsPostgresConn *QgsPostgresProvider::connectionRO() const { - return mTransaction ? mTransaction->connection() : mConnectionRO; + if ( mTransaction ) + return mTransaction->connection(); + + if ( !mConnectionRO ) + mConnectionRO = QgsPostgresConn::connectDb( mUri, true, true, false, !mReadFlags.testFlag( QgsDataProvider::SkipCredentialsRequest ) ); + + return mConnectionRO; } void QgsPostgresProvider::setListening( bool isListening ) @@ -3887,7 +3898,7 @@ QgsRectangle QgsPostgresProvider::extent() const quotedValue( mSchemaName ), quotedValue( mTableName ), quotedValue( mGeometryColumn ) ); - result = mConnectionRO->LoggedPQexec( "QgsPostgresProvider", sql ); + result = connectionRO()->LoggedPQexec( "QgsPostgresProvider", sql ); if ( result.PQresultStatus() == PGRES_TUPLES_OK && result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 ) ) { ext = result.PQgetvalue( 0, 0 ); diff --git a/src/providers/postgres/qgspostgresprovider.h b/src/providers/postgres/qgspostgresprovider.h index 63c72daf891..abdb48dd185 100644 --- a/src/providers/postgres/qgspostgresprovider.h +++ b/src/providers/postgres/qgspostgresprovider.h @@ -450,7 +450,7 @@ class QgsPostgresProvider final: public QgsVectorDataProvider QString paramValue( const QString &fieldvalue, const QString &defaultValue ) const; - QgsPostgresConn *mConnectionRO = nullptr ; //!< Read-only database connection (initially) + mutable QgsPostgresConn *mConnectionRO = nullptr ; //!< Read-only database connection (initially) QgsPostgresConn *mConnectionRW = nullptr ; //!< Read-write database connection (on update) QgsPostgresConn *connectionRO() const;