diff --git a/src/providers/wfs/qgswfsfeatureiterator.cpp b/src/providers/wfs/qgswfsfeatureiterator.cpp index 449731813cbb..35e039c8d6a8 100644 --- a/src/providers/wfs/qgswfsfeatureiterator.cpp +++ b/src/providers/wfs/qgswfsfeatureiterator.cpp @@ -1002,7 +1002,15 @@ QgsFeatureRequest QgsWFSFeatureIterator::buildRequestCache( int genCounter ) { if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { + // Transfer and transform context requestCache.setFilterExpression( mRequest.filterExpression()->expression() ); + QgsExpressionContext ctx { *mRequest.expressionContext( ) }; + QgsExpressionContextScope *scope { ctx.activeScopeForVariable( QgsExpressionContext::EXPR_FIELDS ) }; + if ( scope ) + { + scope->setVariable( QgsExpressionContext::EXPR_FIELDS, mShared->mCacheDataProvider->fields() ); + } + requestCache.setExpressionContext( ctx ); } if ( genCounter >= 0 ) { diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index bfe7e879ae85..60712c46e603 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -33,7 +33,10 @@ QgsVectorDataProvider, QgsFeatureRequest, QgsApplication, - QgsSettings + QgsSettings, + QgsExpression, + QgsExpressionContextUtils, + QgsExpressionContext, ) from qgis.testing import (start_app, unittest @@ -820,11 +823,11 @@ def testWFST10(self): self.assertTrue(vl.isValid()) self.assertEqual(vl.dataProvider().capabilities(), - QgsVectorDataProvider.AddFeatures | - QgsVectorDataProvider.ChangeAttributeValues | - QgsVectorDataProvider.ChangeGeometries | - QgsVectorDataProvider.DeleteFeatures | - QgsVectorDataProvider.SelectAtId) + QgsVectorDataProvider.AddFeatures + | QgsVectorDataProvider.ChangeAttributeValues + | QgsVectorDataProvider.ChangeGeometries + | QgsVectorDataProvider.DeleteFeatures + | QgsVectorDataProvider.SelectAtId) (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) self.assertFalse(ret) @@ -3766,6 +3769,166 @@ def test_NullValues_regression_20961(self): self.assertEqual(str(got_f2[1]['elevation']), 'NULL') self.assertEqual(str(got_f2[1]['name']), 'sdf') + def testFilteredFeatureRequests(self): + """Test https://issues.qgis.org/issues/21077 """ + + endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_filtered_feature_requests' + + with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.1.0'), 'wb') as f: + f.write(""" + + + + points + Title + Abstract + urn:ogc:def:crs:OGC:1.3:CRS84 + + -98.6523 32.7233 + 23.2868 69.9882 + + + +""".encode('UTF-8')) + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=points'), 'wb') as f: + f.write(""" + + + + + + + + + + + + + + + + + +""".encode('UTF-8')) + + get_feature_1 = """ + + + -10981925.67093 3858635.0686243 + 2592274.0488407 11064877.6393476 + + + + + + + 1544231.80343599 5930698.04174612 + 1544231.80343599 5930698.04174612 + + + + + + + 1544231.80343599 5930698.04174612 + + + + + 177 + Xxx + + + + +""" + get_features = """ + + + -10981925.67093 3858635.0686243 + 2592274.0488407 11064877.6393476 + + + + + + + 1544231.80343599 5930698.04174612 + 1544231.80343599 5930698.04174612 + + + + + + + 1544231.80343599 5930698.04174612 + + + + + 177 + Xxx + + + + + + + + + -10977033.701121 3897159.3308746 + -10977033.701121 3897159.3308746 + + + + + + + -10977033.701121 3897159.3308746 + + + + + 5 + qgis + 0 + + + +""" + + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=points&MAXFEATURES=1&SRSNAME=urn:ogc:def:crs:EPSG::4326"""), 'wb') as f: + f.write(get_feature_1.encode('UTF-8')) + + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=points&SRSNAME=urn:ogc:def:crs:EPSG::4326"""), 'wb') as f: + f.write(get_features.encode('UTF-8')) + + vl = QgsVectorLayer("url='http://" + endpoint + "' typename='points' version='1.1.0'", 'test', 'WFS') + self.assertTrue(vl.isValid()) + + # Fill the cache + [f for f in vl.getFeatures()] + + qgis_feat = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'qgis\'')))) + other_feat = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" != \'qgis\'')))) + self.assertEqual(qgis_feat['name'], 'qgis') + self.assertEqual(other_feat['name'], 'Xxx') + + form_scope = QgsExpressionContextUtils.formScope(qgis_feat) + form_exp = QgsExpression('current_value(\'name\') = "name"') + ctx = QgsExpressionContext() + ctx.appendScope(form_scope) + ctx.setFeature(qgis_feat) + self.assertEqual(form_exp.evaluate(ctx), 1) + ctx.setFeature(other_feat) + self.assertEqual(form_exp.evaluate(ctx), 0) + + # For real now! (this failed in issue 21077) + req = QgsFeatureRequest(form_exp) + req.setExpressionContext(ctx) + qgis_feat = next(vl.getFeatures(req)) + if __name__ == '__main__': unittest.main()