LCOV - code coverage report
Current view: top level - core/vector - qgsvectorlayerfeatureiterator.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 213 692 30.8 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsvectorlayerfeatureiterator.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : Dezember 2012
       5                 :            :     copyright            : (C) 2012 by Martin Dobias
       6                 :            :     email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : #include "qgsvectorlayerfeatureiterator.h"
      16                 :            : 
      17                 :            : #include "qgsexpressionfieldbuffer.h"
      18                 :            : #include "qgsgeometrysimplifier.h"
      19                 :            : #include "qgssimplifymethod.h"
      20                 :            : #include "qgsvectordataprovider.h"
      21                 :            : #include "qgsvectorlayereditbuffer.h"
      22                 :            : #include "qgsvectorlayer.h"
      23                 :            : #include "qgsvectorlayerjoinbuffer.h"
      24                 :            : #include "qgsexpressioncontext.h"
      25                 :            : #include "qgsdistancearea.h"
      26                 :            : #include "qgsproject.h"
      27                 :            : #include "qgsmessagelog.h"
      28                 :            : #include "qgsexception.h"
      29                 :            : #include "qgsexpressioncontextutils.h"
      30                 :            : 
      31                 :            : #include <QThreadStorage>
      32                 :            : #include <QStack>
      33                 :            : 
      34                 :        199 : QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *layer )
      35                 :        398 : {
      36                 :        199 :   QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
      37                 :        199 :   mProviderFeatureSource.reset( layer->dataProvider()->featureSource() );
      38                 :        199 :   mFields = layer->fields();
      39                 :        199 :   mId = layer->id();
      40                 :            : 
      41                 :            :   // update layer's join caches if necessary
      42                 :        199 :   if ( layer->mJoinBuffer->containsJoins() )
      43                 :          0 :     layer->mJoinBuffer->createJoinCaches();
      44                 :            : 
      45                 :        199 :   mJoinBuffer.reset( layer->mJoinBuffer->clone() );
      46                 :            : 
      47                 :        199 :   mExpressionFieldBuffer.reset( new QgsExpressionFieldBuffer( *layer->mExpressionFieldBuffer ) );
      48                 :        199 :   mCrs = layer->crs();
      49                 :            : 
      50                 :        199 :   mHasEditBuffer = layer->editBuffer();
      51                 :        199 :   if ( mHasEditBuffer )
      52                 :            :   {
      53                 :            : #if 0
      54                 :            :     // TODO[MD]: after merge
      55                 :            :     if ( request.filterType() == QgsFeatureRequest::FilterFid )
      56                 :            :     {
      57                 :            : 
      58                 :            :       // only copy relevant parts
      59                 :            :       if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
      60                 :            :         mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
      61                 :            : 
      62                 :            :       if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
      63                 :            :         mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
      64                 :            : 
      65                 :            :       if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
      66                 :            :         mDeletedFeatureIds.insert( request.filterFid() );
      67                 :            : 
      68                 :            :       if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
      69                 :            :         mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
      70                 :            : 
      71                 :            :       if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
      72                 :            :         mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
      73                 :            :     }
      74                 :            :     else
      75                 :            :     {
      76                 :            : #endif
      77                 :            :       // If we are inside a transaction the iterator "sees" the current status
      78                 :          4 :       if ( layer->dataProvider() && ! layer->dataProvider()->transaction() )
      79                 :            :       {
      80                 :          4 :         mAddedFeatures = QgsFeatureMap( layer->editBuffer()->addedFeatures() );
      81                 :          4 :         mChangedGeometries = QgsGeometryMap( layer->editBuffer()->changedGeometries() );
      82                 :          4 :         mDeletedFeatureIds = QgsFeatureIds( layer->editBuffer()->deletedFeatureIds() );
      83                 :          4 :         mChangedAttributeValues = QgsChangedAttributesMap( layer->editBuffer()->changedAttributeValues() );
      84                 :          4 :         mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
      85                 :          4 :         mDeletedAttributeIds = QgsAttributeList( layer->editBuffer()->deletedAttributeIds() );
      86                 :          4 :       }
      87                 :            : #if 0
      88                 :            :     }
      89                 :            : #endif
      90                 :          4 :   }
      91                 :            : 
      92                 :        199 :   std::unique_ptr< QgsExpressionContextScope > layerScope( QgsExpressionContextUtils::layerScope( layer ) );
      93                 :        199 :   mLayerScope = *layerScope;
      94                 :        199 : }
      95                 :            : 
      96                 :        392 : QgsVectorLayerFeatureSource::~QgsVectorLayerFeatureSource() = default;
      97                 :            : 
      98                 :         26 : QgsFeatureIterator QgsVectorLayerFeatureSource::getFeatures( const QgsFeatureRequest &request )
      99                 :            : {
     100                 :            :   // return feature iterator that does not own this source
     101                 :         26 :   return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
     102                 :          0 : }
     103                 :            : 
     104                 :          0 : QgsFields QgsVectorLayerFeatureSource::fields() const
     105                 :            : {
     106                 :          0 :   return mFields;
     107                 :            : }
     108                 :            : 
     109                 :       1354 : QgsCoordinateReferenceSystem QgsVectorLayerFeatureSource::crs() const
     110                 :            : {
     111                 :       1354 :   return mCrs;
     112                 :            : }
     113                 :            : 
     114                 :       2004 : QString QgsVectorLayerFeatureSource::id() const
     115                 :            : {
     116                 :       2004 :   return mId;
     117                 :            : }
     118                 :            : 
     119                 :            : 
     120                 :        158 : QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
     121                 :        158 :   : QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request )
     122                 :        158 :   , mFetchedFid( false )
     123                 :            : 
     124                 :        316 : {
     125                 :        158 :   if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs )
     126                 :            :   {
     127                 :          0 :     mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() );
     128                 :          0 :   }
     129                 :            :   try
     130                 :            :   {
     131                 :        158 :     mFilterRect = filterRectToSourceCrs( mTransform );
     132                 :        158 :   }
     133                 :            :   catch ( QgsCsException & )
     134                 :            :   {
     135                 :            :     // can't reproject mFilterRect
     136                 :          0 :     close();
     137                 :            :     return;
     138                 :          0 :   }
     139                 :        158 :   if ( !mFilterRect.isNull() )
     140                 :            :   {
     141                 :            :     // update request to be the unprojected filter rect
     142                 :          0 :     mRequest.setFilterRect( mFilterRect );
     143                 :          0 :   }
     144                 :            : 
     145                 :            :   // check whether the order by clause(s) can be delegated to the provider
     146                 :        158 :   mDelegatedOrderByToProvider = !mSource->mHasEditBuffer;
     147                 :        158 :   if ( !mRequest.orderBy().isEmpty() )
     148                 :            :   {
     149                 :          0 :     QSet<int> attributeIndexes;
     150                 :          0 :     const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields );
     151                 :          0 :     for ( int attrIndex : usedAttributeIndices )
     152                 :            :     {
     153                 :          0 :       if ( mSource->mFields.fieldOrigin( attrIndex ) != QgsFields::OriginProvider )
     154                 :          0 :         mDelegatedOrderByToProvider = false;
     155                 :            : 
     156                 :          0 :       attributeIndexes << attrIndex;
     157                 :            :     }
     158                 :            : 
     159                 :          0 :     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mDelegatedOrderByToProvider )
     160                 :            :     {
     161                 :          0 :       attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
     162                 :          0 :       mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
     163                 :          0 :     }
     164                 :          0 :   }
     165                 :            : 
     166                 :        158 :   if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
     167                 :            :   {
     168                 :          0 :     mRequest.expressionContext()->setFields( mSource->mFields );
     169                 :          0 :     mRequest.filterExpression()->prepare( mRequest.expressionContext() );
     170                 :            : 
     171                 :          0 :     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     172                 :            :     {
     173                 :            :       // ensure that all fields required for filter expressions are prepared
     174                 :          0 :       QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields );
     175                 :          0 :       attributeIndexes += qgis::listToSet( mRequest.subsetOfAttributes() );
     176                 :          0 :       mRequest.setSubsetOfAttributes( qgis::setToList( attributeIndexes ) );
     177                 :          0 :     }
     178                 :          0 :   }
     179                 :            : 
     180                 :        158 :   prepareFields();
     181                 :            : 
     182                 :        158 :   mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty();
     183                 :            : 
     184                 :            :   // by default provider's request is the same
     185                 :        158 :   mProviderRequest = mRequest;
     186                 :            :   // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator,
     187                 :            :   // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect
     188                 :            :   // values
     189                 :        158 :   if ( mRequest.destinationCrs().isValid() )
     190                 :            :   {
     191                 :          0 :     mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() );
     192                 :          0 :   }
     193                 :            : 
     194                 :        158 :   if ( !mDelegatedOrderByToProvider )
     195                 :            :   {
     196                 :          4 :     mProviderRequest.setOrderBy( QgsFeatureRequest::OrderBy() );
     197                 :          4 :   }
     198                 :            : 
     199                 :        158 :   if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     200                 :            :   {
     201                 :            :     // prepare list of attributes to match provider fields
     202                 :         69 :     QSet<int> providerSubset;
     203                 :         69 :     const QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
     204                 :         69 :     int nPendingFields = mSource->mFields.count();
     205                 :         69 :     for ( int attrIndex : subset )
     206                 :            :     {
     207                 :          0 :       if ( attrIndex < 0 || attrIndex >= nPendingFields )
     208                 :          0 :         continue;
     209                 :          0 :       if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
     210                 :          0 :         providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
     211                 :            :     }
     212                 :            : 
     213                 :            :     // This is done in order to be prepared to do fallback order bys
     214                 :            :     // and be sure we have the required columns.
     215                 :            :     // TODO:
     216                 :            :     // It would be nicer to first check if we can compile the order by
     217                 :            :     // and only modify the subset if we cannot.
     218                 :         69 :     if ( !mProviderRequest.orderBy().isEmpty() )
     219                 :            :     {
     220                 :          0 :       const auto usedAttributeIndices = mProviderRequest.orderBy().usedAttributeIndices( mSource->mFields );
     221                 :          0 :       for ( int attrIndex : usedAttributeIndices )
     222                 :            :       {
     223                 :          0 :         providerSubset << attrIndex;
     224                 :            :       }
     225                 :          0 :     }
     226                 :            : 
     227                 :         69 :     mProviderRequest.setSubsetOfAttributes( qgis::setToList( providerSubset ) );
     228                 :         69 :   }
     229                 :            : 
     230                 :        158 :   if ( mProviderRequest.filterType() == QgsFeatureRequest::FilterExpression )
     231                 :            :   {
     232                 :          0 :     const bool needsGeom = mProviderRequest.filterExpression()->needsGeometry();
     233                 :          0 :     const auto constReferencedColumns = mProviderRequest.filterExpression()->referencedColumns();
     234                 :          0 :     for ( const QString &field : constReferencedColumns )
     235                 :            :     {
     236                 :          0 :       int idx = source->mFields.lookupField( field );
     237                 :            : 
     238                 :            :       // If there are fields in the expression which are not of origin provider, the provider will not be able to filter based on them.
     239                 :            :       // In this case we disable the expression filter.
     240                 :          0 :       if ( source->mFields.fieldOrigin( idx ) != QgsFields::OriginProvider )
     241                 :            :       {
     242                 :          0 :         mProviderRequest.disableFilter();
     243                 :            :         // can't limit at provider side
     244                 :          0 :         mProviderRequest.setLimit( -1 );
     245                 :          0 :         if ( needsGeom )
     246                 :            :         {
     247                 :            :           // have to get geometry from provider in order to evaluate expression on client
     248                 :          0 :           mProviderRequest.setFlags( mProviderRequest.flags() & ~QgsFeatureRequest::NoGeometry );
     249                 :          0 :         }
     250                 :          0 :         break;
     251                 :            :       }
     252                 :            :     }
     253                 :          0 :   }
     254                 :            : 
     255                 :        158 :   if ( mSource->mHasEditBuffer )
     256                 :            :   {
     257                 :          4 :     mChangedFeaturesRequest = mProviderRequest;
     258                 :          4 :     QgsFeatureIds changedIds;
     259                 :          4 :     QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
     260                 :          4 :     for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
     261                 :            :     {
     262                 :          0 :       changedIds << attIt.key();
     263                 :          0 :     }
     264                 :          4 :     mChangedFeaturesRequest.setFilterFids( changedIds );
     265                 :            : 
     266                 :          4 :     if ( mChangedFeaturesRequest.limit() > 0 )
     267                 :            :     {
     268                 :          0 :       int providerLimit = mProviderRequest.limit();
     269                 :            : 
     270                 :            :       // features may be deleted in buffer, so increase limit sent to provider
     271                 :          0 :       providerLimit += mSource->mDeletedFeatureIds.size();
     272                 :            : 
     273                 :        158 :       if ( mProviderRequest.filterType() == QgsFeatureRequest::FilterExpression )
     274                 :            :       {
     275                 :            :         // attribute changes may mean some features no longer match expression, so increase limit sent to provider
     276                 :          0 :         providerLimit += mSource->mChangedAttributeValues.size();
     277                 :          0 :       }
     278                 :            : 
     279                 :          0 :       if ( mProviderRequest.filterType() == QgsFeatureRequest::FilterExpression || !mProviderRequest.filterRect().isNull() )
     280                 :            :       {
     281                 :            :         // geometry changes may mean some features no longer match expression or rect, so increase limit sent to provider
     282                 :          0 :         providerLimit += mSource->mChangedGeometries.size();
     283                 :          0 :       }
     284                 :            : 
     285                 :          0 :       mProviderRequest.setLimit( providerLimit );
     286                 :          0 :       mChangedFeaturesRequest.setLimit( providerLimit );
     287                 :          0 :     }
     288                 :          4 :   }
     289                 :            : 
     290                 :        158 :   if ( request.filterType() == QgsFeatureRequest::FilterFid )
     291                 :            :   {
     292                 :         24 :     mFetchedFid = false;
     293                 :         24 :   }
     294                 :            :   else // no filter or filter by rect
     295                 :            :   {
     296                 :        134 :     if ( mSource->mHasEditBuffer )
     297                 :            :     {
     298                 :          3 :       mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
     299                 :          3 :     }
     300                 :            :     else
     301                 :            :     {
     302                 :        289 :       mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
     303                 :            :     }
     304                 :            : 
     305                 :        134 :     rewindEditBuffer();
     306                 :            :   }
     307                 :        158 : }
     308                 :            : 
     309                 :            : 
     310                 :        316 : QgsVectorLayerFeatureIterator::~QgsVectorLayerFeatureIterator()
     311                 :        316 : {
     312                 :        158 :   qDeleteAll( mExpressionFieldInfo );
     313                 :            : 
     314                 :        158 :   close();
     315                 :        316 : }
     316                 :            : 
     317                 :            : /// @cond private
     318                 :            : 
     319                 :            : /**
     320                 :            :  * This class guards against infinite recursion.
     321                 :            :  * The counter will be created per thread and hasStackOverflow will return
     322                 :            :  * TRUE if more than maxDepth instances are created in parallel in a thread.
     323                 :            :  */
     324                 :            : class QgsThreadStackOverflowGuard
     325                 :            : {
     326                 :            :   public:
     327                 :            : 
     328                 :       1186 :     QgsThreadStackOverflowGuard( QThreadStorage<QStack<QString>> &storage, const QString &stackFrameInformation, int maxDepth )
     329                 :       1186 :       : mStorage( storage )
     330                 :       1186 :       , mMaxDepth( maxDepth )
     331                 :            :     {
     332                 :       1186 :       if ( !storage.hasLocalData() )
     333                 :            :       {
     334                 :          2 :         storage.setLocalData( QStack<QString>() );
     335                 :          2 :       }
     336                 :            : 
     337                 :       1186 :       storage.localData().push( stackFrameInformation );
     338                 :       1186 :     }
     339                 :            : 
     340                 :       1186 :     ~QgsThreadStackOverflowGuard()
     341                 :            :     {
     342                 :       1186 :       mStorage.localData().pop();
     343                 :       1186 :     }
     344                 :            : 
     345                 :       1186 :     bool hasStackOverflow() const
     346                 :            :     {
     347                 :       1186 :       if ( mStorage.localData().size() > mMaxDepth )
     348                 :          0 :         return true;
     349                 :            :       else
     350                 :       1186 :         return false;
     351                 :       1186 :     }
     352                 :            : 
     353                 :          0 :     QString topFrames() const
     354                 :            :     {
     355                 :          0 :       QStringList dumpStack;
     356                 :          0 :       const QStack<QString> &stack = mStorage.localData();
     357                 :            : 
     358                 :          0 :       int dumpSize = std::min( stack.size(), 10 );
     359                 :          0 :       for ( int i = 0; i < dumpSize; ++i )
     360                 :            :       {
     361                 :          0 :         dumpStack += stack.at( i );
     362                 :          0 :       }
     363                 :            : 
     364                 :          0 :       return dumpStack.join( '\n' );
     365                 :          0 :     }
     366                 :            : 
     367                 :            :     int depth() const
     368                 :            :     {
     369                 :            :       return mStorage.localData().size();
     370                 :            :     }
     371                 :            : 
     372                 :            :   private:
     373                 :            :     QThreadStorage<QStack<QString>> &mStorage;
     374                 :            :     int mMaxDepth;
     375                 :            : };
     376                 :            : 
     377                 :            : /// @endcond private
     378                 :            : 
     379                 :       1186 : bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature &f )
     380                 :            : {
     381                 :       1186 :   f.setValid( false );
     382                 :            : 
     383                 :       1186 :   if ( mClosed )
     384                 :          0 :     return false;
     385                 :            : 
     386                 :       1186 :   static QThreadStorage<QStack<QString>> sStack;
     387                 :            : 
     388                 :       1186 :   QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
     389                 :            : 
     390                 :       1186 :   if ( guard.hasStackOverflow() )
     391                 :            :   {
     392                 :          0 :     QgsMessageLog::logMessage( QObject::tr( "Stack overflow, too many nested feature iterators.\nIterated layers:\n%3\n..." ).arg( mSource->id(), guard.topFrames() ), QObject::tr( "General" ), Qgis::Critical );
     393                 :          0 :     return false;
     394                 :            :   }
     395                 :            : 
     396                 :       1186 :   if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
     397                 :            :   {
     398                 :         24 :     if ( mFetchedFid )
     399                 :          0 :       return false;
     400                 :         24 :     bool res = nextFeatureFid( f );
     401                 :         24 :     if ( res && postProcessFeature( f ) )
     402                 :            :     {
     403                 :         16 :       mFetchedFid = true;
     404                 :         16 :       return res;
     405                 :            :     }
     406                 :            :     else
     407                 :            :     {
     408                 :          8 :       return false;
     409                 :            :     }
     410                 :            :   }
     411                 :            : 
     412                 :       1162 :   if ( !mFilterRect.isNull() )
     413                 :            :   {
     414                 :          0 :     if ( fetchNextChangedGeomFeature( f ) )
     415                 :          0 :       return true;
     416                 :            : 
     417                 :            :     // no more changed geometries
     418                 :          0 :   }
     419                 :            : 
     420                 :       1162 :   if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
     421                 :            :   {
     422                 :          0 :     if ( fetchNextChangedAttributeFeature( f ) )
     423                 :          0 :       return true;
     424                 :            : 
     425                 :          0 :     if ( fetchNextChangedGeomFeature( f ) )
     426                 :          0 :       return true;
     427                 :            : 
     428                 :            :     // no more changed features
     429                 :          0 :   }
     430                 :            : 
     431                 :       1162 :   while ( fetchNextAddedFeature( f ) )
     432                 :            :   {
     433                 :          3 :     return true;
     434                 :            :   }
     435                 :            :   // no more added features
     436                 :            : 
     437                 :       1159 :   if ( mProviderIterator.isClosed() )
     438                 :            :   {
     439                 :          2 :     mChangedFeaturesIterator.close();
     440                 :          2 :     mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
     441                 :          2 :     mProviderIterator.setInterruptionChecker( mInterruptionChecker );
     442                 :          2 :   }
     443                 :            : 
     444                 :       1159 :   while ( mProviderIterator.nextFeature( f ) )
     445                 :            :   {
     446                 :       1028 :     if ( mFetchConsidered.contains( f.id() ) )
     447                 :          0 :       continue;
     448                 :            : 
     449                 :            :     // TODO[MD]: just one resize of attributes
     450                 :       1028 :     f.setFields( mSource->mFields );
     451                 :            : 
     452                 :            :     // update attributes
     453                 :       1028 :     if ( mSource->mHasEditBuffer )
     454                 :          0 :       updateChangedAttributes( f );
     455                 :            : 
     456                 :       1028 :     if ( mHasVirtualAttributes )
     457                 :          0 :       addVirtualAttributes( f );
     458                 :            : 
     459                 :       1028 :     if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression && mProviderRequest.filterType() != QgsFeatureRequest::FilterExpression )
     460                 :            :     {
     461                 :            :       //filtering by expression, and couldn't do it on the provider side
     462                 :          0 :       mRequest.expressionContext()->setFeature( f );
     463                 :          0 :       if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
     464                 :            :       {
     465                 :            :         //feature did not match filter
     466                 :          0 :         continue;
     467                 :            :       }
     468                 :          0 :     }
     469                 :            : 
     470                 :            :     // update geometry
     471                 :            :     // TODO[MK]: FilterRect check after updating the geometry
     472                 :       1028 :     if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
     473                 :       1028 :       updateFeatureGeometry( f );
     474                 :            : 
     475                 :       1028 :     if ( !postProcessFeature( f ) )
     476                 :          0 :       continue;
     477                 :            : 
     478                 :       1028 :     return true;
     479                 :            :   }
     480                 :            :   // no more provider features
     481                 :            : 
     482                 :        131 :   close();
     483                 :        131 :   return false;
     484                 :       1186 : }
     485                 :            : 
     486                 :            : 
     487                 :            : 
     488                 :          0 : bool QgsVectorLayerFeatureIterator::rewind()
     489                 :            : {
     490                 :          0 :   if ( mClosed )
     491                 :          0 :     return false;
     492                 :            : 
     493                 :          0 :   if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
     494                 :            :   {
     495                 :          0 :     mFetchedFid = false;
     496                 :          0 :   }
     497                 :            :   else
     498                 :            :   {
     499                 :          0 :     mProviderIterator.rewind();
     500                 :          0 :     rewindEditBuffer();
     501                 :            :   }
     502                 :            : 
     503                 :          0 :   return true;
     504                 :          0 : }
     505                 :            : 
     506                 :        289 : bool QgsVectorLayerFeatureIterator::close()
     507                 :            : {
     508                 :        289 :   if ( mClosed )
     509                 :        131 :     return false;
     510                 :            : 
     511                 :        158 :   mProviderIterator.close();
     512                 :            : 
     513                 :        158 :   iteratorClosed();
     514                 :            : 
     515                 :        158 :   mClosed = true;
     516                 :        158 :   return true;
     517                 :        289 : }
     518                 :            : 
     519                 :          0 : void QgsVectorLayerFeatureIterator::setInterruptionChecker( QgsFeedback *interruptionChecker )
     520                 :            : {
     521                 :          0 :   mProviderIterator.setInterruptionChecker( interruptionChecker );
     522                 :          0 :   mInterruptionChecker = interruptionChecker;
     523                 :          0 : }
     524                 :            : 
     525                 :          0 : bool QgsVectorLayerFeatureIterator::isValid() const
     526                 :            : {
     527                 :          0 :   return mProviderIterator.isValid();
     528                 :            : }
     529                 :            : 
     530                 :       1162 : bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature &f )
     531                 :            : {
     532                 :       1162 :   while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )
     533                 :            :   {
     534                 :          3 :     QgsFeatureId fid = mFetchAddedFeaturesIt->id();
     535                 :            : 
     536                 :          3 :     if ( mFetchConsidered.contains( fid ) )
     537                 :            :       // must have changed geometry outside rectangle
     538                 :          0 :       continue;
     539                 :            : 
     540                 :          3 :     useAddedFeature( *mFetchAddedFeaturesIt, f );
     541                 :            : 
     542                 :            :     // can't test for feature acceptance until after calling useAddedFeature
     543                 :            :     // since acceptFeature may rely on virtual fields
     544                 :          3 :     if ( !mRequest.acceptFeature( f ) )
     545                 :            :       // skip features which are not accepted by the filter
     546                 :          0 :       continue;
     547                 :            : 
     548                 :          3 :     if ( !postProcessFeature( f ) )
     549                 :          0 :       continue;
     550                 :            : 
     551                 :          3 :     return true;
     552                 :            :   }
     553                 :            : 
     554                 :       1159 :   mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
     555                 :       1159 :   return false; // no more added features
     556                 :       1162 : }
     557                 :            : 
     558                 :            : 
     559                 :          4 : void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature &src, QgsFeature &f )
     560                 :            : {
     561                 :            :   // since QgsFeature is implicitly shared, it's more efficient to just copy the
     562                 :            :   // whole feature, even if flags like NoGeometry or a subset of attributes is set at the request.
     563                 :            :   // This helps potentially avoid an unnecessary detach of the feature
     564                 :          4 :   f = src;
     565                 :          4 :   f.setValid( true );
     566                 :          4 :   f.setFields( mSource->mFields );
     567                 :            : 
     568                 :          4 :   if ( mHasVirtualAttributes )
     569                 :          0 :     addVirtualAttributes( f );
     570                 :          4 : }
     571                 :            : 
     572                 :            : 
     573                 :            : 
     574                 :          0 : bool QgsVectorLayerFeatureIterator::fetchNextChangedGeomFeature( QgsFeature &f )
     575                 :            : {
     576                 :            :   // check if changed geometries are in rectangle
     577                 :          0 :   for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
     578                 :            :   {
     579                 :          0 :     QgsFeatureId fid = mFetchChangedGeomIt.key();
     580                 :            : 
     581                 :          0 :     if ( mFetchConsidered.contains( fid ) )
     582                 :            :       // skip deleted features
     583                 :          0 :       continue;
     584                 :            : 
     585                 :          0 :     mFetchConsidered << fid;
     586                 :            : 
     587                 :          0 :     if ( !mFilterRect.isNull() && !mFetchChangedGeomIt->intersects( mFilterRect ) )
     588                 :            :       // skip changed geometries not in rectangle and don't check again
     589                 :          0 :       continue;
     590                 :            : 
     591                 :          0 :     useChangedAttributeFeature( fid, *mFetchChangedGeomIt, f );
     592                 :            : 
     593                 :          0 :     if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
     594                 :            :     {
     595                 :          0 :       mRequest.expressionContext()->setFeature( f );
     596                 :          0 :       if ( !mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
     597                 :            :       {
     598                 :          0 :         continue;
     599                 :            :       }
     600                 :          0 :     }
     601                 :            : 
     602                 :          0 :     if ( postProcessFeature( f ) )
     603                 :            :     {
     604                 :            :       // return complete feature
     605                 :          0 :       mFetchChangedGeomIt++;
     606                 :          0 :       return true;
     607                 :            :     }
     608                 :          0 :   }
     609                 :            : 
     610                 :          0 :   return false; // no more changed geometries
     611                 :          0 : }
     612                 :            : 
     613                 :          0 : bool QgsVectorLayerFeatureIterator::fetchNextChangedAttributeFeature( QgsFeature &f )
     614                 :            : {
     615                 :          0 :   while ( mChangedFeaturesIterator.nextFeature( f ) )
     616                 :            :   {
     617                 :          0 :     if ( mFetchConsidered.contains( f.id() ) )
     618                 :            :       // skip deleted features and those already handled by the geometry
     619                 :          0 :       continue;
     620                 :            : 
     621                 :          0 :     mFetchConsidered << f.id();
     622                 :            : 
     623                 :          0 :     updateChangedAttributes( f );
     624                 :            : 
     625                 :          0 :     if ( mHasVirtualAttributes )
     626                 :          0 :       addVirtualAttributes( f );
     627                 :            : 
     628                 :          0 :     mRequest.expressionContext()->setFeature( f );
     629                 :          0 :     if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && postProcessFeature( f ) )
     630                 :            :     {
     631                 :          0 :       return true;
     632                 :            :     }
     633                 :            :   }
     634                 :            : 
     635                 :          0 :   return false;
     636                 :          0 : }
     637                 :            : 
     638                 :            : 
     639                 :          0 : void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid, const QgsGeometry &geom, QgsFeature &f )
     640                 :            : {
     641                 :          0 :   f.setId( fid );
     642                 :          0 :   f.setValid( true );
     643                 :          0 :   f.setFields( mSource->mFields );
     644                 :            : 
     645                 :          0 :   if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ||
     646                 :          0 :        ( mRequest.filterType() == QgsFeatureRequest::FilterExpression && mRequest.filterExpression()->needsGeometry() ) )
     647                 :            :   {
     648                 :          0 :     f.setGeometry( geom );
     649                 :          0 :   }
     650                 :            : 
     651                 :          0 :   bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
     652                 :          0 :   if ( !subsetAttrs || !mRequest.subsetOfAttributes().isEmpty() )
     653                 :            :   {
     654                 :            :     // retrieve attributes from provider
     655                 :          0 :     QgsFeature tmp;
     656                 :            :     //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
     657                 :          0 :     QgsFeatureRequest request;
     658                 :          0 :     request.setFilterFid( fid ).setFlags( QgsFeatureRequest::NoGeometry );
     659                 :          0 :     if ( subsetAttrs )
     660                 :            :     {
     661                 :          0 :       request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
     662                 :          0 :     }
     663                 :          0 :     QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
     664                 :          0 :     if ( fi.nextFeature( tmp ) )
     665                 :            :     {
     666                 :          0 :       if ( mHasVirtualAttributes || mSource->mHasEditBuffer )
     667                 :          0 :         updateChangedAttributes( tmp );
     668                 :          0 :       f.setAttributes( tmp.attributes() );
     669                 :          0 :     }
     670                 :          0 :   }
     671                 :            : 
     672                 :          0 :   addVirtualAttributes( f );
     673                 :          0 : }
     674                 :            : 
     675                 :            : 
     676                 :            : 
     677                 :        134 : void QgsVectorLayerFeatureIterator::rewindEditBuffer()
     678                 :            : {
     679                 :        134 :   mFetchConsidered = mSource->mDeletedFeatureIds;
     680                 :            : 
     681                 :        134 :   mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
     682                 :        134 :   mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
     683                 :        134 : }
     684                 :            : 
     685                 :          0 : void QgsVectorLayerFeatureIterator::prepareJoin( int fieldIdx )
     686                 :            : {
     687                 :          0 :   if ( !mSource->mFields.exists( fieldIdx ) )
     688                 :          0 :     return;
     689                 :            : 
     690                 :          0 :   if ( mSource->mFields.fieldOrigin( fieldIdx ) != QgsFields::OriginJoin )
     691                 :          0 :     return;
     692                 :            : 
     693                 :            :   int sourceLayerIndex;
     694                 :          0 :   const QgsVectorLayerJoinInfo *joinInfo = mSource->mJoinBuffer->joinForFieldIndex( fieldIdx, mSource->mFields, sourceLayerIndex );
     695                 :            :   Q_ASSERT( joinInfo );
     696                 :            : 
     697                 :          0 :   QgsVectorLayer *joinLayer = joinInfo->joinLayer();
     698                 :          0 :   if ( !joinLayer )
     699                 :          0 :     return;  // invalid join (unresolved reference to layer)
     700                 :            : 
     701                 :          0 :   if ( !mFetchJoinInfo.contains( joinInfo ) )
     702                 :            :   {
     703                 :          0 :     FetchJoinInfo info;
     704                 :          0 :     info.joinInfo = joinInfo;
     705                 :          0 :     info.joinLayer = joinLayer;
     706                 :          0 :     info.indexOffset = mSource->mJoinBuffer->joinedFieldsOffset( joinInfo, mSource->mFields );
     707                 :          0 :     info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName() );
     708                 :          0 :     info.joinField = joinLayer->fields().indexFromName( joinInfo->joinFieldName() );
     709                 :            : 
     710                 :            :     // for joined fields, we always need to request the targetField from the provider too
     711                 :          0 :     if ( !mPreparedFields.contains( info.targetField ) && !mFieldsToPrepare.contains( info.targetField ) )
     712                 :          0 :       mFieldsToPrepare << info.targetField;
     713                 :            : 
     714                 :          0 :     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.subsetOfAttributes().contains( info.targetField ) )
     715                 :          0 :       mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << info.targetField );
     716                 :            : 
     717                 :          0 :     mFetchJoinInfo.insert( joinInfo, info );
     718                 :          0 :   }
     719                 :            : 
     720                 :            :   // store field source index - we'll need it when fetching from provider
     721                 :          0 :   mFetchJoinInfo[ joinInfo ].attributes.push_back( sourceLayerIndex );
     722                 :          0 :   mFetchJoinInfo[ joinInfo ].attributesSourceToDestLayerMap[sourceLayerIndex] = fieldIdx;
     723                 :          0 : }
     724                 :            : 
     725                 :            : 
     726                 :          0 : void QgsVectorLayerFeatureIterator::prepareExpression( int fieldIdx )
     727                 :            : {
     728                 :          0 :   static QThreadStorage<QStack<QString>> sStack;
     729                 :            : 
     730                 :          0 :   QgsThreadStackOverflowGuard guard( sStack, mSource->id(), 4 );
     731                 :            : 
     732                 :          0 :   if ( guard.hasStackOverflow() )
     733                 :            :   {
     734                 :          0 :     QgsMessageLog::logMessage( QObject::tr( "Stack overflow when preparing field %1 of layer %2.\nLast frames:\n%3\n..." ).arg( mSource->fields().at( fieldIdx ).name(), mSource->id(), guard.topFrames() ), QObject::tr( "General" ), Qgis::Critical );
     735                 :          0 :     return;
     736                 :            :   }
     737                 :            : 
     738                 :          0 :   const QList<QgsExpressionFieldBuffer::ExpressionField> &exps = mSource->mExpressionFieldBuffer->expressions();
     739                 :            : 
     740                 :          0 :   int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
     741                 :          0 :   std::unique_ptr<QgsExpression> exp = std::make_unique<QgsExpression>( exps[oi].cachedExpression );
     742                 :            : 
     743                 :          0 :   QgsDistanceArea da;
     744                 :          0 :   da.setSourceCrs( mSource->mCrs, QgsProject::instance()->transformContext() );
     745                 :          0 :   da.setEllipsoid( QgsProject::instance()->ellipsoid() );
     746                 :          0 :   exp->setGeomCalculator( &da );
     747                 :          0 :   exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
     748                 :          0 :   exp->setAreaUnits( QgsProject::instance()->areaUnits() );
     749                 :            : 
     750                 :          0 :   if ( !mExpressionContext )
     751                 :          0 :     createExpressionContext();
     752                 :          0 :   exp->prepare( mExpressionContext.get() );
     753                 :          0 :   const QSet<int> referencedColumns = exp->referencedAttributeIndexes( mSource->fields() );
     754                 :            : 
     755                 :          0 :   QSet<int> requestedAttributes = qgis::listToSet( mRequest.subsetOfAttributes() );
     756                 :            : 
     757                 :          0 :   for ( int dependentFieldIdx : referencedColumns )
     758                 :            :   {
     759                 :          0 :     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     760                 :            :     {
     761                 :          0 :       requestedAttributes += dependentFieldIdx;
     762                 :          0 :     }
     763                 :            :     // also need to fetch this dependent field
     764                 :          0 :     if ( !mPreparedFields.contains( dependentFieldIdx ) && !mFieldsToPrepare.contains( dependentFieldIdx ) )
     765                 :          0 :       mFieldsToPrepare << dependentFieldIdx;
     766                 :            :   }
     767                 :            : 
     768                 :          0 :   if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     769                 :            :   {
     770                 :          0 :     mRequest.setSubsetOfAttributes( qgis::setToList( requestedAttributes ) );
     771                 :          0 :   }
     772                 :            : 
     773                 :          0 :   if ( exp->needsGeometry() )
     774                 :            :   {
     775                 :          0 :     mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
     776                 :          0 :   }
     777                 :            : 
     778                 :          0 :   mExpressionFieldInfo.insert( fieldIdx, exp.release() );
     779                 :          0 : }
     780                 :            : 
     781                 :        158 : void QgsVectorLayerFeatureIterator::prepareFields()
     782                 :            : {
     783                 :        158 :   mPreparedFields.clear();
     784                 :        158 :   mFieldsToPrepare.clear();
     785                 :        158 :   mFetchJoinInfo.clear();
     786                 :        158 :   mOrderedJoinInfoList.clear();
     787                 :            : 
     788                 :        158 :   mExpressionContext.reset();
     789                 :            : 
     790                 :        158 :   mFieldsToPrepare = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     791                 :         69 :                      ? mRequest.subsetOfAttributes()
     792                 :         89 :                      : mSource->mFields.allAttributesList();
     793                 :            : 
     794                 :        282 :   while ( !mFieldsToPrepare.isEmpty() )
     795                 :            :   {
     796                 :        124 :     int fieldIdx = mFieldsToPrepare.takeFirst();
     797                 :        124 :     if ( mPreparedFields.contains( fieldIdx ) )
     798                 :          0 :       continue;
     799                 :            : 
     800                 :        124 :     mPreparedFields << fieldIdx;
     801                 :        124 :     prepareField( fieldIdx );
     802                 :            :   }
     803                 :            : 
     804                 :            :   //sort joins by dependency
     805                 :        158 :   if ( !mFetchJoinInfo.empty() )
     806                 :            :   {
     807                 :          0 :     createOrderedJoinList();
     808                 :          0 :   }
     809                 :        158 : }
     810                 :            : 
     811                 :          0 : void QgsVectorLayerFeatureIterator::createOrderedJoinList()
     812                 :            : {
     813                 :          0 :   mOrderedJoinInfoList = mFetchJoinInfo.values();
     814                 :          0 :   if ( mOrderedJoinInfoList.size() < 2 )
     815                 :            :   {
     816                 :          0 :     return;
     817                 :            :   }
     818                 :            : 
     819                 :          0 :   QSet<int> resolvedFields; //todo: get provider / virtual fields without joins
     820                 :            : 
     821                 :            :   //add all provider fields without joins as resolved fields
     822                 :          0 :   QList< int >::const_iterator prepFieldIt = mPreparedFields.constBegin();
     823                 :          0 :   for ( ; prepFieldIt != mPreparedFields.constEnd(); ++prepFieldIt )
     824                 :            :   {
     825                 :          0 :     if ( mSource->mFields.fieldOrigin( *prepFieldIt ) != QgsFields::OriginJoin )
     826                 :            :     {
     827                 :          0 :       resolvedFields.insert( *prepFieldIt );
     828                 :          0 :     }
     829                 :          0 :   }
     830                 :            : 
     831                 :            :   //iterate through the joins. If target field is not yet covered, move the entry to the end of the list
     832                 :            : 
     833                 :            :   //some join combinations might not have a resolution at all
     834                 :          0 :   int maxIterations = ( mOrderedJoinInfoList.size() + 1 ) * mOrderedJoinInfoList.size() / 2.0;
     835                 :          0 :   int currentIteration = 0;
     836                 :            : 
     837                 :          0 :   for ( int i = 0; i < mOrderedJoinInfoList.size() - 1; ++i )
     838                 :            :   {
     839                 :          0 :     if ( !resolvedFields.contains( mOrderedJoinInfoList.at( i ).targetField ) )
     840                 :            :     {
     841                 :          0 :       mOrderedJoinInfoList.append( mOrderedJoinInfoList.at( i ) );
     842                 :          0 :       mOrderedJoinInfoList.removeAt( i );
     843                 :          0 :       --i;
     844                 :          0 :     }
     845                 :            :     else
     846                 :            :     {
     847                 :          0 :       int offset = mOrderedJoinInfoList.at( i ).indexOffset;
     848                 :          0 :       int joinField = mOrderedJoinInfoList.at( i ).joinField;
     849                 :            : 
     850                 :          0 :       QgsAttributeList attributes = mOrderedJoinInfoList.at( i ).attributes;
     851                 :          0 :       for ( int n = 0; n < attributes.size(); n++ )
     852                 :            :       {
     853                 :          0 :         if ( n != joinField )
     854                 :            :         {
     855                 :          0 :           resolvedFields.insert( joinField < n ? n + offset - 1 : n + offset );
     856                 :          0 :         }
     857                 :          0 :       }
     858                 :          0 :     }
     859                 :            : 
     860                 :          0 :     ++currentIteration;
     861                 :          0 :     if ( currentIteration >= maxIterations )
     862                 :            :     {
     863                 :          0 :       break;
     864                 :            :     }
     865                 :          0 :   }
     866                 :          0 : }
     867                 :            : 
     868                 :       1047 : bool QgsVectorLayerFeatureIterator::postProcessFeature( QgsFeature &feature )
     869                 :            : {
     870                 :       1047 :   bool result = checkGeometryValidity( feature );
     871                 :       1047 :   if ( result )
     872                 :       1047 :     geometryToDestinationCrs( feature, mTransform );
     873                 :       1047 :   return result;
     874                 :            : }
     875                 :            : 
     876                 :       1047 : bool QgsVectorLayerFeatureIterator::checkGeometryValidity( const QgsFeature &feature )
     877                 :            : {
     878                 :       1047 :   if ( !feature.hasGeometry() )
     879                 :          0 :     return true;
     880                 :            : 
     881                 :       1047 :   switch ( mRequest.invalidGeometryCheck() )
     882                 :            :   {
     883                 :            :     case QgsFeatureRequest::GeometryNoCheck:
     884                 :       1047 :       return true;
     885                 :            : 
     886                 :            :     case QgsFeatureRequest::GeometrySkipInvalid:
     887                 :            :     {
     888                 :          0 :       if ( !feature.geometry().isGeosValid() )
     889                 :            :       {
     890                 :          0 :         QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
     891                 :          0 :         if ( mRequest.invalidGeometryCallback() )
     892                 :            :         {
     893                 :          0 :           mRequest.invalidGeometryCallback()( feature );
     894                 :          0 :         }
     895                 :          0 :         return false;
     896                 :            :       }
     897                 :          0 :       break;
     898                 :            :     }
     899                 :            : 
     900                 :            :     case QgsFeatureRequest::GeometryAbortOnInvalid:
     901                 :          0 :       if ( !feature.geometry().isGeosValid() )
     902                 :            :       {
     903                 :          0 :         QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), Qgis::Critical );
     904                 :          0 :         close();
     905                 :          0 :         if ( mRequest.invalidGeometryCallback() )
     906                 :            :         {
     907                 :          0 :           mRequest.invalidGeometryCallback()( feature );
     908                 :          0 :         }
     909                 :          0 :         return false;
     910                 :            :       }
     911                 :          0 :       break;
     912                 :            :   }
     913                 :            : 
     914                 :          0 :   return true;
     915                 :       1047 : }
     916                 :            : 
     917                 :        124 : void QgsVectorLayerFeatureIterator::prepareField( int fieldIdx )
     918                 :            : {
     919                 :        124 :   switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
     920                 :            :   {
     921                 :            :     case QgsFields::OriginExpression:
     922                 :          0 :       prepareExpression( fieldIdx );
     923                 :          0 :       break;
     924                 :            : 
     925                 :            :     case QgsFields::OriginJoin:
     926                 :          0 :       if ( mSource->mJoinBuffer->containsJoins() )
     927                 :            :       {
     928                 :          0 :         prepareJoin( fieldIdx );
     929                 :          0 :       }
     930                 :          0 :       break;
     931                 :            : 
     932                 :            :     case QgsFields::OriginUnknown:
     933                 :            :     case QgsFields::OriginProvider:
     934                 :            :     case QgsFields::OriginEdit:
     935                 :        124 :       break;
     936                 :            :   }
     937                 :        124 : }
     938                 :            : 
     939                 :          0 : void QgsVectorLayerFeatureIterator::addJoinedAttributes( QgsFeature &f )
     940                 :            : {
     941                 :          0 :   QList< FetchJoinInfo >::const_iterator joinIt = mOrderedJoinInfoList.constBegin();
     942                 :          0 :   for ( ; joinIt != mOrderedJoinInfoList.constEnd(); ++joinIt )
     943                 :            :   {
     944                 :          0 :     QVariant targetFieldValue = f.attribute( joinIt->targetField );
     945                 :          0 :     if ( !targetFieldValue.isValid() )
     946                 :          0 :       continue;
     947                 :            : 
     948                 :          0 :     const QHash< QString, QgsAttributes> &memoryCache = joinIt->joinInfo->cachedAttributes;
     949                 :          0 :     if ( memoryCache.isEmpty() )
     950                 :          0 :       joinIt->addJoinedAttributesDirect( f, targetFieldValue );
     951                 :            :     else
     952                 :          0 :       joinIt->addJoinedAttributesCached( f, targetFieldValue );
     953                 :          0 :   }
     954                 :          0 : }
     955                 :            : 
     956                 :          0 : void QgsVectorLayerFeatureIterator::addVirtualAttributes( QgsFeature &f )
     957                 :            : {
     958                 :            :   // make sure we have space for newly added attributes
     959                 :          0 :   QgsAttributes attr = f.attributes();
     960                 :          0 :   attr.resize( mSource->mFields.count() );  // Provider attrs count + joined attrs count + expression attrs count
     961                 :          0 :   f.setAttributes( attr );
     962                 :            : 
     963                 :            :   // possible TODO - handle combinations of expression -> join -> expression -> join?
     964                 :            :   // but for now, write that off as too complex and an unlikely rare, unsupported use case
     965                 :            : 
     966                 :          0 :   QList< int > fetchedVirtualAttributes;
     967                 :            :   //first, check through joins for any virtual fields we need
     968                 :          0 :   QMap<const QgsVectorLayerJoinInfo *, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
     969                 :          0 :   for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
     970                 :            :   {
     971                 :          0 :     if ( mExpressionFieldInfo.contains( joinIt->targetField ) )
     972                 :            :     {
     973                 :            :       // have to calculate expression field before we can handle this join
     974                 :          0 :       addExpressionAttribute( f, joinIt->targetField );
     975                 :          0 :       fetchedVirtualAttributes << joinIt->targetField;
     976                 :          0 :     }
     977                 :          0 :   }
     978                 :            : 
     979                 :          0 :   if ( !mFetchJoinInfo.isEmpty() )
     980                 :          0 :     addJoinedAttributes( f );
     981                 :            : 
     982                 :            :   // add remaining expression fields
     983                 :          0 :   if ( !mExpressionFieldInfo.isEmpty() )
     984                 :            :   {
     985                 :          0 :     QMap<int, QgsExpression *>::ConstIterator it = mExpressionFieldInfo.constBegin();
     986                 :          0 :     for ( ; it != mExpressionFieldInfo.constEnd(); ++it )
     987                 :            :     {
     988                 :          0 :       if ( fetchedVirtualAttributes.contains( it.key() ) )
     989                 :          0 :         continue;
     990                 :            : 
     991                 :          0 :       addExpressionAttribute( f, it.key() );
     992                 :          0 :     }
     993                 :          0 :   }
     994                 :          0 : }
     995                 :            : 
     996                 :          0 : void QgsVectorLayerFeatureIterator::addExpressionAttribute( QgsFeature &f, int attrIndex )
     997                 :            : {
     998                 :          0 :   QgsExpression *exp = mExpressionFieldInfo.value( attrIndex );
     999                 :          0 :   if ( exp )
    1000                 :            :   {
    1001                 :          0 :     if ( !mExpressionContext )
    1002                 :          0 :       createExpressionContext();
    1003                 :            : 
    1004                 :          0 :     mExpressionContext->setFeature( f );
    1005                 :          0 :     QVariant val = exp->evaluate( mExpressionContext.get() );
    1006                 :          0 :     ( void )mSource->mFields.at( attrIndex ).convertCompatible( val );
    1007                 :          0 :     f.setAttribute( attrIndex, val );
    1008                 :          0 :   }
    1009                 :            :   else
    1010                 :            :   {
    1011                 :          0 :     f.setAttribute( attrIndex, QVariant() );
    1012                 :            :   }
    1013                 :          0 : }
    1014                 :            : 
    1015                 :        158 : bool QgsVectorLayerFeatureIterator::prepareSimplification( const QgsSimplifyMethod &simplifyMethod )
    1016                 :            : {
    1017                 :        158 :   Q_UNUSED( simplifyMethod )
    1018                 :        158 :   return false;
    1019                 :            : }
    1020                 :            : 
    1021                 :          0 : bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
    1022                 :            : {
    1023                 :            :   Q_UNUSED( methodType )
    1024                 :          0 :   return false;
    1025                 :            : }
    1026                 :            : 
    1027                 :            : 
    1028                 :          0 : void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesCached( QgsFeature &f, const QVariant &joinValue ) const
    1029                 :            : {
    1030                 :          0 :   const QHash<QString, QgsAttributes> &memoryCache = joinInfo->cachedAttributes;
    1031                 :          0 :   QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
    1032                 :          0 :   if ( it == memoryCache.constEnd() )
    1033                 :          0 :     return; // joined value not found -> leaving the attributes empty (null)
    1034                 :            : 
    1035                 :          0 :   int index = indexOffset;
    1036                 :            : 
    1037                 :          0 :   const QgsAttributes &featureAttributes = it.value();
    1038                 :          0 :   for ( int i = 0; i < featureAttributes.count(); ++i )
    1039                 :            :   {
    1040                 :          0 :     f.setAttribute( index++, featureAttributes.at( i ) );
    1041                 :          0 :   }
    1042                 :          0 : }
    1043                 :            : 
    1044                 :            : 
    1045                 :            : 
    1046                 :          0 : void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesDirect( QgsFeature &f, const QVariant &joinValue ) const
    1047                 :            : {
    1048                 :            :   // Shortcut
    1049                 :          0 :   if ( joinLayer && ! joinLayer->hasFeatures() )
    1050                 :            :   {
    1051                 :          0 :     return;
    1052                 :            :   }
    1053                 :            : 
    1054                 :            :   // no memory cache, query the joined values by setting substring
    1055                 :          0 :   QString subsetString;
    1056                 :            : 
    1057                 :          0 :   QString joinFieldName = joinInfo->joinFieldName();
    1058                 :            : 
    1059                 :          0 :   subsetString.append( QStringLiteral( "\"%1\"" ).arg( joinFieldName ) );
    1060                 :            : 
    1061                 :          0 :   if ( joinValue.isNull() )
    1062                 :            :   {
    1063                 :          0 :     subsetString += QLatin1String( " IS NULL" );
    1064                 :          0 :   }
    1065                 :            :   else
    1066                 :            :   {
    1067                 :          0 :     QString v = joinValue.toString();
    1068                 :          0 :     switch ( joinValue.type() )
    1069                 :            :     {
    1070                 :            :       case QVariant::Int:
    1071                 :            :       case QVariant::LongLong:
    1072                 :            :       case QVariant::Double:
    1073                 :          0 :         break;
    1074                 :            : 
    1075                 :            :       default:
    1076                 :            :       case QVariant::String:
    1077                 :          0 :         v.replace( '\'', QLatin1String( "''" ) );
    1078                 :          0 :         v.prepend( '\'' ).append( '\'' );
    1079                 :          0 :         break;
    1080                 :            :     }
    1081                 :          0 :     subsetString += '=' + v;
    1082                 :          0 :   }
    1083                 :            : 
    1084                 :          0 :   QList<int> joinedAttributeIndices;
    1085                 :            : 
    1086                 :            :   // maybe user requested just a subset of layer's attributes
    1087                 :            :   // so we do not have to cache everything
    1088                 :          0 :   if ( joinInfo->hasSubset() )
    1089                 :            :   {
    1090                 :          0 :     const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinInfo );
    1091                 :          0 :     QVector<int> subsetIndices = QgsVectorLayerJoinBuffer::joinSubsetIndices( joinLayer, subsetNames );
    1092                 :          0 :     joinedAttributeIndices = qgis::setToList( qgis::listToSet( attributes ).intersect( qgis::listToSet( subsetIndices.toList() ) ) );
    1093                 :          0 :   }
    1094                 :            :   else
    1095                 :            :   {
    1096                 :          0 :     joinedAttributeIndices = attributes;
    1097                 :            :   }
    1098                 :            : 
    1099                 :            :   // we don't need the join field, it is already present in the other table
    1100                 :          0 :   joinedAttributeIndices.removeAll( joinField );
    1101                 :            : 
    1102                 :            :   // select (no geometry)
    1103                 :          0 :   QgsFeatureRequest request;
    1104                 :          0 :   request.setFlags( QgsFeatureRequest::NoGeometry );
    1105                 :          0 :   request.setSubsetOfAttributes( joinedAttributeIndices );
    1106                 :          0 :   request.setFilterExpression( subsetString );
    1107                 :          0 :   request.setLimit( 1 );
    1108                 :          0 :   QgsFeatureIterator fi = joinLayer->getFeatures( request );
    1109                 :            : 
    1110                 :            :   // get first feature
    1111                 :          0 :   const QList<int> sourceAttrIndexes = attributesSourceToDestLayerMap.keys();
    1112                 :          0 :   QgsFeature fet;
    1113                 :          0 :   if ( fi.nextFeature( fet ) )
    1114                 :            :   {
    1115                 :          0 :     QgsAttributes attr = fet.attributes();
    1116                 :            : 
    1117                 :          0 :     for ( const int sourceAttrIndex : sourceAttrIndexes )
    1118                 :            :     {
    1119                 :          0 :       if ( sourceAttrIndex == joinField )
    1120                 :          0 :         continue;
    1121                 :            : 
    1122                 :          0 :       int destAttrIndex = attributesSourceToDestLayerMap.value( sourceAttrIndex );
    1123                 :            : 
    1124                 :          0 :       f.setAttribute( destAttrIndex, attr.at( sourceAttrIndex ) );
    1125                 :            :     }
    1126                 :          0 :   }
    1127                 :            :   else
    1128                 :            :   {
    1129                 :            :     // no suitable join feature found, keeping empty (null) attributes
    1130                 :            :   }
    1131                 :          0 : }
    1132                 :            : 
    1133                 :            : 
    1134                 :            : 
    1135                 :            : 
    1136                 :         24 : bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature &f )
    1137                 :            : {
    1138                 :         24 :   QgsFeatureId featureId = mRequest.filterFid();
    1139                 :            : 
    1140                 :            :   // deleted already?
    1141                 :         24 :   if ( mSource->mDeletedFeatureIds.contains( featureId ) )
    1142                 :          0 :     return false;
    1143                 :            : 
    1144                 :            :   // has changed geometry?
    1145                 :         24 :   if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
    1146                 :            :   {
    1147                 :          0 :     useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
    1148                 :          0 :     return true;
    1149                 :            :   }
    1150                 :            : 
    1151                 :            :   // added features
    1152                 :         24 :   for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
    1153                 :            :   {
    1154                 :          1 :     if ( iter->id() == featureId )
    1155                 :            :     {
    1156                 :          1 :       useAddedFeature( *iter, f );
    1157                 :          1 :       return true;
    1158                 :            :     }
    1159                 :          0 :   }
    1160                 :            : 
    1161                 :            :   // regular features
    1162                 :         23 :   QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
    1163                 :         23 :   if ( fi.nextFeature( f ) )
    1164                 :            :   {
    1165                 :         15 :     f.setFields( mSource->mFields );
    1166                 :            : 
    1167                 :         15 :     if ( mSource->mHasEditBuffer )
    1168                 :          0 :       updateChangedAttributes( f );
    1169                 :            : 
    1170                 :         15 :     if ( mHasVirtualAttributes )
    1171                 :          0 :       addVirtualAttributes( f );
    1172                 :            : 
    1173                 :         15 :     return true;
    1174                 :            :   }
    1175                 :            : 
    1176                 :          8 :   return false;
    1177                 :         24 : }
    1178                 :            : 
    1179                 :          0 : void QgsVectorLayerFeatureIterator::updateChangedAttributes( QgsFeature &f )
    1180                 :            : {
    1181                 :          0 :   QgsAttributes attrs = f.attributes();
    1182                 :            : 
    1183                 :            :   // remove all attributes that will disappear - from higher indices to lower
    1184                 :          0 :   for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
    1185                 :            :   {
    1186                 :          0 :     attrs.remove( mSource->mDeletedAttributeIds[idx] );
    1187                 :          0 :   }
    1188                 :            : 
    1189                 :            :   // adjust size to accommodate added attributes
    1190                 :          0 :   attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
    1191                 :            : 
    1192                 :            :   // update changed attributes
    1193                 :          0 :   if ( mSource->mChangedAttributeValues.contains( f.id() ) )
    1194                 :            :   {
    1195                 :          0 :     const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
    1196                 :          0 :     for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
    1197                 :          0 :       attrs[it.key()] = it.value();
    1198                 :          0 :   }
    1199                 :          0 :   f.setAttributes( attrs );
    1200                 :          0 : }
    1201                 :            : 
    1202                 :       1028 : void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f )
    1203                 :            : {
    1204                 :       1028 :   if ( mSource->mChangedGeometries.contains( f.id() ) )
    1205                 :          0 :     f.setGeometry( mSource->mChangedGeometries[f.id()] );
    1206                 :       1028 : }
    1207                 :            : 
    1208                 :          0 : void QgsVectorLayerFeatureIterator::createExpressionContext()
    1209                 :            : {
    1210                 :          0 :   mExpressionContext = std::make_unique< QgsExpressionContext >();
    1211                 :          0 :   mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
    1212                 :          0 :   mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );
    1213                 :          0 :   mExpressionContext->appendScope( new QgsExpressionContextScope( mSource->mLayerScope ) );
    1214                 :          0 : }
    1215                 :            : 
    1216                 :          0 : bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys )
    1217                 :            : {
    1218                 :          0 :   Q_UNUSED( orderBys )
    1219                 :          0 :   return mDelegatedOrderByToProvider;
    1220                 :            : }
    1221                 :            : 
    1222                 :            : 
    1223                 :            : //
    1224                 :            : // QgsVectorLayerSelectedFeatureSource
    1225                 :            : //
    1226                 :            : 
    1227                 :          0 : QgsVectorLayerSelectedFeatureSource::QgsVectorLayerSelectedFeatureSource( QgsVectorLayer *layer )
    1228                 :          0 :   : mSource( layer )
    1229                 :          0 :   , mSelectedFeatureIds( layer->selectedFeatureIds() )
    1230                 :          0 :   , mWkbType( layer->wkbType() )
    1231                 :          0 :   , mName( layer->name() )
    1232                 :          0 :   , mLayer( layer )
    1233                 :          0 : {}
    1234                 :            : 
    1235                 :          0 : QgsFeatureIterator QgsVectorLayerSelectedFeatureSource::getFeatures( const QgsFeatureRequest &request ) const
    1236                 :            : {
    1237                 :          0 :   QgsFeatureRequest req( request );
    1238                 :            : 
    1239                 :            :   // while QgsVectorLayerSelectedFeatureIterator will reject any features not in mSelectedFeatureIds,
    1240                 :            :   // we still tweak the feature request to only request selected feature ids wherever we can -- this
    1241                 :            :   // allows providers to optimise the request and avoid requesting features we don't need
    1242                 :            :   // note that we can't do this for some request types - e.g. expression based requests, so
    1243                 :            :   // in that case we just pass the request on to the provider and let QgsVectorLayerSelectedFeatureIterator
    1244                 :            :   // do ALL the filtering
    1245                 :          0 :   if ( req.filterFids().isEmpty() && req.filterType() == QgsFeatureRequest::FilterNone )
    1246                 :            :   {
    1247                 :          0 :     req.setFilterFids( mSelectedFeatureIds );
    1248                 :          0 :   }
    1249                 :          0 :   else if ( !req.filterFids().isEmpty() )
    1250                 :            :   {
    1251                 :          0 :     QgsFeatureIds reqIds = mSelectedFeatureIds;
    1252                 :          0 :     reqIds.intersect( req.filterFids() );
    1253                 :          0 :     req.setFilterFids( reqIds );
    1254                 :          0 :   }
    1255                 :            : 
    1256                 :          0 :   return QgsFeatureIterator( new QgsVectorLayerSelectedFeatureIterator( mSelectedFeatureIds, req, mSource ) );
    1257                 :          0 : }
    1258                 :            : 
    1259                 :          0 : QgsCoordinateReferenceSystem QgsVectorLayerSelectedFeatureSource::sourceCrs() const
    1260                 :            : {
    1261                 :          0 :   return mSource.crs();
    1262                 :            : }
    1263                 :            : 
    1264                 :          0 : QgsFields QgsVectorLayerSelectedFeatureSource::fields() const
    1265                 :            : {
    1266                 :          0 :   return mSource.fields();
    1267                 :            : }
    1268                 :            : 
    1269                 :          0 : QgsWkbTypes::Type QgsVectorLayerSelectedFeatureSource::wkbType() const
    1270                 :            : {
    1271                 :          0 :   return mWkbType;
    1272                 :            : }
    1273                 :            : 
    1274                 :          0 : long QgsVectorLayerSelectedFeatureSource::featureCount() const
    1275                 :            : {
    1276                 :          0 :   return mSelectedFeatureIds.count();
    1277                 :            : }
    1278                 :            : 
    1279                 :          0 : QString QgsVectorLayerSelectedFeatureSource::sourceName() const
    1280                 :            : {
    1281                 :          0 :   return mName;
    1282                 :            : }
    1283                 :            : 
    1284                 :          0 : QgsExpressionContextScope *QgsVectorLayerSelectedFeatureSource::createExpressionContextScope() const
    1285                 :            : {
    1286                 :          0 :   if ( mLayer )
    1287                 :          0 :     return mLayer->createExpressionContextScope();
    1288                 :            :   else
    1289                 :          0 :     return nullptr;
    1290                 :          0 : }
    1291                 :            : 
    1292                 :          0 : QgsFeatureSource::SpatialIndexPresence QgsVectorLayerSelectedFeatureSource::hasSpatialIndex() const
    1293                 :            : {
    1294                 :          0 :   if ( mLayer )
    1295                 :          0 :     return mLayer->hasSpatialIndex();
    1296                 :            :   else
    1297                 :          0 :     return QgsFeatureSource::SpatialIndexUnknown;
    1298                 :          0 : }
    1299                 :            : 
    1300                 :            : //
    1301                 :            : // QgsVectorLayerSelectedFeatureIterator
    1302                 :            : //
    1303                 :            : 
    1304                 :            : ///@cond PRIVATE
    1305                 :          0 : QgsVectorLayerSelectedFeatureIterator::QgsVectorLayerSelectedFeatureIterator( const QgsFeatureIds &selectedFeatureIds, const QgsFeatureRequest &request, QgsVectorLayerFeatureSource &source )
    1306                 :          0 :   : QgsAbstractFeatureIterator( request )
    1307                 :          0 :   , mSelectedFeatureIds( selectedFeatureIds )
    1308                 :          0 : {
    1309                 :          0 :   QgsFeatureRequest sourceRequest = request;
    1310                 :          0 :   if ( sourceRequest.filterType() == QgsFeatureRequest::FilterExpression && sourceRequest.limit() > 0 )
    1311                 :            :   {
    1312                 :            :     // we can't pass the request limit to the provider here - otherwise the provider will
    1313                 :            :     // limit the number of returned features and may only return a bunch of matching features
    1314                 :            :     // which AREN'T in the selected feature set
    1315                 :          0 :     sourceRequest.setLimit( -1 );
    1316                 :          0 :   }
    1317                 :          0 :   mIterator = source.getFeatures( sourceRequest );
    1318                 :          0 : }
    1319                 :            : 
    1320                 :          0 : bool QgsVectorLayerSelectedFeatureIterator::rewind()
    1321                 :            : {
    1322                 :          0 :   return mIterator.rewind();
    1323                 :            : }
    1324                 :            : 
    1325                 :          0 : bool QgsVectorLayerSelectedFeatureIterator::close()
    1326                 :            : {
    1327                 :          0 :   return mIterator.close();
    1328                 :            : }
    1329                 :            : 
    1330                 :          0 : bool QgsVectorLayerSelectedFeatureIterator::fetchFeature( QgsFeature &f )
    1331                 :            : {
    1332                 :          0 :   while ( mIterator.nextFeature( f ) )
    1333                 :            :   {
    1334                 :          0 :     if ( mSelectedFeatureIds.contains( f.id() ) )
    1335                 :          0 :       return true;
    1336                 :            :   }
    1337                 :          0 :   return false;
    1338                 :          0 : }
    1339                 :            : 
    1340                 :            : ///@endcond

Generated by: LCOV version 1.14