Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
QgsVectorLayerFeatureIterator: Don't worry about concurrent edits
An iterator will always use the data from the edit buffer which was
valid, at the time the iterator was created. Therefore, there are no
more problems with randomly disappearing or changing features while
iterating
  • Loading branch information
m-kuhn committed Jun 25, 2013
1 parent 55135d6 commit c4b616b
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayereditbuffer.h
Expand Up @@ -106,6 +106,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
/** Changed geometries which are not commited. */
inline const QgsGeometryMap& changedGeometries() { return mChangedGeometries; }

inline const QgsFeatureIds deletedFeatureIds() { return mDeletedFeatureIds; }
//QString dumpEditBuffer();


Expand Down Expand Up @@ -157,7 +158,6 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
protected:
QgsVectorLayer* L;
friend class QgsVectorLayer;
friend class QgsVectorLayerFeatureIterator;

friend class QgsVectorLayerUndoCommand;
friend class QgsVectorLayerUndoCommandAddFeature;
Expand Down
128 changes: 72 additions & 56 deletions src/core/qgsvectorlayerfeatureiterator.cpp
Expand Up @@ -23,9 +23,18 @@
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request )
: QgsAbstractFeatureIterator( request ), L( layer )
{

QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer;

if ( L->editBuffer() )
{
mAddedFeatures = QgsFeatureMap( L->editBuffer()->addedFeatures() );
mChangedGeometries = QgsGeometryMap( L->editBuffer()->changedGeometries() );
mDeletedFeatureIds = QgsFeatureIds( L->editBuffer()->deletedFeatureIds() );
mChangedAttributeValues = QgsChangedAttributesMap( L->editBuffer()->changedAttributeValues() );
mAddedAttributes = QList<QgsField>( L->editBuffer()->addedAttributes() );
mDeletedAttributeIds = QgsAttributeList( L->editBuffer()->deletedAttributeIds() );
}

// prepare joins: may add more attributes to fetch (in order to allow join)
if ( joinBuffer->containsJoins() )
prepareJoins();
Expand Down Expand Up @@ -86,24 +95,19 @@ bool QgsVectorLayerFeatureIterator::nextFeature( QgsFeature& f )
return res;
}

QgsVectorLayerEditBuffer* editBuffer = L->editBuffer();

if ( editBuffer )
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
if ( fetchNextChangedGeomFeature( f ) )
return true;

// no more changed geometries
}

if ( fetchNextAddedFeature( f ) )
if ( fetchNextChangedGeomFeature( f ) )
return true;

// no more added features
// no more changed geometries
}

if ( fetchNextAddedFeature( f ) )
return true;

// no more added features

while ( mProviderIterator.nextFeature( f ) )
{
if ( mFetchConsidered.contains( f.id() ) )
Expand All @@ -113,15 +117,14 @@ bool QgsVectorLayerFeatureIterator::nextFeature( QgsFeature& f )
f.setFields( &L->mUpdatedFields );

// update attributes
if ( editBuffer )
editBuffer->updateChangedAttributes( f );
updateChangedAttributes( f );

if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );

// update geometry
if ( editBuffer && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
editBuffer->updateFeatureGeometry( f );
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
updateFeatureGeometry( f );

return true;
}
Expand Down Expand Up @@ -166,9 +169,7 @@ bool QgsVectorLayerFeatureIterator::close()

bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f )
{
QgsVectorLayerEditBuffer* editBuffer = L->editBuffer();

for ( ; mFetchAddedFeaturesIt != editBuffer->mAddedFeatures.end(); mFetchAddedFeaturesIt++ )
for ( ; mFetchAddedFeaturesIt != mAddedFeatures.constEnd(); mFetchAddedFeaturesIt++ )
{
QgsFeatureId fid = mFetchAddedFeaturesIt->id();

Expand Down Expand Up @@ -198,7 +199,7 @@ void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsF
f.setValid( true );
f.setFields( &L->mUpdatedFields );

if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
f.setGeometry( *src.geometry() );

// TODO[MD]: if subset set just some attributes
Expand All @@ -213,10 +214,8 @@ void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsF

bool QgsVectorLayerFeatureIterator::fetchNextChangedGeomFeature( QgsFeature& f )
{
QgsVectorLayerEditBuffer* editBuffer = L->editBuffer();

// check if changed geometries are in rectangle
for ( ; mFetchChangedGeomIt != editBuffer->mChangedGeometries.end(); mFetchChangedGeomIt++ )
for ( ; mFetchChangedGeomIt != mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
{
QgsFeatureId fid = mFetchChangedGeomIt.key();

Expand Down Expand Up @@ -261,8 +260,7 @@ void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid
QgsFeatureIterator fi = L->dataProvider()->getFeatures( request );
if ( fi.nextFeature( tmp ) )
{
if ( L->editBuffer() )
L->editBuffer()->updateChangedAttributes( tmp );
updateChangedAttributes( tmp );
f.setAttributes( tmp.attributes() );
}
}
Expand All @@ -275,15 +273,10 @@ void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid

void QgsVectorLayerFeatureIterator::rewindEditBuffer()
{
QgsVectorLayerEditBuffer* editBuffer = L->editBuffer();
mFetchConsidered = mDeletedFeatureIds;

mFetchConsidered = editBuffer ? editBuffer->mDeletedFeatureIds : QSet<QgsFeatureId>();

if ( editBuffer )
{
mFetchAddedFeaturesIt = editBuffer->mAddedFeatures.begin();
mFetchChangedGeomIt = editBuffer->mChangedGeometries.begin();
}
mFetchAddedFeaturesIt = mAddedFeatures.constBegin();
mFetchChangedGeomIt = mChangedGeometries.constBegin();
}


Expand Down Expand Up @@ -376,7 +369,7 @@ void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesCached( Qg
{
const QHash<QString, QgsAttributes>& memoryCache = joinInfo->cachedAttributes;
QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
if ( it == memoryCache.end() )
if ( it == memoryCache.constEnd() )
return; // joined value not found -> leaving the attributes empty (null)

int index = indexOffset;
Expand Down Expand Up @@ -446,39 +439,34 @@ void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesDirect( Qg

bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature& f )
{
QgsVectorLayerEditBuffer* editBuffer = L->editBuffer();
QgsFeatureId featureId = mRequest.filterFid();

if ( editBuffer )
// deleted already?
if ( mDeletedFeatureIds.contains( featureId ) )
return false;

// has changed geometry?
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mChangedGeometries.contains( featureId ) )
{
// deleted already?
if ( editBuffer->mDeletedFeatureIds.contains( featureId ) )
return false;
useChangedAttributeFeature( featureId, mChangedGeometries[featureId], f );
return true;
}

// has changed geometry?
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && editBuffer->mChangedGeometries.contains( featureId ) )
// added features
for ( QgsFeatureMap::ConstIterator iter = mAddedFeatures.constBegin(); iter != mAddedFeatures.constEnd(); ++iter )
{
if ( iter->id() == featureId )
{
useChangedAttributeFeature( featureId, editBuffer->mChangedGeometries[featureId], f );
useAddedFeature( *iter, f );
return true;
}

// added features
for ( QgsFeatureMap::iterator iter = editBuffer->mAddedFeatures.begin(); iter != editBuffer->mAddedFeatures.end(); ++iter )
{
if ( iter->id() == featureId )
{
useAddedFeature( *iter, f );
return true;
}
}
}

// regular features
QgsFeatureIterator fi = L->dataProvider()->getFeatures( mProviderRequest );
if ( fi.nextFeature( f ) )
{
if ( editBuffer )
editBuffer->updateChangedAttributes( f );
updateChangedAttributes( f );

if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
Expand All @@ -488,3 +476,31 @@ bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature& f )

return false;
}

void QgsVectorLayerFeatureIterator::updateChangedAttributes( QgsFeature &f )
{
QgsAttributes& attrs = f.attributes();

// remove all attributes that will disappear - from higher indices to lower
for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
{
attrs.remove( mDeletedAttributeIds[idx] );
}

// adjust size to accommodate added attributes
attrs.resize( attrs.count() + mAddedAttributes.count() );

// update changed attributes
if ( mChangedAttributeValues.contains( f.id() ) )
{
const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); it++ )
attrs[it.key()] = it.value();
}
}

void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f )
{
if ( mChangedGeometries.contains( f.id() ) )
f.setGeometry( mChangedGeometries[f.id()] );
}
33 changes: 25 additions & 8 deletions src/core/qgsvectorlayerfeatureiterator.h
Expand Up @@ -22,6 +22,7 @@
typedef QMap<QgsFeatureId, QgsFeature> QgsFeatureMap;

class QgsVectorLayer;
class QgsVectorLayerEditBuffer;
struct QgsVectorJoinInfo;

class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureIterator
Expand All @@ -46,17 +47,19 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
QgsFeatureRequest mProviderRequest;
QgsFeatureIterator mProviderIterator;

#if 0
// general stuff
//bool mFetching;
//QgsRectangle mFetchRect;
//QgsAttributeList mFetchAttributes;
//QgsAttributeList mFetchProvAttributes;
//bool mFetchGeometry;
bool mFetching;
QgsRectangle mFetchRect;
QgsAttributeList mFetchAttributes;
QgsAttributeList mFetchProvAttributes;
bool mFetchGeometry;
#endif

// only related to editing
QSet<QgsFeatureId> mFetchConsidered;
QgsGeometryMap::iterator mFetchChangedGeomIt;
QgsFeatureMap::iterator mFetchAddedFeaturesIt;
QgsGeometryMap::ConstIterator mFetchChangedGeomIt;
QgsFeatureMap::ConstIterator mFetchAddedFeaturesIt;

bool mFetchedFid; // when iterating by FID: indicator whether it has been fetched yet or not

Expand All @@ -69,6 +72,12 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
bool nextFeatureFid( QgsFeature& f );
void addJoinedAttributes( QgsFeature &f );

/** Update feature with uncommited attribute updates */
void updateChangedAttributes( QgsFeature& f );

/** Update feature with uncommited geometry updates */
void updateFeatureGeometry( QgsFeature& f );

/** Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAttributes().
Created in the select() method of QgsVectorLayerJoinBuffer for the joins that contain fetched attributes
*/
Expand All @@ -85,11 +94,19 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
void addJoinedAttributesDirect( QgsFeature& f, const QVariant& joinValue ) const;
};

// A deep-copy is only performed, if the original maps change
// see here https://github.com/qgis/Quantum-GIS/pull/673
// for explanation
QgsFeatureMap mAddedFeatures;
QgsGeometryMap mChangedGeometries;
QgsFeatureIds mDeletedFeatureIds;
QList<QgsField> mAddedAttributes;
QgsChangedAttributesMap mChangedAttributeValues;
QgsAttributeList mDeletedAttributeIds;

/** Informations about joins used in the current select() statement.
Allows faster mapping of attribute ids compared to mVectorJoins */
QMap<QgsVectorLayer*, FetchJoinInfo> mFetchJoinInfo;

};

#endif // QGSVECTORLAYERFEATUREITERATOR_H

0 comments on commit c4b616b

Please sign in to comment.