Skip to content

Commit d5e57d3

Browse files
authored
Merge pull request #8186 from rouault/fix_20104
[GDAL provider] Make sure that setEditable(true) invalides cached GDAL handles to get proper refresh (fixes #20104)
2 parents b777ab2 + 651ccb4 commit d5e57d3

File tree

2 files changed

+57
-15
lines changed

2 files changed

+57
-15
lines changed

src/providers/gdal/qgsgdalprovider.cpp

+11-15
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ bool QgsGdalProvider::cacheGdalHandlesForLaterReuse( QgsGdalProvider *provider,
392392
{
393393
mgDatasetCacheSize --;
394394
DatasetPair pair = mgDatasetCache[ candidateProvider ].takeLast();
395-
if ( pair.mGdalBaseDataset )
395+
if ( pair.mGdalBaseDataset != pair.mGdalDataset )
396396
{
397397
GDALDereferenceDataset( pair.mGdalBaseDataset );
398398
}
@@ -435,7 +435,7 @@ void QgsGdalProvider::closeCachedGdalHandlesFor( QgsGdalProvider *provider )
435435
{
436436
mgDatasetCacheSize --;
437437
DatasetPair pair = iter.value().takeLast();
438-
if ( pair.mGdalBaseDataset )
438+
if ( pair.mGdalBaseDataset != pair.mGdalDataset )
439439
{
440440
GDALDereferenceDataset( pair.mGdalBaseDataset );
441441
}
@@ -464,7 +464,7 @@ QgsGdalProvider::~QgsGdalProvider()
464464
}
465465
else
466466
{
467-
if ( mGdalBaseDataset )
467+
if ( mGdalBaseDataset != mGdalDataset )
468468
{
469469
GDALDereferenceDataset( mGdalBaseDataset );
470470
}
@@ -490,7 +490,6 @@ QgsGdalProvider::~QgsGdalProvider()
490490
}
491491

492492

493-
// This was used by raster layer to reload data
494493
void QgsGdalProvider::closeDataset()
495494
{
496495
if ( !mValid )
@@ -499,11 +498,16 @@ void QgsGdalProvider::closeDataset()
499498
}
500499
mValid = false;
501500

502-
GDALDereferenceDataset( mGdalBaseDataset );
501+
if ( mGdalBaseDataset != mGdalDataset )
502+
{
503+
GDALDereferenceDataset( mGdalBaseDataset );
504+
}
503505
mGdalBaseDataset = nullptr;
504506

505507
GDALClose( mGdalDataset );
506508
mGdalDataset = nullptr;
509+
510+
closeCachedGdalHandlesFor( this );
507511
}
508512

509513
QString QgsGdalProvider::htmlMetadata()
@@ -2605,7 +2609,6 @@ void QgsGdalProvider::initBaseDataset()
26052609
{
26062610
QgsLogger::warning( QStringLiteral( "Warped VRT Creation failed." ) );
26072611
mGdalDataset = mGdalBaseDataset;
2608-
GDALReferenceDataset( mGdalDataset );
26092612
}
26102613
else
26112614
{
@@ -2615,7 +2618,6 @@ void QgsGdalProvider::initBaseDataset()
26152618
else
26162619
{
26172620
mGdalDataset = mGdalBaseDataset;
2618-
GDALReferenceDataset( mGdalDataset );
26192621
}
26202622

26212623
if ( !hasGeoTransform )
@@ -2644,12 +2646,7 @@ void QgsGdalProvider::initBaseDataset()
26442646
{
26452647
appendError( ERRMSG( tr( "Cannot get GDAL raster band: %1" ).arg( msg ) ) );
26462648

2647-
GDALDereferenceDataset( mGdalBaseDataset );
2648-
mGdalBaseDataset = nullptr;
2649-
2650-
GDALClose( mGdalDataset );
2651-
mGdalDataset = nullptr;
2652-
mValid = false;
2649+
closeDataset();
26532650
return;
26542651
}
26552652
// if there are subdatasets, leave the dataset open for subsequent queries
@@ -2956,8 +2953,7 @@ bool QgsGdalProvider::remove()
29562953
if ( mGdalDataset )
29572954
{
29582955
GDALDriverH driver = GDALGetDatasetDriver( mGdalDataset );
2959-
GDALClose( mGdalDataset );
2960-
mGdalDataset = nullptr;
2956+
closeDataset();
29612957

29622958
CPLErrorReset();
29632959
CPLErr err = GDALDeleteDataset( driver, dataSourceUri( true ).toUtf8().constData() );

tests/src/providers/testqgsgdalprovider.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class TestQgsGdalProvider : public QObject
5454
void bandName(); // test band name based on `gtiff` tags (#7317)
5555
void bandNameNoDescription(); // test band name for when no description or tags available (#16047)
5656
void bandNameWithDescription(); // test band name for when description available (#16047)
57+
void interactionBetweenRasterChangeAndCache(); // test that updading a raster invalidates the GDAL dataset cache (#20104)
5758

5859
private:
5960
QString mTestDataDir;
@@ -275,5 +276,50 @@ void TestQgsGdalProvider::bandNameWithDescription()
275276
delete provider;
276277
}
277278

279+
void TestQgsGdalProvider::interactionBetweenRasterChangeAndCache()
280+
{
281+
double geoTransform[6] = { 0, 2, 0, 0, 0, -2};
282+
QgsCoordinateReferenceSystem crs;
283+
QString filename = QStringLiteral( "/vsimem/temp.tif" );
284+
285+
// Create a all-0 dataset
286+
auto provider = QgsRasterDataProvider::create(
287+
QStringLiteral( "gdal" ), filename, "GTiff", 1, Qgis::Byte, 1, 1, geoTransform, crs );
288+
delete provider;
289+
290+
// Open it
291+
provider = dynamic_cast< QgsRasterDataProvider * >(
292+
QgsProviderRegistry::instance()->createProvider(
293+
QStringLiteral( "gdal" ), filename, QgsDataProvider::ProviderOptions() ) );
294+
QVERIFY( provider );
295+
auto rp = dynamic_cast< QgsRasterDataProvider * >( provider );
296+
QVERIFY( rp );
297+
298+
// Create a first clone, and destroys it
299+
auto rpClone = dynamic_cast< QgsRasterDataProvider *>( rp->clone() );
300+
QVERIFY( rpClone );
301+
QCOMPARE( rpClone->sample( QgsPointXY( 0.5, -0.5 ), 1 ), 0.0 );
302+
delete rpClone;
303+
// Now the main provider should have a cached GDAL dataset corresponding
304+
// to the one that was used by rpClone
305+
306+
// Modify the raster
307+
rp->setEditable( true );
308+
auto rblock = new QgsRasterBlock( Qgis::Byte, 1, 1 );
309+
rblock->setValue( 0, 0, 255 );
310+
rp->writeBlock( rblock, 1, 0, 0 );
311+
delete rblock;
312+
rp->setEditable( false );
313+
314+
// Creates a new clone, and check that we get an updated sample value
315+
rpClone = dynamic_cast< QgsRasterDataProvider *>( rp->clone() );
316+
QVERIFY( rpClone );
317+
QCOMPARE( rpClone->sample( QgsPointXY( 0.5, -0.5 ), 1 ), 255.0 );
318+
delete rpClone;
319+
320+
provider->remove();
321+
delete provider;
322+
}
323+
278324
QGSTEST_MAIN( TestQgsGdalProvider )
279325
#include "testqgsgdalprovider.moc"

0 commit comments

Comments
 (0)