diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index f0b204c153e8..3396b3c8d678 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -250,7 +250,11 @@ QSet QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) c referencedIndexes = fields.allAttributesList().toSet(); break; } - referencedIndexes << fields.lookupField( fieldName ); + const int idx = fields.lookupField( fieldName ); + if ( idx >= 0 ) + { + referencedIndexes << idx; + } } return referencedIndexes; diff --git a/src/providers/oracle/qgsoraclefeatureiterator.cpp b/src/providers/oracle/qgsoraclefeatureiterator.cpp index b963b3f57e22..e5e19d95c282 100644 --- a/src/providers/oracle/qgsoraclefeatureiterator.cpp +++ b/src/providers/oracle/qgsoraclefeatureiterator.cpp @@ -70,23 +70,22 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource *sour // ensure that all attributes required for expression filter are being fetched if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { - const auto constReferencedColumns = mRequest.filterExpression()->referencedColumns(); - for ( const QString &field : constReferencedColumns ) + const QSet attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields ); + for ( int attrIdx : attributeIndexes ) { - int attrIdx = mSource->mFields.lookupField( field ); if ( !mAttributeList.contains( attrIdx ) ) mAttributeList << attrIdx; } } // ensure that all attributes required for order by are fetched - const QSet< QString > orderByAttributes = mRequest.orderBy().usedAttributes(); - for ( const QString &attr : orderByAttributes ) + const auto orderByAttributes = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); + for ( int attrIdx : orderByAttributes ) { - int attrIndex = mSource->mFields.lookupField( attr ); - if ( !mAttributeList.contains( attrIndex ) ) - mAttributeList << attrIndex; + if ( !mAttributeList.contains( attrIdx ) ) + mAttributeList << attrIdx; } + } else mAttributeList = mSource->mFields.allAttributesList(); diff --git a/src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp b/src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp index c6e6f1ba28ad..770f544fffa8 100644 --- a/src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp +++ b/src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp @@ -138,10 +138,9 @@ QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerF // ensure that all attributes required for expression filter are being fetched if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { - const auto constReferencedColumns = request.filterExpression()->referencedColumns(); - for ( const QString &field : constReferencedColumns ) + const QSet attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); + for ( int attrIdx : attributeIndexes ) { - int attrIdx = mSource->mFields.lookupField( field ); if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } diff --git a/tests/src/python/featuresourcetestbase.py b/tests/src/python/featuresourcetestbase.py index c56a8f896193..2ef866ad1190 100644 --- a/tests/src/python/featuresourcetestbase.py +++ b/tests/src/python/featuresourcetestbase.py @@ -715,3 +715,10 @@ def testMaximumValue(self): def testAllFeatureIds(self): ids = set([f.id() for f in self.source.getFeatures()]) self.assertEqual(set(self.source.allFeatureIds()), ids) + + def testSubsetOfAttributesWithFilterExprWithNonExistingColumn(self): + """ Test fix for https://github.com/qgis/QGIS/issues/33878 """ + request = QgsFeatureRequest().setSubsetOfAttributes([0]) + request.setFilterExpression("non_existing = 1") + features = [f for f in self.source.getFeatures(request)] + self.assertEqual(len(features), 0) diff --git a/tests/src/python/test_qgsexpression.py b/tests/src/python/test_qgsexpression.py index 0f679bfe7dd8..8fe42aa74419 100644 --- a/tests/src/python/test_qgsexpression.py +++ b/tests/src/python/test_qgsexpression.py @@ -15,7 +15,7 @@ from qgis.PyQt.QtCore import QVariant from qgis.testing import unittest from qgis.utils import qgsfunction -from qgis.core import QgsExpression, QgsFeatureRequest, QgsExpressionContext, NULL +from qgis.core import QgsExpression, QgsFeatureRequest, QgsFields, QgsExpressionContext, NULL class TestQgsExpressionCustomFunctions(unittest.TestCase): @@ -264,6 +264,12 @@ def testCreateFieldEqualityExpression(self): res = '"my\'field" = TRUE' self.assertEqual(e.createFieldEqualityExpression(field, value), res) + def testReferencedAttributeIndexesNonExistingField(self): + e = QgsExpression() + e.setExpression("foo = 1") + self.assertTrue(e.isValid()) + self.assertEqual(len(e.referencedAttributeIndexes(QgsFields())), 0) + if __name__ == "__main__": unittest.main() diff --git a/tests/testdata/provider/spatialite.db b/tests/testdata/provider/spatialite.db index e37acc2da191..33a3f86b9f36 100644 Binary files a/tests/testdata/provider/spatialite.db and b/tests/testdata/provider/spatialite.db differ