diff --git a/src/core/qgsfeatureiterator.h b/src/core/qgsfeatureiterator.h index eb1a82f286a..6515beca8f1 100644 --- a/src/core/qgsfeatureiterator.h +++ b/src/core/qgsfeatureiterator.h @@ -58,6 +58,34 @@ class CORE_EXPORT QgsAbstractFeatureIterator }; + +/** helper template that cares of two things: 1. automatic deletion of source if owned by iterator, 2. notification of open/closed iterator */ +template +class QgsAbstractFeatureIteratorFromSource : public QgsAbstractFeatureIterator +{ +public: + QgsAbstractFeatureIteratorFromSource( T* source, bool ownSource, const QgsFeatureRequest& request ) + : QgsAbstractFeatureIterator( request ), mSource( source ), mOwnSource( ownSource ) + { + mSource->iteratorOpened( this ); + } + + ~QgsAbstractFeatureIteratorFromSource() + { + if ( mOwnSource ) + delete mSource; + } + +protected: + //! to be called by from subclass in close() + void iteratorClosed() { mSource->iteratorClosed( this ); } + + T* mSource; + bool mOwnSource; +}; + + + /** * \ingroup core * Wrapper for iterator of features from vector data provider or vector layer diff --git a/src/core/qgsfeaturerequest.cpp b/src/core/qgsfeaturerequest.cpp index 134e7623f24..7a8a21c2dd8 100644 --- a/src/core/qgsfeaturerequest.cpp +++ b/src/core/qgsfeaturerequest.cpp @@ -168,3 +168,28 @@ bool QgsFeatureRequest::acceptFeature( const QgsFeature& feature ) return true; } + +#include "qgsfeatureiterator.h" +#include "qgslogger.h" + +QgsAbstractFeatureSource::~QgsAbstractFeatureSource() +{ + while ( !mActiveIterators.empty() ) + { + QgsAbstractFeatureIterator *it = *mActiveIterators.begin(); + QgsDebugMsg( "closing active iterator" ); + it->close(); + } +} + +void QgsAbstractFeatureSource::iteratorOpened( QgsAbstractFeatureIterator* it ) +{ + mActiveIterators.insert( it ); +} + +void QgsAbstractFeatureSource::iteratorClosed( QgsAbstractFeatureIterator* it ) +{ + mActiveIterators.remove( it ); +} + + diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h index b99dfdbbb37..847d29057dd 100644 --- a/src/core/qgsfeaturerequest.h +++ b/src/core/qgsfeaturerequest.h @@ -146,4 +146,24 @@ class CORE_EXPORT QgsFeatureRequest Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags ) +class QgsFeatureIterator; +class QgsAbstractFeatureIterator; + +/** base class that can be used for any class that is capable of returning features */ +class QgsAbstractFeatureSource +{ +public: + virtual ~QgsAbstractFeatureSource(); + + virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ) = 0; + +protected: + void iteratorOpened( QgsAbstractFeatureIterator* it ); + void iteratorClosed( QgsAbstractFeatureIterator* it ); + + QSet< QgsAbstractFeatureIterator* > mActiveIterators; + + template friend class QgsAbstractFeatureIteratorFromSource; +}; + #endif // QGSFEATUREREQUEST_H diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 5b53bcb003b..2cffb456ac8 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -180,3 +180,11 @@ int QgsFields::fieldNameIndex( const QString& fieldName ) const } return -1; } + +QgsAttributeList QgsFields::allAttributesList() const +{ + QgsAttributeList lst; + for ( int i = 0; i < mFields.count(); ++i ) + lst.append( i ); + return lst; +} diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index 1b2a7956eca..8ecacd5cf56 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -20,6 +20,8 @@ #include #include +typedef QList QgsAttributeList; + /** \ingroup core * Encapsulate a field in an attribute table or data source. * QgsField stores metadata about an attribute field, including name, type @@ -221,6 +223,10 @@ class CORE_EXPORT QgsFields //! @note added in 2.1 int fieldNameIndex( const QString& fieldName ) const; + //! Utility function to get list of attribute indexes + //! @note added in 2.1 + QgsAttributeList allAttributesList() const; + //! Utility function to return a list of QgsField instances QList toList() const; diff --git a/src/core/qgsmaprendererjob.cpp b/src/core/qgsmaprendererjob.cpp index ee46c4ef6af..f3b4a809c09 100644 --- a/src/core/qgsmaprendererjob.cpp +++ b/src/core/qgsmaprendererjob.cpp @@ -183,7 +183,8 @@ void QgsMapRendererCustomPainterJob::start() QPaintDevice* thePaintDevice = mPainter->device(); - Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", "pre-set DPI not equal to painter's DPI" ); + QString errMsg = QString("pre-set DPI not equal to painter's DPI (%1 vs %2)").arg(thePaintDevice->logicalDpiX()).arg(mSettings.outputDpi()); + Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", errMsg.toAscii().data() ); delete mLabelingEngine; mLabelingEngine = 0; diff --git a/src/core/qgsmapsettings.cpp b/src/core/qgsmapsettings.cpp index 47c590f17f2..748fdb8665d 100644 --- a/src/core/qgsmapsettings.cpp +++ b/src/core/qgsmapsettings.cpp @@ -131,6 +131,7 @@ void QgsMapSettings::updateDerived() QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / mMapUnitsPerPixel ) ).arg( qgsDoubleToString( mVisibleExtent.height() / mMapUnitsPerPixel ) ) ); QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mScale ) ) ); + mValid = true; } diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index 3ec4cba39e9..fcc9b2a5fb5 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -228,13 +228,7 @@ QMap QgsVectorDataProvider::fieldNameMap() const QgsAttributeList QgsVectorDataProvider::attributeIndexes() { - int count = fields().count(); - QgsAttributeList list; - - for ( int i = 0; i < count; i++ ) - list.append( i ); - - return list; + return fields().allAttributesList(); } const QList< QgsVectorDataProvider::NativeType > &QgsVectorDataProvider::nativeTypes() const diff --git a/src/core/qgsvectordataprovider.h b/src/core/qgsvectordataprovider.h index 00ec22287ca..025418b080e 100644 --- a/src/core/qgsvectordataprovider.h +++ b/src/core/qgsvectordataprovider.h @@ -103,6 +103,24 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider */ virtual ~QgsVectorDataProvider(); + /** + * Return feature source object that can be used for querying provider's data. The returned feature source + * is independent from provider - any changes to provider's state (e.g. change of subset string) will not be + * reflected in the feature source, therefore it can be safely used for processing in background without + * having to care about possible changes within provider that may happen concurrently. Also, even in the case + * of provider being deleted, any feature source obtained from the provider will be kept alive and working + * (they are independent and owned by the caller). + * + * Sometimes there are cases when some data needs to be shared between vector data provider and its feature source. + * In such cases, the implementation must ensure that the data is not susceptible to run condition. For example, + * if it is possible that both feature source and provider may need reading/writing to some shared data at the + * same time, some synchronization mechanisms must be used (e.g. mutexes) to prevent data corruption. + * + * @note added in 2.1 + * @return new instance of QgsAbstractFeatureSource (caller is responsible for deleting it) + */ + virtual QgsAbstractFeatureSource* featureSource() const { Q_ASSERT(0 && "All providers must support featureSource()"); return 0; } + /** * Returns the permanent storage type for this layer as a friendly name. */ @@ -342,9 +360,9 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider */ virtual bool isSaveAndLoadStyleToDBSupported() { return false; } - protected: - QVariant convertValue( QVariant::Type type, QString value ); + static QVariant convertValue( QVariant::Type type, QString value ); +protected: void clearMinMaxCache(); void fillMinMaxCache(); diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index aeef95847e6..546b04b8cc7 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -906,7 +906,7 @@ QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request if ( !mDataProvider ) return QgsFeatureIterator(); - return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, request ) ); + return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( new QgsVectorLayerFeatureSource( this ), true, request ) ); } @@ -2270,10 +2270,7 @@ const QgsFields &QgsVectorLayer::pendingFields() const QgsAttributeList QgsVectorLayer::pendingAllAttributesList() { - QgsAttributeList lst; - for ( int i = 0; i < mUpdatedFields.count(); ++i ) - lst.append( i ); - return lst; + return mUpdatedFields.allAttributesList(); } QgsAttributeList QgsVectorLayer::pendingPkAttributesList() diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 2e0db71e8cc..87c0bf7b493 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1668,7 +1668,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer // Feature counts for each renderer symbol QMap mSymbolFeatureCountMap; - friend class QgsVectorLayerFeatureIterator; + friend class QgsVectorLayerFeatureSource; }; #endif diff --git a/src/core/qgsvectorlayerfeatureiterator.cpp b/src/core/qgsvectorlayerfeatureiterator.cpp index 14779883f05..a2f6947b0dc 100644 --- a/src/core/qgsvectorlayerfeatureiterator.cpp +++ b/src/core/qgsvectorlayerfeatureiterator.cpp @@ -20,17 +20,15 @@ #include "qgsvectorlayereditbuffer.h" #include "qgsvectorlayerjoinbuffer.h" -QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request ) - : QgsAbstractFeatureIterator( request ) + +QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource(QgsVectorLayer *layer) { - mProvider = layer->dataProvider(); + mProviderFeatureSource = layer->dataProvider()->featureSource(); mFields = layer->pendingFields(); - mAllAttributesList = layer->pendingAllAttributesList(); mJoinBuffer = new QgsVectorLayerJoinBuffer( *layer->mJoinBuffer ); - mChangedFeaturesRequest = mRequest; - - if ( layer->editBuffer() ) + mHasEditBuffer = layer->editBuffer(); + if ( mHasEditBuffer ) { mAddedFeatures = QgsFeatureMap( layer->editBuffer()->addedFeatures() ); mChangedGeometries = QgsGeometryMap( layer->editBuffer()->changedGeometries() ); @@ -38,11 +36,34 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* la mChangedAttributeValues = QgsChangedAttributesMap( layer->editBuffer()->changedAttributeValues() ); mAddedAttributes = QList( layer->editBuffer()->addedAttributes() ); mDeletedAttributeIds = QgsAttributeList( layer->editBuffer()->deletedAttributeIds() ); - mChangedFeaturesRequest.setFilterFids( layer->editBuffer()->changedAttributeValues().keys().toSet() ); + } +} + +QgsVectorLayerFeatureSource::~QgsVectorLayerFeatureSource() +{ + delete mJoinBuffer; + delete mProviderFeatureSource; +} + +QgsFeatureIterator QgsVectorLayerFeatureSource::getFeatures( const QgsFeatureRequest& request ) +{ + // return feature iterator that does not own this source + return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) ); +} + + +QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator(QgsVectorLayerFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) + : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) +{ + mChangedFeaturesRequest = mRequest; + + if ( mSource->mHasEditBuffer ) + { + mChangedFeaturesRequest.setFilterFids( mSource->mChangedAttributeValues.keys().toSet() ); } // prepare joins: may add more attributes to fetch (in order to allow join) - if ( mJoinBuffer->containsJoins() ) + if ( mSource->mJoinBuffer->containsJoins() ) prepareJoins(); // by default provider's request is the same @@ -53,13 +74,13 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* la // prepare list of attributes to match provider fields QgsAttributeList providerSubset; QgsAttributeList subset = mProviderRequest.subsetOfAttributes(); - int nPendingFields = mFields.count(); + int nPendingFields = mSource->mFields.count(); for ( int i = 0; i < subset.count(); ++i ) { int attrIndex = subset[i]; if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue; - if ( mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider ) - providerSubset << mFields.fieldOriginIndex( attrIndex ); + if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider ) + providerSubset << mSource->mFields.fieldOriginIndex( attrIndex ); } mProviderRequest.setSubsetOfAttributes( providerSubset ); } @@ -70,13 +91,13 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* la } else // no filter or filter by rect { - if ( layer->editBuffer() ) + if ( mSource->mHasEditBuffer ) { - mChangedFeaturesIterator = layer->dataProvider()->getFeatures( mChangedFeaturesRequest ); + mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest ); } else { - mProviderIterator = layer->dataProvider()->getFeatures( mProviderRequest ); + mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest ); } rewindEditBuffer(); @@ -84,7 +105,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* la if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { - mRequest.filterExpression()->prepare( mFields ); + mRequest.filterExpression()->prepare( mSource->mFields ); } } @@ -92,8 +113,6 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* la QgsVectorLayerFeatureIterator::~QgsVectorLayerFeatureIterator() { close(); - - delete mJoinBuffer; } @@ -139,7 +158,7 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f ) if ( mProviderIterator.isClosed() ) { mChangedFeaturesIterator.close(); - mProviderIterator = mProvider->getFeatures( mProviderRequest ); + mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest ); } while ( mProviderIterator.nextFeature( f ) ) @@ -148,7 +167,7 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f ) continue; // TODO[MD]: just one resize of attributes - f.setFields( &mFields ); + f.setFields( &mSource->mFields ); // update attributes updateChangedAttributes( f ); @@ -196,6 +215,8 @@ bool QgsVectorLayerFeatureIterator::close() mProviderIterator.close(); + iteratorClosed(); + mClosed = true; return true; } @@ -205,7 +226,7 @@ bool QgsVectorLayerFeatureIterator::close() bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f ) { - while ( mFetchAddedFeaturesIt-- != mAddedFeatures.constBegin() ) + while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() ) { QgsFeatureId fid = mFetchAddedFeaturesIt->id(); @@ -222,7 +243,7 @@ bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f ) return true; } - mFetchAddedFeaturesIt = mAddedFeatures.constBegin(); + mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin(); return false; // no more added features } @@ -231,7 +252,7 @@ void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsF { f.setFeatureId( src.id() ); f.setValid( true ); - f.setFields( &mFields ); + f.setFields( &mSource->mFields ); if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ) f.setGeometry( *src.geometry() ); @@ -249,7 +270,7 @@ void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsF bool QgsVectorLayerFeatureIterator::fetchNextChangedGeomFeature( QgsFeature& f ) { // check if changed geometries are in rectangle - for ( ; mFetchChangedGeomIt != mChangedGeometries.constEnd(); mFetchChangedGeomIt++ ) + for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ ) { QgsFeatureId fid = mFetchChangedGeomIt.key(); @@ -302,7 +323,7 @@ void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid { f.setFeatureId( fid ); f.setValid( true ); - f.setFields( &mFields ); + f.setFields( &mSource->mFields ); if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ) f.setGeometry( geom ); @@ -319,7 +340,7 @@ void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid { request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() ); } - QgsFeatureIterator fi = mProvider->getFeatures( request ); + QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request ); if ( fi.nextFeature( tmp ) ) { updateChangedAttributes( tmp ); @@ -335,28 +356,28 @@ void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid void QgsVectorLayerFeatureIterator::rewindEditBuffer() { - mFetchConsidered = mDeletedFeatureIds; + mFetchConsidered = mSource->mDeletedFeatureIds; - mFetchAddedFeaturesIt = mAddedFeatures.constEnd(); - mFetchChangedGeomIt = mChangedGeometries.constBegin(); + mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd(); + mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin(); } void QgsVectorLayerFeatureIterator::prepareJoins() { - QgsAttributeList fetchAttributes = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mAllAttributesList; + QgsAttributeList fetchAttributes = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working mFetchJoinInfo.clear(); for ( QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin(); attIt != fetchAttributes.constEnd(); ++attIt ) { - if ( mFields.fieldOrigin( *attIt ) != QgsFields::OriginJoin ) + if ( mSource->mFields.fieldOrigin( *attIt ) != QgsFields::OriginJoin ) continue; int sourceLayerIndex; - const QgsVectorJoinInfo* joinInfo = mJoinBuffer->joinForFieldIndex( *attIt, mFields, sourceLayerIndex ); + const QgsVectorJoinInfo* joinInfo = mSource->mJoinBuffer->joinForFieldIndex( *attIt, mSource->mFields, sourceLayerIndex ); Q_ASSERT( joinInfo ); QgsVectorLayer* joinLayer = qobject_cast( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) ); @@ -371,7 +392,7 @@ void QgsVectorLayerFeatureIterator::prepareJoins() if ( joinInfo->targetFieldName.isEmpty() ) info.targetField = joinInfo->targetFieldIndex; //for compatibility with 1.x else - info.targetField = mFields.indexFromName( joinInfo->targetFieldName ); + info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName ); if ( joinInfo->joinFieldName.isEmpty() ) info.joinField = joinInfo->joinFieldIndex; //for compatibility with 1.x @@ -402,7 +423,7 @@ void QgsVectorLayerFeatureIterator::prepareJoins() void QgsVectorLayerFeatureIterator::addJoinedAttributes( QgsFeature &f ) { // make sure we have space for newly added attributes - f.attributes().resize( mFields.count() ); // f.attributes().count() + mJoinedAttributesCount ); + f.attributes().resize( mSource->mFields.count() ); // f.attributes().count() + mJoinedAttributesCount ); QMap::const_iterator joinIt = mFetchJoinInfo.constBegin(); for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt ) @@ -525,18 +546,18 @@ bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature& f ) QgsFeatureId featureId = mRequest.filterFid(); // deleted already? - if ( mDeletedFeatureIds.contains( featureId ) ) + if ( mSource->mDeletedFeatureIds.contains( featureId ) ) return false; // has changed geometry? - if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mChangedGeometries.contains( featureId ) ) + if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) ) { - useChangedAttributeFeature( featureId, mChangedGeometries[featureId], f ); + useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f ); return true; } // added features - for ( QgsFeatureMap::ConstIterator iter = mAddedFeatures.constBegin(); iter != mAddedFeatures.constEnd(); ++iter ) + for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter ) { if ( iter->id() == featureId ) { @@ -546,7 +567,7 @@ bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature& f ) } // regular features - QgsFeatureIterator fi = mProvider->getFeatures( mProviderRequest ); + QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest ); if ( fi.nextFeature( f ) ) { updateChangedAttributes( f ); @@ -565,18 +586,18 @@ 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 ) + for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx ) { - attrs.remove( mDeletedAttributeIds[idx] ); + attrs.remove( mSource->mDeletedAttributeIds[idx] ); } // adjust size to accommodate added attributes - attrs.resize( attrs.count() + mAddedAttributes.count() ); + attrs.resize( attrs.count() + mSource->mAddedAttributes.count() ); // update changed attributes - if ( mChangedAttributeValues.contains( f.id() ) ) + if ( mSource->mChangedAttributeValues.contains( f.id() ) ) { - const QgsAttributeMap &map = mChangedAttributeValues[f.id()]; + const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()]; for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); it++ ) attrs[it.key()] = it.value(); } @@ -584,6 +605,7 @@ void QgsVectorLayerFeatureIterator::updateChangedAttributes( QgsFeature &f ) void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f ) { - if ( mChangedGeometries.contains( f.id() ) ) - f.setGeometry( mChangedGeometries[f.id()] ); + if ( mSource->mChangedGeometries.contains( f.id() ) ) + f.setGeometry( mSource->mChangedGeometries[f.id()] ); } + diff --git a/src/core/qgsvectorlayerfeatureiterator.h b/src/core/qgsvectorlayerfeatureiterator.h index 7d83936f6b9..686b8c7eec8 100644 --- a/src/core/qgsvectorlayerfeatureiterator.h +++ b/src/core/qgsvectorlayerfeatureiterator.h @@ -26,10 +26,46 @@ class QgsVectorLayerEditBuffer; struct QgsVectorJoinInfo; class QgsVectorLayerJoinBuffer; -class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureIterator + +class QgsVectorLayerFeatureIterator; + +/** Partial snapshot of vector layer's state (only the members necessary for access to features) */ +class QgsVectorLayerFeatureSource : public QgsAbstractFeatureSource +{ +public: + QgsVectorLayerFeatureSource( QgsVectorLayer* layer ); + ~QgsVectorLayerFeatureSource(); + + virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ); + + friend class QgsVectorLayerFeatureIterator; + +protected: + + QgsAbstractFeatureSource* mProviderFeatureSource; + + QgsVectorLayerJoinBuffer* mJoinBuffer; + + QgsFields mFields; + + bool mHasEditBuffer; + + // 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 mAddedAttributes; + QgsChangedAttributesMap mChangedAttributeValues; + QgsAttributeList mDeletedAttributeIds; +}; + + +class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureIteratorFromSource { public: - QgsVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request ); + QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ); ~QgsVectorLayerFeatureIterator(); @@ -47,25 +83,12 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera //! while for others filtering is left to the provider implementation. inline virtual bool nextFeatureFilterExpression( QgsFeature &f ) { return fetchFeature( f ); } - QgsVectorDataProvider* mProvider; - QgsFields mFields; - QgsAttributeList mAllAttributesList; - - QgsVectorLayerJoinBuffer* mJoinBuffer; QgsFeatureRequest mProviderRequest; QgsFeatureIterator mProviderIterator; QgsFeatureRequest mChangedFeaturesRequest; QgsFeatureIterator mChangedFeaturesIterator; -#if 0 - // general stuff - bool mFetching; - QgsRectangle mFetchRect; - QgsAttributeList mFetchAttributes; - QgsAttributeList mFetchProvAttributes; - bool mFetchGeometry; -#endif // only related to editing QSet mFetchConsidered; @@ -106,16 +129,6 @@ 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 mAddedAttributes; - QgsChangedAttributesMap mChangedAttributeValues; - QgsAttributeList mDeletedAttributeIds; - /** Informations about joins used in the current select() statement. Allows faster mapping of attribute ids compared to mVectorJoins */ QMap mFetchJoinInfo; diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index f01c6e3aa4a..e138030f908 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -11,6 +11,7 @@ #include "qgssymbollayerv2.h" #include "qgssymbolv2.h" #include "qgsvectorlayer.h" +#include "qgsvectorlayerfeatureiterator.h" #include @@ -28,6 +29,8 @@ QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRender , mDiagrams( false ) , mLayerTransparency( 0 ) { + mSource = new QgsVectorLayerFeatureSource( layer ); + mRendererV2 = layer->rendererV2() ? layer->rendererV2()->clone() : 0; mSelectedFeatureIds = layer->selectedFeaturesIds(); @@ -82,9 +85,6 @@ QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRender prepareLabeling( layer, attrNames ); prepareDiagrams( layer, attrNames ); - mFit = layer->getFeatures( QgsFeatureRequest() - .setFilterRect( mContext.extent() ) - .setSubsetOfAttributes( attrNames, mFields ) ); } @@ -92,6 +92,7 @@ QgsVectorLayerRenderer::~QgsVectorLayerRenderer() { delete mRendererV2; delete mCache; + delete mSource; } @@ -116,11 +117,15 @@ bool QgsVectorLayerRenderer::render() mRendererV2->startRender( mContext, mFields ); + QgsFeatureIterator fit = mSource->getFeatures( QgsFeatureRequest() + .setFilterRect( mContext.extent() ) + .setSubsetOfAttributes( mRendererV2->usedAttributes(), mFields ) ); + if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() ) - drawRendererV2Levels(); + drawRendererV2Levels( fit ); else - drawRendererV2(); + drawRendererV2( fit ); //apply layer transparency for vector layers if ( mContext.useAdvancedEffects() && mLayerTransparency != 0 ) @@ -139,10 +144,10 @@ bool QgsVectorLayerRenderer::render() -void QgsVectorLayerRenderer::drawRendererV2() +void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit ) { QgsFeature fet; - while ( mFit.nextFeature( fet ) ) + while ( fit.nextFeature( fet ) ) { try { @@ -192,7 +197,7 @@ void QgsVectorLayerRenderer::drawRendererV2() stopRendererV2( NULL ); } -void QgsVectorLayerRenderer::drawRendererV2Levels() +void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit ) { QHash< QgsSymbolV2*, QList > features; // key = symbol, value = array of features @@ -207,7 +212,7 @@ void QgsVectorLayerRenderer::drawRendererV2Levels() // 1. fetch features QgsFeature fet; - while ( mFit.nextFeature( fet ) ) + while ( fit.nextFeature( fet ) ) { if ( !fet.geometry() ) continue; // skip features without geometry diff --git a/src/core/qgsvectorlayerrenderer.h b/src/core/qgsvectorlayerrenderer.h index ab16c9c37e2..2366e4eb6e6 100644 --- a/src/core/qgsvectorlayerrenderer.h +++ b/src/core/qgsvectorlayerrenderer.h @@ -4,6 +4,7 @@ class QgsFeatureRendererV2; class QgsRenderContext; class QgsVectorLayer; +class QgsVectorLayerFeatureSource; class QgsDiagramRendererV2; class QgsDiagramLayerSettings; @@ -24,6 +25,7 @@ typedef QList QgsAttributeList; #include "qgsmaplayerrenderer.h" + class QgsVectorLayerRenderer : public QgsMapLayerRenderer { public: @@ -42,11 +44,11 @@ private: /** Draw layer with renderer V2. QgsFeatureRenderer::startRender() needs to be called before using this method */ - void drawRendererV2(); + void drawRendererV2( QgsFeatureIterator& fit ); /** Draw layer with renderer V2 using symbol levels. QgsFeatureRenderer::startRender() needs to be called before using this method */ - void drawRendererV2Levels(); + void drawRendererV2Levels( QgsFeatureIterator& fit ); /** Stop version 2 renderer and selected renderer (if required) */ void stopRendererV2( QgsSingleSymbolRendererV2* selRenderer ); @@ -56,11 +58,11 @@ protected: QgsRenderContext& mContext; - QgsFields mFields; + QgsFields mFields; // TODO: use fields from mSource QgsFeatureIds mSelectedFeatureIds; - QgsFeatureIterator mFit; + QgsVectorLayerFeatureSource* mSource; QgsFeatureRendererV2 *mRendererV2; diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 068febc7590..e73c290dc1e 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -432,6 +432,12 @@ QgsMapLayer* QgsMapCanvas::currentLayer() void QgsMapCanvas::refresh() { + if ( !mSettings.hasValidSettings() ) + { + qDebug("CANVAS refresh - invalid settings -> nothing to do"); + return; + } + if ( !mRenderFlag || mFrozen ) // do we really need two flags controlling rendering? { qDebug("CANVAS render flag off"); diff --git a/src/gui/qgsmapoverviewcanvas.cpp b/src/gui/qgsmapoverviewcanvas.cpp index 51a746c3a81..015efc5fa20 100644 --- a/src/gui/qgsmapoverviewcanvas.cpp +++ b/src/gui/qgsmapoverviewcanvas.cpp @@ -255,6 +255,9 @@ void QgsMapOverviewCanvas::updatePanningWidget( const QPoint& pos ) void QgsMapOverviewCanvas::refresh() { + if ( !mSettings.hasValidSettings() ) + return; // makes no sense to render anything + if (mJob) { qDebug("oveview - cancelling old"); diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp index 12639a6d37e..c926d3f0e42 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.cpp +++ b/src/providers/ogr/qgsogrfeatureiterator.cpp @@ -32,33 +32,35 @@ // - mEncoding -QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request ) - : QgsAbstractFeatureIterator( request ) - , P( p ) +QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) + : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) , ogrDataSource( 0 ) , ogrLayer( 0 ) , mSubsetStringSet( false ) { mFeatureFetched = false; - ogrDataSource = OGROpen( TO8F( P->filePath() ), false, NULL ); + ogrDataSource = OGROpen( TO8F( mSource->mFilePath ), false, NULL ); - if ( P->layerName().isNull() ) + if ( mSource->mLayerName.isNull() ) { - ogrLayer = OGR_DS_GetLayer( ogrDataSource, P->layerIndex() ); + ogrLayer = OGR_DS_GetLayer( ogrDataSource, mSource->mLayerIndex ); } else { - ogrLayer = OGR_DS_GetLayerByName( ogrDataSource, TO8( p->layerName() ) ); + ogrLayer = OGR_DS_GetLayerByName( ogrDataSource, TO8( mSource->mLayerName ) ); } - if ( !P->subsetString().isEmpty() ) + if ( !mSource->mSubsetString.isEmpty() ) { - ogrLayer = P->setSubsetString( ogrLayer, ogrDataSource ); + ogrLayer = QgsOgrUtils::setSubsetString( ogrLayer, ogrDataSource, mSource->mEncoding, mSource->mSubsetString ); mSubsetStringSet = true; } - ensureRelevantFields(); + // make sure we fetch just relevant fields + mFetchGeometry = ( mRequest.filterType() == QgsFeatureRequest::FilterRect ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); + QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); + QgsOgrUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs ); // spatial query to select features if ( mRequest.filterType() == QgsFeatureRequest::FilterRect ) @@ -87,14 +89,6 @@ QgsOgrFeatureIterator::~QgsOgrFeatureIterator() close(); } -void QgsOgrFeatureIterator::ensureRelevantFields() -{ - mFetchGeometry = ( mRequest.filterType() == QgsFeatureRequest::FilterRect ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); - QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : P->attributeIndexes(); - P->setRelevantFields( mFetchGeometry, attrs ); - P->mRelevantFieldsForNextFeature = true; -} - bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature ) { @@ -103,9 +97,6 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature ) if ( mClosed ) return false; - if ( !P->mRelevantFieldsForNextFeature ) - ensureRelevantFields(); - if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) { OGRFeatureH fet = OGR_L_GetFeature( ogrLayer, FID_TO_NUMBER( mRequest.filterFid() ) ); @@ -158,7 +149,7 @@ bool QgsOgrFeatureIterator::close() if ( mClosed ) return false; - P->mActiveIterators.remove( this ); + iteratorClosed(); if ( mSubsetStringSet ) { @@ -187,9 +178,9 @@ void QgsOgrFeatureIterator::getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature if ( OGR_F_IsFieldSet( ogrFet, attindex ) ) { - switch ( P->mAttributeFields[attindex].type() ) + switch ( mSource->mFields[attindex].type() ) { - case QVariant::String: value = QVariant( P->mEncoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attindex ) ) ); break; + case QVariant::String: value = QVariant( mSource->mEncoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attindex ) ) ); break; case QVariant::Int: value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attindex ) ); break; case QVariant::Double: value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attindex ) ); break; case QVariant::Date: @@ -198,7 +189,7 @@ void QgsOgrFeatureIterator::getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature int year, month, day, hour, minute, second, tzf; OGR_F_GetFieldAsDateTime( ogrFet, attindex, &year, &month, &day, &hour, &minute, &second, &tzf ); - if ( P->mAttributeFields[attindex].type() == QVariant::Date ) + if ( mSource->mFields[attindex].type() == QVariant::Date ) value = QDate( year, month, day ); else value = QDateTime( QDate( year, month, day ), QTime( hour, minute, second ) ); @@ -220,11 +211,11 @@ void QgsOgrFeatureIterator::getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature ) { feature.setFeatureId( OGR_F_GetFID( fet ) ); - feature.initAttributes( P->fields().count() ); - feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups + feature.initAttributes( mSource->mFields.count() ); + feature.setFields( &mSource->mFields ); // allow name-based attribute lookups bool useIntersect = mRequest.flags() & QgsFeatureRequest::ExactIntersect; - bool geometryTypeFilter = P->mOgrGeometryTypeFilter != wkbUnknown; + bool geometryTypeFilter = mSource->mOgrGeometryTypeFilter != wkbUnknown; if ( mFetchGeometry || useIntersect || geometryTypeFilter ) { OGRGeometryH geom = OGR_F_GetGeometryRef( fet ); @@ -238,7 +229,7 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature ) feature.setGeometryAndOwnership( wkb, OGR_G_WkbSize( geom ) ); } if (( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) ) - || ( geometryTypeFilter && ( !feature.geometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry()->wkbType() ) != P->mOgrGeometryTypeFilter ) ) ) + || ( geometryTypeFilter && ( !feature.geometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry()->wkbType() ) != mSource->mOgrGeometryTypeFilter ) ) ) { OGR_F_Destroy( fet ); return false; @@ -262,7 +253,7 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature ) else { // all attributes - for ( int idx = 0; idx < P->mAttributeFields.count(); ++idx ) + for ( int idx = 0; idx < mSource->mFields.count(); ++idx ) { getFeatureAttribute( fet, feature, idx ); } @@ -270,3 +261,20 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature ) return true; } + + +QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider* p ) +{ + mFilePath = p->filePath(); + mLayerName = p->layerName(); + mLayerIndex = p->layerIndex(); + mSubsetString = p->mSubsetString; + mEncoding = p->mEncoding; // no copying - this is a borrowed pointer from Qt + mFields = p->mAttributeFields; + mOgrGeometryTypeFilter = p->mOgrGeometryTypeFilter; +} + +QgsFeatureIterator QgsOgrFeatureSource::getFeatures( const QgsFeatureRequest& request ) +{ + return QgsFeatureIterator( new QgsOgrFeatureIterator( this, false, request ) ); +} diff --git a/src/providers/ogr/qgsogrfeatureiterator.h b/src/providers/ogr/qgsogrfeatureiterator.h index a8aa2ffde64..eda09cc4c3e 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.h +++ b/src/providers/ogr/qgsogrfeatureiterator.h @@ -19,12 +19,32 @@ #include +class QgsOgrFeatureIterator; class QgsOgrProvider; -class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator +class QgsOgrFeatureSource : public QgsAbstractFeatureSource +{ +public: + QgsOgrFeatureSource( const QgsOgrProvider* p ); + + virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ); + +protected: + QString mFilePath; + QString mLayerName; + int mLayerIndex; + QString mSubsetString; + QTextCodec* mEncoding; + QgsFields mFields; + OGRwkbGeometryType mOgrGeometryTypeFilter; + + friend class QgsOgrFeatureIterator; +}; + +class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource { public: - QgsOgrFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request ); + QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ); ~QgsOgrFeatureIterator(); @@ -38,9 +58,6 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator //! fetch next feature, return true on success virtual bool fetchFeature( QgsFeature& feature ); - QgsOgrProvider* P; - - void ensureRelevantFields(); bool readFeature( OGRFeatureH fet, QgsFeature& feature ); diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 61e1bd02fba..0b4d1aae364 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -370,13 +370,6 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri ) QgsOgrProvider::~QgsOgrProvider() { - while ( !mActiveIterators.empty() ) - { - QgsOgrFeatureIterator *it = *mActiveIterators.begin(); - QgsDebugMsg( "closing active iterator" ); - it->close(); - } - if ( ogrLayer != ogrOrigLayer ) { OGR_DS_ReleaseResultSet( ogrDataSource, ogrLayer ); @@ -392,6 +385,11 @@ QgsOgrProvider::~QgsOgrProvider() } } +QgsAbstractFeatureSource* QgsOgrProvider::featureSource() const +{ + return new QgsOgrFeatureSource( this ); +} + bool QgsOgrProvider::setSubsetString( QString theSQL, bool updateFeatureCount ) { QgsCPLErrorHandler handler; @@ -714,14 +712,21 @@ QString QgsOgrProvider::storageType() const return ogrDriverName; } + void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes ) +{ + QgsOgrUtils::setRelevantFields( ogrLayer, mAttributeFields.count(), fetchGeometry, fetchAttributes ); +} + + +void QgsOgrUtils::setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes ) { #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800 if ( OGR_L_TestCapability( ogrLayer, OLCIgnoreFields ) ) { QVector ignoredFields; OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn( ogrLayer ); - for ( int i = 0; i < mAttributeFields.size(); i++ ) + for ( int i = 0; i < fieldCount; i++ ) { if ( !fetchAttributes.contains( i ) ) { @@ -737,10 +742,6 @@ void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeLi OGR_L_SetIgnoredFields( ogrLayer, ignoredFields.data() ); } - - // mark that relevant fields may not be set appropriately for nextFeature() calls - mRelevantFieldsForNextFeature = false; - #else Q_UNUSED( fetchGeometry ); Q_UNUSED( fetchAttributes ); @@ -749,7 +750,7 @@ void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeLi QgsFeatureIterator QgsOgrProvider::getFeatures( const QgsFeatureRequest& request ) { - return QgsFeatureIterator( new QgsOgrFeatureIterator( this, request ) ); + return QgsFeatureIterator( new QgsOgrFeatureIterator( static_cast( featureSource() ), true, request ) ); } @@ -2316,6 +2317,11 @@ QVariant QgsOgrProvider::maximumValue( int index ) } QByteArray QgsOgrProvider::quotedIdentifier( QByteArray field ) +{ + return QgsOgrUtils::quotedIdentifier( field, ogrDriverName ); +} + +QByteArray QgsOgrUtils::quotedIdentifier( QByteArray field, const QString& ogrDriverName ) { if ( ogrDriverName == "MySQL" ) { @@ -2419,22 +2425,30 @@ OGRwkbGeometryType QgsOgrProvider::ogrWkbSingleFlatten( OGRwkbGeometryType type } OGRLayerH QgsOgrProvider::setSubsetString( OGRLayerH layer, OGRDataSourceH ds ) +{ + return QgsOgrUtils::setSubsetString( layer, ds, mEncoding, mSubsetString ); +} + +OGRLayerH QgsOgrUtils::setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTextCodec* encoding, const QString& subsetString ) { QByteArray layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( layer ) ); + OGRSFDriverH ogrDriver = OGR_DS_GetDriver( ds ); + QString ogrDriverName = OGR_Dr_GetName( ogrDriver ); + if ( ogrDriverName == "ODBC" ) //the odbc driver does not like schema names for subset { - QString layerNameString = mEncoding->toUnicode( layerName ); + QString layerNameString = encoding->toUnicode( layerName ); int dotIndex = layerNameString.indexOf( "." ); if ( dotIndex > 1 ) { QString modifiedLayerName = layerNameString.right( layerNameString.size() - dotIndex - 1 ); - layerName = mEncoding->fromUnicode( modifiedLayerName ); + layerName = encoding->fromUnicode( modifiedLayerName ); } } - QByteArray sql = "SELECT * FROM " + quotedIdentifier( layerName ); - sql += " WHERE " + mEncoding->fromUnicode( mSubsetString ); + QByteArray sql = "SELECT * FROM " + quotedIdentifier( layerName, ogrDriverName ); + sql += " WHERE " + encoding->fromUnicode( subsetString ); - QgsDebugMsg( QString( "SQL: %1" ).arg( mEncoding->toUnicode( sql ) ) ); + QgsDebugMsg( QString( "SQL: %1" ).arg( encoding->toUnicode( sql ) ) ); return OGR_DS_ExecuteSQL( ds, sql.constData(), NULL, NULL ); } diff --git a/src/providers/ogr/qgsogrprovider.h b/src/providers/ogr/qgsogrprovider.h index de9f789d6c9..61b177a8aaf 100644 --- a/src/providers/ogr/qgsogrprovider.h +++ b/src/providers/ogr/qgsogrprovider.h @@ -72,6 +72,8 @@ class QgsOgrProvider : public QgsVectorDataProvider */ virtual ~QgsOgrProvider(); + virtual QgsAbstractFeatureSource* featureSource() const; + virtual QgsCoordinateReferenceSystem crs(); /** @@ -252,11 +254,11 @@ class QgsOgrProvider : public QgsVectorDataProvider /** Get single flatten geometry type */ static OGRwkbGeometryType ogrWkbSingleFlatten( OGRwkbGeometryType type ); - QString layerName() { return mLayerName; } + QString layerName() const { return mLayerName; } - QString filePath() { return mFilePath; } + QString filePath() const { return mFilePath; } - int layerIndex() { return mLayerIndex; } + int layerIndex() const { return mLayerIndex; } QTextCodec* textEncoding() { return mEncoding; } @@ -326,13 +328,6 @@ class QgsOgrProvider : public QgsVectorDataProvider mutable QStringList mSubLayerList; - /** Flag whether OGR will return fields required by nextFeature() calls. - The relevant fields are first set in select(), however the setting may be - interferred by some other calls. This flag ensures they are set again - to correct values. - */ - bool mRelevantFieldsForNextFeature; - /**Adds one feature*/ bool addFeature( QgsFeature& f ); /**Deletes one feature*/ @@ -343,6 +338,14 @@ class QgsOgrProvider : public QgsVectorDataProvider OGRLayerH setSubsetString( OGRLayerH layer, OGRDataSourceH ds ); - friend class QgsOgrFeatureIterator; - QSet< QgsOgrFeatureIterator* > mActiveIterators; + friend class QgsOgrFeatureSource; +}; + + +class QgsOgrUtils +{ +public: + static void setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes ); + static OGRLayerH setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTextCodec* encoding, const QString& subsetString ); + static QByteArray quotedIdentifier( QByteArray field, const QString& ogrDriverName ); }; diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp index a521ea8c63d..c844be49ca3 100644 --- a/src/providers/postgres/qgspostgresconn.cpp +++ b/src/providers/postgres/qgspostgresconn.cpp @@ -33,6 +33,8 @@ #include #endif +//#define POSTGRES_SHARED_CONNECTIONS + QgsPostgresResult::~QgsPostgresResult() { if ( mRes ) @@ -128,6 +130,7 @@ const int QgsPostgresConn::sGeomTypeSelectLimit = 100; QgsPostgresConn *QgsPostgresConn::connectDb( QString conninfo, bool readonly ) { +#ifdef POSTGRES_SHARED_CONNECTIONS QMap &connections = readonly ? QgsPostgresConn::sConnectionsRO : QgsPostgresConn::sConnectionsRW; @@ -137,6 +140,7 @@ QgsPostgresConn *QgsPostgresConn::connectDb( QString conninfo, bool readonly ) connections[conninfo]->mRef++; return connections[conninfo]; } +#endif QgsPostgresConn *conn = new QgsPostgresConn( conninfo, readonly ); @@ -146,7 +150,9 @@ QgsPostgresConn *QgsPostgresConn::connectDb( QString conninfo, bool readonly ) return 0; } +#ifdef POSTGRES_SHARED_CONNECTIONS connections.insert( conninfo, conn ); +#endif return conn; } @@ -157,6 +163,7 @@ QgsPostgresConn::QgsPostgresConn( QString conninfo, bool readOnly ) , mConnInfo( conninfo ) , mGotPostgisVersion( false ) , mReadOnly( readOnly ) + , mNextCursorId( 0 ) { QgsDebugMsg( QString( "New PostgreSQL connection for " ) + conninfo ); @@ -192,8 +199,9 @@ QgsPostgresConn::QgsPostgresConn( QString conninfo, bool readOnly ) if ( PQstatus() != CONNECTION_OK ) { + QString errorMsg = PQerrorMessage(); PQfinish(); - QgsMessageLog::logMessage( tr( "Connection to database failed" ), tr( "PostGIS" ) ); + QgsMessageLog::logMessage( tr( "Connection to database failed" ) + "\n" + errorMsg, tr( "PostGIS" ) ); mRef = 0; return; } @@ -260,14 +268,16 @@ void QgsPostgresConn::disconnect() if ( --mRef > 0 ) return; +#ifdef POSTGRES_SHARED_CONNECTIONS QMap& connections = mReadOnly ? sConnectionsRO : sConnectionsRW; QString key = connections.key( this, QString::null ); Q_ASSERT( !key.isNull() ); connections.remove( key ); +#endif - deleteLater(); + delete this; } /* private */ @@ -837,6 +847,11 @@ bool QgsPostgresConn::closeCursor( QString cursorName ) return true; } +QString QgsPostgresConn::uniqueCursorName() +{ + return QString( "qgis_%1" ).arg( ++mNextCursorId ); +} + bool QgsPostgresConn::PQexecNR( QString query, bool retry ) { QgsPostgresResult res = PQexec( query, false ); diff --git a/src/providers/postgres/qgspostgresconn.h b/src/providers/postgres/qgspostgresconn.h index 574af5dd618..d2219ed8662 100644 --- a/src/providers/postgres/qgspostgresconn.h +++ b/src/providers/postgres/qgspostgresconn.h @@ -41,6 +41,15 @@ enum QgsPostgresGeometryColumnType sctTopoGeometry }; +enum QgsPostgresPrimaryKeyType +{ + pktUnknown, + pktInt, + pktTid, + pktOid, + pktFidMap +}; + /** Layer Property structure */ // TODO: Fill to Postgres/PostGIS specifications struct QgsPostgresLayerProperty @@ -187,6 +196,8 @@ class QgsPostgresConn : public QObject bool openCursor( QString cursorName, QString declare ); bool closeCursor( QString cursorName ); + QString uniqueCursorName(); + #if 0 PGconn *pgConnection() { return mConn; } #endif @@ -319,6 +330,8 @@ class QgsPostgresConn : public QObject */ bool mSwapEndian; void deduceEndian(); + + int mNextCursorId; }; #endif diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index 93b4b51317d..c24257f218d 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -20,63 +20,52 @@ #include -// provider: -// - mProviderId -// - mGeometryColumn -// - mSpatialColType -// - mRequestedSrid -// - mDetectedSrid -// - mRequestedGeomType -// - mDetectedGeomType -// - mSqlWhereClause -// - mPrimaryKeyType -// - mPrimaryKeyAttrs -// - mAttributeFields -// - mFeaturesCounted -// - field() -// - convertValue() -// - lookupFid() -// - quotedIdentifier() -// - endianString() - const int QgsPostgresFeatureIterator::sFeatureQueueSize = 2000; -QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresProvider* p, const QgsFeatureRequest& request ) - : QgsAbstractFeatureIterator( request ), P( p ) +QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) + : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) , mFeatureQueueSize( sFeatureQueueSize ) { - mCursorName = QString( "qgisf%1_%2" ).arg( P->mProviderId ).arg( P->mIteratorCounter++ ); + mConn = QgsPostgresConn::connectDb( mSource->mConnInfo, true ); - P->mActiveIterators << this; + if ( !mConn ) + { + mClosed = true; + iteratorClosed(); + return; + } + + mCursorName = mConn->uniqueCursorName(); QString whereClause; - if ( request.filterType() == QgsFeatureRequest::FilterRect && !P->mGeometryColumn.isNull() ) + if ( request.filterType() == QgsFeatureRequest::FilterRect && !mSource->mGeometryColumn.isNull() ) { whereClause = whereClauseRect(); } else if ( request.filterType() == QgsFeatureRequest::FilterFid ) { - whereClause = P->whereClause( request.filterFid() ); + whereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { - whereClause = P->whereClause( request.filterFids() ); + whereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); } - if ( !P->mSqlWhereClause.isEmpty() ) + if ( !mSource->mSqlWhereClause.isEmpty() ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; - whereClause += "(" + P->mSqlWhereClause + ")"; + whereClause += "(" + mSource->mSqlWhereClause + ")"; } if ( !declareCursor( whereClause ) ) { mClosed = true; + iteratorClosed(); return; } @@ -101,21 +90,21 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature ) { QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName ); QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 ); - if ( P->mConnectionRO->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously + if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously { - QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); + QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); } QgsPostgresResult queryResult; for ( ;; ) { - queryResult = P->mConnectionRO->PQgetResult(); + queryResult = mConn->PQgetResult(); if ( !queryResult.result() ) break; if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK ) { - QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); + QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) ); break; } @@ -136,15 +125,8 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature ) QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) ); close(); - /* only updates the feature count if it was already once. - * Otherwise, this would lead to false feature count if - * an existing project is open at a restrictive extent. - */ - if ( P->mFeaturesCounted > 0 && P->mFeaturesCounted < mFetched ) - { - QgsDebugMsg( QString( "feature count adjusted from %1 to %2" ).arg( P->mFeaturesCounted ).arg( mFetched ) ); - P->mFeaturesCounted = mFetched; - } + mSource->mShared->ensureFeaturesCountedAtLeast( mFetched ); + return false; } @@ -165,7 +147,7 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature ) mFetched++; feature.setValid( true ); - feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups + feature.setFields( &mSource->mFields ); // allow name-based attribute lookups return true; } @@ -177,7 +159,7 @@ bool QgsPostgresFeatureIterator::rewind() return false; // move cursor to first record - P->mConnectionRO->PQexecNR( QString( "move absolute 0 in %1" ).arg( mCursorName ) ); + mConn->PQexecNR( QString( "move absolute 0 in %1" ).arg( mCursorName ) ); mFeatureQueue.empty(); mFetched = 0; @@ -189,14 +171,17 @@ bool QgsPostgresFeatureIterator::close() if ( mClosed ) return false; - P->mConnectionRO->closeCursor( mCursorName ); + mConn->closeCursor( mCursorName ); + + mConn->disconnect(); + mConn = 0; while ( !mFeatureQueue.empty() ) { mFeatureQueue.dequeue(); } - P->mActiveIterators.remove( this ); + iteratorClosed(); mClosed = true; return true; @@ -207,7 +192,7 @@ bool QgsPostgresFeatureIterator::close() QString QgsPostgresFeatureIterator::whereClauseRect() { QgsRectangle rect = mRequest.filterRect(); - if ( P->mSpatialColType == sctGeography ) + if ( mSource->mSpatialColType == sctGeography ) { rect = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ).intersect( &rect ); if ( !rect.isFinite() ) @@ -215,11 +200,11 @@ QString QgsPostgresFeatureIterator::whereClauseRect() } QString qBox; - if ( P->mConnectionRO->majorVersion() < 2 ) + if ( mConn->majorVersion() < 2 ) { qBox = QString( "setsrid('BOX3D(%1)'::box3d,%2)" ) .arg( rect.asWktCoordinates() ) - .arg( P->mRequestedSrid.isEmpty() ? P->mDetectedSrid : P->mRequestedSrid ); + .arg( mSource->mRequestedSrid.isEmpty() ? mSource->mDetectedSrid : mSource->mRequestedSrid ); } else { @@ -228,33 +213,33 @@ QString QgsPostgresFeatureIterator::whereClauseRect() .arg( qgsDoubleToString( rect.yMinimum() ) ) .arg( qgsDoubleToString( rect.xMaximum() ) ) .arg( qgsDoubleToString( rect.yMaximum() ) ) - .arg( P->mRequestedSrid.isEmpty() ? P->mDetectedSrid : P->mRequestedSrid ); + .arg( mSource->mRequestedSrid.isEmpty() ? mSource->mDetectedSrid : mSource->mRequestedSrid ); } QString whereClause = QString( "%1 && %2" ) - .arg( P->quotedIdentifier( P->mGeometryColumn ) ) + .arg( QgsPostgresConn::quotedIdentifier( mSource->mGeometryColumn ) ) .arg( qBox ); if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { whereClause += QString( " AND %1(%2%3,%4)" ) - .arg( P->mConnectionRO->majorVersion() < 2 ? "intersects" : "st_intersects" ) - .arg( P->quotedIdentifier( P->mGeometryColumn ) ) - .arg( P->mSpatialColType == sctGeography ? "::geometry" : "" ) + .arg( mConn->majorVersion() < 2 ? "intersects" : "st_intersects" ) + .arg( QgsPostgresConn::quotedIdentifier( mSource->mGeometryColumn ) ) + .arg( mSource->mSpatialColType == sctGeography ? "::geometry" : "" ) .arg( qBox ); } - if ( !P->mRequestedSrid.isEmpty() && ( P->mRequestedSrid != P->mDetectedSrid || P->mRequestedSrid.toInt() == 0 ) ) + if ( !mSource->mRequestedSrid.isEmpty() && ( mSource->mRequestedSrid != mSource->mDetectedSrid || mSource->mRequestedSrid.toInt() == 0 ) ) { whereClause += QString( " AND %1(%2%3)=%4" ) - .arg( P->mConnectionRO->majorVersion() < 2 ? "srid" : "st_srid" ) - .arg( P->quotedIdentifier( P->mGeometryColumn ) ) - .arg( P->mSpatialColType == sctGeography ? "::geography" : "" ) - .arg( P->mRequestedSrid ); + .arg( mConn->majorVersion() < 2 ? "srid" : "st_srid" ) + .arg( QgsPostgresConn::quotedIdentifier( mSource->mGeometryColumn ) ) + .arg( mSource->mSpatialColType == sctGeography ? "::geography" : "" ) + .arg( mSource->mRequestedSrid ); } - if ( P->mRequestedGeomType != QGis::WKBUnknown && P->mRequestedGeomType != P->mDetectedGeomType ) + if ( mSource->mRequestedGeomType != QGis::WKBUnknown && mSource->mRequestedGeomType != mSource->mDetectedGeomType ) { - whereClause += QString( " AND %1" ).arg( QgsPostgresConn::postgisTypeFilter( P->mGeometryColumn, P->mRequestedGeomType, P->mSpatialColType == sctGeography ) ); + whereClause += QString( " AND %1" ).arg( QgsPostgresConn::postgisTypeFilter( mSource->mGeometryColumn, mSource->mRequestedGeomType, mSource->mSpatialColType == sctGeography ) ); } return whereClause; @@ -264,80 +249,80 @@ QString QgsPostgresFeatureIterator::whereClauseRect() bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause ) { - mFetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && !P->mGeometryColumn.isNull(); + mFetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && !mSource->mGeometryColumn.isNull(); + + // TODO: check that all field indexes exist + //if ( !hasAllFields ) + //{ + // rewind(); + // return false; + //} + - try - { QString query = "SELECT ", delim = ""; if ( mFetchGeometry ) { query += QString( "%1(%2%3,'%4')" ) - .arg( P->mConnectionRO->majorVersion() < 2 ? "asbinary" : "st_asbinary" ) - .arg( P->quotedIdentifier( P->mGeometryColumn ) ) - .arg( P->mSpatialColType == sctGeography ? "::geometry" : "" ) - .arg( P->endianString() ); + .arg( mConn->majorVersion() < 2 ? "asbinary" : "st_asbinary" ) + .arg( QgsPostgresConn::quotedIdentifier( mSource->mGeometryColumn ) ) + .arg( mSource->mSpatialColType == sctGeography ? "::geometry" : "" ) + .arg( QgsPostgresProvider::endianString() ); delim = ","; } - switch ( P->mPrimaryKeyType ) + switch ( mSource->mPrimaryKeyType ) { - case QgsPostgresProvider::pktOid: + case pktOid: query += delim + "oid"; delim = ","; break; - case QgsPostgresProvider::pktTid: + case pktTid: query += delim + "ctid"; delim = ","; break; - case QgsPostgresProvider::pktInt: - query += delim + P->quotedIdentifier( P->field( P->mPrimaryKeyAttrs[0] ).name() ); + case pktInt: + query += delim + QgsPostgresConn::quotedIdentifier( mSource->mFields[ mSource->mPrimaryKeyAttrs[0] ].name() ); delim = ","; break; - case QgsPostgresProvider::pktFidMap: - foreach ( int idx, P->mPrimaryKeyAttrs ) + case pktFidMap: + foreach ( int idx, mSource->mPrimaryKeyAttrs ) { - query += delim + P->mConnectionRO->fieldExpression( P->field( idx ) ); + query += delim + mConn->fieldExpression( mSource->mFields[idx] ); delim = ","; } break; - case QgsPostgresProvider::pktUnknown: + case pktUnknown: QgsDebugMsg( "Cannot declare cursor without primary key." ); return false; break; } bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; - foreach ( int idx, subsetOfAttributes ? mRequest.subsetOfAttributes() : P->attributeIndexes() ) + foreach ( int idx, subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList() ) { - if ( P->mPrimaryKeyAttrs.contains( idx ) ) + if ( mSource->mPrimaryKeyAttrs.contains( idx ) ) continue; - query += delim + P->mConnectionRO->fieldExpression( P->field( idx ) ); + query += delim + mConn->fieldExpression( mSource->mFields[idx] ); } - query += " FROM " + P->mQuery; + query += " FROM " + mSource->mQuery; if ( !whereClause.isEmpty() ) query += QString( " WHERE %1" ).arg( whereClause ); - if ( !P->mConnectionRO->openCursor( mCursorName, query ) ) + if ( !mConn->openCursor( mCursorName, query ) ) { // reloading the fields might help next time around rewind(); - P->loadFields(); + // TODO how to cleanly force reload of fields? P->loadFields(); return false; } - } - catch ( QgsPostgresProvider::PGFieldNotFound ) - { - rewind(); - return false; - } return true; } @@ -345,9 +330,7 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause ) bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature ) { - try - { - feature.initAttributes( P->fields().count() ); + feature.initAttributes( mSource->mFields.count() ); int col = 0; @@ -491,26 +474,26 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes(); - switch ( P->mPrimaryKeyType ) + switch ( mSource->mPrimaryKeyType ) { - case QgsPostgresProvider::pktOid: - case QgsPostgresProvider::pktTid: - case QgsPostgresProvider::pktInt: - fid = P->mConnectionRO->getBinaryInt( queryResult, row, col++ ); - if ( P->mPrimaryKeyType == QgsPostgresProvider::pktInt && - ( !subsetOfAttributes || fetchAttributes.contains( P->mPrimaryKeyAttrs[0] ) ) ) - feature.setAttribute( P->mPrimaryKeyAttrs[0], fid ); + case pktOid: + case pktTid: + case pktInt: + fid = mConn->getBinaryInt( queryResult, row, col++ ); + if ( mSource->mPrimaryKeyType == pktInt && + ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) ) + feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid ); break; - case QgsPostgresProvider::pktFidMap: + case pktFidMap: { QList primaryKeyVals; - foreach ( int idx, P->mPrimaryKeyAttrs ) + foreach ( int idx, mSource->mPrimaryKeyAttrs ) { - const QgsField &fld = P->field( idx ); + const QgsField &fld = mSource->mFields[idx]; - QVariant v = P->convertValue( fld.type(), queryResult.PQgetvalue( row, col ) ); + QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) ); primaryKeyVals << v; if ( !subsetOfAttributes || fetchAttributes.contains( idx ) ) @@ -519,11 +502,12 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int col++; } - fid = P->lookupFid( QVariant( primaryKeyVals ) ); + fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) ); + } break; - case QgsPostgresProvider::pktUnknown: + case pktUnknown: Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" ); return false; } @@ -539,25 +523,45 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int } else { - for ( int idx = 0; idx < P->mAttributeFields.count(); ++idx ) + for ( int idx = 0; idx < mSource->mFields.count(); ++idx ) getFeatureAttribute( idx, queryResult, row, col, feature ); } return true; - } - catch ( QgsPostgresProvider::PGFieldNotFound ) - { - return false; - } } void QgsPostgresFeatureIterator::getFeatureAttribute( int idx, QgsPostgresResult& queryResult, int row, int& col, QgsFeature& feature ) { - if ( P->mPrimaryKeyAttrs.contains( idx ) ) + if ( mSource->mPrimaryKeyAttrs.contains( idx ) ) return; - QVariant v = P->convertValue( P->mAttributeFields[idx].type(), queryResult.PQgetvalue( row, col ) ); + QVariant v = QgsPostgresProvider::convertValue( mSource->mFields[idx].type(), queryResult.PQgetvalue( row, col ) ); feature.setAttribute( idx, v ); col++; } + + +// ------------------ + +QgsPostgresFeatureSource::QgsPostgresFeatureSource( const QgsPostgresProvider* p ) + : mConnInfo( p->mUri.connectionInfo() ) + , mGeometryColumn( p->mGeometryColumn ) + , mSqlWhereClause( p->mSqlWhereClause ) + , mFields( p->mAttributeFields ) + , mSpatialColType( p->mSpatialColType ) + , mRequestedSrid( p->mRequestedSrid ) + , mDetectedSrid( p->mDetectedSrid ) + , mRequestedGeomType( p->mRequestedGeomType ) + , mDetectedGeomType( p->mDetectedGeomType ) + , mPrimaryKeyType( p->mPrimaryKeyType ) + , mPrimaryKeyAttrs( p->mPrimaryKeyAttrs ) + , mQuery( p->mQuery ) + , mShared( p->mShared ) +{ +} + +QgsFeatureIterator QgsPostgresFeatureSource::getFeatures( const QgsFeatureRequest& request ) +{ + return QgsFeatureIterator( new QgsPostgresFeatureIterator( this, false, request ) ); +} diff --git a/src/providers/postgres/qgspostgresfeatureiterator.h b/src/providers/postgres/qgspostgresfeatureiterator.h index a69d63a9c36..3e76d55c7ae 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.h +++ b/src/providers/postgres/qgspostgresfeatureiterator.h @@ -19,14 +19,48 @@ #include +#include "qgspostgresprovider.h" class QgsPostgresProvider; class QgsPostgresResult; -class QgsPostgresFeatureIterator : public QgsAbstractFeatureIterator + +class QgsPostgresFeatureSource : public QgsAbstractFeatureSource +{ +public: + QgsPostgresFeatureSource( const QgsPostgresProvider* p ); + + virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ); + +protected: + + QString mConnInfo; + + QString mGeometryColumn; + QString mSqlWhereClause; + QgsFields mFields; + QgsPostgresGeometryColumnType mSpatialColType; + QString mRequestedSrid; + QString mDetectedSrid; + QGis::WkbType mRequestedGeomType; //! geometry type requested in the uri + QGis::WkbType mDetectedGeomType; //! geometry type detected in the database + QgsPostgresPrimaryKeyType mPrimaryKeyType; + QList mPrimaryKeyAttrs; + QString mQuery; + // TODO: loadFields() + + QSharedPointer mShared; + + friend class QgsPostgresFeatureIterator; +}; + + +class QgsPostgresConn; + +class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource { public: - QgsPostgresFeatureIterator( QgsPostgresProvider* p, const QgsFeatureRequest& request ); + QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ); ~QgsPostgresFeatureIterator(); @@ -40,7 +74,7 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIterator //! fetch next feature, return true on success virtual bool fetchFeature( QgsFeature& feature ); - QgsPostgresProvider* P; + QgsPostgresConn* mConn; QString whereClauseRect(); bool getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature ); diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 13242a302f8..24c91528e0a 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -39,7 +39,6 @@ const QString POSTGRES_KEY = "postgres"; const QString POSTGRES_DESCRIPTION = "PostgreSQL/PostGIS data provider"; -int QgsPostgresProvider::sProviderIds = 0; QgsPostgresProvider::QgsPostgresProvider( QString const & uri ) : QgsVectorDataProvider( uri ) @@ -48,14 +47,12 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri ) , mSpatialColType( sctNone ) , mDetectedGeomType( QGis::WKBUnknown ) , mRequestedGeomType( QGis::WKBUnknown ) + , mShared( new QgsPostgresSharedData ) , mUseEstimatedMetadata( false ) , mSelectAtIdDisabled( false ) , mConnectionRO( 0 ) , mConnectionRW( 0 ) - , mFidCounter( 0 ) - , mIteratorCounter( 0 ) { - mProviderId = sProviderIds++; QgsDebugMsg( QString( "URI: %1 " ).arg( uri ) ); @@ -137,7 +134,6 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri ) } mLayerExtent.setMinimal(); - mFeaturesCounted = -1; // set the primary key if ( !determinePrimaryKey() ) @@ -219,18 +215,18 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri ) QgsPostgresProvider::~QgsPostgresProvider() { - while ( !mActiveIterators.empty() ) - { - QgsPostgresFeatureIterator *it = *mActiveIterators.begin(); - QgsDebugMsg( "closing active iterator" ); - it->close(); - } - disconnectDb(); QgsDebugMsg( "deconstructing." ); } + +QgsAbstractFeatureSource* QgsPostgresProvider::featureSource() const +{ + return new QgsPostgresFeatureSource( this ); +} + + void QgsPostgresProvider::disconnectDb() { if ( mConnectionRO ) @@ -329,20 +325,6 @@ static bool operator<( const QVariant &a, const QVariant &b ) return a.canConvert( QVariant::String ) && b.canConvert( QVariant::String ) && a.toString() < b.toString(); } -QgsFeatureId QgsPostgresProvider::lookupFid( const QVariant &v ) -{ - QMap::const_iterator it = mKeyToFid.find( v ); - - if ( it != mKeyToFid.constEnd() ) - { - return it.value(); - } - - mFidToKey.insert( ++mFidCounter, v ); - mKeyToFid.insert( v, mFidCounter ); - - return mFidCounter; -} QgsFeatureIterator QgsPostgresProvider::getFeatures( const QgsFeatureRequest& request ) { @@ -352,7 +334,7 @@ QgsFeatureIterator QgsPostgresProvider::getFeatures( const QgsFeatureRequest& re return QgsFeatureIterator(); } - return QgsFeatureIterator( new QgsPostgresFeatureIterator( this, request ) ); + return QgsFeatureIterator( new QgsPostgresFeatureIterator( static_cast( featureSource() ), false, request ) ); } @@ -425,11 +407,11 @@ void QgsPostgresProvider::appendPkParams( QgsFeatureId featureId, QStringList &p case pktFidMap: { + QVariant pkValsVariant = mShared->lookupKey( featureId ); QList pkVals; - QMap::const_iterator it = mFidToKey.find( featureId ); - if ( it != mFidToKey.constEnd() ) + if ( !pkValsVariant.isNull() ) { - pkVals = it.value().toList(); + pkVals = pkValsVariant.toList(); Q_ASSERT( pkVals.size() == mPrimaryKeyAttrs.size() ); } @@ -456,11 +438,18 @@ void QgsPostgresProvider::appendPkParams( QgsFeatureId featureId, QStringList &p } } + QString QgsPostgresProvider::whereClause( QgsFeatureId featureId ) const +{ + return QgsPostgresUtils::whereClause( featureId, mAttributeFields, mConnectionRO, mPrimaryKeyType, mPrimaryKeyAttrs, mShared ); +} + + +QString QgsPostgresUtils::whereClause( QgsFeatureId featureId, const QgsFields& fields, QgsPostgresConn* conn, QgsPostgresPrimaryKeyType pkType, const QList& pkAttrs, QSharedPointer sharedData ) { QString whereClause; - switch ( mPrimaryKeyType ) + switch ( pkType ) { case pktTid: whereClause = QString( "ctid='(%1,%2)'" ) @@ -473,26 +462,26 @@ QString QgsPostgresProvider::whereClause( QgsFeatureId featureId ) const break; case pktInt: - Q_ASSERT( mPrimaryKeyAttrs.size() == 1 ); - whereClause = QString( "%1=%2" ).arg( quotedIdentifier( field( mPrimaryKeyAttrs[0] ).name() ) ).arg( featureId ); + Q_ASSERT( pkAttrs.size() == 1 ); + whereClause = QString( "%1=%2" ).arg( QgsPostgresConn::quotedIdentifier( fields[ pkAttrs[0] ].name() ) ).arg( featureId ); break; case pktFidMap: { - QMap::const_iterator it = mFidToKey.find( featureId ); - if ( it != mFidToKey.constEnd() ) + QVariant pkValsVariant = sharedData->lookupKey( featureId ); + if ( !pkValsVariant.isNull() ) { - QList pkVals = it.value().toList(); + QList pkVals = pkValsVariant.toList(); - Q_ASSERT( pkVals.size() == mPrimaryKeyAttrs.size() ); + Q_ASSERT( pkVals.size() == pkAttrs.size() ); QString delim = ""; - for ( int i = 0; i < mPrimaryKeyAttrs.size(); i++ ) + for ( int i = 0; i < pkAttrs.size(); i++ ) { - int idx = mPrimaryKeyAttrs[i]; - const QgsField &fld = field( idx ); + int idx = pkAttrs[i]; + const QgsField &fld = fields[ idx ]; - whereClause += delim + QString( "%1=%2" ).arg( mConnectionRO->fieldExpression( fld ) ).arg( quotedValue( pkVals[i].toString() ) ); + whereClause += delim + QString( "%1=%2" ).arg( conn->fieldExpression( fld ) ).arg( QgsPostgresConn::quotedValue( pkVals[i].toString() ) ); delim = " AND "; } } @@ -513,12 +502,12 @@ QString QgsPostgresProvider::whereClause( QgsFeatureId featureId ) const return whereClause; } -QString QgsPostgresProvider::whereClause( QgsFeatureIds featureIds ) const +QString QgsPostgresUtils::whereClause( QgsFeatureIds featureIds, const QgsFields& fields, QgsPostgresConn* conn, QgsPostgresPrimaryKeyType pkType, const QList& pkAttrs, QSharedPointer sharedData ) { QStringList whereClauses; foreach ( const QgsFeatureId featureId, featureIds ) { - whereClauses << whereClause( featureId ); + whereClauses << whereClause( featureId, fields, conn, pkType, pkAttrs, sharedData ); } return whereClauses.join( " AND " ); @@ -1773,7 +1762,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist ) primaryKeyVals << attrs[ idx ]; } - features->setFeatureId( lookupFid( QVariant( primaryKeyVals ) ) ); + features->setFeatureId( mShared->lookupFid( QVariant( primaryKeyVals ) ) ); } QgsDebugMsgLevel( QString( "new fid=%1" ).arg( features->id() ), 4 ); } @@ -1782,8 +1771,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist ) mConnectionRW->PQexecNR( "DEALLOCATE addfeatures" ); mConnectionRW->PQexecNR( "COMMIT" ); - if ( mFeaturesCounted >= 0 ) - mFeaturesCounted += flist.size(); + mShared->addFeaturesCounted( flist.size() ); } catch ( PGException &e ) { @@ -1821,9 +1809,7 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds & id ) if ( result.PQresultStatus() != PGRES_COMMAND_OK ) throw PGException( result ); - QVariant v = mFidToKey[ *it ]; - mFidToKey.remove( *it ); - mKeyToFid.remove( v ); + mShared->removeFid( *it ); } mConnectionRW->PQexecNR( "COMMIT" ); @@ -1839,8 +1825,7 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds & id ) dropOrphanedTopoGeoms(); } - if ( mFeaturesCounted >= 0 ) - mFeaturesCounted -= id.size(); + mShared->addFeaturesCounted( id.size() ); } catch ( PGException &e ) { @@ -2038,9 +2023,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap & // update feature id map if key was changed if ( pkChanged && mPrimaryKeyType == pktFidMap ) { - QVariant v = mFidToKey[ fid ]; - mFidToKey.remove( fid ); - mKeyToFid.remove( v ); + QVariant v = mShared->removeFid( fid ); QList k = v.toList(); @@ -2053,8 +2036,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap & k[i] = attrs[ idx ]; } - mFidToKey.insert( fid, k ); - mKeyToFid.insert( k, fid ); + mShared->insertFid( fid, k ); } } @@ -2333,7 +2315,7 @@ bool QgsPostgresProvider::setSubsetString( QString theSQL, bool updateFeatureCou if ( updateFeatureCount ) { - mFeaturesCounted = -1; + mShared->setFeaturesCounted( -1 ); } mLayerExtent.setMinimal(); @@ -2345,8 +2327,9 @@ bool QgsPostgresProvider::setSubsetString( QString theSQL, bool updateFeatureCou */ long QgsPostgresProvider::featureCount() const { - if ( mFeaturesCounted >= 0 ) - return mFeaturesCounted; + int featuresCounted = mShared->featuresCounted(); + if ( featuresCounted >= 0 ) + return featuresCounted; // get total number of features QString sql; @@ -2367,11 +2350,12 @@ long QgsPostgresProvider::featureCount() const QgsDebugMsg( "number of features as text: " + result.PQgetvalue( 0, 0 ) ); - mFeaturesCounted = result.PQgetvalue( 0, 0 ).toLong(); + long num = result.PQgetvalue( 0, 0 ).toLong(); + mShared->setFeaturesCounted( num ); - QgsDebugMsg( "number of features: " + QString::number( mFeaturesCounted ) ); + QgsDebugMsg( "number of features: " + QString::number( num ) ); - return mFeaturesCounted; + return num; } QgsRectangle QgsPostgresProvider::extent() @@ -3536,3 +3520,94 @@ QGISEXTERN QString getStyleById( const QString& uri, QString styleId, QString& e return ""; } } + + +// ---------- + +QgsPostgresSharedData::QgsPostgresSharedData() + : mFeaturesCounted( -1 ) + , mFidCounter( 0 ) +{ +} + +void QgsPostgresSharedData::addFeaturesCounted( long diff ) +{ + QMutexLocker locker( &mMutex ); + + if ( mFeaturesCounted >= 0 ) + mFeaturesCounted += diff; +} + +void QgsPostgresSharedData::ensureFeaturesCountedAtLeast( long fetched ) +{ + QMutexLocker locker( &mMutex ); + + /* only updates the feature count if it was already once. + * Otherwise, this would lead to false feature count if + * an existing project is open at a restrictive extent. + */ + if ( mFeaturesCounted > 0 && mFeaturesCounted < fetched ) + { + QgsDebugMsg( QString( "feature count adjusted from %1 to %2" ).arg( mFeaturesCounted ).arg( fetched ) ); + mFeaturesCounted = fetched; + } +} + +long QgsPostgresSharedData::featuresCounted() +{ + QMutexLocker locker( &mMutex ); + return mFeaturesCounted; +} + +void QgsPostgresSharedData::setFeaturesCounted( long count ) +{ + QMutexLocker locker( &mMutex ); + mFeaturesCounted = count; +} + + +QgsFeatureId QgsPostgresSharedData::lookupFid( const QVariant &v ) +{ + QMutexLocker locker( &mMutex ); + + QMap::const_iterator it = mKeyToFid.find( v ); + + if ( it != mKeyToFid.constEnd() ) + { + return it.value(); + } + + mFidToKey.insert( ++mFidCounter, v ); + mKeyToFid.insert( v, mFidCounter ); + + return mFidCounter; +} + + +QVariant QgsPostgresSharedData::removeFid( QgsFeatureId fid ) +{ + QMutexLocker locker( &mMutex ); + + QVariant v = mFidToKey[ fid ]; + mFidToKey.remove( fid ); + mKeyToFid.remove( v ); + return v; +} + +void QgsPostgresSharedData::insertFid( QgsFeatureId fid, const QVariant& k ) +{ + QMutexLocker locker( &mMutex ); + + mFidToKey.insert( fid, k ); + mKeyToFid.insert( k, fid ); +} + +QVariant QgsPostgresSharedData::lookupKey( QgsFeatureId featureId ) +{ + QMutexLocker locker( &mMutex ); + + QMap::const_iterator it = mFidToKey.find( featureId ); + if ( it != mFidToKey.constEnd() ) + return it.value(); + return QVariant(); +} diff --git a/src/providers/postgres/qgspostgresprovider.h b/src/providers/postgres/qgspostgresprovider.h index b2f8d4d99bd..31a37b996ef 100644 --- a/src/providers/postgres/qgspostgresprovider.h +++ b/src/providers/postgres/qgspostgresprovider.h @@ -29,6 +29,7 @@ class QgsField; class QgsGeometry; class QgsPostgresFeatureIterator; +class QgsPostgresSharedData; #include "qgsdatasourceuri.h" @@ -69,6 +70,8 @@ class QgsPostgresProvider : public QgsVectorDataProvider //! Destructor virtual ~QgsPostgresProvider(); + virtual QgsAbstractFeatureSource* featureSource() const; + /** * Returns the permanent storage type for this layer as a friendly name. */ @@ -107,7 +110,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider /** * Return a string representation of the endian-ness for the layer */ - QString endianString(); + static QString endianString(); /** * Changes the stored extent for this layer to the supplied extent. @@ -372,7 +375,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider /** * Data type for the primary key */ - enum { pktUnknown, pktInt, pktTid, pktOid, pktFidMap } mPrimaryKeyType; + QgsPostgresPrimaryKeyType mPrimaryKeyType; /** * Data type for the spatial column @@ -387,13 +390,14 @@ class QgsPostgresProvider : public QgsVectorDataProvider QString mGeometryColumn; //! name of the geometry column QgsRectangle mLayerExtent; //! Rectangle that contains the extent (bounding box) of the layer - mutable long mFeaturesCounted; //! Number of features in the layer QGis::WkbType mDetectedGeomType; //! geometry type detected in the database QGis::WkbType mRequestedGeomType; //! geometry type requested in the uri QString mDetectedSrid; //! Spatial reference detected in the database QString mRequestedSrid; //! Spatial reference requested in the uri + QSharedPointer mShared; //!< Mutable data shared between provider and feature sources + bool getGeometryDetails(); //! @{ Only used with TopoGeometry layers @@ -471,16 +475,56 @@ class QgsPostgresProvider : public QgsVectorDataProvider static QString quotedIdentifier( QString ident ) { return QgsPostgresConn::quotedIdentifier( ident ); } static QString quotedValue( QVariant value ) { return QgsPostgresConn::quotedValue( value ); } - static int sProviderIds; + friend class QgsPostgresFeatureSource; +}; - QMap mKeyToFid; // map key values to feature id - QMap mFidToKey; // map feature back to fea - QgsFeatureId mFidCounter; // next feature id if map is used - QgsFeatureId lookupFid( const QVariant &v ); // lookup existing mapping or add a new one - int mIteratorCounter; // iterator counter - friend class QgsPostgresFeatureIterator; - QSet< QgsPostgresFeatureIterator * > mActiveIterators; +/** Assorted Postgres utility functions */ +class QgsPostgresUtils +{ +public: + static QString whereClause( QgsFeatureId featureId, + const QgsFields& fields, + QgsPostgresConn* conn, + QgsPostgresPrimaryKeyType pkType, + const QList& pkAttrs, + QSharedPointer sharedData ); + + static QString whereClause( QgsFeatureIds featureIds, + const QgsFields& fields, + QgsPostgresConn* conn, + QgsPostgresPrimaryKeyType pkType, + const QList& pkAttrs, + QSharedPointer sharedData ); +}; + +/** Data shared between provider class and its feature sources. Ideally there should + * be as few members as possible because there could be simultaneous reads/writes + * from different threads and therefore locking has to be involved. */ +class QgsPostgresSharedData +{ +public: + QgsPostgresSharedData(); + + long featuresCounted(); + void setFeaturesCounted( long count ); + void addFeaturesCounted( long diff ); + void ensureFeaturesCountedAtLeast( long fetched ); + + // FID lookups + QgsFeatureId lookupFid( const QVariant &v ); // lookup existing mapping or add a new one + QVariant removeFid( QgsFeatureId fid ); + void insertFid( QgsFeatureId fid, const QVariant& k ); + QVariant lookupKey( QgsFeatureId featureId ); + +protected: + QMutex mMutex; //!< Access to all data members is guarded by the mutex + + long mFeaturesCounted; //! Number of features in the layer + + QgsFeatureId mFidCounter; // next feature id if map is used + QMap mKeyToFid; // map key values to feature id + QMap mFidToKey; // map feature back to fea }; #endif diff --git a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp index a939f22e6f0..084594777ef 100644 --- a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp +++ b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp @@ -37,17 +37,14 @@ // quotedIdentifier() -QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteProvider* p, const QgsFeatureRequest& request ) - : QgsAbstractFeatureIterator( request ) - , P( p ) +QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) + : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) , sqliteStatement( NULL ) { - P->mActiveIterators << this; - - mFetchGeometry = !P->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); + mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); QString whereClause; - if ( request.filterType() == QgsFeatureRequest::FilterRect && !P->mGeometryColumn.isNull() ) + if ( request.filterType() == QgsFeatureRequest::FilterRect && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause += whereClauseRect(); @@ -58,13 +55,13 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteProvide whereClause += whereClauseFid(); } - if ( !P->mSubsetString.isEmpty() ) + if ( !mSource->mSubsetString.isEmpty() ) { if ( !whereClause.isEmpty() ) { whereClause += " AND "; } - whereClause += "( " + P->mSubsetString + ")"; + whereClause += "( " + mSource->mSubsetString + ")"; } // preparing the SQL statement @@ -129,7 +126,7 @@ bool QgsSpatiaLiteFeatureIterator::close() if ( mClosed ) return false; - P->mActiveIterators.remove( this ); + iteratorClosed(); if ( sqliteStatement ) { @@ -156,34 +153,34 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( QString whereClause ) const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes(); for ( QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it ) { - sql += "," + fieldName( P->field( *it ) ); + sql += "," + fieldName( mSource->mFields.field( *it ) ); colIdx++; } } else { // fetch all attributes - for ( int idx = 0; idx < P->attributeFields.count(); ++idx ) + for ( int idx = 0; idx < mSource->mFields.count(); ++idx ) { - sql += "," + fieldName( P->attributeFields[idx] ); + sql += "," + fieldName( mSource->mFields[idx] ); colIdx++; } } if ( mFetchGeometry ) { - sql += QString( ", AsBinary(%1)" ).arg( P->quotedIdentifier( P->mGeometryColumn ) ); + sql += QString( ", AsBinary(%1)" ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mSource->mGeometryColumn ) ); mGeomColIdx = colIdx; } - sql += QString( " FROM %1" ).arg( P->mQuery ); + sql += QString( " FROM %1" ).arg( mSource->mQuery ); if ( !whereClause.isEmpty() ) sql += QString( " WHERE %1" ).arg( whereClause ); - if ( sqlite3_prepare_v2( P->sqliteHandle, sql.toUtf8().constData(), -1, &sqliteStatement, NULL ) != SQLITE_OK ) + if ( sqlite3_prepare_v2( mSource->sqliteHandle, sql.toUtf8().constData(), -1, &sqliteStatement, NULL ) != SQLITE_OK ) { // some error occurred - QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql ).arg( sqlite3_errmsg( P->sqliteHandle ) ), QObject::tr( "SpatiaLite" ) ); + QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql ).arg( sqlite3_errmsg( mSource->sqliteHandle ) ), QObject::tr( "SpatiaLite" ) ); return false; } } @@ -198,7 +195,7 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( QString whereClause ) QString QgsSpatiaLiteFeatureIterator::quotedPrimaryKey() { - return !P->isQuery ? "ROWID" : P->quotedIdentifier( P->mPrimaryKey ); + return !mSource->isQuery ? "ROWID" : QgsSpatiaLiteProvider::quotedIdentifier( mSource->mPrimaryKey ); } QString QgsSpatiaLiteFeatureIterator::whereClauseFid() @@ -214,41 +211,41 @@ QString QgsSpatiaLiteFeatureIterator::whereClauseRect() if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { // we are requested to evaluate a true INTERSECT relationship - whereClause += QString( "Intersects(%1, BuildMbr(%2)) AND " ).arg( P->quotedIdentifier( P->mGeometryColumn ) ).arg( mbr( rect ) ); + whereClause += QString( "Intersects(%1, BuildMbr(%2)) AND " ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( mbr( rect ) ); } - if ( P->mVShapeBased ) + if ( mSource->mVShapeBased ) { // handling a VirtualShape layer - whereClause += QString( "MbrIntersects(%1, BuildMbr(%2))" ).arg( P->quotedIdentifier( P->mGeometryColumn ) ).arg( mbr( rect ) ); + whereClause += QString( "MbrIntersects(%1, BuildMbr(%2))" ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( mbr( rect ) ); } else { - if ( P->spatialIndexRTree ) + if ( mSource->spatialIndexRTree ) { // using the RTree spatial index QString mbrFilter = QString( "xmin <= %1 AND " ).arg( qgsDoubleToString( rect.xMaximum() ) ); mbrFilter += QString( "xmax >= %1 AND " ).arg( qgsDoubleToString( rect.xMinimum() ) ); mbrFilter += QString( "ymin <= %1 AND " ).arg( qgsDoubleToString( rect.yMaximum() ) ); mbrFilter += QString( "ymax >= %1" ).arg( qgsDoubleToString( rect.yMinimum() ) ); - QString idxName = QString( "idx_%1_%2" ).arg( P->mIndexTable ).arg( P->mIndexGeometry ); + QString idxName = QString( "idx_%1_%2" ).arg( mSource->mIndexTable ).arg( mSource->mIndexGeometry ); whereClause += QString( "%1 IN (SELECT pkid FROM %2 WHERE %3)" ) .arg( quotedPrimaryKey() ) - .arg( P->quotedIdentifier( idxName ) ) + .arg( QgsSpatiaLiteProvider::quotedIdentifier( idxName ) ) .arg( mbrFilter ); } - else if ( P->spatialIndexMbrCache ) + else if ( mSource->spatialIndexMbrCache ) { // using the MbrCache spatial index - QString idxName = QString( "cache_%1_%2" ).arg( P->mIndexTable ).arg( P->mIndexGeometry ); + QString idxName = QString( "cache_%1_%2" ).arg( mSource->mIndexTable ).arg( mSource->mIndexGeometry ); whereClause += QString( "%1 IN (SELECT rowid FROM %2 WHERE mbr = FilterMbrIntersects(%3))" ) .arg( quotedPrimaryKey() ) - .arg( P->quotedIdentifier( idxName ) ) + .arg( QgsSpatiaLiteProvider::quotedIdentifier( idxName ) ) .arg( mbr( rect ) ); } else { // using simple MBR filtering - whereClause += QString( "MbrIntersects(%1, BuildMbr(%2))" ).arg( P->quotedIdentifier( P->mGeometryColumn ) ).arg( mbr( rect ) ); + whereClause += QString( "MbrIntersects(%1, BuildMbr(%2))" ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( mbr( rect ) ); } } return whereClause; @@ -267,7 +264,7 @@ QString QgsSpatiaLiteFeatureIterator::mbr( const QgsRectangle& rect ) QString QgsSpatiaLiteFeatureIterator::fieldName( const QgsField& fld ) { - QString fieldname = P->quotedIdentifier( fld.name() ); + QString fieldname = QgsSpatiaLiteProvider::quotedIdentifier( fld.name() ); const QString type = fld.typeName().toLower(); if ( type.contains( "geometry" ) || type.contains( "point" ) || type.contains( "line" ) || type.contains( "polygon" ) ) @@ -291,7 +288,7 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f if ( ret != SQLITE_ROW ) { // some unexpected error occurred - QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( P->sqliteHandle ) ) ), QObject::tr( "SpatiaLite" ) ); + QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mSource->sqliteHandle ) ) ), QObject::tr( "SpatiaLite" ) ); return false; } @@ -302,8 +299,8 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f feature.setGeometryAndOwnership( 0, 0 ); } - feature.initAttributes( P->fields().count() ); - feature.setFields( &P->attributeFields ); // allow name-based attribute lookups + feature.initAttributes( mSource->mFields.count() ); + feature.setFields( &mSource->mFields ); // allow name-based attribute lookups int ic; int n_columns = sqlite3_column_count( stmt ); @@ -327,13 +324,13 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f if ( ic <= mRequest.subsetOfAttributes().size() ) { int attrIndex = mRequest.subsetOfAttributes()[ic-1]; - feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, P->attributeFields[attrIndex].type() ) ); + feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields[attrIndex].type() ) ); } } else { int attrIndex = ic - 1; - feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, P->attributeFields[attrIndex].type() ) ); + feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields[attrIndex].type() ) ); } } } @@ -374,7 +371,7 @@ void QgsSpatiaLiteFeatureIterator::getFeatureGeometry( sqlite3_stmt* stmt, int i size_t geom_size = 0; const void *blob = sqlite3_column_blob( stmt, ic ); size_t blob_size = sqlite3_column_bytes( stmt, ic ); - P->convertToGeosWKB(( const unsigned char * )blob, blob_size, + QgsSpatiaLiteProvider::convertToGeosWKB(( const unsigned char * )blob, blob_size, &featureGeom, &geom_size ); if ( featureGeom ) feature.setGeometryAndOwnership( featureGeom, geom_size ); @@ -387,3 +384,31 @@ void QgsSpatiaLiteFeatureIterator::getFeatureGeometry( sqlite3_stmt* stmt, int i feature.setGeometryAndOwnership( 0, 0 ); } } + + +QgsSpatiaLiteFeatureSource::QgsSpatiaLiteFeatureSource( const QgsSpatiaLiteProvider* p ) + : mGeometryColumn( p->mGeometryColumn ) + , mSubsetString( p->mSubsetString ) + , mFields( p->attributeFields ) + , mQuery( p->mQuery ) + , isQuery( p->isQuery ) + , mVShapeBased( p->mVShapeBased ) + , mIndexTable( p->mIndexTable ) + , mIndexGeometry( p->mIndexGeometry ) + , mPrimaryKey( p->mPrimaryKey ) + , spatialIndexRTree( p->spatialIndexRTree ) + , spatialIndexMbrCache( p->spatialIndexMbrCache ) + , handle( QgsSpatiaLiteProvider::SqliteHandles::openDb( p->mSqlitePath ) ) + , sqliteHandle( handle->handle() ) +{ +} + +QgsSpatiaLiteFeatureSource::~QgsSpatiaLiteFeatureSource() +{ + QgsSpatiaLiteProvider::SqliteHandles::closeDb( handle ); +} + +QgsFeatureIterator QgsSpatiaLiteFeatureSource::getFeatures( const QgsFeatureRequest& request ) +{ + return QgsFeatureIterator( new QgsSpatiaLiteFeatureIterator( this, false, request ) ); +} diff --git a/src/providers/spatialite/qgsspatialitefeatureiterator.h b/src/providers/spatialite/qgsspatialitefeatureiterator.h index 1e9268d87c5..783b1b29b7b 100644 --- a/src/providers/spatialite/qgsspatialitefeatureiterator.h +++ b/src/providers/spatialite/qgsspatialitefeatureiterator.h @@ -23,12 +23,41 @@ extern "C" #include } +#include "qgsspatialiteprovider.h" + class QgsSpatiaLiteProvider; -class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIterator +class QgsSpatiaLiteFeatureSource : public QgsAbstractFeatureSource +{ +public: + QgsSpatiaLiteFeatureSource( const QgsSpatiaLiteProvider* p ); + ~QgsSpatiaLiteFeatureSource(); + + virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ); + +protected: + QString mGeometryColumn; + QString mSubsetString; + QgsFields mFields; + QString mQuery; + bool isQuery; + bool mVShapeBased; + QString mIndexTable; + QString mIndexGeometry; + QString mPrimaryKey; + bool spatialIndexRTree; + bool spatialIndexMbrCache; + + QgsSpatiaLiteProvider::SqliteHandles* handle; + sqlite3 *sqliteHandle; + + friend class QgsSpatiaLiteFeatureIterator; +}; + +class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource { public: - QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteProvider* p, const QgsFeatureRequest& request ); + QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ); ~QgsSpatiaLiteFeatureIterator(); @@ -43,8 +72,6 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIterator //! fetch next feature, return true on success virtual bool fetchFeature( QgsFeature& feature ); - QgsSpatiaLiteProvider* P; - QString whereClauseRect(); QString whereClauseFid(); QString mbr( const QgsRectangle& rect ); diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index fda8af5abc0..eff2c6ca150 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -577,16 +577,14 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri ) QgsSpatiaLiteProvider::~QgsSpatiaLiteProvider() { - while ( !mActiveIterators.empty() ) - { - QgsSpatiaLiteFeatureIterator *it = *mActiveIterators.begin(); - QgsDebugMsg( "closing active iterator" ); - it->close(); - } - closeDb(); } +QgsAbstractFeatureSource* QgsSpatiaLiteProvider::featureSource() const +{ + return new QgsSpatiaLiteFeatureSource( this ); +} + #ifdef SPATIALITE_VERSION_GE_4_0_0 // only if libspatialite version is >= 4.0.0 void QgsSpatiaLiteProvider::loadFieldsAbstractInterface( gaiaVectorLayerPtr lyr ) @@ -889,7 +887,7 @@ QgsFeatureIterator QgsSpatiaLiteProvider::getFeatures( const QgsFeatureRequest& QgsDebugMsg( "Read attempt on an invalid SpatiaLite data source" ); return QgsFeatureIterator(); } - return QgsFeatureIterator( new QgsSpatiaLiteFeatureIterator( this, request ) ); + return QgsFeatureIterator( new QgsSpatiaLiteFeatureIterator( new QgsSpatiaLiteFeatureSource( this ), true, request ) ); } diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h index 31bacc72b23..a349c396e46 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.h +++ b/src/providers/spatialite/qgsspatialiteprovider.h @@ -14,6 +14,9 @@ email : a.furieri@lqt.it * * ***************************************************************************/ +#ifndef QGSSPATIALITEPROVIDER_H +#define QGSSPATIALITEPROVIDER_H + extern "C" { #include @@ -70,6 +73,8 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider //! Destructor virtual ~ QgsSpatiaLiteProvider(); + virtual QgsAbstractFeatureSource* featureSource() const; + /** * Returns the permanent storage type for this layer as a friendly name. */ @@ -409,8 +414,14 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider bool getFeature( sqlite3_stmt *stmt, bool fetchGeometry, QgsFeature &feature, const QgsAttributeList &fetchAttributes ); - void convertToGeosWKB( const unsigned char *blob, size_t blob_size, +public: + // static functions + + static void convertToGeosWKB( const unsigned char *blob, size_t blob_size, unsigned char **wkb, size_t *geom_size ); + static int computeMultiWKB3Dsize( const unsigned char *p_in, int little_endian, + int endian_arch ); +private: int computeSizeFromMultiWKB2D( const unsigned char *p_in, int nDims, int little_endian, int endian_arch ); @@ -423,8 +434,6 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider void convertFromGeosWKB3D( const unsigned char *blob, size_t blob_size, unsigned char *wkb, size_t geom_size, int nDims, int little_endian, int endian_arch ); - int computeMultiWKB3Dsize( const unsigned char *p_in, int little_endian, - int endian_arch ); void convertFromGeosWKB( const unsigned char *blob, size_t blob_size, unsigned char **wkb, size_t *geom_size, int dims ); @@ -446,12 +455,12 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider GEOS_3D_GEOMETRYCOLLECTION = -2147483641, }; - struct SLFieldNotFound {}; //! Exception to throw - public: static QString quotedIdentifier( QString id ); static QString quotedValue( QString value ); + struct SLFieldNotFound {}; //! Exception to throw + class SqliteHandles { // @@ -515,6 +524,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider */ SqliteHandles *handle; - friend class QgsSpatiaLiteFeatureIterator; - QSet< QgsSpatiaLiteFeatureIterator * > mActiveIterators; + friend class QgsSpatiaLiteFeatureSource; }; + +#endif