diff --git a/python/core/qgsvectorlayerfeatureiterator.sip b/python/core/qgsvectorlayerfeatureiterator.sip index 40f6aaceb2f..30340517ab3 100644 --- a/python/core/qgsvectorlayerfeatureiterator.sip +++ b/python/core/qgsvectorlayerfeatureiterator.sip @@ -61,6 +61,7 @@ class QgsVectorLayerFeatureSource : QgsAbstractFeatureSource + }; diff --git a/src/core/qgsvectorlayerfeatureiterator.cpp b/src/core/qgsvectorlayerfeatureiterator.cpp index ad6acfca0d5..08f33e105f2 100644 --- a/src/core/qgsvectorlayerfeatureiterator.cpp +++ b/src/core/qgsvectorlayerfeatureiterator.cpp @@ -79,6 +79,9 @@ QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer * } #endif } + + std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) ); + mLayerScope = *layerScope; } QgsVectorLayerFeatureSource::~QgsVectorLayerFeatureSource() @@ -614,6 +617,15 @@ void QgsVectorLayerFeatureIterator::prepareExpression( int fieldIdx ) exp->setAreaUnits( QgsProject::instance()->areaUnits() ); exp->prepare( mExpressionContext.get() ); + Q_FOREACH ( const QString &col, exp->referencedColumns() ) + { + if ( mSource->fields().lookupField( col ) == fieldIdx ) + { + // circular reference - expression depends on column itself + delete exp; + return; + } + } mExpressionFieldInfo.insert( fieldIdx, exp ); Q_FOREACH ( const QString &col, exp->referencedColumns() ) @@ -644,7 +656,7 @@ void QgsVectorLayerFeatureIterator::prepareFields() mExpressionContext.reset( new QgsExpressionContext() ); mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() ); mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); - mExpressionContext->setFields( mSource->mFields ); + mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) ); mFieldsToPrepare = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); diff --git a/src/core/qgsvectorlayerfeatureiterator.h b/src/core/qgsvectorlayerfeatureiterator.h index 0c6cd50d99e..8c3392ca111 100644 --- a/src/core/qgsvectorlayerfeatureiterator.h +++ b/src/core/qgsvectorlayerfeatureiterator.h @@ -84,6 +84,8 @@ class CORE_EXPORT QgsVectorLayerFeatureSource : public QgsAbstractFeatureSource QgsFields mFields; + QgsExpressionContextScope mLayerScope; + bool mHasEditBuffer; // A deep-copy is only performed, if the original maps change diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index bbce317f9c9..30fe77f7e80 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -1609,6 +1609,11 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(layer.pendingFields().count(), cnt) + # expression field which references itself + idx = layer.addExpressionField('sum(test2)', QgsField('test2', QVariant.LongLong)) + fet = next(layer.getFeatures()) + self.assertEqual(fet['test2'], NULL) + def test_ExpressionFieldEllipsoidLengthCalculation(self): #create a temporary layer temp_layer = QgsVectorLayer("LineString?crs=epsg:3111&field=pk:int", "vl", "memory") @@ -1829,6 +1834,27 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertTrue(ok) self.assertEqual(val, 'this is a test') + def testAggregateInVirtualField(self): + """ + Test aggregates in a virtual field + """ + layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory") + pr = layer.dataProvider() + + int_values = [4, 2, 3, 2, 5, None, 8] + features = [] + for i in int_values: + f = QgsFeature() + f.setFields(layer.fields()) + f.setAttributes([i]) + features.append(f) + assert pr.addFeatures(features) + + field = QgsField('virtual', QVariant.Double) + layer.addExpressionField('sum(fldint*2)', field) + vals = [f['virtual'] for f in layer.getFeatures()] + self.assertEqual(vals, [48, 48, 48, 48, 48, 48, 48]) + def onLayerOpacityChanged(self, tr): self.opacityTest = tr