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
|