mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Introduction of concept of feature sources for vector data.
Feature sources of providers/layers should act as immutable snapshots of the state of provider or layer, not being affected by any concurrent changes to provider or layer while the source is in use. Currently working just with OGR, Postgres, SpatiaLite providers.
This commit is contained in:
parent
5590ab4a09
commit
9d001853c0
@ -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<typename T>
|
||||
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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<typename> friend class QgsAbstractFeatureIteratorFromSource;
|
||||
};
|
||||
|
||||
#endif // QGSFEATUREREQUEST_H
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
typedef QList<int> 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<QgsField> toList() const;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -228,13 +228,7 @@ QMap<QString, int> 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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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()
|
||||
|
@ -1668,7 +1668,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
|
||||
// Feature counts for each renderer symbol
|
||||
QMap<QgsSymbolV2*, long> mSymbolFeatureCountMap;
|
||||
|
||||
friend class QgsVectorLayerFeatureIterator;
|
||||
friend class QgsVectorLayerFeatureSource;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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<QgsField>( 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<QgsVectorLayer*>( 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<QgsVectorLayer*, FetchJoinInfo>::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()] );
|
||||
}
|
||||
|
||||
|
@ -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<QgsField> mAddedAttributes;
|
||||
QgsChangedAttributesMap mChangedAttributeValues;
|
||||
QgsAttributeList mDeletedAttributeIds;
|
||||
};
|
||||
|
||||
|
||||
class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>
|
||||
{
|
||||
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<QgsFeatureId> 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<QgsField> mAddedAttributes;
|
||||
QgsChangedAttributesMap mChangedAttributeValues;
|
||||
QgsAttributeList mDeletedAttributeIds;
|
||||
|
||||
/** Informations about joins used in the current select() statement.
|
||||
Allows faster mapping of attribute ids compared to mVectorJoins */
|
||||
QMap<QgsVectorLayer*, FetchJoinInfo> mFetchJoinInfo;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "qgssymbollayerv2.h"
|
||||
#include "qgssymbolv2.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectorlayerfeatureiterator.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
@ -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<QgsFeature> > 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
|
||||
|
@ -4,6 +4,7 @@
|
||||
class QgsFeatureRendererV2;
|
||||
class QgsRenderContext;
|
||||
class QgsVectorLayer;
|
||||
class QgsVectorLayerFeatureSource;
|
||||
|
||||
class QgsDiagramRendererV2;
|
||||
class QgsDiagramLayerSettings;
|
||||
@ -24,6 +25,7 @@ typedef QList<int> 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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
@ -19,12 +19,32 @@
|
||||
|
||||
#include <ogr_api.h>
|
||||
|
||||
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<QgsOgrFeatureSource>
|
||||
{
|
||||
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 );
|
||||
|
||||
|
@ -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<const char*> 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<QgsOgrFeatureSource*>( 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 );
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
};
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <netinet/in.h>
|
||||
#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<QString, QgsPostgresConn *> &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<QString, QgsPostgresConn *>& 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 );
|
||||
|
@ -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
|
||||
|
@ -20,63 +20,52 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
// 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<QVariant> 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 ) );
|
||||
}
|
||||
|
@ -19,14 +19,48 @@
|
||||
|
||||
#include <QQueue>
|
||||
|
||||
#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<int> mPrimaryKeyAttrs;
|
||||
QString mQuery;
|
||||
// TODO: loadFields()
|
||||
|
||||
QSharedPointer<QgsPostgresSharedData> mShared;
|
||||
|
||||
friend class QgsPostgresFeatureIterator;
|
||||
};
|
||||
|
||||
|
||||
class QgsPostgresConn;
|
||||
|
||||
class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>
|
||||
{
|
||||
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 );
|
||||
|
@ -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<QVariant, QgsFeatureId>::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<QgsPostgresFeatureSource*>( featureSource() ), false, request ) );
|
||||
}
|
||||
|
||||
|
||||
@ -425,11 +407,11 @@ void QgsPostgresProvider::appendPkParams( QgsFeatureId featureId, QStringList &p
|
||||
|
||||
case pktFidMap:
|
||||
{
|
||||
QVariant pkValsVariant = mShared->lookupKey( featureId );
|
||||
QList<QVariant> pkVals;
|
||||
QMap<QgsFeatureId, QVariant>::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<int>& pkAttrs, QSharedPointer<QgsPostgresSharedData> 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<QgsFeatureId, QVariant>::const_iterator it = mFidToKey.find( featureId );
|
||||
if ( it != mFidToKey.constEnd() )
|
||||
QVariant pkValsVariant = sharedData->lookupKey( featureId );
|
||||
if ( !pkValsVariant.isNull() )
|
||||
{
|
||||
QList<QVariant> pkVals = it.value().toList();
|
||||
QList<QVariant> 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<int>& pkAttrs, QSharedPointer<QgsPostgresSharedData> 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<QVariant> 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<QVariant, QgsFeatureId>::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<QgsFeatureId, QVariant>::const_iterator it = mFidToKey.find( featureId );
|
||||
if ( it != mFidToKey.constEnd() )
|
||||
return it.value();
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -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<QgsPostgresSharedData> 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<QVariant, QgsFeatureId> mKeyToFid; // map key values to feature id
|
||||
QMap<QgsFeatureId, QVariant> 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<int>& pkAttrs,
|
||||
QSharedPointer<QgsPostgresSharedData> sharedData );
|
||||
|
||||
static QString whereClause( QgsFeatureIds featureIds,
|
||||
const QgsFields& fields,
|
||||
QgsPostgresConn* conn,
|
||||
QgsPostgresPrimaryKeyType pkType,
|
||||
const QList<int>& pkAttrs,
|
||||
QSharedPointer<QgsPostgresSharedData> 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<QVariant, QgsFeatureId> mKeyToFid; // map key values to feature id
|
||||
QMap<QgsFeatureId, QVariant> mFidToKey; // map feature back to fea
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
@ -23,12 +23,41 @@ extern "C"
|
||||
#include <sqlite3.h>
|
||||
}
|
||||
|
||||
#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<QgsSpatiaLiteFeatureSource>
|
||||
{
|
||||
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 );
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,6 +14,9 @@ email : a.furieri@lqt.it
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSSPATIALITEPROVIDER_H
|
||||
#define QGSSPATIALITEPROVIDER_H
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <sys/types.h>
|
||||
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user