From a5158c313b1a7de6e5814942727c85a13dc55e91 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 20 Sep 2025 18:33:14 +0200 Subject: [PATCH] Make 'gdal raster/vector clip' work with --like dataset being a PostgreSQL (vector) dataset Fixes #13091 --- .../utilities/test_gdalalg_raster_clip.py | 46 +++++++++++++++++++ gcore/gdalalgorithm.cpp | 21 +++++++++ 2 files changed, 67 insertions(+) diff --git a/autotest/utilities/test_gdalalg_raster_clip.py b/autotest/utilities/test_gdalalg_raster_clip.py index b87d288babf3..613b501351a7 100755 --- a/autotest/utilities/test_gdalalg_raster_clip.py +++ b/autotest/utilities/test_gdalalg_raster_clip.py @@ -104,6 +104,52 @@ def test_gdalalg_raster_clip_like(): assert ds.GetRasterBand(1).Checksum() == 3695 +@pytest.mark.require_driver("PostgreSQL") +def test_gdalalg_raster_clip_like_postgis(): + + val = gdal.GetConfigOption("OGR_PG_CONNECTION_STRING", None) + if val is not None: + pg_connection_string = val + else: + pg_connection_string = "dbname=autotest" + + try: + pg_ds = gdal.OpenEx( + "PG:" + pg_connection_string, gdal.OF_VECTOR | gdal.OF_UPDATE + ) + pg_ds.CreateLayer("test_gdalalg_raster_clip_like_postgis_one") + pg_ds.CreateLayer("test_gdalalg_raster_clip_like_postgis_two") + pg_ds.Close() + + except RuntimeError: + if val is None: + pytest.skip( + f"OGR_PG_CONNECTION_STRING not specified; Postgres is not available using default connection string {pg_connection_string}" + ) + else: + pytest.skip( + f"Postgres is not available using supplied OGR_PG_CONNECTION_STRING {pg_connection_string}" + ) + + try: + alg = get_alg() + alg["input"] = "../gcore/data/byte.tif" + alg["output"] = "" + alg["output-format"] = "MEM" + alg["like"] = "PG:" + pg_connection_string + with pytest.raises( + Exception, + match="Only single layer dataset can be specified with --like when neither --like-layer or --like-sql have been specified", + ): + alg.Run() + finally: + pg_ds = gdal.OpenEx( + "PG:" + pg_connection_string, gdal.OF_VECTOR | gdal.OF_UPDATE + ) + pg_ds.ExecuteSQL("DROP TABLE test_gdalalg_raster_clip_like_postgis_one CASCADE") + pg_ds.ExecuteSQL("DROP TABLE test_gdalalg_raster_clip_like_postgis_two CASCADE") + + def test_gdalalg_raster_clip_like_error(tmp_vsimem): alg = get_alg() diff --git a/gcore/gdalalgorithm.cpp b/gcore/gdalalgorithm.cpp index 397c7e3f8bff..e7a727c11898 100644 --- a/gcore/gdalalgorithm.cpp +++ b/gcore/gdalalgorithm.cpp @@ -2589,6 +2589,27 @@ bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg, m_oMapDatasetNameToDataset.erase(oIter); } + // A bit of a hack for situations like 'gdal raster clip --like "PG:..."' + // where the PG: dataset will be first opened with the PostGISRaster + // driver whereas the PostgreSQL (vector) one is actually wanted. + if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 && + (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() && + aosOpenOptions.empty()) + { + auto poDrv = poDS->GetDriver(); + if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster")) + { + // Retry with PostgreSQL (vector) driver + std::unique_ptr poTmpDS(GDALDataset::Open( + osDatasetName.c_str(), flags & ~GDAL_OF_RASTER)); + if (poTmpDS) + { + poDS->ReleaseRef(); + poDS = poTmpDS.release(); + } + } + } + if (assignToOutputArg) { // Avoid opening twice the same datasource if it is both