1 /***************************************************************************
2 qgsvirtuallayerprovider.cpp Virtual layer data provider
3 begin : Jan, 2015
4 copyright : (C) 2015 Hugo Mercier, Oslandia
5 email : hugo dot mercier at oslandia dot com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 extern "C"
18 {
19 #include <sqlite3.h>
20 #include <spatialite.h>
21 }
22
23 #include <QUrl>
24
25 #include <stdexcept>
26
27 #include "qgsvirtuallayerprovider.h"
28 #include "qgsvirtuallayerdefinition.h"
29 #include "qgsvirtuallayerfeatureiterator.h"
30 #include "qgsvectorlayer.h"
31 #include "qgsproject.h"
32 #include "qgslogger.h"
33 #include "qgsapplication.h"
34
35 #include "qgsvirtuallayerprovider.h"
36 #include "qgsvirtuallayersqlitemodule.h"
37 #include "qgsvirtuallayerqueryparser.h"
38
39 #ifdef HAVE_GUI
40 #include "qgssourceselectprovider.h"
41 #include "qgsvirtuallayersourceselect.h"
42 #endif
43
44 const QString VIRTUAL_LAYER_KEY = QStringLiteral( "virtual" );
45 const QString VIRTUAL_LAYER_DESCRIPTION = QStringLiteral( "Virtual layer data provider" );
46
47 const QString VIRTUAL_LAYER_QUERY_VIEW = QStringLiteral( "_query" );
48
quotedColumn(QString name)49 static QString quotedColumn( QString name )
50 {
51 return "\"" + name.replace( QLatin1String( "\"" ), QLatin1String( "\"\"" ) ) + "\"";
52 }
53
54 #define PROVIDER_ERROR( msg ) do { mError = QgsError( msg, VIRTUAL_LAYER_KEY ); QgsDebugMsg( msg ); } while(0)
55
56
QgsVirtualLayerProvider(QString const & uri,const QgsDataProvider::ProviderOptions & options,QgsDataProvider::ReadFlags flags)57 QgsVirtualLayerProvider::QgsVirtualLayerProvider( QString const &uri,
58 const QgsDataProvider::ProviderOptions &options,
59 QgsDataProvider::ReadFlags flags )
60 : QgsVectorDataProvider( uri, options, flags )
61 {
62 mError.clear();
63
64 QUrl url = QUrl::fromEncoded( uri.toUtf8() );
65 if ( !url.isValid() )
66 {
67 mValid = false;
68 PROVIDER_ERROR( "Malformed URL" );
69 return;
70 }
71
72 // xxxxx = open a virtual layer
73 // xxxxx?key=value&key=value = create a virtual layer
74 // ?key=value = create a temporary virtual layer
75
76 // read url
77 try
78 {
79 mDefinition = QgsVirtualLayerDefinition::fromUrl( url );
80
81 mSubset = mDefinition.subsetString();
82
83 if ( !mDefinition.isLazy() )
84 {
85 reloadData();
86 }
87 }
88 catch ( std::runtime_error &e )
89 {
90 mValid = false;
91 PROVIDER_ERROR( e.what() );
92 return;
93 }
94
95 if ( mDefinition.geometrySrid() != -1 )
96 {
97 Q_NOWARN_DEPRECATED_PUSH
98 mCrs = QgsCoordinateReferenceSystem( mDefinition.geometrySrid() );
99 Q_NOWARN_DEPRECATED_POP
100 }
101 }
102
reloadProviderData()103 void QgsVirtualLayerProvider::reloadProviderData()
104 {
105 if ( mDefinition.sourceLayers().empty() && !mDefinition.filePath().isEmpty() && mDefinition.query().isEmpty() )
106 {
107 // open the file
108 mValid = openIt();
109 }
110 else
111 {
112 // create the file
113 mValid = createIt();
114 }
115 }
116
loadSourceLayers()117 bool QgsVirtualLayerProvider::loadSourceLayers()
118 {
119 const auto constSourceLayers = mDefinition.sourceLayers();
120 for ( const QgsVirtualLayerDefinition::SourceLayer &layer : constSourceLayers )
121 {
122 if ( layer.isReferenced() )
123 {
124 QgsMapLayer *l = QgsProject::instance()->mapLayer( layer.reference() );
125 if ( !l )
126 {
127 PROVIDER_ERROR( QString( "Cannot find layer %1" ).arg( layer.reference() ) );
128 return false;
129 }
130 if ( l->type() != QgsMapLayerType::VectorLayer )
131 {
132 PROVIDER_ERROR( QString( "Layer %1 is not a vector layer" ).arg( layer.reference() ) );
133 return false;
134 }
135 // add the layer to the list
136 QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( l );
137 mLayers << SourceLayer( vl, layer.name() );
138 // connect to modification signals to invalidate statistics
139 connect( vl, &QgsVectorLayer::featureAdded, this, &QgsVirtualLayerProvider::invalidateStatistics );
140 connect( vl, &QgsVectorLayer::featureDeleted, this, &QgsVirtualLayerProvider::invalidateStatistics );
141 connect( vl, &QgsVectorLayer::geometryChanged, this, &QgsVirtualLayerProvider::invalidateStatistics );
142 connect( vl, &QgsVectorLayer::updatedFields, this, [ = ] { createVirtualTable( vl, layer.name() ); } );
143 }
144 else
145 {
146 mLayers << SourceLayer( layer.provider(), layer.source(), layer.name(), layer.encoding() );
147 }
148 }
149 return true;
150 }
151
openIt()152 bool QgsVirtualLayerProvider::openIt()
153 {
154 spatialite_init( 0 );
155
156 mPath = mDefinition.filePath();
157
158 try
159 {
160 QgsScopedSqlite p( mPath );
161 mSqlite = p;
162 }
163 catch ( std::runtime_error &e )
164 {
165 PROVIDER_ERROR( e.what() );
166 return false;
167 }
168
169 {
170 Sqlite::Query q( mSqlite.get(), QStringLiteral( "SELECT name FROM sqlite_master WHERE name='_meta'" ) );
171 if ( q.step() != SQLITE_ROW )
172 {
173 PROVIDER_ERROR( "No metadata tables!" );
174 return false;
175 }
176 }
177 // look for the correct version and the stored url
178 {
179 Sqlite::Query q( mSqlite.get(), QStringLiteral( "SELECT version, url FROM _meta" ) );
180 int version = 0;
181 if ( q.step() == SQLITE_ROW )
182 {
183 version = q.columnInt( 0 );
184 if ( version != VIRTUAL_LAYER_VERSION )
185 {
186 PROVIDER_ERROR( "Wrong virtual layer version!" );
187 return false;
188 }
189 mDefinition = QgsVirtualLayerDefinition::fromUrl( QUrl( q.columnText( 1 ) ) );
190 }
191 }
192 // overwrite the uri part of the definition
193 mDefinition.setFilePath( mPath );
194
195
196 // load source layers
197 if ( !loadSourceLayers() )
198 {
199 return false;
200 }
201
202 /* only one table */
203 if ( mDefinition.query().isEmpty() )
204 {
205 mTableName = mLayers[0].name;
206 }
207 else
208 {
209 mTableName = VIRTUAL_LAYER_QUERY_VIEW;
210 }
211
212 mSubset = mDefinition.subsetString();
213
214 return true;
215 }
216
createIt()217 bool QgsVirtualLayerProvider::createIt()
218 {
219 using namespace QgsVirtualLayerQueryParser;
220
221 // consistency check
222 if ( mDefinition.sourceLayers().size() > 1 && mDefinition.query().isEmpty() )
223 {
224 PROVIDER_ERROR( QString( "Don't know how to join layers, please specify a query" ) );
225 return false;
226 }
227
228 if ( mDefinition.sourceLayers().empty() && mDefinition.filePath().isEmpty() && mDefinition.query().isEmpty() )
229 {
230 PROVIDER_ERROR( QString( "Please specify at least one source layer or a query" ) );
231 return false;
232 }
233
234 if ( !mDefinition.filePath().isEmpty() && mDefinition.hasReferencedLayers() )
235 {
236 PROVIDER_ERROR( QString( "Cannot store referenced layers" ) );
237 return false;
238 }
239
240
241 QVector<ColumnDef> gFields;
242 if ( !mDefinition.query().isEmpty() )
243 {
244
245 QStringList tables = referencedTables( mDefinition.query() );
246 const auto constTables = tables;
247 for ( const QString &tname : constTables )
248 {
249 // is it in source layers ?
250 if ( mDefinition.hasSourceLayer( tname ) )
251 {
252 continue;
253 }
254 // is it in loaded layers ?
255 bool found = false;
256 const auto constMapLayers = QgsProject::instance()->mapLayers();
257 for ( const QgsMapLayer *l : constMapLayers )
258 {
259 if ( l->type() != QgsMapLayerType::VectorLayer )
260 continue;
261
262 const QgsVectorLayer *vl = static_cast<const QgsVectorLayer *>( l );
263 if ( ( vl->name() == tname ) || ( vl->name().compare( tname.toLower(), Qt::CaseInsensitive ) == 0 ) || ( vl->id() == tname ) )
264 {
265 mDefinition.addSource( tname, vl->id() );
266 found = true;
267 break;
268 }
269 }
270 if ( !found )
271 {
272 PROVIDER_ERROR( QString( "Referenced table %1 in query not found!" ).arg( tname ) );
273 return false;
274 }
275 }
276 }
277
278 QString path;
279 mPath = mDefinition.filePath();
280 // use a temporary file if needed
281 if ( mPath.isEmpty() )
282 path = QStringLiteral( ":memory:" );
283 else
284 path = mPath;
285
286 spatialite_init( 0 );
287
288 try
289 {
290 QgsScopedSqlite sqlite( path );
291 mSqlite = sqlite;
292 }
293 catch ( std::runtime_error &e )
294 {
295 PROVIDER_ERROR( e.what() );
296 return false;
297 }
298
299 resetSqlite();
300 initVirtualLayerMetadata( mSqlite.get() );
301
302 bool noGeometry = mDefinition.geometryWkbType() == QgsWkbTypes::NoGeometry;
303
304 // load source layers (and populate mLayers)
305 if ( !loadSourceLayers() )
306 {
307 return false;
308 }
309
310 // now create virtual tables based on layers
311 for ( int i = 0; i < mLayers.size(); i++ )
312 {
313 QgsVectorLayer *vlayer = mLayers.at( i ).layer;
314 QString vname = mLayers.at( i ).name;
315 if ( vlayer )
316 {
317 createVirtualTable( vlayer, vname );
318 }
319 else
320 {
321 QString provider = mLayers.at( i ).provider;
322 // double each single quote
323 provider.replace( QLatin1String( "'" ), QLatin1String( "''" ) );
324 QString source = mLayers.at( i ).source;
325 source.replace( QLatin1String( "'" ), QLatin1String( "''" ) );
326 QString encoding = mLayers.at( i ).encoding;
327 QString createStr = QStringLiteral( "DROP TABLE IF EXISTS \"%1\"; CREATE VIRTUAL TABLE \"%1\" USING QgsVLayer('%2','%4',%3)" )
328 .arg( vname,
329 provider,
330 encoding,
331 source ); // source must be the last argument here, since it can contains '%x' strings that would be replaced
332 Sqlite::Query::exec( mSqlite.get(), createStr );
333 }
334 }
335
336 QgsFields tfields;
337 if ( !mDefinition.query().isEmpty() )
338 {
339 // look for column types of the query
340 TableDef columns = columnDefinitionsFromQuery( mSqlite.get(), mDefinition.query() );
341
342 for ( int i = 0; i < columns.size(); i++ )
343 {
344 ColumnDef &c = columns[i];
345
346 if ( c.name().isEmpty() )
347 {
348 PROVIDER_ERROR( QString( "Result column #%1 has no name!" ).arg( i + 1 ) );
349 return false;
350 }
351
352 // then override types by the ones defined in the url
353 if ( mDefinition.fields().indexFromName( c.name() ) != -1 )
354 {
355 c.setScalarType( mDefinition.fields().field( c.name() ).type() );
356 }
357
358 if ( c.isGeometry() )
359 {
360 gFields << c;
361 }
362 // if the geometry field is not detected as a geometry, move it to the geometry fields
363 // with the provided type and srid
364 else if ( mDefinition.hasDefinedGeometry() && c.name() == mDefinition.geometryField() )
365 {
366 ColumnDef g;
367 g.setName( mDefinition.geometryField() );
368 g.setGeometry( mDefinition.geometryWkbType() );
369 g.setSrid( mDefinition.geometrySrid() );
370 gFields << g;
371 }
372 // default type: string
373 else if ( c.scalarType() == QVariant::Invalid )
374 {
375 c.setScalarType( QVariant::String );
376 }
377 else
378 {
379 tfields.append( QgsField( c.name(), c.scalarType() ) );
380 }
381 }
382
383 // process geometry field
384 if ( !noGeometry )
385 {
386 // no geometry field defined yet, take the first detected
387 if ( mDefinition.geometryField().isEmpty() )
388 {
389 if ( gFields.count() > 0 )
390 {
391 mDefinition.setGeometryField( gFields[0].name() );
392 mDefinition.setGeometryWkbType( gFields[0].wkbType() );
393 mDefinition.setGeometrySrid( gFields[0].srid() );
394 }
395 }
396 // a geometry field is named, but has no type yet
397 // look for a detected type
398 else if ( !mDefinition.hasDefinedGeometry() )
399 {
400 bool found = false;
401 for ( int i = 0; i < gFields.size(); i++ )
402 {
403 if ( gFields[i].name() == mDefinition.geometryField() )
404 {
405 // override the geometry type
406 mDefinition.setGeometryWkbType( gFields[i].wkbType() );
407 mDefinition.setGeometrySrid( gFields[i].srid() );
408 found = true;
409 break;
410 }
411 }
412 if ( !found )
413 {
414 PROVIDER_ERROR( "Cannot find the specified geometry field!" );
415 return false;
416 }
417 }
418
419 if ( !mDefinition.geometryField().isEmpty() && !mDefinition.hasDefinedGeometry() )
420 {
421 PROVIDER_ERROR( "Can't deduce the geometry type of the geometry field!" );
422 return false;
423 }
424 }
425
426 // save field definitions
427 mDefinition.setFields( tfields );
428
429 mTableName = VIRTUAL_LAYER_QUERY_VIEW;
430
431 // create a view
432 QString viewStr = QStringLiteral( "DROP VIEW IF EXISTS %1; CREATE VIEW %1 AS %2" )
433 .arg( VIRTUAL_LAYER_QUERY_VIEW,
434 mDefinition.query() );
435 Sqlite::Query::exec( mSqlite.get(), viewStr );
436 }
437 else
438 {
439 // no query => implies we must only have one virtual table
440 mTableName = mLayers[0].name;
441
442 TableDef td = tableDefinitionFromVirtualTable( mSqlite.get(), mTableName );
443 const auto constTd = td;
444 for ( const ColumnDef &c : constTd )
445 {
446 if ( !c.isGeometry() )
447 {
448 tfields.append( QgsField( c.name(), c.scalarType() ) );
449 }
450 else if ( !noGeometry )
451 {
452 mDefinition.setGeometryField( QStringLiteral( "geometry" ) );
453 mDefinition.setGeometryWkbType( c.wkbType() );
454 mDefinition.setGeometrySrid( c.srid() );
455 }
456 }
457 mDefinition.setFields( tfields );
458 }
459
460 // Save the definition back to the sqlite file
461 {
462 Sqlite::Query q( mSqlite.get(), QStringLiteral( "UPDATE _meta SET url=?" ) );
463 q.bind( mDefinition.toUrl().toString() );
464 q.step();
465 }
466
467 return true;
468 }
469
createVirtualTable(QgsVectorLayer * vlayer,const QString & vname)470 void QgsVirtualLayerProvider::createVirtualTable( QgsVectorLayer *vlayer, const QString &vname )
471 {
472 QString createStr = QStringLiteral( "DROP TABLE IF EXISTS \"%1\"; CREATE VIRTUAL TABLE \"%1\" USING QgsVLayer(%2);" ).arg( vname, vlayer->id() );
473 Sqlite::Query::exec( mSqlite.get(), createStr );
474 }
475
cancelReload()476 bool QgsVirtualLayerProvider::cancelReload()
477 {
478 return mSqlite.interrupt();
479 }
480
resetSqlite()481 void QgsVirtualLayerProvider::resetSqlite()
482 {
483 bool hasSpatialrefsys = false;
484 {
485 Sqlite::Query q( mSqlite.get(), QStringLiteral( "SELECT name FROM sqlite_master WHERE name='spatial_ref_sys'" ) );
486 hasSpatialrefsys = q.step() == SQLITE_ROW;
487 }
488
489 QString sql = QStringLiteral( "DROP TABLE IF EXISTS _meta;" );
490 if ( !hasSpatialrefsys )
491 {
492 sql += QLatin1String( "SELECT InitSpatialMetadata(1);" );
493 }
494 Sqlite::Query::exec( mSqlite.get(), sql );
495 }
496
featureSource() const497 QgsAbstractFeatureSource *QgsVirtualLayerProvider::featureSource() const
498 {
499 return new QgsVirtualLayerFeatureSource( this );
500 }
501
storageType() const502 QString QgsVirtualLayerProvider::storageType() const
503 {
504 return QStringLiteral( "No storage per se, view data from other data sources" );
505 }
506
crs() const507 QgsCoordinateReferenceSystem QgsVirtualLayerProvider::crs() const
508 {
509 return mCrs;
510 }
511
getFeatures(const QgsFeatureRequest & request) const512 QgsFeatureIterator QgsVirtualLayerProvider::getFeatures( const QgsFeatureRequest &request ) const
513 {
514 return QgsFeatureIterator( new QgsVirtualLayerFeatureIterator( new QgsVirtualLayerFeatureSource( this ), false, request ) );
515 }
516
subsetString() const517 QString QgsVirtualLayerProvider::subsetString() const
518 {
519 return mSubset;
520 }
521
setSubsetString(const QString & subset,bool updateFeatureCount)522 bool QgsVirtualLayerProvider::setSubsetString( const QString &subset, bool updateFeatureCount )
523 {
524 if ( subset == mSubset )
525 return true;
526
527 mSubset = subset;
528 clearMinMaxCache();
529 if ( updateFeatureCount )
530 updateStatistics();
531
532 mDefinition.setSubsetString( subset );
533
534 setDataSourceUri( mDefinition.toString() );
535
536 emit dataChanged();
537
538 return true;
539 }
540
541
wkbType() const542 QgsWkbTypes::Type QgsVirtualLayerProvider::wkbType() const
543 {
544 return static_cast<QgsWkbTypes::Type>( mDefinition.geometryWkbType() );
545 }
546
featureCount() const547 long QgsVirtualLayerProvider::featureCount() const
548 {
549 if ( !mCachedStatistics )
550 {
551 updateStatistics();
552 }
553 return mFeatureCount;
554 }
555
extent() const556 QgsRectangle QgsVirtualLayerProvider::extent() const
557 {
558 if ( !mCachedStatistics )
559 {
560 updateStatistics();
561 }
562 return mExtent;
563 }
564
updateStatistics() const565 void QgsVirtualLayerProvider::updateStatistics() const
566 {
567 bool hasGeometry = mDefinition.geometryWkbType() != QgsWkbTypes::NoGeometry;
568 QString subset = mSubset.isEmpty() ? QString() : " WHERE " + mSubset;
569 QString sql = QStringLiteral( "SELECT Count(*)%1 FROM %2%3" )
570 .arg( hasGeometry ? QStringLiteral( ",Min(MbrMinX(%1)),Min(MbrMinY(%1)),Max(MbrMaxX(%1)),Max(MbrMaxY(%1))" ).arg( quotedColumn( mDefinition.geometryField() ) ) : QString(),
571 mTableName,
572 subset );
573
574 try
575 {
576 Sqlite::Query q( mSqlite.get(), sql );
577 if ( q.step() == SQLITE_ROW )
578 {
579 mFeatureCount = q.columnInt64( 0 );
580 if ( hasGeometry )
581 {
582 double x1, y1, x2, y2;
583 x1 = q.columnDouble( 1 );
584 y1 = q.columnDouble( 2 );
585 x2 = q.columnDouble( 3 );
586 y2 = q.columnDouble( 4 );
587 mExtent = QgsRectangle( x1, y1, x2, y2 );
588 }
589 mCachedStatistics = true;
590 }
591 }
592 catch ( std::runtime_error &e )
593 {
594 pushError( tr( "Error while executing feature count request : %1" ).arg( e.what() ) );
595 mFeatureCount = 0;
596 return;
597 }
598 }
599
invalidateStatistics()600 void QgsVirtualLayerProvider::invalidateStatistics()
601 {
602 mCachedStatistics = false;
603 }
604
fields() const605 QgsFields QgsVirtualLayerProvider::fields() const
606 {
607 return mDefinition.fields();
608 }
609
isValid() const610 bool QgsVirtualLayerProvider::isValid() const
611 {
612 return mValid;
613 }
614
capabilities() const615 QgsVectorDataProvider::Capabilities QgsVirtualLayerProvider::capabilities() const
616 {
617 QgsVectorDataProvider::Capabilities capabilities = CancelSupport;
618
619 if ( !mDefinition.uid().isNull() )
620 {
621 capabilities |= SelectAtId;
622 }
623
624 return capabilities;
625 }
626
name() const627 QString QgsVirtualLayerProvider::name() const
628 {
629 return VIRTUAL_LAYER_KEY;
630 }
631
description() const632 QString QgsVirtualLayerProvider::description() const
633 {
634 return VIRTUAL_LAYER_DESCRIPTION;
635 }
636
pkAttributeIndexes() const637 QgsAttributeList QgsVirtualLayerProvider::pkAttributeIndexes() const
638 {
639 if ( !mDefinition.uid().isNull() )
640 {
641 const QgsFields &fields = mDefinition.fields();
642 for ( int i = 0; i < fields.size(); i++ )
643 {
644 if ( fields.at( i ).name().compare( mDefinition.uid(), Qt::CaseInsensitive ) == 0 )
645 {
646 QgsAttributeList l;
647 l << i;
648 return l;
649 }
650 }
651 }
652 return QgsAttributeList();
653 }
654
dependencies() const655 QSet<QgsMapLayerDependency> QgsVirtualLayerProvider::dependencies() const
656 {
657 QSet<QgsMapLayerDependency> deps;
658 const auto constSourceLayers = mDefinition.sourceLayers();
659 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
660 {
661 if ( l.isReferenced() )
662 deps << QgsMapLayerDependency( l.reference(), QgsMapLayerDependency::PresenceDependency, QgsMapLayerDependency::FromProvider );
663 }
664 return deps;
665 }
666
createProvider(const QString & uri,const QgsDataProvider::ProviderOptions & options,QgsDataProvider::ReadFlags flags)667 QgsVirtualLayerProvider *QgsVirtualLayerProviderMetadata::createProvider(
668 const QString &uri,
669 const QgsDataProvider::ProviderOptions &options,
670 QgsDataProvider::ReadFlags flags )
671 {
672 return new QgsVirtualLayerProvider( uri, options, flags );
673 }
674
675
676 #ifdef HAVE_GUI
677
678 //! Provider for virtual layers source select
679 class QgsVirtualSourceSelectProvider : public QgsSourceSelectProvider
680 {
681 public:
682
providerKey() const683 QString providerKey() const override { return QStringLiteral( "virtual" ); }
text() const684 QString text() const override { return QObject::tr( "Virtual Layer" ); }
ordering() const685 int ordering() const override { return QgsSourceSelectProvider::OrderDatabaseProvider + 60; }
toolTip() const686 QString toolTip() const override { return QObject::tr( "Add Virtual Layer" ); }
icon() const687 QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); }
createDataSourceWidget(QWidget * parent=nullptr,Qt::WindowFlags fl=Qt::Widget,QgsProviderRegistry::WidgetMode widgetMode=QgsProviderRegistry::WidgetMode::Embedded) const688 QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override
689 {
690 return new QgsVirtualLayerSourceSelect( parent, fl, widgetMode );
691 }
692 };
693
694
QgsVirtualLayerProviderGuiMetadata()695 QgsVirtualLayerProviderGuiMetadata::QgsVirtualLayerProviderGuiMetadata()
696 : QgsProviderGuiMetadata( VIRTUAL_LAYER_KEY )
697 {
698 }
699
sourceSelectProviders()700 QList<QgsSourceSelectProvider *> QgsVirtualLayerProviderGuiMetadata::sourceSelectProviders()
701 {
702 QList<QgsSourceSelectProvider *> providers;
703 providers << new QgsVirtualSourceSelectProvider;
704 return providers;
705 }
706 #endif
707
QgsVirtualLayerProviderMetadata()708 QgsVirtualLayerProviderMetadata::QgsVirtualLayerProviderMetadata():
709 QgsProviderMetadata( VIRTUAL_LAYER_KEY, VIRTUAL_LAYER_DESCRIPTION )
710 {
711 }
712
713
providerMetadataFactory()714 QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
715 {
716 return new QgsVirtualLayerProviderMetadata();
717 }
718
719 #ifdef HAVE_GUI
providerGuiMetadataFactory()720 QGISEXTERN QgsProviderGuiMetadata *providerGuiMetadataFactory()
721 {
722 return new QgsVirtualLayerProviderGuiMetadata();
723 }
724 #endif
725