diff --git a/python/plugins/processing/algs/qgis/PostGISExecuteAndLoadSQL.py b/python/plugins/processing/algs/qgis/PostGISExecuteAndLoadSQL.py deleted file mode 100644 index 3b4528db7676..000000000000 --- a/python/plugins/processing/algs/qgis/PostGISExecuteAndLoadSQL.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -*************************************************************************** - PostGISExecuteAndLoadSQL.py - --------------------- - Date : May 2018 - Copyright : (C) 2018 by Anita Graser - Email : anitagraser at gmx dot at - --------------------- - based on PostGISExecuteSQL.py by Victor Olaya and Carterix Geomatics -*************************************************************************** -* * -* 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. * -* * -*************************************************************************** -""" - -__author__ = "Anita Graser" -__date__ = "May 2018" -__copyright__ = "(C) 2018, Anita Graser" - -from qgis.core import ( - QgsProcessingException, - QgsProcessingParameterString, - QgsVectorLayer, - QgsDataSourceUri, - QgsProcessing, - QgsProcessingOutputVectorLayer, - QgsProcessingContext, - QgsProcessingParameterProviderConnection, - QgsProviderRegistry, - QgsProviderConnectionException, - QgsProcessingAlgorithm, -) -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm - - -class PostGISExecuteAndLoadSQL(QgisAlgorithm): - DATABASE = "DATABASE" - SQL = "SQL" - OUTPUT = "OUTPUT" - ID_FIELD = "ID_FIELD" - GEOMETRY_FIELD = "GEOMETRY_FIELD" - - def group(self): - return self.tr("Database") - - def groupId(self): - return "database" - - def __init__(self): - super().__init__() - - def flags(self): - return ( - super().flags() - | QgsProcessingAlgorithm.Flag.FlagNotAvailableInStandaloneTool - | QgsProcessingAlgorithm.Flag.FlagRequiresProject - ) - - def initAlgorithm(self, config=None): - db_param = QgsProcessingParameterProviderConnection( - self.DATABASE, self.tr("Database (connection name)"), "postgres" - ) - self.addParameter(db_param) - self.addParameter( - QgsProcessingParameterString(self.SQL, self.tr("SQL query"), multiLine=True) - ) - self.addParameter( - QgsProcessingParameterString( - self.ID_FIELD, self.tr("Unique ID field name"), defaultValue="id" - ) - ) - self.addParameter( - QgsProcessingParameterString( - self.GEOMETRY_FIELD, - self.tr("Geometry field name"), - defaultValue="geom", - optional=True, - ) - ) - self.addOutput( - QgsProcessingOutputVectorLayer( - self.OUTPUT, - self.tr("Output layer"), - QgsProcessing.SourceType.TypeVectorAnyGeometry, - ) - ) - - def name(self): - return "postgisexecuteandloadsql" - - def displayName(self): - return self.tr("PostgreSQL execute and load SQL") - - def shortDescription(self): - return self.tr( - "Executes a SQL command on a PostgreSQL database and loads the result as a table." - ) - - def shortHelpString(self): - return self.tr( - "This algorithm performs a SQL database query on a PostGIS database connected to QGIS and loads the query results as a new layer." - ) - - def tags(self): - return self.tr("postgis,table,database").split(",") - - def processAlgorithm(self, parameters, context, feedback): - connection_name = self.parameterAsConnectionName( - parameters, self.DATABASE, context - ) - id_field = self.parameterAsString(parameters, self.ID_FIELD, context) - geom_field = self.parameterAsString(parameters, self.GEOMETRY_FIELD, context) - - # resolve connection details to uri - try: - md = QgsProviderRegistry.instance().providerMetadata("postgres") - conn = md.createConnection(connection_name) - except QgsProviderConnectionException: - raise QgsProcessingException( - self.tr("Could not retrieve connection details for {}").format( - connection_name - ) - ) - - uri = QgsDataSourceUri(conn.uri()) - - sql = self.parameterAsString(parameters, self.SQL, context) - sql = sql.replace("\n", " ") - uri.setDataSource("", "(" + sql.rstrip(";") + ")", geom_field, "", id_field) - - vlayer = QgsVectorLayer(uri.uri(), "layername", "postgres") - - if not vlayer.isValid(): - raise QgsProcessingException( - self.tr( - """This layer is invalid! - Please check the PostGIS log for error messages.""" - ) - ) - - context.temporaryLayerStore().addMapLayer(vlayer) - context.addLayerToLoadOnCompletion( - vlayer.id(), - QgsProcessingContext.LayerDetails( - "SQL layer", context.project(), self.OUTPUT - ), - ) - - return {self.OUTPUT: vlayer.id()} diff --git a/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py index bf8950e6ffae..1e6b73e3269b 100644 --- a/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py @@ -42,7 +42,6 @@ from .PointsDisplacement import PointsDisplacement from .PointsFromLines import PointsFromLines from .PolarPlot import PolarPlot -from .PostGISExecuteAndLoadSQL import PostGISExecuteAndLoadSQL from .RandomExtractWithinSubsets import RandomExtractWithinSubsets from .RandomPointsAlongLines import RandomPointsAlongLines from .RandomPointsLayer import RandomPointsLayer @@ -98,7 +97,6 @@ def getAlgs(self): PointsDisplacement(), PointsFromLines(), PolarPlot(), - PostGISExecuteAndLoadSQL(), RandomExtractWithinSubsets(), RandomPointsAlongLines(), RandomPointsLayer(), diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 717b32e84a64..8015c490c293 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -122,6 +122,7 @@ set(QGIS_ANALYSIS_SRCS processing/qgsalgorithmdropgeometry.cpp processing/qgsalgorithmdropmzvalues.cpp processing/qgsalgorithmdxfexport.cpp + processing/qgsalgorithmexecuteandloadpostgisquery.cpp processing/qgsalgorithmexecutepostgisquery.cpp processing/qgsalgorithmexecutespatialitequery.cpp processing/qgsalgorithmexecutespatialitequeryregistered.cpp diff --git a/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.cpp b/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.cpp new file mode 100644 index 000000000000..eab1e7dd0c85 --- /dev/null +++ b/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + qgsalgorithmexecuteandloadpostgisquery.cpp + --------------------- + begin : December 2025 + copyright : (C) 2025 by Alexander Bruy + email : alexander dot bruy 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 "qgsalgorithmexecuteandloadpostgisquery.h" + +#include "qgsabstractdatabaseproviderconnection.h" +#include "qgsprovidermetadata.h" +#include "qgsproviderregistry.h" +#include "qgsvectorlayer.h" + +///@cond PRIVATE + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::name() const +{ + return u"postgisexecuteandloadsql"_s; +} + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::displayName() const +{ + return QObject::tr( "PostgreSQL execute and load SQL" ); +} + +QStringList QgsExecuteAndLoadPostgisQueryAlgorithm::tags() const +{ + return QObject::tr( "database,sql,postgresql,postgis,execute,load,layer,table" ).split( ',' ); +} + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::group() const +{ + return QObject::tr( "Database" ); +} + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::groupId() const +{ + return u"database"_s; +} + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm performs a SQL database query on a PostgreSQL database connected to QGIS and loads the query results as a new layer." ); +} + +QString QgsExecuteAndLoadPostgisQueryAlgorithm::shortDescription() const +{ + return QObject::tr( "Executes a SQL command on a PostgreSQL database and loads the result as a layer." ); +} + +Qgis::ProcessingAlgorithmFlags QgsExecuteAndLoadPostgisQueryAlgorithm::flags() const +{ + return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NotAvailableInStandaloneTool | Qgis::ProcessingAlgorithmFlag::RequiresProject; +} + +QgsExecuteAndLoadPostgisQueryAlgorithm *QgsExecuteAndLoadPostgisQueryAlgorithm::createInstance() const +{ + return new QgsExecuteAndLoadPostgisQueryAlgorithm(); +} + +void QgsExecuteAndLoadPostgisQueryAlgorithm::initAlgorithm( const QVariantMap & ) +{ + addParameter( new QgsProcessingParameterProviderConnection( u"DATABASE"_s, QObject::tr( "Database (connection name)" ), u"postgres"_s ) ); + addParameter( new QgsProcessingParameterString( u"SQL"_s, QObject::tr( "SQL query" ), QVariant(), true ) ); + addParameter( new QgsProcessingParameterString( u"ID_FIELD"_s, QObject::tr( "Unique ID field name" ), u"id"_s ) ); + addParameter( new QgsProcessingParameterString( u"GEOMETRY_FIELD"_s, QObject::tr( "Geometry field name" ), u"geom"_s, false, true ) ); + addOutput( new QgsProcessingOutputVectorLayer( u"OUTPUT"_s, QObject::tr( "Output layer" ) ) ); +} + +QVariantMap QgsExecuteAndLoadPostgisQueryAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) +{ + Q_UNUSED( feedback ); + + const QString connName = parameterAsConnectionName( parameters, u"DATABASE"_s, context ); + + std::unique_ptr conn; + try + { + QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( u"postgres"_s ); + conn.reset( static_cast( md->createConnection( connName ) ) ); + } + catch ( QgsProviderConnectionException & ) + { + throw QgsProcessingException( QObject::tr( "Could not retrieve connection details for %1" ).arg( connName ) ); + } + + QString sql = parameterAsString( parameters, u"SQL"_s, context ).replace( '\n', ' ' ); + if ( sql.endsWith( ';' ) ) + { + sql.chop( 1 ); + } + const QString idField = parameterAsString( parameters, u"ID_FIELD"_s, context ); + const QString geomField = parameterAsString( parameters, u"GEOMETRY_FIELD"_s, context ); + + QgsDataSourceUri uri( conn->uri() ); + uri.setDataSource( QString(), u"(%1)"_s.arg( sql ), geomField, QString(), idField ); + + auto layer = std::make_unique( uri.uri(), u"layername"_s, u"postgres"_s ); + if ( !layer->isValid() ) + { + throw QgsProcessingException( QObject::tr( "This layer is invalid! Please check the PostGIS log for error messages." ) ); + } + + const QString layerId = layer->id(); + const QgsProcessingContext::LayerDetails details( u"SQL layer"_s, context.project(), u"OUTPUT"_s, QgsProcessingUtils::LayerHint::Vector ); + context.addLayerToLoadOnCompletion( layerId, details ); + context.temporaryLayerStore()->addMapLayer( layer.release() ); + + QVariantMap outputs; + outputs.insert( u"OUTPUT"_s, layerId ); + return outputs; +} + +///@endcond diff --git a/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.h b/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.h new file mode 100644 index 000000000000..259878f20f25 --- /dev/null +++ b/src/analysis/processing/qgsalgorithmexecuteandloadpostgisquery.h @@ -0,0 +1,52 @@ +/*************************************************************************** + qgsalgorithmexecuteandloadpostgisquery.h + ------------------------------ + begin : December 2025 + copyright : (C) 2025 by Alexander Bruy + email : alexander dot bruy 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 QGSALGORITHMEXECUTEANDLOADPOSTGISQUERY_H +#define QGSALGORITHMEXECUTEANDLOADPOSTGISQUERY_H + +#define SIP_NO_FILE + +#include "qgsprocessingalgorithm.h" + +///@cond PRIVATE + +/** + * Native execute and load PostGIS query algorithm. + */ +class QgsExecuteAndLoadPostgisQueryAlgorithm : public QgsProcessingAlgorithm +{ + public: + QgsExecuteAndLoadPostgisQueryAlgorithm() = default; + void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; + QString name() const override; + QString displayName() const override; + QStringList tags() const override; + QString group() const override; + QString groupId() const override; + QString shortHelpString() const override; + QString shortDescription() const override; + Qgis::ProcessingAlgorithmFlags flags() const override; + QgsExecuteAndLoadPostgisQueryAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QVariantMap processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; +}; + +///@endcond PRIVATE + + +#endif // QGSALGORITHMEXECUTEANDLOADPOSTGISQUERY_H diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp index 2c2c542de066..5e993b2a2dab 100644 --- a/src/analysis/processing/qgsnativealgorithms.cpp +++ b/src/analysis/processing/qgsnativealgorithms.cpp @@ -94,6 +94,7 @@ #include "qgsalgorithmdropgeometry.h" #include "qgsalgorithmdropmzvalues.h" #include "qgsalgorithmdxfexport.h" +#include "qgsalgorithmexecuteandloadpostgisquery.h" #include "qgsalgorithmexecutepostgisquery.h" #include "qgsalgorithmexecutespatialitequery.h" #include "qgsalgorithmexecutespatialitequeryregistered.h" @@ -431,6 +432,7 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsDropGeometryAlgorithm() ); addAlgorithm( new QgsDropMZValuesAlgorithm() ); addAlgorithm( new QgsDxfExportAlgorithm() ); + addAlgorithm( new QgsExecuteAndLoadPostgisQueryAlgorithm() ); addAlgorithm( new QgsExecutePostgisQueryAlgorithm() ); addAlgorithm( new QgsExecuteRegisteredSpatialiteQueryAlgorithm() ); addAlgorithm( new QgsExecuteSpatialiteQueryAlgorithm() );