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
This commit is contained in:
Matthias Kuhn 2013-06-23 14:54:27 +02:00
parent 55135d6007
commit c4b616b762
3 changed files with 98 additions and 65 deletions

View File

@ -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();
@ -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;

View File

@ -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();
@ -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() ) )
@ -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;
}
@ -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();
@ -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
@ -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();
@ -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() );
}
}
@ -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();
}
@ -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;
@ -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 );
@ -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()] );
}

View File

@ -22,6 +22,7 @@
typedef QMap<QgsFeatureId, QgsFeature> QgsFeatureMap;
class QgsVectorLayer;
class QgsVectorLayerEditBuffer;
struct QgsVectorJoinInfo;
class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureIterator
@ -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
@ -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
*/
@ -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