QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsquerybuilder.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquerybuilder.cpp - Query Builder
3 --------------------------------------
4 Date : 2004-11-19
5 Copyright : (C) 2004 by Gary E.Sherman
6 Email : sherman at mrcc.com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include "qgsquerybuilder.h"
16#include "qgslogger.h"
17#include "qgsproject.h"
18#include "qgssettings.h"
19#include "qgsvectorlayer.h"
21#include "qgsapplication.h"
22#include "qgshelp.h"
23#include "qgsgui.h"
24
25#include <QDomDocument>
26#include <QDomElement>
27#include <QFileDialog>
28#include <QInputDialog>
29#include <QListView>
30#include <QMessageBox>
31#include <QPushButton>
32#include <QTextStream>
33
34
35// constructor used when the query builder must make its own
36// connection to the database
38 QWidget *parent, Qt::WindowFlags fl )
39 : QgsSubsetStringEditorInterface( parent, fl )
40 , mPreviousFieldRow( -1 )
41 , mLayer( layer )
42{
43 setupUi( this );
45 connect( btnEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnEqual_clicked );
46 connect( btnLessThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessThan_clicked );
47 connect( btnGreaterThan, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterThan_clicked );
48 connect( btnPct, &QPushButton::clicked, this, &QgsQueryBuilder::btnPct_clicked );
49 connect( btnIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnIn_clicked );
50 connect( btnNotIn, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotIn_clicked );
51 connect( btnLike, &QPushButton::clicked, this, &QgsQueryBuilder::btnLike_clicked );
52 connect( btnILike, &QPushButton::clicked, this, &QgsQueryBuilder::btnILike_clicked );
53 connect( lstFields, &QListView::clicked, this, &QgsQueryBuilder::lstFields_clicked );
54 connect( lstFields, &QListView::doubleClicked, this, &QgsQueryBuilder::lstFields_doubleClicked );
55 connect( lstValues, &QListView::doubleClicked, this, &QgsQueryBuilder::lstValues_doubleClicked );
56 connect( btnLessEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnLessEqual_clicked );
57 connect( btnGreaterEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnGreaterEqual_clicked );
58 connect( btnNotEqual, &QPushButton::clicked, this, &QgsQueryBuilder::btnNotEqual_clicked );
59 connect( btnAnd, &QPushButton::clicked, this, &QgsQueryBuilder::btnAnd_clicked );
60 connect( btnNot, &QPushButton::clicked, this, &QgsQueryBuilder::btnNot_clicked );
61 connect( btnOr, &QPushButton::clicked, this, &QgsQueryBuilder::btnOr_clicked );
62 connect( btnGetAllValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnGetAllValues_clicked );
63 connect( btnSampleValues, &QPushButton::clicked, this, &QgsQueryBuilder::btnSampleValues_clicked );
64 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsQueryBuilder::showHelp );
65
66 QPushButton *pbn = new QPushButton( tr( "&Test" ) );
67 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
68 connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::test );
69
70 pbn = new QPushButton( tr( "&Clear" ) );
71 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
72 connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::clear );
73
74 pbn = new QPushButton( tr( "&Save…" ) );
75 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
76 pbn->setToolTip( tr( "Save query to QQF file" ) );
77 connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::saveQuery );
78
79 pbn = new QPushButton( tr( "&Load…" ) );
80 buttonBox->addButton( pbn, QDialogButtonBox::ActionRole );
81 pbn->setToolTip( tr( "Load query from QQF file" ) );
82 connect( pbn, &QAbstractButton::clicked, this, &QgsQueryBuilder::loadQuery );
83
84 setupGuiViews();
85
86 mOrigSubsetString = layer->subsetString();
87 connect( layer, &QgsVectorLayer::subsetStringChanged, this, &QgsQueryBuilder::layerSubsetStringChanged );
88 layerSubsetStringChanged();
89
90 lblDataUri->setText( tr( "Set provider filter on %1" ).arg( layer->name() ) );
91 mTxtSql->setText( mOrigSubsetString );
92
93 mFilterLineEdit->setShowSearchIcon( true );
94 mFilterLineEdit->setPlaceholderText( tr( "Search…" ) );
95 connect( mFilterLineEdit, &QgsFilterLineEdit::textChanged, this, &QgsQueryBuilder::onTextChanged );
96
97 populateFields();
98}
99
100void QgsQueryBuilder::showEvent( QShowEvent *event )
101{
102 mTxtSql->setFocus();
103 QDialog::showEvent( event );
104}
105
106void QgsQueryBuilder::populateFields()
107{
108 const QgsFields &fields = mLayer->fields();
109 mTxtSql->setFields( fields );
110 for ( int idx = 0; idx < fields.count(); ++idx )
111 {
112 if ( fields.fieldOrigin( idx ) != QgsFields::OriginProvider )
113 {
114 // only consider native fields
115 continue;
116 }
117 QStandardItem *myItem = new QStandardItem( fields.at( idx ).displayNameWithAlias() );
118 myItem->setData( idx );
119 myItem->setEditable( false );
120 mModelFields->insertRow( mModelFields->rowCount(), myItem );
121 }
122
123 // All fields get ... setup
124 setupLstFieldsModel();
125}
126
127void QgsQueryBuilder::setupLstFieldsModel()
128{
129 lstFields->setModel( mModelFields );
130}
131
132void QgsQueryBuilder::setupGuiViews()
133{
134 //Initialize the models
135 mModelFields = new QStandardItemModel();
136 mModelValues = new QStandardItemModel();
137 mProxyValues = new QSortFilterProxyModel();
138 mProxyValues->setSourceModel( mModelValues );
139 // Modes
140 lstFields->setViewMode( QListView::ListMode );
141 lstValues->setViewMode( QListView::ListMode );
142 lstFields->setSelectionBehavior( QAbstractItemView::SelectRows );
143 lstValues->setSelectionBehavior( QAbstractItemView::SelectRows );
144 // Performance tip since Qt 4.1
145 lstFields->setUniformItemSizes( true );
146 lstValues->setUniformItemSizes( true );
147 // Colored rows
148 lstFields->setAlternatingRowColors( true );
149 lstValues->setAlternatingRowColors( true );
150 lstValues->setModel( mProxyValues );
151}
152
153void QgsQueryBuilder::fillValues( int idx, int limit )
154{
155 // clear the model
156 mModelValues->clear();
157
158 // determine the field type
159 QList<QVariant> values = qgis::setToList( mLayer->uniqueValues( idx, limit ) );
160 std::sort( values.begin(), values.end() );
161
162 const QString nullValue = QgsApplication::nullRepresentation();
163
164 QgsDebugMsgLevel( QStringLiteral( "nullValue: %1" ).arg( nullValue ), 2 );
165
166 const auto constValues = values;
167 for ( const QVariant &var : constValues )
168 {
169 QString value;
170 if ( QgsVariantUtils::isNull( var ) )
171 value = nullValue;
172 else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
173 value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
174 else if ( var.type() == QVariant::List || var.type() == QVariant::StringList )
175 {
176 const QVariantList list = var.toList();
177 for ( const QVariant &val : list )
178 {
179 if ( !value.isEmpty() )
180 value.append( ", " );
181 value.append( QgsVariantUtils::isNull( val ) ? nullValue : val.toString() );
182 }
183 }
184 else
185 value = var.toString();
186
187 QStandardItem *myItem = new QStandardItem( value );
188 myItem->setEditable( false );
189 myItem->setData( var, Qt::UserRole + 1 );
190 mModelValues->insertRow( mModelValues->rowCount(), myItem );
191 QgsDebugMsgLevel( QStringLiteral( "Value is null: %1\nvalue: %2" ).arg( QgsVariantUtils::isNull( var ) ).arg( QgsVariantUtils::isNull( var ) ? nullValue : var.toString() ), 2 );
192 }
193}
194
195void QgsQueryBuilder::btnSampleValues_clicked()
196{
197 lstValues->setCursor( Qt::WaitCursor );
198
199 const QString prevSubsetString = mLayer->subsetString();
200 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
201 {
202 mIgnoreLayerSubsetStringChangedSignal = true;
203 mLayer->setSubsetString( QString() );
204 }
205
206 //Clear and fill the mModelValues
207 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), 25 );
208
209 if ( prevSubsetString != mLayer->subsetString() )
210 {
211 mLayer->setSubsetString( prevSubsetString );
212 mIgnoreLayerSubsetStringChangedSignal = false;
213 }
214
215 lstValues->setCursor( Qt::ArrowCursor );
216}
217
218void QgsQueryBuilder::btnGetAllValues_clicked()
219{
220 lstValues->setCursor( Qt::WaitCursor );
221
222 const QString prevSubsetString = mLayer->subsetString();
223 if ( mUseUnfilteredLayer->isChecked() && !prevSubsetString.isEmpty() )
224 {
225 mIgnoreLayerSubsetStringChangedSignal = true;
226 mLayer->setSubsetString( QString() );
227 }
228
229 //Clear and fill the mModelValues
230 fillValues( mModelFields->data( lstFields->currentIndex(), Qt::UserRole + 1 ).toInt(), -1 );
231
232 if ( prevSubsetString != mLayer->subsetString() )
233 {
234 mLayer->setSubsetString( prevSubsetString );
235 mIgnoreLayerSubsetStringChangedSignal = false;
236 }
237
238 lstValues->setCursor( Qt::ArrowCursor );
239}
240
242{
243 // test the sql statement to see if it works
244 // by counting the number of records that would be
245 // returned
246
247 if ( mLayer->setSubsetString( mTxtSql->text() ) )
248 {
249 const long long featureCount { mLayer->featureCount() };
250 // Check for errors
251 if ( featureCount < 0 )
252 {
253 QMessageBox::warning( this,
254 tr( "Query Result" ),
255 tr( "An error occurred when executing the query, please check the expression syntax." ) );
256 }
257 else
258 {
259 QMessageBox::information( this,
260 tr( "Query Result" ),
261 tr( "The where clause returned %n row(s).", "returned test rows", featureCount ) );
262 }
263 }
264 else if ( mLayer->dataProvider()->hasErrors() )
265 {
266 QMessageBox::warning( this,
267 tr( "Query Result" ),
268 tr( "An error occurred when executing the query." )
269 + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char( '\n' ) ) ) );
270 mLayer->dataProvider()->clearErrors();
271 }
272 else
273 {
274 QMessageBox::warning( this,
275 tr( "Query Result" ),
276 tr( "An error occurred when executing the query." ) );
277 }
278}
279
281{
282 if ( mTxtSql->text() != mOrigSubsetString )
283 {
284 if ( !mLayer->setSubsetString( mTxtSql->text() ) )
285 {
286 //error in query - show the problem
287 if ( mLayer->dataProvider()->hasErrors() )
288 {
289 QMessageBox::warning( this,
290 tr( "Query Result" ),
291 tr( "An error occurred when executing the query." )
292 + tr( "\nThe data provider said:\n%1" ).arg( mLayer->dataProvider()->errors().join( QLatin1Char( '\n' ) ) ) );
293 mLayer->dataProvider()->clearErrors();
294 }
295 else
296 {
297 QMessageBox::warning( this, tr( "Query Result" ), tr( "Error in query. The subset string could not be set." ) );
298 }
299
300 return;
301 }
302 }
303
304 QDialog::accept();
305}
306
308{
309 if ( mLayer->subsetString() != mOrigSubsetString )
310 mLayer->setSubsetString( mOrigSubsetString );
311
312 QDialog::reject();
313}
314
315void QgsQueryBuilder::btnEqual_clicked()
316{
317 mTxtSql->insertText( QStringLiteral( " = " ) );
318 mTxtSql->setFocus();
319}
320
321void QgsQueryBuilder::btnLessThan_clicked()
322{
323 mTxtSql->insertText( QStringLiteral( " < " ) );
324 mTxtSql->setFocus();
325}
326
327void QgsQueryBuilder::btnGreaterThan_clicked()
328{
329 mTxtSql->insertText( QStringLiteral( " > " ) );
330 mTxtSql->setFocus();
331}
332
333void QgsQueryBuilder::btnPct_clicked()
334{
335 mTxtSql->insertText( QStringLiteral( "%" ) );
336 mTxtSql->setFocus();
337}
338
339void QgsQueryBuilder::btnIn_clicked()
340{
341 mTxtSql->insertText( QStringLiteral( " IN " ) );
342 mTxtSql->setFocus();
343}
344
345void QgsQueryBuilder::btnNotIn_clicked()
346{
347 mTxtSql->insertText( QStringLiteral( " NOT IN " ) );
348 mTxtSql->setFocus();
349}
350
351void QgsQueryBuilder::btnLike_clicked()
352{
353 mTxtSql->insertText( QStringLiteral( " LIKE " ) );
354 mTxtSql->setFocus();
355}
356
357QString QgsQueryBuilder::sql() const
358{
359 return mTxtSql->text();
360}
361
362void QgsQueryBuilder::setSql( const QString &sqlStatement )
363{
364 mTxtSql->setText( sqlStatement );
365}
366
367void QgsQueryBuilder::lstFields_clicked( const QModelIndex &index )
368{
369 if ( mPreviousFieldRow != index.row() )
370 {
371 mPreviousFieldRow = index.row();
372
373 btnSampleValues->setEnabled( true );
374 btnGetAllValues->setEnabled( true );
375
376 mModelValues->clear();
377 mFilterLineEdit->clear();
378 }
379}
380
381void QgsQueryBuilder::lstFields_doubleClicked( const QModelIndex &index )
382{
383 mTxtSql->insertText( '\"' + mLayer->fields().at( mModelFields->data( index, Qt::UserRole + 1 ).toInt() ).name() + '\"' );
384 mTxtSql->setFocus();
385}
386
387void QgsQueryBuilder::lstValues_doubleClicked( const QModelIndex &index )
388{
389 const QVariant value = index.data( Qt::UserRole + 1 );
390 if ( QgsVariantUtils::isNull( value ) )
391 mTxtSql->insertText( QStringLiteral( "NULL" ) );
392 else if ( value.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
393 mTxtSql->insertText( '\'' + value.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) ) + '\'' );
394 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong || value.type() == QVariant::Bool )
395 mTxtSql->insertText( value.toString() );
396 else
397 mTxtSql->insertText( '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'' );
398
399 mTxtSql->setFocus();
400}
401
402void QgsQueryBuilder::btnLessEqual_clicked()
403{
404 mTxtSql->insertText( QStringLiteral( " <= " ) );
405 mTxtSql->setFocus();
406}
407
408void QgsQueryBuilder::btnGreaterEqual_clicked()
409{
410 mTxtSql->insertText( QStringLiteral( " >= " ) );
411 mTxtSql->setFocus();
412}
413
414void QgsQueryBuilder::btnNotEqual_clicked()
415{
416 mTxtSql->insertText( QStringLiteral( " != " ) );
417 mTxtSql->setFocus();
418}
419
420void QgsQueryBuilder::btnAnd_clicked()
421{
422 mTxtSql->insertText( QStringLiteral( " AND " ) );
423 mTxtSql->setFocus();
424}
425
426void QgsQueryBuilder::btnNot_clicked()
427{
428 mTxtSql->insertText( QStringLiteral( " NOT " ) );
429 mTxtSql->setFocus();
430}
431
432void QgsQueryBuilder::btnOr_clicked()
433{
434 mTxtSql->insertText( QStringLiteral( " OR " ) );
435 mTxtSql->setFocus();
436}
437
438void QgsQueryBuilder::onTextChanged( const QString &text )
439{
440 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
441 mProxyValues->setFilterWildcard( text );
442}
443
445{
446 mTxtSql->clear();
447 mLayer->setSubsetString( QString() );
448}
449
450void QgsQueryBuilder::btnILike_clicked()
451{
452 mTxtSql->insertText( QStringLiteral( " ILIKE " ) );
453 mTxtSql->setFocus();
454}
455
457{
458 lblDataUri->setText( uri );
459}
460
461void QgsQueryBuilder::showHelp()
462{
463 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#query-builder" ) );
464}
465
467{
468 const bool ok = saveQueryToFile( mTxtSql->text() );
469 Q_UNUSED( ok )
470}
471
472bool QgsQueryBuilder::saveQueryToFile( const QString &subset )
473{
474 QgsSettings s;
475 const QString lastQueryFileDir = s.value( QStringLiteral( "/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
476 //save as qqf (QGIS query file)
477 QString saveFileName = QFileDialog::getSaveFileName( nullptr, tr( "Save Query to File" ), lastQueryFileDir, tr( "Query files (*.qqf *.QQF)" ) );
478 if ( saveFileName.isNull() )
479 {
480 return false;
481 }
482
483 if ( !saveFileName.endsWith( QLatin1String( ".qqf" ), Qt::CaseInsensitive ) )
484 {
485 saveFileName += QLatin1String( ".qqf" );
486 }
487
488 QFile saveFile( saveFileName );
489 if ( !saveFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
490 {
491 QMessageBox::critical( nullptr, tr( "Save Query to File" ), tr( "Could not open file for writing." ) );
492 return false ;
493 }
494
495 QDomDocument xmlDoc;
496 QDomElement queryElem = xmlDoc.createElement( QStringLiteral( "Query" ) );
497 const QDomText queryTextNode = xmlDoc.createTextNode( subset );
498 queryElem.appendChild( queryTextNode );
499 xmlDoc.appendChild( queryElem );
500
501 QTextStream fileStream( &saveFile );
502 xmlDoc.save( fileStream, 2 );
503
504 const QFileInfo fi( saveFile );
505 s.setValue( QStringLiteral( "/UI/lastQueryFileDir" ), fi.absolutePath() );
506 return true;
507}
508
510{
511 QString subset;
512 if ( loadQueryFromFile( subset ) )
513 {
514 mTxtSql->clear();
515 mTxtSql->insertText( subset );
516 }
517}
518
520{
521 const QgsSettings s;
522 const QString lastQueryFileDir = s.value( QStringLiteral( "/UI/lastQueryFileDir" ), QDir::homePath() ).toString();
523
524 const QString queryFileName = QFileDialog::getOpenFileName( nullptr, tr( "Load Query from File" ), lastQueryFileDir, tr( "Query files" ) + " (*.qqf);;" + tr( "All files" ) + " (*)" );
525 if ( queryFileName.isNull() )
526 {
527 return false;
528 }
529
530 QFile queryFile( queryFileName );
531 if ( !queryFile.open( QIODevice::ReadOnly ) )
532 {
533 QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "Could not open file for reading." ) );
534 return false;
535 }
536 QDomDocument queryDoc;
537 if ( !queryDoc.setContent( &queryFile ) )
538 {
539 QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "File is not a valid xml document." ) );
540 return false;
541 }
542
543 const QDomElement queryElem = queryDoc.firstChildElement( QStringLiteral( "Query" ) );
544 if ( queryElem.isNull() )
545 {
546 QMessageBox::critical( nullptr, tr( "Load Query from File" ), tr( "File is not a valid query document." ) );
547 return false;
548 }
549
550 subset = queryElem.text();
551 return true;
552}
553
554void QgsQueryBuilder::layerSubsetStringChanged()
555{
556 if ( mIgnoreLayerSubsetStringChangedSignal )
557 return;
558 mUseUnfilteredLayer->setDisabled( mLayer->subsetString().isEmpty() );
559}
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
QString name
Definition qgsfield.h:62
QString displayNameWithAlias() const
Returns the name to use when displaying this field and adds the alias in parenthesis if it is defined...
Definition qgsfield.cpp:96
Container of fields for a vector layer.
Definition qgsfields.h:45
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition qgsfields.h:51
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:194
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
QString name
Definition qgsmaplayer.h:77
QString providerType() const
Returns the provider type (provider key) for this layer.
static bool loadQueryFromFile(QString &subset)
Load query from the XML file.
void loadQuery()
Load query from the XML file.
void saveQuery()
Save query to the XML file.
void accept() override
void reject() override
void setDatasourceDescription(const QString &uri)
void setSql(const QString &sqlStatement)
Set the sql statement to display in the dialog.
virtual void test()
The default implementation tests that the constructed sql statement to see if the vector layer data p...
static bool saveQueryToFile(const QString &subset)
Save query to the XML file.
void showEvent(QShowEvent *event) override
QgsQueryBuilder(QgsVectorLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
This constructor is used when the query builder is called from the vector layer properties dialog.
QString sql() const
Returns the sql statement entered in the dialog.
This class is a composition of two QSettings instances:
Definition qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Interface for a dialog that can edit subset strings.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
void clearErrors()
Clear recorded errors.
QStringList errors() const
Gets recorded errors.
bool hasErrors() const
Provider has errors to report.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39