Skip to content

Commit 4e5597a

Browse files
authored
Merge pull request #4848 from nyalldawson/virtual_agg
Fix aggregate calculation in virtual fields
2 parents 3a2710e + 8711473 commit 4e5597a

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

python/core/qgsvectorlayerfeatureiterator.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class QgsVectorLayerFeatureSource : QgsAbstractFeatureSource
6161

6262

6363

64+
6465
};
6566

6667

src/core/qgsvectorlayerfeatureiterator.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *
7979
}
8080
#endif
8181
}
82+
83+
std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
84+
mLayerScope = *layerScope;
8285
}
8386

8487
QgsVectorLayerFeatureSource::~QgsVectorLayerFeatureSource()
@@ -614,6 +617,15 @@ void QgsVectorLayerFeatureIterator::prepareExpression( int fieldIdx )
614617
exp->setAreaUnits( QgsProject::instance()->areaUnits() );
615618

616619
exp->prepare( mExpressionContext.get() );
620+
Q_FOREACH ( const QString &col, exp->referencedColumns() )
621+
{
622+
if ( mSource->fields().lookupField( col ) == fieldIdx )
623+
{
624+
// circular reference - expression depends on column itself
625+
delete exp;
626+
return;
627+
}
628+
}
617629
mExpressionFieldInfo.insert( fieldIdx, exp );
618630

619631
Q_FOREACH ( const QString &col, exp->referencedColumns() )
@@ -644,7 +656,7 @@ void QgsVectorLayerFeatureIterator::prepareFields()
644656
mExpressionContext.reset( new QgsExpressionContext() );
645657
mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
646658
mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
647-
mExpressionContext->setFields( mSource->mFields );
659+
mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
648660

649661
mFieldsToPrepare = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
650662

src/core/qgsvectorlayerfeatureiterator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CORE_EXPORT QgsVectorLayerFeatureSource : public QgsAbstractFeatureSource
8484

8585
QgsFields mFields;
8686

87+
QgsExpressionContextScope mLayerScope;
88+
8789
bool mHasEditBuffer;
8890

8991
// A deep-copy is only performed, if the original maps change

tests/src/python/test_qgsvectorlayer.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,11 @@ def test_ExpressionField(self):
16091609

16101610
self.assertEqual(layer.pendingFields().count(), cnt)
16111611

1612+
# expression field which references itself
1613+
idx = layer.addExpressionField('sum(test2)', QgsField('test2', QVariant.LongLong))
1614+
fet = next(layer.getFeatures())
1615+
self.assertEqual(fet['test2'], NULL)
1616+
16121617
def test_ExpressionFieldEllipsoidLengthCalculation(self):
16131618
#create a temporary layer
16141619
temp_layer = QgsVectorLayer("LineString?crs=epsg:3111&field=pk:int", "vl", "memory")
@@ -1829,6 +1834,27 @@ def testAggregate(self):
18291834
self.assertTrue(ok)
18301835
self.assertEqual(val, 'this is a test')
18311836

1837+
def testAggregateInVirtualField(self):
1838+
"""
1839+
Test aggregates in a virtual field
1840+
"""
1841+
layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
1842+
pr = layer.dataProvider()
1843+
1844+
int_values = [4, 2, 3, 2, 5, None, 8]
1845+
features = []
1846+
for i in int_values:
1847+
f = QgsFeature()
1848+
f.setFields(layer.fields())
1849+
f.setAttributes([i])
1850+
features.append(f)
1851+
assert pr.addFeatures(features)
1852+
1853+
field = QgsField('virtual', QVariant.Double)
1854+
layer.addExpressionField('sum(fldint*2)', field)
1855+
vals = [f['virtual'] for f in layer.getFeatures()]
1856+
self.assertEqual(vals, [48, 48, 48, 48, 48, 48, 48])
1857+
18321858
def onLayerOpacityChanged(self, tr):
18331859
self.opacityTest = tr
18341860

0 commit comments

Comments
 (0)