Skip to content

Commit 4bcd2e4

Browse files
authoredJan 6, 2018
Merge pull request #6002 from elpaso/bugfix-17795-2-18-backport
[bugfix][ogr] Recompute capabilities when subsetfilter is set
2 parents 8127c4c + 0f2a520 commit 4bcd2e4

File tree

5 files changed

+158
-86
lines changed

5 files changed

+158
-86
lines changed
 

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 106 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -503,89 +503,8 @@ QgsAbstractFeatureSource* QgsOgrProvider::featureSource() const
503503

504504
bool QgsOgrProvider::setSubsetString( const QString& theSQL, bool updateFeatureCount )
505505
{
506-
QgsCPLErrorHandler handler;
507-
508-
if ( !ogrDataSource )
509-
return false;
510-
511-
if ( theSQL == mSubsetString && mFeaturesCounted >= 0 )
512-
return true;
513-
514-
OGRLayerH prevLayer = ogrLayer;
515-
QString prevSubsetString = mSubsetString;
516-
mSubsetString = theSQL;
517-
518-
if ( !mSubsetString.isEmpty() )
519-
{
520-
521-
ogrLayer = setSubsetString( ogrOrigLayer, ogrDataSource );
522-
if ( !ogrLayer )
523-
{
524-
pushError( tr( "OGR[%1] error %2: %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
525-
ogrLayer = prevLayer;
526-
mSubsetString = prevSubsetString;
527-
return false;
528-
}
529-
}
530-
else
531-
{
532-
ogrLayer = ogrOrigLayer;
533-
}
534-
535-
if ( prevLayer != ogrOrigLayer )
536-
{
537-
OGR_DS_ReleaseResultSet( ogrDataSource, prevLayer );
538-
}
539-
540-
QString uri = mFilePath;
541-
if ( !mLayerName.isNull() )
542-
{
543-
uri += QString( "|layername=%1" ).arg( mLayerName );
544-
}
545-
else if ( mLayerIndex >= 0 )
546-
{
547-
uri += QString( "|layerid=%1" ).arg( mLayerIndex );
548-
}
549-
550-
if ( !mSubsetString.isEmpty() )
551-
{
552-
uri += QString( "|subset=%1" ).arg( mSubsetString );
553-
}
554-
555-
if ( mOgrGeometryTypeFilter != wkbUnknown )
556-
{
557-
uri += QString( "|geometrytype=%1" ).arg( ogrWkbGeometryTypeName( mOgrGeometryTypeFilter ) );
558-
}
559-
560-
if ( uri != dataSourceUri() )
561-
{
562-
QgsOgrConnPool::instance()->unref( dataSourceUri() );
563-
setDataSourceUri( uri );
564-
QgsOgrConnPool::instance()->ref( dataSourceUri() );
565-
}
566-
567-
OGR_L_ResetReading( ogrLayer );
568-
569-
// getting the total number of features in the layer
570-
// TODO: This can be expensive, do we really need it!
571-
if ( updateFeatureCount )
572-
{
573-
recalculateFeatureCount();
574-
}
575-
576-
// check the validity of the layer
577-
QgsDebugMsg( "checking validity" );
578-
loadFields();
579-
QgsDebugMsg( "Done checking validity" );
580-
581-
invalidateCachedExtent( false );
582-
583-
// Changing the filter may change capabilities
584-
computeCapabilities();
585-
586-
emit dataChanged();
587-
588-
return true;
506+
// Always update capabilities
507+
return _setSubsetString( theSQL, updateFeatureCount, true );
589508
}
590509

591510
QString QgsOgrProvider::subsetString()
@@ -1640,6 +1559,94 @@ bool QgsOgrProvider::commitTransaction()
16401559
return true;
16411560
}
16421561

1562+
bool QgsOgrProvider::_setSubsetString( const QString &theSQL, bool updateFeatureCount, bool updateCapabilities )
1563+
{
1564+
QgsCPLErrorHandler handler;
1565+
1566+
if ( !ogrDataSource )
1567+
return false;
1568+
1569+
if ( theSQL == mSubsetString && mFeaturesCounted >= 0 )
1570+
return true;
1571+
1572+
OGRLayerH prevLayer = ogrLayer;
1573+
QString prevSubsetString = mSubsetString;
1574+
mSubsetString = theSQL;
1575+
1576+
if ( !mSubsetString.isEmpty() )
1577+
{
1578+
1579+
ogrLayer = setSubsetString( ogrOrigLayer, ogrDataSource );
1580+
if ( !ogrLayer )
1581+
{
1582+
pushError( tr( "OGR[%1] error %2: %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
1583+
ogrLayer = prevLayer;
1584+
mSubsetString = prevSubsetString;
1585+
return false;
1586+
}
1587+
}
1588+
else
1589+
{
1590+
ogrLayer = ogrOrigLayer;
1591+
}
1592+
1593+
if ( prevLayer != ogrOrigLayer )
1594+
{
1595+
OGR_DS_ReleaseResultSet( ogrDataSource, prevLayer );
1596+
}
1597+
1598+
QString uri = mFilePath;
1599+
if ( !mLayerName.isNull() )
1600+
{
1601+
uri += QString( "|layername=%1" ).arg( mLayerName );
1602+
}
1603+
else if ( mLayerIndex >= 0 )
1604+
{
1605+
uri += QString( "|layerid=%1" ).arg( mLayerIndex );
1606+
}
1607+
1608+
if ( !mSubsetString.isEmpty() )
1609+
{
1610+
uri += QString( "|subset=%1" ).arg( mSubsetString );
1611+
}
1612+
1613+
if ( mOgrGeometryTypeFilter != wkbUnknown )
1614+
{
1615+
uri += QString( "|geometrytype=%1" ).arg( ogrWkbGeometryTypeName( mOgrGeometryTypeFilter ) );
1616+
}
1617+
1618+
if ( uri != dataSourceUri() )
1619+
{
1620+
QgsOgrConnPool::instance()->unref( dataSourceUri() );
1621+
setDataSourceUri( uri );
1622+
QgsOgrConnPool::instance()->ref( dataSourceUri() );
1623+
}
1624+
1625+
OGR_L_ResetReading( ogrLayer );
1626+
1627+
// getting the total number of features in the layer
1628+
// TODO: This can be expensive, do we really need it!
1629+
if ( updateFeatureCount )
1630+
{
1631+
recalculateFeatureCount();
1632+
}
1633+
1634+
// check the validity of the layer
1635+
QgsDebugMsg( "checking validity" );
1636+
loadFields();
1637+
QgsDebugMsg( "Done checking validity" );
1638+
1639+
invalidateCachedExtent( false );
1640+
1641+
// Changing the filter may change capabilities
1642+
if ( updateCapabilities )
1643+
computeCapabilities();
1644+
1645+
emit dataChanged();
1646+
1647+
return true;
1648+
}
1649+
16431650

16441651
bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
16451652
{
@@ -2008,10 +2015,18 @@ int QgsOgrProvider::capabilities() const
20082015
void QgsOgrProvider::computeCapabilities()
20092016
{
20102017
int ability = 0;
2018+
bool updateModeActivated = false;
20112019

20122020
// collect abilities reported by OGR
20132021
if ( ogrLayer )
20142022
{
2023+
// We want the layer in rw mode or capabilities will be wrong
2024+
// If mUpdateModeStackDepth > 0, it means that an updateMode is already active and that we have write access
2025+
if ( mUpdateModeStackDepth == 0 )
2026+
{
2027+
updateModeActivated = enterUpdateMode( );
2028+
}
2029+
20152030
// Whilst the OGR documentation (e.g. at
20162031
// http://www.gdal.org/ogr/classOGRLayer.html#a17) states "The capability
20172032
// codes that can be tested are represented as strings, but #defined
@@ -2138,6 +2153,10 @@ void QgsOgrProvider::computeCapabilities()
21382153
}
21392154
}
21402155

2156+
if ( updateModeActivated )
2157+
leaveUpdateMode();
2158+
2159+
21412160
mCapabilities = ability;
21422161
}
21432162

@@ -3620,7 +3639,8 @@ void QgsOgrProvider::open( OpenMode mode )
36203639
mSubsetString = "";
36213640
// Block signals to avoid endless recusion reloadData -> emit dataChanged -> reloadData
36223641
blockSignals( true );
3623-
mValid = setSubsetString( origSubsetString );
3642+
// Do not update capabilities: it will be done later
3643+
mValid = _setSubsetString( origSubsetString, true, false );
36243644
blockSignals( false );
36253645
if ( mValid )
36263646
{
@@ -3696,7 +3716,8 @@ void QgsOgrProvider::open( OpenMode mode )
36963716
{
36973717
int featuresCountedBackup = mFeaturesCounted;
36983718
mFeaturesCounted = -1;
3699-
mValid = setSubsetString( mSubsetString, false );
3719+
// Do not update capabilities here
3720+
mValid = _setSubsetString( mSubsetString, false, false );
37003721
mFeaturesCounted = featuresCountedBackup;
37013722
}
37023723
}

‎src/providers/ogr/qgsogrprovider.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
312312
//! Commits a transaction
313313
bool commitTransaction();
314314

315+
//! Does the real job of settings the subset string and adds an argument to disable update capabilities
316+
bool _setSubsetString( const QString &theSQL, bool updateFeatureCount = true, bool updateCapabilities = true );
317+
315318
QgsFields mAttributeFields;
316319
bool mFirstFieldIsFid;
317320
OGRDataSourceH ogrDataSource;

‎tests/src/python/test_provider_ogr_gpkg.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
from osgeo import gdal, ogr
2222

2323
from qgis.PyQt.QtCore import QCoreApplication, QSettings
24-
from qgis.core import QgsVectorLayer, QgsVectorLayerImport, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsRectangle
24+
from qgis.core import QgsVectorLayer, QgsVectorLayerImport, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsRectangle, QgsVectorDataProvider
2525
from qgis.testing import start_app, unittest
2626
from utilities import unitTestDataPath
2727

28+
from utilities import unitTestDataPath
29+
30+
TEST_DATA_DIR = unitTestDataPath()
31+
2832
start_app()
2933

3034

@@ -419,6 +423,26 @@ def testGeopackageLargeFID(self):
419423
self.assertTrue(vl.deleteFeature(1234567890123))
420424
self.assertTrue(vl.commitChanges())
421425

426+
def testSubSetStringEditable_bug17795(self):
427+
"""Test that a layer is not editable after setting a subset and it's reverted to editable after the filter is removed"""
428+
429+
isEditable = QgsVectorDataProvider.ChangeAttributeValues
430+
testPath = TEST_DATA_DIR + '/' + 'provider/bug_17795.gpkg|layername=bug_17795'
431+
432+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
433+
self.assertTrue(vl.isValid())
434+
self.assertTrue(vl.dataProvider().capabilities() & isEditable)
435+
436+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
437+
vl.setSubsetString('')
438+
self.assertTrue(vl.isValid())
439+
self.assertTrue(vl.dataProvider().capabilities() & isEditable)
440+
441+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
442+
vl.setSubsetString('"category" = \'one\'')
443+
self.assertTrue(vl.isValid())
444+
self.assertFalse(vl.dataProvider().capabilities() & isEditable)
445+
422446

423447
if __name__ == '__main__':
424448
unittest.main()

‎tests/src/python/test_provider_shapefile.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,5 +481,29 @@ def testRepackAtFirstSave(self):
481481
self.assertTrue(ds.GetLayer(0).GetFeatureCount(), original_feature_count - 1)
482482
ds = None
483483

484+
def testSubSetStringEditable_bug17795(self):
485+
"""Test that a layer is not editable after setting a subset and it's reverted to editable after the filter is removed"""
486+
487+
testPath = TEST_DATA_DIR + '/' + 'lines.shp'
488+
isEditable = QgsVectorDataProvider.ChangeAttributeValues
489+
490+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
491+
self.assertTrue(vl.isValid())
492+
self.assertTrue(vl.dataProvider().capabilities() & isEditable)
493+
494+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
495+
vl.setSubsetString('')
496+
self.assertTrue(vl.isValid())
497+
self.assertTrue(vl.dataProvider().capabilities() & isEditable)
498+
499+
vl = QgsVectorLayer(testPath, 'subset_test', 'ogr')
500+
vl.setSubsetString('"Name" = \'Arterial\'')
501+
self.assertTrue(vl.isValid())
502+
self.assertFalse(vl.dataProvider().capabilities() & isEditable)
503+
504+
vl.setSubsetString('')
505+
self.assertTrue(vl.dataProvider().capabilities() & isEditable)
506+
507+
484508
if __name__ == '__main__':
485509
unittest.main()
116 KB
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.