Skip to content

Commit

Permalink
Fix various crashes on a vector layer with a broken provider
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault authored and nyalldawson committed May 31, 2021
1 parent af93382 commit d75e341
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/app/qgisapp.cpp
Expand Up @@ -8609,7 +8609,7 @@ void QgisApp::fieldCalculator()
void QgisApp::attributeTable( QgsAttributeTableFilterModel::FilterMode filter )
{
QgsVectorLayer *myLayer = qobject_cast<QgsVectorLayer *>( activeLayer() );
if ( !myLayer )
if ( !myLayer || !myLayer->dataProvider() )
{
return;
}
Expand Down Expand Up @@ -8881,7 +8881,7 @@ void QgisApp::saveStyleFile( QgsMapLayer *layer )
layer = activeLayer();
}

if ( !layer )
if ( !layer || !layer->dataProvider() )
return;

switch ( layer->type() )
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgisappinterface.cpp
Expand Up @@ -501,7 +501,7 @@ void QgisAppInterface::showLayerProperties( QgsMapLayer *l )

QDialog *QgisAppInterface::showAttributeTable( QgsVectorLayer *l, const QString &filterExpression )
{
if ( l )
if ( l && l->dataProvider() )
{
QgsAttributeTableDialog *dialog = new QgsAttributeTableDialog( l, QgsAttributeTableFilterModel::ShowFilteredList );
dialog->setFilterExpression( filterExpression );
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsmaptoolidentifyaction.cpp
Expand Up @@ -93,7 +93,7 @@ void QgsMapToolIdentifyAction::showAttributeTable( QgsMapLayer *layer, const QLi
resultsDialog()->clear();

QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
if ( !vl )
if ( !vl || !vl->dataProvider() )
return;

QString filter = QStringLiteral( "$id IN (" );
Expand Down
7 changes: 4 additions & 3 deletions src/app/qgsprojectproperties.cpp
Expand Up @@ -804,7 +804,10 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
currentLayer = it.value();
if ( currentLayer->type() == QgsMapLayerType::VectorLayer )
{

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
QgsVectorDataProvider *provider = vlayer->dataProvider();
if ( !provider )
continue;
QTableWidgetItem *twi = new QTableWidgetItem( QString::number( j ) );
twWFSLayers->setVerticalHeaderItem( j, twi );

Expand All @@ -822,8 +825,6 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
psb->setValue( QgsProject::instance()->readNumEntry( QStringLiteral( "WFSLayersPrecision" ), "/" + currentLayer->id(), 8 ) );
twWFSLayers->setCellWidget( j, 2, psb );

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
QgsVectorDataProvider *provider = vlayer->dataProvider();
if ( ( provider->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) && ( provider->capabilities() & QgsVectorDataProvider::ChangeGeometries ) )
{
QCheckBox *cbu = new QCheckBox();
Expand Down
11 changes: 7 additions & 4 deletions src/core/qgsrelationmanager.cpp
Expand Up @@ -266,12 +266,15 @@ QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelatio
const auto constLayers = layers;
for ( const QgsVectorLayer *layer : constLayers )
{
const auto constDiscoverRelations = layer->dataProvider()->discoverRelations( layer, layers );
for ( const QgsRelation &relation : constDiscoverRelations )
if ( const QgsVectorDataProvider *provider = layer->dataProvider() )
{
if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
const auto constDiscoverRelations = provider->discoverRelations( layer, layers );
for ( const QgsRelation &relation : constDiscoverRelations )
{
result.append( relation );
if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
{
result.append( relation );
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsvectorlayer.cpp
Expand Up @@ -269,7 +269,8 @@ QgsVectorLayer *QgsVectorLayer::clone() const
layer->addJoin( join );
}

layer->setProviderEncoding( dataProvider()->encoding() );
if ( mDataProvider )
layer->setProviderEncoding( mDataProvider->encoding() );
layer->setDisplayExpression( displayExpression() );
layer->setMapTipTemplate( mapTipTemplate() );
layer->setReadOnly( isReadOnly() );
Expand Down
26 changes: 17 additions & 9 deletions src/core/qgsvectorlayerfeatureiterator.cpp
Expand Up @@ -31,7 +31,9 @@
QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *layer )
{
QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
mProviderFeatureSource.reset( layer->dataProvider()->featureSource() );
const QgsVectorDataProvider *provider = layer->dataProvider();
if ( provider )
mProviderFeatureSource.reset( provider->featureSource() );
mFields = layer->fields();
mId = layer->id();

Expand Down Expand Up @@ -286,13 +288,16 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
}
else // no filter or filter by rect
{
if ( mSource->mHasEditBuffer )
{
mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
}
else
if ( mSource->mProviderFeatureSource )
{
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
if ( mSource->mHasEditBuffer )
{
mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
}
else
{
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
}
}

rewindEditBuffer();
Expand Down Expand Up @@ -430,8 +435,11 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature &f )
if ( mProviderIterator.isClosed() )
{
mChangedFeaturesIterator.close();
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
mProviderIterator.setInterruptionChecker( mInterruptionChecker );
if ( mSource->mProviderFeatureSource )
{
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
mProviderIterator.setInterruptionChecker( mInterruptionChecker );
}
}

while ( mProviderIterator.nextFeature( f ) )
Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgsmaptoolidentify.cpp
Expand Up @@ -511,7 +511,7 @@ QMap<QString, QString> QgsMapToolIdentify::derivedAttributesForPoint( const QgsP

bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
{
if ( !layer || !layer->isSpatial() )
if ( !layer || !layer->isSpatial() || !layer->dataProvider() )
return false;

if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
Expand Down
8 changes: 6 additions & 2 deletions src/gui/vector/qgsfieldcalculator.cpp
Expand Up @@ -59,9 +59,13 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer *vl, QWidget *parent )

if ( !vl )
return;
QgsVectorDataProvider *dataProvider = vl->dataProvider();
if ( !dataProvider )
return;

mCanAddAttribute = vl->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes;
mCanChangeAttributeValue = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
const QgsVectorDataProvider::Capabilities caps = dataProvider->capabilities();
mCanAddAttribute = caps & QgsVectorDataProvider::AddAttributes;
mCanChangeAttributeValue = caps & QgsVectorDataProvider::ChangeAttributeValues;

QgsExpressionContext expContext( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );

Expand Down
7 changes: 6 additions & 1 deletion src/gui/vector/qgssourcefieldsproperties.cpp
Expand Up @@ -321,6 +321,11 @@ void QgsSourceFieldsProperties::editingToggled()

void QgsSourceFieldsProperties::addAttributeClicked()
{
if ( !mLayer || !mLayer->dataProvider() )
{
return;
}

QgsAddAttrDialog dialog( mLayer, this );
if ( dialog.exec() == QDialog::Accepted )
{
Expand Down Expand Up @@ -364,7 +369,7 @@ void QgsSourceFieldsProperties::deleteAttributeClicked()

void QgsSourceFieldsProperties::calculateFieldClicked()
{
if ( !mLayer )
if ( !mLayer || !mLayer->dataProvider() )
{
return;
}
Expand Down
2 changes: 2 additions & 0 deletions src/gui/vector/qgsvectorlayerproperties.cpp
Expand Up @@ -1107,6 +1107,8 @@ void QgsVectorLayerProperties::loadDefaultMetadata()

void QgsVectorLayerProperties::saveStyleAs()
{
if ( !mLayer->dataProvider() )
return;
QgsVectorLayerSaveStyleDialog dlg( mLayer );
QgsSettings settings;

Expand Down
184 changes: 184 additions & 0 deletions tests/src/python/test_qgsvectorlayer.py
Expand Up @@ -22,6 +22,7 @@

from qgis.core import (QgsWkbTypes,
QgsAction,
QgsAuxiliaryStorage,
QgsCoordinateTransformContext,
QgsDataProvider,
QgsDefaultValue,
Expand Down Expand Up @@ -51,6 +52,7 @@
QgsLineSymbol,
QgsMapLayerStyle,
QgsMapLayerDependency,
QgsRenderContext,
QgsPalLayerSettings,
QgsVectorLayerSimpleLabeling,
QgsSingleCategoryDiagramRenderer,
Expand Down Expand Up @@ -3465,6 +3467,188 @@ def testSubsetStringInvalidLayer(self):
vl2.readXml(elem, QgsReadWriteContext())
self.assertEqual(vl2.subsetString(), 'xxxxxxxxx')

def testLayerWithoutProvider(self):
"""Test that we don't crash when invoking methods on a layer with a broken provider"""
layer = QgsVectorLayer("test", "test", "broken_provider")
layer.clone()
layer.storageType()
layer.capabilitiesString()
layer.dataComment()
layer.displayField()
layer.setDisplayExpression('')
layer.displayExpression()
layer.dataProvider()
layer.temporalProperties()
layer.setProviderEncoding('utf-8')
layer.setCoordinateSystem()
layer.addJoin(QgsVectorLayerJoinInfo())
layer.removeJoin('id')
layer.joinBuffer()
layer.vectorJoins()
layer.setDependencies([])
layer.dependencies()
idx = layer.addExpressionField('1+1', QgsField('foo'))
# layer.expressionField(idx)
# layer.updateExpressionField(idx, '')
# layer.removeExpressionField(idx)
layer.actions()
layer.serverProperties()
layer.selectedFeatureCount()
layer.selectByRect(QgsRectangle())
layer.selectByExpression('1')
layer.selectByIds([0])
layer.modifySelection([], [])
layer.invertSelection()
layer.selectAll()
layer.invertSelectionInRectangle(QgsRectangle())
layer.selectedFeatures()
layer.getSelectedFeatures()
layer.selectedFeatureIds()
layer.boundingBoxOfSelected()
layer.labelsEnabled()
layer.setLabelsEnabled(False)
layer.diagramsEnabled()
layer.setDiagramRenderer(None)
layer.diagramRenderer()
layer.diagramLayerSettings()
layer.setDiagramLayerSettings(QgsDiagramLayerSettings())
layer.renderer()
layer.setRenderer(None)
layer.geometryType()
layer.wkbType()
layer.sourceCrs()
layer.sourceName()
layer.readXml
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
layer.writeXml(elem, doc, QgsReadWriteContext())
layer.readXml(elem, QgsReadWriteContext())
layer.encodedSource('', QgsReadWriteContext())
layer.decodedSource('', 'invalid_provider', QgsReadWriteContext())
layer.resolveReferences(QgsProject())
layer.saveStyleToDatabase('name', 'description', False, 'uiFileContent')
layer.listStylesInDatabase()
layer.getStyleFromDatabase('id')
layer.deleteStyleFromDatabase('id')
layer.loadNamedStyle('uri', False)
layer.loadAuxiliaryLayer(QgsAuxiliaryStorage())
layer.setAuxiliaryLayer(None)
layer.auxiliaryLayer()
# layer.readSymbology()
# layer.readStyle()
# layer.writeSymbology()
# layer.writeStyle()
# layer.writeSld()
# layer.readSld()
layer.featureCount(None)
layer.symbolFeatureIds(None)
layer.hasFeatures()
layer.loadDefaultStyle()
layer.countSymbolFeatures()
layer.setSubsetString(None)
layer.subsetString()
layer.getFeatures()
layer.getFeature(0)
layer.getGeometry(0)
layer.getFeatures([0])
layer.getFeatures(QgsRectangle())
layer.addFeature(QgsFeature())
layer.updateFeature(QgsFeature())
layer.insertVertex(0, 0, 0, False)
layer.moveVertex(0, 0, 0, False)
layer.moveVertexV2(QgsPoint(), 0, False)
layer.deleteVertex(0, 0)
layer.deleteSelectedFeatures()
layer.addRing([QgsPointXY()])
# layer.addRing(QgsPointSequence())
# layer.addRing(QgsCurve())
# layer.addPart()
layer.translateFeature(0, 0, 0)
layer.splitParts([])
layer.splitFeatures([])
layer.addTopologicalPoints(QgsPoint())
layer.labeling()
layer.setLabeling(None)
layer.isEditable()
layer.isSpatial()
layer.isModified()
layer.isAuxiliaryField(0)
layer.reload()
layer.createMapRenderer(QgsRenderContext())
layer.extent()
layer.sourceExtent()
layer.fields()
layer.attributeList()
layer.primaryKeyAttributes()
layer.featureCount()
layer.setReadOnly(False)
layer.changeGeometry(0, QgsGeometry())
layer.changeAttributeValue(0, 0, '')
layer.changeAttributeValues(0, {})
layer.addAttribute(QgsField('foo'))
layer.setFieldAlias(0, 'bar')
layer.removeFieldAlias(0)
layer.renameAttribute(0, 'bar')
layer.attributeAlias(0)
layer.attributeDisplayName(0)
layer.attributeAliases()
layer.deleteAttribute(0)
layer.deleteAttributes([])
layer.deleteFeature(0)
layer.deleteFeatures([])
layer.commitChanges()
layer.commitErrors()
layer.rollBack()
layer.referencingRelations(0)
layer.editBuffer()
layer.beginEditCommand('foo')
layer.endEditCommand()
layer.destroyEditCommand()
layer.updateFields()
layer.defaultValue(0)
layer.setDefaultValueDefinition(0, layer.defaultValueDefinition(0))
layer.fieldConstraints(0)
layer.fieldConstraintsAndStrength(0)
layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
layer.removeFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
layer.constraintExpression(0)
layer.constraintDescription(0)
layer.setConstraintExpression(0, '1')
layer.setEditorWidgetSetup(0, QgsEditorWidgetSetup('Hidden', {}))
layer.editorWidgetSetup(0)
layer.uniqueValues(0)
layer.uniqueStringsMatching(0, None)
layer.minimumValue(0)
layer.maximumValue(0)
layer.aggregate(QgsAggregateCalculator.Count, 'foo')
layer.setFeatureBlendMode(QPainter.CompositionMode_Screen)
layer.featureBlendMode()
layer.htmlMetadata()
layer.setSimplifyMethod(layer.simplifyMethod())
# layer.simplifyDrawingCanbeApplied()
layer.conditionalStyles()
layer.attributeTableConfig()
layer.setAttributeTableConfig(layer.attributeTableConfig())
layer.mapTipTemplate()
layer.setMapTipTemplate('')
layer.createExpressionContext()
layer.editFormConfig()
layer.setEditFormConfig(layer.editFormConfig())
layer.setReadExtentFromXml(False)
layer.readExtentFromXml()
layer.isEditCommandActive()
layer.storedExpressionManager()
layer.select(0)
layer.select([])
layer.deselect(0)
layer.deselect([])
layer.removeSelection()
layer.reselect()
layer.updateExtents()
layer.startEditing()
layer.setTransformContext(QgsCoordinateTransformContext())
layer.hasSpatialIndex()
# layer.accept(QgsStyleEntityVisitorInterface())

# TODO:
# - fetch rect: feat with changed geometry: 1. in rect, 2. out of rect
Expand Down

0 comments on commit d75e341

Please sign in to comment.