QGIS API Documentation 4.3.0-Master (ffcfc20b9b4)
Loading...
Searching...
No Matches
qgsalgorithmexecutesql.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmexecutesql.cpp
3 ---------------------------------
4 begin : August 2026
5 copyright : (C) 2026 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsfeaturerequest.h"
21#include "qgsvectorfilewriter.h"
22#include "qgsvectorlayer.h"
24
25#include <QString>
26
27using namespace Qt::StringLiterals;
28
30QgsExecuteSqlAlgorithm::QgsExecuteSqlAlgorithm()
31{
32 mGeometryTypes = {
33 { Qgis::WkbType::Unknown, QObject::tr( "Autodetect" ) },
34 { Qgis::WkbType::NoGeometry, QObject::tr( "No geometry" ) },
35 { Qgis::WkbType::Point, QObject::tr( "Point" ) },
36 { Qgis::WkbType::LineString, QObject::tr( "LineString" ) },
37 { Qgis::WkbType::Polygon, QObject::tr( "Polygon" ) },
38 { Qgis::WkbType::MultiPoint, QObject::tr( "MultiPoint" ) },
39 { Qgis::WkbType::MultiLineString, QObject::tr( "MultiLineString" ) },
40 { Qgis::WkbType::MultiPolygon, QObject::tr( "MultiPolygon" ) }
41 };
42}
43
44QString QgsExecuteSqlAlgorithm::name() const
45{
46 return u"executesql"_s;
47}
48
49QString QgsExecuteSqlAlgorithm::displayName() const
50{
51 return QObject::tr( "Execute SQL" );
52}
53
54QStringList QgsExecuteSqlAlgorithm::tags() const
55{
56 return QObject::tr( "virtual,query,sql" ).split( ',' );
57}
58
59QString QgsExecuteSqlAlgorithm::group() const
60{
61 return QObject::tr( "Vector general" );
62}
63
64QString QgsExecuteSqlAlgorithm::groupId() const
65{
66 return u"vectorgeneral"_s;
67}
68
69QString QgsExecuteSqlAlgorithm::shortDescription() const
70{
71 return QObject::tr( "Runs an SQL query on vector layers using virtual layers." );
72}
73
74QString QgsExecuteSqlAlgorithm::shortHelpString() const
75{
76 return QObject::tr(
77 "This algorithm executes an SQL query on input vector layers using QGIS virtual layers.\n\n"
78 "Input layers are made available inside the query using aliases 'input1', 'input2', ..., 'inputN', corresponding to the order of layers supplied.\n\n"
79 "The query engine uses SQLite and SpatiaLite syntax, allowing spatial functions like ST_Intersects, ST_Buffer, and attribute aggregation. "
80 "Additionally, QGIS variable expressions in the format [% @var %] will be evaluated before running the query.\n\n"
81 "The result of the query will be materialized and stored in a new layer."
82 );
83}
84
85QgsExecuteSqlAlgorithm *QgsExecuteSqlAlgorithm::createInstance() const
86{
87 return new QgsExecuteSqlAlgorithm();
88}
89
90Qgis::ProcessingAlgorithmFlags QgsExecuteSqlAlgorithm::flags() const
91{
93}
94
95void QgsExecuteSqlAlgorithm::initAlgorithm( const QVariantMap & )
96{
97 auto inputDataSources = std::make_unique<
98 QgsProcessingParameterMultipleLayers>( u"INPUT_DATASOURCES"_s, QObject::tr( "Input data sources (called input1, .., inputN in the query)" ), Qgis::ProcessingSourceType::Vector, QVariant(), true );
99 inputDataSources->setHelp(
100 QObject::tr( "Input vector layers to query. Inside the SQL statement, these layers are referenced as 'input1', 'input2', ..., 'inputN' according to their order in this list." )
101 );
102 addParameter( inputDataSources.release() );
103
104 auto inputQuery = std::make_unique<QgsProcessingParameterString>( u"INPUT_QUERY"_s, QObject::tr( "SQL query" ) );
105 inputQuery->setHelp(
106 QObject::tr( "The SQL query to execute using SQLite/SpatiaLite syntax. Example: 'SELECT * FROM input1 WHERE area > 100'. Expressions enclosed in [% %] will be expanded before execution." )
107 );
108 inputQuery->setMetadata( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"executesql"_s } } ) } } );
109
110 addParameter( inputQuery.release() );
111
112 auto inputUidField = std::make_unique<QgsProcessingParameterString>( u"INPUT_UID_FIELD"_s, QObject::tr( "Unique identifier field" ), QVariant(), false, true );
113 inputUidField->setHelp(
114 QObject::tr( "Defines the field to be used as a unique integer identifier (primary key) for output features. If left empty, an autoincrementing ID field will be automatically generated." )
115 );
116 addParameter( inputUidField.release() );
117
118 auto inputGeometryField = std::make_unique<QgsProcessingParameterString>( u"INPUT_GEOMETRY_FIELD"_s, QObject::tr( "Geometry field" ), QVariant(), false, true );
119 inputGeometryField->setHelp( QObject::tr( "Specifies the name of the column in the query output that contains the feature geometries (e.g. 'geometry' or 'geom')." ) );
120 addParameter( inputGeometryField.release() );
121
122 QStringList geometryTypeOptions;
123 geometryTypeOptions.reserve( static_cast<int>( mGeometryTypes.size() ) );
124 for ( const std::pair<Qgis::WkbType, QString> &typePair : std::as_const( mGeometryTypes ) )
125 {
126 geometryTypeOptions.append( typePair.second );
127 }
128
129 auto inputGeometryType = std::make_unique<QgsProcessingParameterEnum>( u"INPUT_GEOMETRY_TYPE"_s, QObject::tr( "Geometry type" ), geometryTypeOptions, false, 0 );
130 inputGeometryType->setHelp( QObject::tr( "Explicitly defines the geometry type of the query result. If set to 'Autodetect', the algorithm will attempt to infer the type from the result features." ) );
131 addParameter( inputGeometryType.release() );
132
133 auto inputGeometryCrs = std::make_unique<QgsProcessingParameterCrs>( u"INPUT_GEOMETRY_CRS"_s, QObject::tr( "CRS" ), QVariant(), true );
134 inputGeometryCrs->setHelp( QObject::tr( "Specifies the coordinate reference system (CRS) for the output geometry. If left empty, the algorithm will attempt to infer the CRS from the input layers." ) );
135 addParameter( inputGeometryCrs.release() );
136
137 auto output = std::make_unique<QgsProcessingParameterFeatureSink>( u"OUTPUT"_s, QObject::tr( "SQL Output" ) );
138 output->setHelp( QObject::tr( "Specifies the destination layer for the features returned by the SQL query." ) );
139 addParameter( output.release() );
140}
141
142QVariantMap QgsExecuteSqlAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
143{
144 QGS_MARK_ALGORITHM_SOURCE
145
146 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u"INPUT_DATASOURCES"_s, context );
147 const QString query = parameterAsString( parameters, u"INPUT_QUERY"_s, context );
148 const QString uniqueIdentifierField = parameterAsString( parameters, u"INPUT_UID_FIELD"_s, context );
149 const QString geometryField = parameterAsString( parameters, u"INPUT_GEOMETRY_FIELD"_s, context );
150
151 const int geometryTypeIndex = parameterAsEnum( parameters, u"INPUT_GEOMETRY_TYPE"_s, context );
152 const Qgis::WkbType geometryType = ( geometryTypeIndex >= 0 && geometryTypeIndex < static_cast<int>( mGeometryTypes.size() ) ) ? mGeometryTypes.at( geometryTypeIndex ).first : Qgis::WkbType::Unknown;
153
154 const QgsCoordinateReferenceSystem geometryCrs = parameterAsCrs( parameters, u"INPUT_GEOMETRY_CRS"_s, context );
155
156 QgsVirtualLayerDefinition layerDefinition;
157 int layerIndex = 1;
158 for ( QgsMapLayer *layer : std::as_const( layers ) )
159 {
160 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
161 if ( !vectorLayer || !vectorLayer->isValid() )
162 {
163 continue;
164 }
165
166 // Issue https://github.com/qgis/QGIS/issues/24041
167 // When using this algorithm from the graphic modeler, it may try to
168 // access (thanks the QgsVirtualLayerProvider) to memory layer that
169 // belongs to temporary QgsMapLayerStore, not project.
170 // So, we write them to disk if this is the case.
171 if ( context.project() && !context.project()->mapLayer( vectorLayer->id() ) )
172 {
173 const QString basename = u"memorylayer."_s + QgsVectorFileWriter::supportedFormatExtensions().value( 0 );
174 const QString temporaryPath = QgsProcessingUtils::generateTempFilename( basename, &context );
175
177 saveOptions.fileEncoding = vectorLayer->dataProvider()->encoding();
178 QgsVectorFileWriter::writeAsVectorFormatV3( vectorLayer, temporaryPath, context.transformContext(), saveOptions );
179 layerDefinition.addSource( u"input%1"_s.arg( layerIndex ), temporaryPath, u"ogr"_s );
180 }
181 else
182 {
183 layerDefinition.addSource( u"input%1"_s.arg( layerIndex ), vectorLayer->id() );
184 }
185 layerIndex++;
186 }
187
188 if ( query.trimmed().isEmpty() )
189 {
190 throw QgsProcessingException( QObject::tr( "Empty SQL. Please enter valid SQL expression and try again." ) );
191 }
192
193 QgsExpressionContext localContext = createExpressionContext( parameters, context );
194 const QString expandedQuery = QgsExpression::replaceExpressionText( query, &localContext );
195
196 feedback->pushInfo( QObject::tr( "Executing query:" ) );
197 feedback->pushCommandInfo( expandedQuery );
198
199 layerDefinition.setQuery( expandedQuery );
200
201 if ( !uniqueIdentifierField.isEmpty() )
202 {
203 layerDefinition.setUid( uniqueIdentifierField );
204 }
205
206 if ( geometryType == Qgis::WkbType::NoGeometry )
207 {
209 }
210 else
211 {
212 if ( !geometryField.isEmpty() )
213 {
214 layerDefinition.setGeometryField( geometryField );
215 }
216 if ( geometryType != Qgis::WkbType::Unknown )
217 {
218 layerDefinition.setGeometryWkbType( geometryType );
219 }
220 if ( geometryCrs.isValid() )
221 {
222 layerDefinition.setGeometrySrid( geometryCrs.postgisSrid() );
223 }
224 }
225
226 QgsVectorLayer virtualLayer( layerDefinition.toString(), u"temp_vlayer"_s, u"virtual"_s );
227 if ( !virtualLayer.isValid() )
228 {
229 throw QgsProcessingException( virtualLayer.dataProvider() ? virtualLayer.dataProvider()->error().summary() : QObject::tr( "Invalid virtual layer" ) );
230 }
231
232 if ( virtualLayer.wkbType() == Qgis::WkbType::Unknown )
233 {
234 throw QgsProcessingException( QObject::tr( "Cannot find geometry field" ) );
235 }
236
237 QString destinationId;
238 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, destinationId, virtualLayer.fields(), virtualLayer.wkbType(), virtualLayer.crs() ) );
239 if ( !sink )
240 {
241 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
242 }
243
244 QgsFeatureIterator featureIterator = virtualLayer.getFeatures();
245 const double progressStep = virtualLayer.featureCount() > 0 ? 100.0 / virtualLayer.featureCount() : 0.0;
246 long long currentFeatureIndex = 0;
247 QgsFeature inputFeature;
248 while ( featureIterator.nextFeature( inputFeature ) )
249 {
250 if ( feedback->isCanceled() )
251 {
252 break;
253 }
254
255 sink->addFeature( inputFeature, QgsFeatureSink::Flag::FastInsert );
256 feedback->featureAddedToSink( u"OUTPUT"_s );
257 feedback->setProgress( static_cast<int>( currentFeatureIndex * progressStep ) );
258 currentFeatureIndex++;
259 }
260 sink->finalize();
261 feedback->featureSinkFinalized( u"OUTPUT"_s );
262
263 QVariantMap outputs;
264 outputs.insert( u"OUTPUT"_s, destinationId );
265 return outputs;
266}
267
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3755
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3826
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ NoGeometry
No geometry.
Definition qgis.h:312
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ Unknown
Unknown.
Definition qgis.h:295
@ NoThreading
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
Definition qgis.h:3805
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
long postgisSrid() const
Returns PostGIS SRID for the CRS.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString id
Definition qgsmaplayer.h:86
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
virtual void pushCommandInfo(const QString &info)
Pushes an informational message containing a command from the algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
void setHelp(const QString &help)
Sets the help for the parameter.
A parameter for processing algorithms which accepts multiple map layers.
static QString generateTempFilename(const QString &basename, const QgsProcessingContext *context=nullptr)
Returns a temporary filename for a given file, putting it into a temporary folder (creating that fold...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QString encoding() const
Returns the encoding which is used for accessing data.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
Represents a vector layer which manages a vector based dataset.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
Manipulates the definition of a virtual layer.
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
QString toString() const
Converts the definition into a QString that can be read by the virtual layer provider.
void setGeometryWkbType(Qgis::WkbType t)
Sets the type of the geometry.
void setQuery(const QString &query)
Sets the SQL query.