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:
Martin Dobias 2013-12-03 12:26:47 +07:00
parent 5590ab4a09
commit 9d001853c0
31 changed files with 846 additions and 410 deletions

View File

@ -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

View File

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

View File

@ -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

View File

@ -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;
}

View File

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

View File

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

View File

@ -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;
}

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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