Skip to content
Permalink
Browse files

[vector layer] Fix feature request order by not working against non-p…

…rovider fields
  • Loading branch information
github-actions authored and nyalldawson committed Jun 26, 2020
1 parent defa799 commit 2b6e1940a1fd5ec7125e7e800951e1af2f1a9bf2
@@ -140,14 +140,35 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
mRequest.setFilterRect( mFilterRect );
}

// check whether the order by clause(s) can be delegated to the provider
mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
if ( !mRequest.orderBy().isEmpty() )
{
QSet<int> attributeIndexes;
const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
for ( int attrIndex : usedAttributeIndices )
{
if ( mSource->mFields.fieldOrigin( attrIndex ) != QgsFields::OriginProvider )
mDelegatedOrderByToProvider = false;

attributeIndexes << attrIndex;
}

if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mDelegatedOrderByToProvider )
{
attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
}
}

if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
{
mRequest.expressionContext()->setFields( mSource->mFields );
mRequest.filterExpression()->prepare( mRequest.expressionContext() );

if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
{
//ensure that all fields required for filter expressions are prepared
// ensure that all fields required for filter expressions are prepared
QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
@@ -168,6 +189,11 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
}

if ( !mDelegatedOrderByToProvider )
{
mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
}

if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
{
// prepare list of attributes to match provider fields
@@ -1182,7 +1208,7 @@ void QgsVectorLayerFeatureIterator::createExpressionContext()
bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
{
Q_UNUSED( orderBys )
return true;
return mDelegatedOrderByToProvider;
}


@@ -288,6 +288,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
* Checks a feature's geometry for validity, if requested in feature request.
*/
bool checkGeometryValidity( const QgsFeature &feature );

bool mDelegatedOrderByToProvider = false;
};


@@ -14,12 +14,15 @@

import os

from qgis.core import (QgsVectorLayer,
from qgis.core import (QgsAuxiliaryStorage,
QgsAuxiliaryLayer,
QgsVectorLayer,
QgsFeatureRequest,
QgsFeature,
QgsField,
NULL,
QgsProject,
QgsPropertyDefinition,
QgsVectorLayerJoinInfo,
QgsGeometry)
from qgis.testing import start_app, unittest
@@ -293,6 +296,129 @@ def test_JoinUsingFeatureRequestExpression(self):

QgsProject.instance().removeMapLayers([layer.id(), joinLayer.id()])

def test_FeatureRequestSortByVirtualField(self):
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f1 = QgsFeature()
f1.setAttributes(["test", 123])
f2 = QgsFeature()
f2.setAttributes(["test", 124])
self.assertTrue(pr.addFeatures([f1, f2]))

idx = layer.addExpressionField('if("fldint"=123,3,2)', QgsField('exp1', QVariant.LongLong)) # NOQA

QgsProject.instance().addMapLayers([layer])

request = QgsFeatureRequest()
request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('exp1', True)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [2, 1])

request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('exp1', False)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [1, 2])

QgsProject.instance().removeMapLayers([layer.id()])

def test_FeatureRequestSortByJoinField(self):
""" test sorting requested features using a joined columns """
joinLayer = QgsVectorLayer(
"Point?field=x:string&field=y:integer&field=z:integer",
"joinlayer", "memory")
pr = joinLayer.dataProvider()
f1 = QgsFeature()
f1.setAttributes(["foo", 123, 321])
f2 = QgsFeature()
f2.setAttributes(["bar", 124, 654])
self.assertTrue(pr.addFeatures([f1, f2]))

layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f1 = QgsFeature()
f1.setAttributes(["test", 123])
f2 = QgsFeature()
f2.setAttributes(["test", 124])
self.assertTrue(pr.addFeatures([f1, f2]))

QgsProject.instance().addMapLayers([layer, joinLayer])

join = QgsVectorLayerJoinInfo()
join.setTargetFieldName("fldint")
join.setJoinLayer(joinLayer)
join.setJoinFieldName("y")
join.setUsingMemoryCache(True)
layer.addJoin(join)

request = QgsFeatureRequest()
request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('joinlayer_z', True)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [1, 2])

request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('joinlayer_z', False)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [2, 1])

QgsProject.instance().removeMapLayers([layer.id(), joinLayer.id()])

def test_ZFeatureRequestSortByAuxiliaryField(self):
s = QgsAuxiliaryStorage()
self.assertTrue(s.isValid())

layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f1 = QgsFeature()
f1.setAttributes(["test", 123])
f2 = QgsFeature()
f2.setAttributes(["test", 124])
self.assertTrue(pr.addFeatures([f1, f2]))

# Create a new auxiliary layer with 'pk' as key
pkf = layer.fields().field(layer.fields().indexOf('fldint'))
al = s.createAuxiliaryLayer(pkf, layer)
self.assertTrue(al.isValid())
layer.setAuxiliaryLayer(al)

prop = QgsPropertyDefinition()
prop.setComment('test_field')
prop.setDataType(QgsPropertyDefinition.DataTypeNumeric)
prop.setOrigin('user')
prop.setName('custom')
self.assertTrue(al.addAuxiliaryField(prop))

layer.startEditing()
i = 2
for feat in layer.getFeatures():
feat.setAttribute(2, i)
layer.updateFeature(feat)
i -= 1
layer.commitChanges()

request = QgsFeatureRequest()
request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(layer.fields()[2].name(), True)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [2, 1])

request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(layer.fields()[2].name(), False)]))
ids = []
for feat in layer.getFeatures(request):
ids.append(feat.id())
self.assertEqual(ids, [1, 2])

QgsProject.instance().removeMapLayers([layer.id()])

def test_invalidGeometryFilter(self):
layer = QgsVectorLayer(
"Polygon?field=x:string",

0 comments on commit 2b6e194

Please sign in to comment.
You can’t perform that action at this time.