Skip to content

Commit 402ee9d

Browse files
committed
Fix missing features when combining FilterExpression requests with
subsets of attributes and the filter expression requires fields which are not included in the attribute subset Note: I've only fixed the providers which implement the vector provider conformance unit tests. Other providers (eg GRASS) should also implement a similar fix.
1 parent 62c86f0 commit 402ee9d

8 files changed

+93
-2
lines changed

src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
138138
mLoadGeometry = false;
139139
}
140140

141+
// ensure that all attributes required for expression filter are being fetched
142+
if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression )
143+
{
144+
QgsAttributeList attrs = request.subsetOfAttributes();
145+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
146+
{
147+
int attrIdx = mSource->mFields.fieldNameIndex( field );
148+
if ( !attrs.contains( attrIdx ) )
149+
attrs << attrIdx;
150+
}
151+
mRequest.setSubsetOfAttributes( attrs );
152+
}
153+
141154
QgsDebugMsg( QString( "Iterator is scanning file: " ) + ( mMode == FileScan ? "Yes" : "No" ) );
142155
QgsDebugMsg( QString( "Iterator is loading geometries: " ) + ( mLoadGeometry ? "Yes" : "No" ) );
143156
QgsDebugMsg( QString( "Iterator is testing geometries: " ) + ( mTestGeometry ? "Yes" : "No" ) );

src/providers/mssql/qgsmssqlfeatureiterator.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,20 @@ void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest& request )
7777
mAttributesToFetch.append( mFidCol );
7878

7979
bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
80-
Q_FOREACH ( int i, subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList() )
80+
QgsAttributeList attrs = subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
81+
82+
// ensure that all attributes required for expression filter are being fetched
83+
if ( subsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression )
84+
{
85+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
86+
{
87+
int attrIdx = mSource->mFields.fieldNameIndex( field );
88+
if ( !attrs.contains( attrIdx ) )
89+
attrs << attrIdx;
90+
}
91+
}
92+
93+
Q_FOREACH ( int i, attrs )
8194
{
8295
QString fieldname = mSource->mFields.at( i ).name();
8396
if ( mSource->mFidColName == fieldname )

src/providers/ogr/qgsogrfeatureiterator.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
6464
mFetchGeometry = ( !mRequest.filterRect().isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
6565
QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
6666

67+
// ensure that all attributes required for expression filter are being fetched
68+
if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression )
69+
{
70+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
71+
{
72+
int attrIdx = mSource->mFields.fieldNameIndex( field );
73+
if ( !attrs.contains( attrIdx ) )
74+
attrs << attrIdx;
75+
}
76+
mRequest.setSubsetOfAttributes( attrs );
77+
}
78+
6779
// make sure we fetch just relevant fields
6880
// unless it's a VRT data source filtered by geometry as we don't know which
6981
// attributes make up the geometry and OGR won't fetch them to evaluate the

src/providers/oracle/qgsoraclefeatureiterator.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,23 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* sour
4444
mAttributeList = mRequest.subsetOfAttributes();
4545
if ( mAttributeList.isEmpty() )
4646
mAttributeList = mSource->mFields.allAttributesList();
47+
48+
// ensure that all attributes required for expression filter are being fetched
49+
if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
50+
{
51+
Q_FOREACH ( const QString& field, mRequest.filterExpression()->referencedColumns() )
52+
{
53+
int attrIdx = mSource->mFields.fieldNameIndex( field );
54+
if ( !mAttributeList.contains( attrIdx ) )
55+
mAttributeList << attrIdx;
56+
}
57+
}
58+
4759
}
4860
else
4961
mAttributeList = mSource->mFields.allAttributesList();
5062

63+
5164
QString whereClause;
5265

5366
if ( !mRequest.filterRect().isNull() && !mSource->mGeometryColumn.isNull() && mSource->mHasSpatialIndex )

src/providers/postgres/qgspostgresfeatureiterator.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
8989
}
9090
else if ( request.filterType() == QgsFeatureRequest::FilterExpression )
9191
{
92+
// ensure that all attributes required for expression filter are being fetched
93+
if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
94+
{
95+
QgsAttributeList attrs = mRequest.subsetOfAttributes();
96+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
97+
{
98+
int attrIdx = mSource->mFields.fieldNameIndex( field );
99+
if ( !attrs.contains( attrIdx ) )
100+
attrs << attrIdx;
101+
}
102+
mRequest.setSubsetOfAttributes( attrs );
103+
}
104+
92105
if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
93106
{
94107
//IMPORTANT - this MUST be the last clause added!

src/providers/spatialite/qgsspatialitefeatureiterator.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
8383
//IMPORTANT - this MUST be the last clause added!
8484
else if ( request.filterType() == QgsFeatureRequest::FilterExpression )
8585
{
86+
// ensure that all attributes required for expression filter are being fetched
87+
if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression )
88+
{
89+
QgsAttributeList attrs = request.subsetOfAttributes();
90+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
91+
{
92+
int attrIdx = mSource->mFields.fieldNameIndex( field );
93+
if ( !attrs.contains( attrIdx ) )
94+
attrs << attrIdx;
95+
}
96+
mRequest.setSubsetOfAttributes( attrs );
97+
}
98+
8699
if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
87100
{
88101
QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source );

src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,22 @@ QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerF
8080
mFields = mSource->provider()->fields();
8181
if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes )
8282
{
83-
8483
// copy only selected fields
8584
Q_FOREACH ( int idx, request.subsetOfAttributes() )
8685
{
8786
mAttributes << idx;
8887
}
88+
89+
// ensure that all attributes required for expression filter are being fetched
90+
if ( request.filterType() == QgsFeatureRequest::FilterExpression )
91+
{
92+
Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() )
93+
{
94+
int attrIdx = mFields.fieldNameIndex( field );
95+
if ( !mAttributes.contains( attrIdx ) )
96+
mAttributes << attrIdx;
97+
}
98+
}
8999
}
90100
else
91101
{

tests/src/python/providertestbase.py

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def assert_query(self, provider, expression, expected):
2121
result = set([f['pk'] for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression(expression))])
2222
assert set(expected) == result, 'Expected {} and got {} when testing expression "{}"'.format(set(expected), result, expression)
2323

24+
# Also check that filter works when referenced fields are not being retrieved by request
25+
result = set([f['pk'] for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression(expression).setSubsetOfAttributes([0]))])
26+
assert set(expected) == result, 'Expected {} and got {} when testing expression "{}" using empty attribute subset'.format(set(expected), result, expression)
27+
2428
'''
2529
This is a collection of tests for vector data providers and kept generic.
2630
To make use of it, subclass it and set self.provider to a provider you want to test.

0 commit comments

Comments
 (0)