Skip to content

Commit

Permalink
Merge pull request #3764 from nyalldawson/cache_fix2
Browse files Browse the repository at this point in the history
Cache fixes
  • Loading branch information
nyalldawson committed Nov 16, 2016
2 parents 87d2ac3 + 8f8624a commit 6727ea7
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 98 deletions.
34 changes: 0 additions & 34 deletions python/core/qgscacheindexfeatureid.sip
Expand Up @@ -6,42 +6,8 @@ class QgsCacheIndexFeatureId : QgsAbstractCacheIndex
public:
QgsCacheIndexFeatureId( QgsVectorLayerCache* );

/**
* Is called, whenever a feature is removed from the cache. You should update your indexes, so
* they become invalid in case this feature was required to successfuly answer a request.
*/
virtual void flushFeature( const QgsFeatureId fid );

/**
* Sometimes, the whole cache changes its state and its easier to just withdraw everything.
* In this case, this method is issued. Be sure to clear all cache information in here.
*/
virtual void flush();

/**
* @brief
* Implement this method to update the the indices, in case you need information contained by the request
* to properly index. (E.g. spatial index)
* Does nothing by default
*
* @param featureRequest The feature request that was answered
* @param fids The feature ids that have been returned
*/
virtual void requestCompleted( const QgsFeatureRequest& featureRequest, const QgsFeatureIds& fids );

/**
* Is called, when a feature request is issued on a cached layer.
* If this cache index is able to completely answer the feature request, it will return true
* and write the list of feature ids of cached features to cachedFeatures. If it is not able
* it will return false and the cachedFeatures state is undefined.
*
* @param featureIterator A reference to a {@link QgsFeatureIterator}. A valid featureIterator will
* be assigned in case this index is able to answer the request and the return
* value is true.
* @param featureRequest The feature request, for which this index is queried.
*
* @return True, if this index holds the information to answer the request.
*
*/
virtual bool getCacheIterator( QgsFeatureIterator& featureIterator, const QgsFeatureRequest& featureRequest );
};
18 changes: 17 additions & 1 deletion python/core/qgsvectorlayercache.sip
Expand Up @@ -65,9 +65,18 @@ class QgsVectorLayerCache : QObject
* be used for slow data sources, be aware, that the call to this method might take a long time.
*
* @param fullCache True: enable full caching, False: disable full caching
* @see hasFullCache()
*/
void setFullCache( bool fullCache );

/** Returns true if the cache is complete, ie it contains all features. This may happen as
* a result of a call to setFullCache() or by through a feature request which resulted in
* all available features being cached.
* @see setFullCache()
* @note added in QGIS 3.0
*/
bool hasFullCache() const;

/**
* @brief
* Adds a {@link QgsAbstractCacheIndex} to this cache. Cache indices know about features present
Expand Down Expand Up @@ -114,8 +123,15 @@ class QgsVectorLayerCache : QObject
* Check if a certain feature id is cached.
* @param fid The feature id to look for
* @return True if this id is in the cache
* @see cachedFeatureIds()
*/
bool isFidCached( const QgsFeatureId fid ) const;

/** Returns the set of feature IDs for features which are cached.
* @note added in QGIS 3.0
* @see isFidCached()
*/
bool isFidCached( const QgsFeatureId fid );
QgsFeatureIds cachedFeatureIds() const;

