@@ -93,11 +93,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
93
93
, mFetchedFid( false )
94
94
, mInterruptionChecker( nullptr )
95
95
{
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 ();
101
97
102
98
mHasVirtualAttributes = !mFetchJoinInfo .isEmpty () || !mExpressionFieldInfo .isEmpty ();
103
99
@@ -461,128 +457,132 @@ void QgsVectorLayerFeatureIterator::rewindEditBuffer()
461
457
mFetchChangedGeomIt = mSource ->mChangedGeometries .constBegin ();
462
458
}
463
459
460
+ void QgsVectorLayerFeatureIterator::prepareJoin ( int fieldIdx )
461
+ {
462
+ if ( !mSource ->mFields .exists ( fieldIdx ) )
463
+ return ;
464
464
465
+ if ( mSource ->mFields .fieldOrigin ( fieldIdx ) != QgsFields::OriginJoin )
466
+ return ;
465
467
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 );
470
471
471
- mFetchJoinInfo .clear ();
472
+ QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance ()->mapLayer ( joinInfo->joinLayerId ) );
473
+ Q_ASSERT ( joinLayer );
472
474
473
- for ( QgsAttributeList::const_iterator attIt = fetchAttributes. constBegin (); attIt != fetchAttributes. constEnd (); ++attIt )
475
+ if ( ! mFetchJoinInfo . contains ( joinInfo ) )
474
476
{
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 );
477
481
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 );
480
486
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 );
484
491
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 ;
487
495
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 );
511
498
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 );
514
500
}
515
501
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 );
519
504
}
520
505
521
- void QgsVectorLayerFeatureIterator::prepareExpressions ( )
506
+ void QgsVectorLayerFeatureIterator::prepareExpression ( int fieldIdx )
522
507
{
523
- const QList<QgsExpressionFieldBuffer::ExpressionField> exps = mSource ->mExpressionFieldBuffer ->expressions ();
508
+ const QList<QgsExpressionFieldBuffer::ExpressionField>& exps = mSource ->mExpressionFieldBuffer ->expressions ();
524
509
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 );
529
512
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 () )
532
525
{
533
- if ( mSource ->mFields .fieldOrigin ( i ) == QgsFields::OriginExpression )
526
+ int dependantFieldIdx = mSource ->mFields .fieldNameIndex ( col );
527
+ if ( mRequest .flags () & QgsFeatureRequest::SubsetOfAttributes )
534
528
{
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 );
541
530
}
531
+ // also need to fetch this dependant field
532
+ if ( !mPreparedFields .contains ( dependantFieldIdx ) && !mFieldsToPrepare .contains ( dependantFieldIdx ) )
533
+ mFieldsToPrepare << dependantFieldIdx;
542
534
}
543
535
544
- QList< int > virtualFieldsProcessed;
545
- while ( !virtualFieldsToFetch.isEmpty () )
536
+ if ( exp->needsGeometry () )
546
537
{
547
- int fieldIdx = virtualFieldsToFetch. takeFirst ( );
548
- if ( virtualFieldsProcessed. contains ( fieldIdx ) )
549
- continue ;
538
+ mRequest . setFlags ( mRequest . flags () & ~QgsFeatureRequest::NoGeometry );
539
+ }
540
+ }
550
541
551
- virtualFieldsProcessed << fieldIdx;
542
+ void QgsVectorLayerFeatureIterator::prepareFields ()
543
+ {
544
+ mPreparedFields .clear ();
545
+ mFieldsToPrepare .clear ();
546
+ mFetchJoinInfo .clear ();
552
547
548
+ mExpressionContext .reset ( new QgsExpressionContext () );
549
+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::globalScope () );
550
+ mExpressionContext ->appendScope ( QgsExpressionContextUtils::projectScope () );
551
+ mExpressionContext ->setFields ( mSource ->mFields );
553
552
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 ();
556
554
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 ;
564
560
565
- exp->prepare ( mExpressionContext .data () );
566
- mExpressionFieldInfo .insert ( fieldIdx, exp );
561
+ mPreparedFields << fieldIdx;
562
+ prepareField ( fieldIdx );
563
+ }
564
+ }
567
565
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 () )
572
576
{
573
- mRequest . setSubsetOfAttributes ( mRequest . subsetOfAttributes () << dependantFieldIdx );
577
+ prepareJoin ( fieldIdx );
574
578
}
575
- // also need to fetch this dependant field
576
- if ( mSource ->mFields .fieldOrigin ( dependantFieldIdx ) == QgsFields::OriginExpression )
577
- virtualFieldsToFetch << dependantFieldIdx;
578
- }
579
+ break ;
579
580
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 ;
584
585
}
585
-
586
586
}
587
587
588
588
void QgsVectorLayerFeatureIterator::addJoinedAttributes ( QgsFeature &f )
@@ -612,24 +612,48 @@ void QgsVectorLayerFeatureIterator::addVirtualAttributes( QgsFeature& f )
612
612
attr.resize ( mSource ->mFields .count () ); // Provider attrs count + joined attrs count + expression attrs count
613
613
f.setAttributes ( attr );
614
614
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
+
615
631
if ( !mFetchJoinInfo .isEmpty () )
616
632
addJoinedAttributes ( f );
617
633
634
+ // add remaining expression fields
618
635
if ( !mExpressionFieldInfo .isEmpty () )
619
636
{
620
637
QMap<int , QgsExpression*>::ConstIterator it = mExpressionFieldInfo .constBegin ();
621
-
622
638
for ( ; it != mExpressionFieldInfo .constEnd (); ++it )
623
639
{
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 () );
629
644
}
630
645
}
631
646
}
632
647
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
+
633
657
bool QgsVectorLayerFeatureIterator::prepareSimplification ( const QgsSimplifyMethod& simplifyMethod )
634
658
{
635
659
Q_UNUSED ( simplifyMethod );
0 commit comments