@@ -93,11 +93,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
9393 , mFetchedFid( false )
9494 , mInterruptionChecker( nullptr )
9595{
96- prepareExpressions ();
97-
98- // prepare joins: may add more attributes to fetch (in order to allow join)
99- if ( mSource ->mJoinBuffer ->containsJoins () )
100- prepareJoins ();
96+ prepareFields ();
10197
10298 mHasVirtualAttributes = !mFetchJoinInfo .isEmpty () || !mExpressionFieldInfo .isEmpty ();
10399
@@ -461,128 +457,132 @@ void QgsVectorLayerFeatureIterator::rewindEditBuffer()
461457 mFetchChangedGeomIt = mSource ->mChangedGeometries .constBegin ();
462458}
463459
460+ void QgsVectorLayerFeatureIterator::prepareJoin ( int fieldIdx )
461+ {
462+ if ( !mSource ->mFields .exists ( fieldIdx ) )
463+ return ;
464464
465+ if ( mSource ->mFields .fieldOrigin ( fieldIdx ) != QgsFields::OriginJoin )
466+ return ;
465467
466- void QgsVectorLayerFeatureIterator::prepareJoins ()
467- {
468- QgsAttributeList fetchAttributes = ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest .subsetOfAttributes () : mSource ->mFields .allAttributesList ();
469- QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working
468+ int sourceLayerIndex;
469+ const QgsVectorJoinInfo* joinInfo = mSource ->mJoinBuffer ->joinForFieldIndex ( fieldIdx, mSource ->mFields , sourceLayerIndex );
470+ Q_ASSERT ( joinInfo );
470471
471- mFetchJoinInfo .clear ();
472+ QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance ()->mapLayer ( joinInfo->joinLayerId ) );
473+ Q_ASSERT ( joinLayer );
472474
473- for ( QgsAttributeList::const_iterator attIt = fetchAttributes. constBegin (); attIt != fetchAttributes. constEnd (); ++attIt )
475+ if ( ! mFetchJoinInfo . contains ( joinInfo ) )
474476 {
475- if ( !mSource ->mFields .exists ( *attIt ) )
476- continue ;
477+ FetchJoinInfo info;
478+ info.joinInfo = joinInfo;
479+ info.joinLayer = joinLayer;
480+ info.indexOffset = mSource ->mJoinBuffer ->joinedFieldsOffset ( joinInfo, mSource ->mFields );
477481
478- if ( mSource ->mFields .fieldOrigin ( *attIt ) != QgsFields::OriginJoin )
479- continue ;
482+ if ( joinInfo->targetFieldName .isEmpty () )
483+ info.targetField = joinInfo->targetFieldIndex ; // for compatibility with 1.x
484+ else
485+ info.targetField = mSource ->mFields .indexFromName ( joinInfo->targetFieldName );
480486
481- int sourceLayerIndex;
482- const QgsVectorJoinInfo* joinInfo = mSource ->mJoinBuffer ->joinForFieldIndex ( *attIt, mSource ->mFields , sourceLayerIndex );
483- Q_ASSERT ( joinInfo );
487+ if ( joinInfo->joinFieldName .isEmpty () )
488+ info.joinField = joinInfo->joinFieldIndex ; // for compatibility with 1.x
489+ else
490+ info.joinField = joinLayer->fields ().indexFromName ( joinInfo->joinFieldName );
484491
485- QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance ()->mapLayer ( joinInfo->joinLayerId ) );
486- Q_ASSERT ( joinLayer );
492+ // for joined fields, we always need to request the targetField from the provider too
493+ if ( !mPreparedFields .contains ( info.targetField ) && !mFieldsToPrepare .contains ( info.targetField ) )
494+ mFieldsToPrepare << info.targetField ;
487495
488- if ( !mFetchJoinInfo .contains ( joinInfo ) )
489- {
490- FetchJoinInfo info;
491- info.joinInfo = joinInfo;
492- info.joinLayer = joinLayer;
493- info.indexOffset = mSource ->mJoinBuffer ->joinedFieldsOffset ( joinInfo, mSource ->mFields );
494-
495- if ( joinInfo->targetFieldName .isEmpty () )
496- info.targetField = joinInfo->targetFieldIndex ; // for compatibility with 1.x
497- else
498- info.targetField = mSource ->mFields .indexFromName ( joinInfo->targetFieldName );
499-
500- if ( joinInfo->joinFieldName .isEmpty () )
501- info.joinField = joinInfo->joinFieldIndex ; // for compatibility with 1.x
502- else
503- info.joinField = joinLayer->fields ().indexFromName ( joinInfo->joinFieldName );
504-
505- // for joined fields, we always need to request the targetField from the provider too
506- if ( !fetchAttributes.contains ( info.targetField ) )
507- sourceJoinFields << info.targetField ;
508-
509- mFetchJoinInfo .insert ( joinInfo, info );
510- }
496+ if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes && !mRequest .subsetOfAttributes ().contains ( info.targetField ) )
497+ mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () << info.targetField );
511498
512- // store field source index - we'll need it when fetching from provider
513- mFetchJoinInfo [ joinInfo ].attributes .push_back ( sourceLayerIndex );
499+ mFetchJoinInfo .insert ( joinInfo, info );
514500 }
515501
516- // add sourceJoinFields if we're using a subset
517- if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
518- mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () + sourceJoinFields );
502+ // store field source index - we'll need it when fetching from provider
503+ mFetchJoinInfo [ joinInfo ].attributes .push_back ( sourceLayerIndex );
519504}
520505
521- void QgsVectorLayerFeatureIterator::prepareExpressions ( )
506+ void QgsVectorLayerFeatureIterator::prepareExpression ( int fieldIdx )
522507{
523- const QList<QgsExpressionFieldBuffer::ExpressionField> exps = mSource ->mExpressionFieldBuffer ->expressions ();
508+ const QList<QgsExpressionFieldBuffer::ExpressionField>& exps = mSource ->mExpressionFieldBuffer ->expressions ();
524509
525- mExpressionContext .reset ( new QgsExpressionContext () );
526- mExpressionContext ->appendScope ( QgsExpressionContextUtils::globalScope () );
527- mExpressionContext ->appendScope ( QgsExpressionContextUtils::projectScope () );
528- mExpressionContext ->setFields ( mSource ->mFields );
510+ int oi = mSource ->mFields .fieldOriginIndex ( fieldIdx );
511+ QgsExpression* exp = new QgsExpression ( exps[oi].cachedExpression );
529512
530- QList< int > virtualFieldsToFetch;
531- for ( int i = 0 ; i < mSource ->mFields .count (); i++ )
513+ QgsDistanceArea da;
514+ da.setSourceCrs ( mSource ->mCrsId );
515+ da.setEllipsoidalMode ( true );
516+ da.setEllipsoid ( QgsProject::instance ()->readEntry ( " Measure" , " /Ellipsoid" , GEO_NONE ) );
517+ exp->setGeomCalculator ( da );
518+ exp->setDistanceUnits ( QgsProject::instance ()->distanceUnits () );
519+ exp->setAreaUnits ( QgsProject::instance ()->areaUnits () );
520+
521+ exp->prepare ( mExpressionContext .data () );
522+ mExpressionFieldInfo .insert ( fieldIdx, exp );
523+
524+ Q_FOREACH ( const QString& col, exp->referencedColumns () )
532525 {
533- if ( mSource ->mFields .fieldOrigin ( i ) == QgsFields::OriginExpression )
526+ int dependantFieldIdx = mSource ->mFields .fieldNameIndex ( col );
527+ if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
534528 {
535- // Only prepare if there is no subset defined or the subset contains this field
536- if ( !( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
537- || mRequest .subsetOfAttributes ().contains ( i ) )
538- {
539- virtualFieldsToFetch << i;
540- }
529+ mRequest .setSubsetOfAttributes ( mRequest .subsetOfAttributes () << dependantFieldIdx );
541530 }
531+ // also need to fetch this dependant field
532+ if ( !mPreparedFields .contains ( dependantFieldIdx ) && !mFieldsToPrepare .contains ( dependantFieldIdx ) )
533+ mFieldsToPrepare << dependantFieldIdx;
542534 }
543535
544- QList< int > virtualFieldsProcessed;
545- while ( !virtualFieldsToFetch.isEmpty () )
536+ if ( exp->needsGeometry () )
546537 {
547- int fieldIdx = virtualFieldsToFetch. takeFirst ( );
548- if ( virtualFieldsProcessed. contains ( fieldIdx ) )
549- continue ;
538+ mRequest . setFlags ( mRequest . flags () & ~QgsFeatureRequest::NoGeometry );
539+ }
540+ }
550541
551- virtualFieldsProcessed << fieldIdx;
542+ void QgsVectorLayerFeatureIterator::prepareFields ()
543+ {
544+ mPreparedFields .clear ();
545+ mFieldsToPrepare .clear ();
546+ mFetchJoinInfo .clear ();
552547
548+ mExpressionContext .reset ( new QgsExpressionContext () );
549+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::globalScope () );
550+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::projectScope () );
551+ mExpressionContext ->setFields ( mSource ->mFields );
553552
554- int oi = mSource ->mFields .fieldOriginIndex ( fieldIdx );
555- QgsExpression* exp = new QgsExpression ( exps[oi].cachedExpression );
553+ mFieldsToPrepare = ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest .subsetOfAttributes () : mSource ->mFields .allAttributesList ();
556554
557- QgsDistanceArea da;
558- da.setSourceCrs ( mSource ->mCrsId );
559- da.setEllipsoidalMode ( true );
560- da.setEllipsoid ( QgsProject::instance ()->readEntry ( " Measure" , " /Ellipsoid" , GEO_NONE ) );
561- exp->setGeomCalculator ( da );
562- exp->setDistanceUnits ( QgsProject::instance ()->distanceUnits () );
563- exp->setAreaUnits ( QgsProject::instance ()->areaUnits () );
555+ while ( !mFieldsToPrepare .isEmpty () )
556+ {
557+ int fieldIdx = mFieldsToPrepare .takeFirst ();
558+ if ( mPreparedFields .contains ( fieldIdx ) )
559+ continue ;
564560
565- exp->prepare ( mExpressionContext .data () );
566- mExpressionFieldInfo .insert ( fieldIdx, exp );
561+ mPreparedFields << fieldIdx;
562+ prepareField ( fieldIdx );
563+ }
564+ }
567565
568- Q_FOREACH ( const QString& col, exp->referencedColumns () )
569- {
570- int dependantFieldIdx = mSource ->mFields .fieldNameIndex ( col );
571- if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
566+ void QgsVectorLayerFeatureIterator::prepareField ( int fieldIdx )
567+ {
568+ switch ( mSource ->mFields .fieldOrigin ( fieldIdx ) )
569+ {
570+ case QgsFields::OriginExpression:
571+ prepareExpression ( fieldIdx );
572+ break ;
573+
574+ case QgsFields::OriginJoin:
575+ if ( mSource ->mJoinBuffer ->containsJoins () )
572576 {
573- mRequest . setSubsetOfAttributes ( mRequest . subsetOfAttributes () << dependantFieldIdx );
577+ prepareJoin ( fieldIdx );
574578 }
575- // also need to fetch this dependant field
576- if ( mSource ->mFields .fieldOrigin ( dependantFieldIdx ) == QgsFields::OriginExpression )
577- virtualFieldsToFetch << dependantFieldIdx;
578- }
579+ break ;
579580
580- if ( exp-> needsGeometry () )
581- {
582- mRequest . setFlags ( mRequest . flags () & ~QgsFeatureRequest::NoGeometry );
583- }
581+ case QgsFields::OriginUnknown:
582+ case QgsFields::OriginProvider:
583+ case QgsFields::OriginEdit:
584+ break ;
584585 }
585-
586586}
587587
588588void QgsVectorLayerFeatureIterator::addJoinedAttributes ( QgsFeature &f )
@@ -612,24 +612,48 @@ void QgsVectorLayerFeatureIterator::addVirtualAttributes( QgsFeature& f )
612612 attr.resize ( mSource ->mFields .count () ); // Provider attrs count + joined attrs count + expression attrs count
613613 f.setAttributes ( attr );
614614
615+ // possible TODO - handle combinations of expression -> join -> expression -> join?
616+ // but for now, write that off as too complex and an unlikely rare, unsupported use case
617+
618+ QList< int > fetchedVirtualAttributes;
619+ // first, check through joins for any virtual fields we need
620+ QMap<const QgsVectorJoinInfo*, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo .constBegin ();
621+ for ( ; joinIt != mFetchJoinInfo .constEnd (); ++joinIt )
622+ {
623+ if ( mExpressionFieldInfo .contains ( joinIt->targetField ) )
624+ {
625+ // have to calculate expression field before we can handle this join
626+ addExpressionAttribute ( f, joinIt->targetField );
627+ fetchedVirtualAttributes << joinIt->targetField ;
628+ }
629+ }
630+
615631 if ( !mFetchJoinInfo .isEmpty () )
616632 addJoinedAttributes ( f );
617633
634+ // add remaining expression fields
618635 if ( !mExpressionFieldInfo .isEmpty () )
619636 {
620637 QMap<int , QgsExpression*>::ConstIterator it = mExpressionFieldInfo .constBegin ();
621-
622638 for ( ; it != mExpressionFieldInfo .constEnd (); ++it )
623639 {
624- QgsExpression* exp = it.value ();
625- mExpressionContext ->setFeature ( f );
626- QVariant val = exp->evaluate ( mExpressionContext .data () );
627- mSource ->mFields .at ( it.key () ).convertCompatible ( val );
628- f.setAttribute ( it.key (), val );
640+ if ( fetchedVirtualAttributes.contains ( it.key () ) )
641+ continue ;
642+
643+ addExpressionAttribute ( f, it.key () );
629644 }
630645 }
631646}
632647
648+ void QgsVectorLayerFeatureIterator::addExpressionAttribute ( QgsFeature& f, int attrIndex )
649+ {
650+ QgsExpression* exp = mExpressionFieldInfo .value ( attrIndex );
651+ mExpressionContext ->setFeature ( f );
652+ QVariant val = exp->evaluate ( mExpressionContext .data () );
653+ mSource ->mFields .at ( attrIndex ).convertCompatible ( val );
654+ f.setAttribute ( attrIndex, val );
655+ }
656+
633657bool QgsVectorLayerFeatureIterator::prepareSimplification ( const QgsSimplifyMethod& simplifyMethod )
634658{
635659 Q_UNUSED ( simplifyMethod );
0 commit comments