Skip to content

Commit

Permalink
GPKG: Add heuristics to try to detect corrupted RTree generated by GD…
Browse files Browse the repository at this point in the history
…AL 3.6.0, and disable use of the rtree
  • Loading branch information
rouault authored and github-actions[bot] committed Dec 13, 2022
1 parent aa069fc commit c4f8495
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
39 changes: 39 additions & 0 deletions autotest/ogr/ogr_gpkg.py
Expand Up @@ -7331,6 +7331,45 @@ def test_ogr_gpkg_background_rtree_build(filename):
gdal.Unlink(filename)


###############################################################################


def test_ogr_gpkg_detect_broken_rtree_gdal_3_6_0():

filename = "/vsimem/test_ogr_gpkg_detect_broken_rtree_gdal_3_6_0.gpkg"

ds = gdaltest.gpkg_dr.CreateDataSource(filename)
lyr = ds.CreateLayer("foo")
for i in range(100):
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometryDirectly(
ogr.CreateGeometryFromWkt("POINT(%d %d)" % (i % 10, i // 10))
)
lyr.CreateFeature(f)
ds = None

# Voluntary corrupt the RTree by removing the entry for the last feature
ds = ogr.Open(filename, update=1)
sql_lyr = ds.ExecuteSQL("DELETE FROM rtree_foo_geom WHERE id = 100")
ds.ReleaseResultSet(sql_lyr)
ds = None

with gdaltest.config_option("OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100"):
ds = ogr.Open(filename)
lyr = ds.GetLayer(0)
with gdaltest.error_handler():
gdal.ErrorReset()
lyr.SetSpatialFilterRect(8.5, 8.5, 9.5, 9.5)
assert (
"Spatial index (perhaps created with GDAL 3.6.0) of table foo is corrupted"
in gdal.GetLastErrorMsg()
)
assert lyr.GetFeatureCount() == 1
ds = None

gdal.Unlink(filename)


###############################################################################
# Test ST_Area()

Expand Down
38 changes: 38 additions & 0 deletions ogr/ogrsf_frmts/gpkg/ogrgeopackagetablelayer.cpp
Expand Up @@ -4170,6 +4170,44 @@ bool OGRGeoPackageTableLayer::HasSpatialIndex()
m_osFIDForRTree = m_pszFidColumn;
}

// Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
// Cf https://github.com/OSGeo/gdal/pull/6911
if( m_bHasSpatialIndex )
{
const auto nFC = GetTotalFeatureCount();
if( nFC >= atoi(CPLGetConfigOption(
"OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")) )
{
CPLString osSQL = "SELECT 1 FROM \"";
osSQL += SQLEscapeName(pszT);
osSQL += "\" WHERE \"";
osSQL += SQLEscapeName(GetFIDColumn());
osSQL += "\" = ";
osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
osSQL += " AND \"";
osSQL += SQLEscapeName(pszC);
osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
osSQL += SQLEscapeName(pszC);
osSQL += "\")";
if( SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1 )
{
osSQL = "SELECT 1 FROM \"";
osSQL += SQLEscapeName(m_osRTreeName);
osSQL += "\" WHERE id = ";
osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
if( SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0 )
{
CPLError(CE_Warning, CPLE_AppDefined,
"Spatial index (perhaps created with GDAL 3.6.0) "
"of table %s is corrupted. Disabling its use. "
"This file should be recreated or its spatial "
"index recreated", m_pszTableName);
m_bHasSpatialIndex = false;
}
}
}
}

return CPL_TO_BOOL(m_bHasSpatialIndex);
}

Expand Down

0 comments on commit c4f8495

Please sign in to comment.