/**
* Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features
Expand Down
4 changes: 2 additions & 2 deletions src/core/qgscacheindex.h
Expand Up @@ -58,8 +58,8 @@ class CORE_EXPORT QgsAbstractCacheIndex
/**
* Is called, when a feature request is issued on a cached layer.
* If this cache index is able to completely answer the feature request, it will return true
* and write the list of feature ids of cached features to cachedFeatures. If it is not able
* it will return false and the cachedFeatures state is undefined.
* and set the iterator to a valid iterator over the cached features. If it is not able
* it will return false.
*
* @param featureIterator A reference to a {@link QgsFeatureIterator}. A valid featureIterator will
* be assigned in case this index is able to answer the request and the return
Expand Down
32 changes: 28 additions & 4 deletions src/core/qgscacheindexfeatureid.cpp
Expand Up @@ -42,12 +42,36 @@ void QgsCacheIndexFeatureId::requestCompleted( const QgsFeatureRequest& featureR

bool QgsCacheIndexFeatureId::getCacheIterator( QgsFeatureIterator &featureIterator, const QgsFeatureRequest &featureRequest )
{
if ( featureRequest.filterType() == QgsFeatureRequest::FilterFid )
switch ( featureRequest.filterType() )
{
if ( C->isFidCached( featureRequest.filterFid() ) )
case QgsFeatureRequest::FilterFid:
{
featureIterator = QgsFeatureIterator( new QgsCachedFeatureIterator( C, featureRequest ) );
return true;
if ( C->isFidCached( featureRequest.filterFid() ) )
{
featureIterator = QgsFeatureIterator( new QgsCachedFeatureIterator( C, featureRequest ) );
return true;
}
break;
}
case QgsFeatureRequest::FilterFids:
{
if ( C->cachedFeatureIds().contains( featureRequest.filterFids() ) )
{
featureIterator = QgsFeatureIterator( new QgsCachedFeatureIterator( C, featureRequest ) );
return true;
}
break;
}
case QgsFeatureRequest::FilterNone:
case QgsFeatureRequest::FilterRect:
case QgsFeatureRequest::FilterExpression:
{
if ( C->hasFullCache() )
{
featureIterator = QgsFeatureIterator( new QgsCachedFeatureIterator( C, featureRequest ) );
return true;
}
break;
}
}

Expand Down
34 changes: 0 additions & 34 deletions src/core/qgscacheindexfeatureid.h
Expand Up @@ -28,43 +28,9 @@ class CORE_EXPORT QgsCacheIndexFeatureId : public QgsAbstractCacheIndex
public:
QgsCacheIndexFeatureId( QgsVectorLayerCache* );

/**
* Is called, whenever a feature is removed from the cache. You should update your indexes, so
* they become invalid in case this feature was required to successfuly answer a request.
*/
virtual void flushFeature( const QgsFeatureId fid ) override;

/**
* Sometimes, the whole cache changes its state and its easier to just withdraw everything.
* In this case, this method is issued. Be sure to clear all cache information in here.
*/
virtual void flush() override;

/**
* @brief
* Implement this method to update the the indices, in case you need information contained by the request
* to properly index. (E.g. spatial index)
* Does nothing by default
*
* @param featureRequest The feature request that was answered
* @param fids The feature ids that have been returned
*/
virtual void requestCompleted( const QgsFeatureRequest& featureRequest, const QgsFeatureIds& fids ) override;

/**
* Is called, when a feature request is issued on a cached layer.
* If this cache index is able to completely answer the feature request, it will return true
* and write the list of feature ids of cached features to cachedFeatures. If it is not able
* it will return false and the cachedFeatures state is undefined.
*
* @param featureIterator A reference to a {@link QgsFeatureIterator}. A valid featureIterator will
* be assigned in case this index is able to answer the request and the return
* value is true.
* @param featureRequest The feature request, for which this index is queried.
*
* @return True, if this index holds the information to answer the request.
*
*/
virtual bool getCacheIterator( QgsFeatureIterator& featureIterator, const QgsFeatureRequest& featureRequest ) override;

private:
Expand Down
67 changes: 56 additions & 11 deletions src/core/qgsvectorlayercache.cpp
Expand Up @@ -174,12 +174,16 @@ QgsVectorLayer* QgsVectorLayerCache::layer()
void QgsVectorLayerCache::requestCompleted( const QgsFeatureRequest& featureRequest, const QgsFeatureIds& fids )
{
// If a request is too large for the cache don't notify to prevent from indexing incomplete requests
if ( fids.count() < mCache.size() )
if ( fids.count() <= mCache.size() )
{
Q_FOREACH ( QgsAbstractCacheIndex* idx, mCacheIndices )
{
idx->requestCompleted( featureRequest, fids );
}
if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone )
{
mFullCache = true;
}
}
}

Expand Down Expand Up @@ -266,6 +270,54 @@ void QgsVectorLayerCache::invalidate()
emit invalidated();
}

bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator& it )
{
// check first for available indices
Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
{
if ( idx->getCacheIterator( it, featureRequest ) )
{
return true;
}
}

// no indexes available, but maybe we have already cached all required features anyway?
switch ( featureRequest.filterType() )
{
case QgsFeatureRequest::FilterFid:
{
if ( mCache.contains( featureRequest.filterFid() ) )
{
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
return true;
}
break;
}
case QgsFeatureRequest::FilterFids:
{
if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
{
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
return true;
}
break;
}
case QgsFeatureRequest::FilterNone:
case QgsFeatureRequest::FilterRect:
case QgsFeatureRequest::FilterExpression:
{
if ( mFullCache )
{
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
return true;
}
break;
}

}
return false;
}

QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &featureRequest )
{
QgsFeatureIterator it;
Expand All @@ -281,15 +333,8 @@ QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &fe
}
else
{
// Check if an index is able to deliver the requested features
Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
{
if ( idx->getCacheIterator( it, featureRequest ) )
{
requiresWriterIt = false;
break;
}
}
// may still be able to satisfy request using cache
requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
}
}
else
Expand Down Expand Up @@ -319,7 +364,7 @@ QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &fe
return it;
}

bool QgsVectorLayerCache::isFidCached( const QgsFeatureId fid )
bool QgsVectorLayerCache::isFidCached( const QgsFeatureId fid ) const
{
return mCache.contains( fid );
}
Expand Down
29 changes: 28 additions & 1 deletion src/core/qgsvectorlayercache.h
Expand Up @@ -133,9 +133,18 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
* be used for slow data sources, be aware, that the call to this method might take a long time.
*
* @param fullCache True: enable full caching, False: disable full caching
* @see hasFullCache()
*/
void setFullCache( bool fullCache );

/** Returns true if the cache is complete, ie it contains all features. This may happen as
* a result of a call to setFullCache() or by through a feature request which resulted in
* all available features being cached.
* @see setFullCache()
* @note added in QGIS 3.0
*/
bool hasFullCache() const { return mFullCache; }

/**
* @brief
* Adds a {@link QgsAbstractCacheIndex} to this cache. Cache indices know about features present
Expand Down Expand Up @@ -196,8 +205,15 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
* Check if a certain feature id is cached.
* @param fid The feature id to look for
* @return True if this id is in the cache
* @see cachedFeatureIds()
*/
bool isFidCached( const QgsFeatureId fid ) const;

/** Returns the set of feature IDs for features which are cached.
* @note added in QGIS 3.0
* @see isFidCached()
*/
bool isFidCached( const QgsFeatureId fid );
QgsFeatureIds cachedFeatureIds() const { return mCache.keys().toSet(); }

/**
* Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features
Expand Down Expand Up @@ -329,5 +345,16 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
friend class QgsCachedFeatureIterator;
friend class QgsCachedFeatureWriterIterator;
friend class QgsCachedFeature;

/** Returns true if the cache contains all the features required for a specified request.
* @param featureRequest feature request
* @param it will be set to iterator for matching features
* @returns true if cache can satisfy request
* @note this method only checks for available features, not whether the cache
* contains required attributes or geometry. For that, use checkInformationCovered()
*/
bool canUseCacheForRequest( const QgsFeatureRequest& featureRequest, QgsFeatureIterator& it );

friend class TestVectorLayerCache;
};
#endif // QgsVectorLayerCache_H
6 changes: 0 additions & 6 deletions src/gui/attributetable/qgsattributetablefiltermodel.cpp
Expand Up @@ -294,11 +294,6 @@ void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
disconnect( mCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentsChanged() ) );
}

if ( filterMode == ShowSelected )
{
generateListOfVisibleFeatures();
}

mFilterMode = filterMode;
invalidateFilter();
}
Expand Down Expand Up @@ -359,7 +354,6 @@ void QgsAttributeTableFilterModel::selectionChanged()
{
if ( ShowSelected == mFilterMode )
{
generateListOfVisibleFeatures();
invalidateFilter();
}
else if ( mSelectedOnTop )
Expand Down
2 changes: 0 additions & 2 deletions src/gui/attributetable/qgsdualview.cpp
Expand Up @@ -706,9 +706,7 @@ void QgsDualView::progress( int i, bool& cancel )
mProgressDlg->show();
}

mProgressDlg->setValue( i );
mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );

QCoreApplication::processEvents();

cancel = mProgressDlg && mProgressDlg->wasCanceled();
Expand Down
2 changes: 1 addition & 1 deletion src/ui/qgsoptionsbase.ui
Expand Up @@ -1530,7 +1530,7 @@
<number>0</number>
</property>
<property name="maximum">
<number>100000</number>
<number>10000000</number>
</property>
<property name="singleStep">
<number>1000</number>
Expand Down

0 comments on commit 6727ea7

Please sign in to comment.