QGIS API Documentation 4.3.0-Master (7d9941090cd)
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 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u"INPUT_DATASOURCES"_s, context );
145 const QString query = parameterAsString( parameters, u"INPUT_QUERY"_s, context );
146 const QString uniqueIdentifierField = parameterAsString( parameters, u"INPUT_UID_FIELD"_s, context );
147 const QString geometryField = parameterAsString( parameters, u"INPUT_GEOMETRY_FIELD"_s, context );
148
149 const int geometryTypeIndex = parameterAsEnum( parameters, u"INPUT_GEOMETRY_TYPE"_s, context );
150 const Qgis::WkbType geometryType = ( geometryTypeIndex >= 0 && geometryTypeIndex < static_cast<int>( mGeometryTypes.size() ) ) ? mGeometryTypes.at( geometryTypeIndex ).first : Qgis::WkbType::Unknown;
151
152 const QgsCoordinateReferenceSystem geometryCrs = parameterAsCrs( parameters, u"INPUT_GEOMETRY_CRS"_s, context );
153
154 QgsVirtualLayerDefinition layerDefinition;
155 int layerIndex = 1;
156 for ( QgsMapLayer *layer : std::as_const( layers ) )
157 {
158 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
159 if ( !vectorLayer || !vectorLayer->isValid() )
160 {
161 continue;
162 }
163
164 // Issue https://github.com/qgis/QGIS/issues/24041
165 // When using this algorithm from the graphic modeler, it may try to
166 // access (thanks the QgsVirtualLayerProvider) to memory layer that
167 // belongs to temporary QgsMapLayerStore, not project.
168 // So, we write them to disk if this is the case.
169 if ( context.project() && !context.project()->mapLayer( vectorLayer->id() ) )
170 {
171 const QString basename = u"memorylayer."_s + QgsVectorFileWriter::supportedFormatExtensions().value( 0 );
172 const QString temporaryPath = QgsProcessingUtils::generateTempFilename( basename, &context );
173
175 saveOptions.fileEncoding = vectorLayer->dataProvider()->encoding();
176 QgsVectorFileWriter::writeAsVectorFormatV3( vectorLayer, temporaryPath, context.transformContext(), saveOptions );
177 layerDefinition.addSource( u"input%1"_s.arg( layerIndex ), temporaryPath, u"ogr"_s );
178 }
179 else
180 {
181 layerDefinition.addSource( u"input%1"_s.arg( layerIndex ), vectorLayer->id() );
182 }
183 layerIndex++;
184 }
185
186 if ( query.trimmed().isEmpty() )
187 {
188 throw QgsProcessingException( QObject::tr( "Empty SQL. Please enter valid SQL expression and try again." ) );
189 }
190
191 QgsExpressionContext localContext = createExpressionContext( parameters, context );
192 const QString expandedQuery = QgsExpression::replaceExpressionText( query, &localContext );
193
194 feedback->pushInfo( QObject::tr( "Executing query:" ) );
195 feedback->pushCommandInfo( expandedQuery );
196
197 layerDefinition.setQuery( expandedQuery );
198
199 if ( !uniqueIdentifierField.isEmpty() )
200 {
201 layerDefinition.setUid( uniqueIdentifierField );
202 }
203
204 if ( geometryType == Qgis::WkbType::NoGeometry )
205 {
207 }
208 else
209 {
210 if ( !geometryField.isEmpty() )
211 {
212 layerDefinition.setGeometryField( geometryField );
213 }
214 if ( geometryType != Qgis::WkbType::Unknown )
215 {
216 layerDefinition.setGeometryWkbType( geometryType );
217 }
218 if ( geometryCrs.isValid() )
219 {
220 layerDefinition.setGeometrySrid( geometryCrs.postgisSrid() );
221 }
222 }
223
224 QgsVectorLayer virtualLayer( layerDefinition.toString(), u"temp_vlayer"_s, u"virtual"_s );
225 if ( !virtualLayer.isValid() )
226 {
227 throw QgsProcessingException( virtualLayer.dataProvider() ? virtualLayer.dataProvider()->error().summary() : QObject::tr( "Invalid virtual layer" ) );
228 }
229
230 if ( virtualLayer.wkbType() == Qgis::WkbType::Unknown )
231 {
232 throw QgsProcessingException( QObject::tr( "Cannot find geometry field" ) );
233 }
234
235 QString destinationId;
236 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, destinationId, virtualLayer.fields(), virtualLayer.wkbType(), virtualLayer.crs() ) );
237 if ( !sink )
238 {
239 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
240 }
241
242 QgsFeatureIterator featureIterator = virtualLayer.getFeatures();
243 const double progressStep = virtualLayer.featureCount() > 0 ? 100.0 / virtualLayer.featureCount() : 0.0;
244 long long currentFeatureIndex = 0;
245 QgsFeature inputFeature;
246 while ( featureIterator.nextFeature( inputFeature ) )
247 {
248 if ( feedback->isCanceled() )
249 {
250 break;
251 }
252
253 sink->addFeature( inputFeature, QgsFeatureSink::Flag::FastInsert );
254 feedback->featureAddedToSink( u"OUTPUT"_s );
255 feedback->setProgress( static_cast<int>( currentFeatureIndex * progressStep ) );
256 currentFeatureIndex++;
257 }
258 sink->finalize();
259 feedback->featureSinkFinalized( u"OUTPUT"_s );
260
261 QVariantMap outputs;
262 outputs.insert( u"OUTPUT"_s, destinationId );
263 return outputs;
264}
265
@ 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.