Skip to content

Commit

Permalink
GPKG: fixes to make most operations compatible with PRAGMA foreign_ke…
Browse files Browse the repository at this point in the history
…ys=1 (fixes OSGeo#9135)
  • Loading branch information
rouault committed Jan 25, 2024
1 parent da8d811 commit 23d37d7
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 30 deletions.
59 changes: 38 additions & 21 deletions autotest/ogr/ogr_gpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1884,11 +1884,11 @@ def test_ogr_gpkg_20a(tmp_vsimem):
ds = None

# Unable to parse srs_id '4326' well-known text 'invalid'
with gdal.quiet_errors():
with gdal.config_option("OGR_SQLITE_PRAGMA", "FOREIGN_KEYS=0"), gdal.quiet_errors():
ds = ogr.Open(fname, update=1)
ds.ExecuteSQL("DELETE FROM gpkg_spatial_ref_sys WHERE srs_id = 4326")
ds = None

ds.ExecuteSQL("DELETE FROM gpkg_spatial_ref_sys WHERE srs_id = 4326")
ds = None
with gdal.config_option(
"OGR_GPKG_FOREIGN_KEY_CHECK", "NO"
), gdaltest.error_handler():
Expand All @@ -1901,24 +1901,25 @@ def test_ogr_gpkg_20b(tmp_vsimem):

fname = tmp_vsimem / "ogr_gpkg_20.gpkg"

ds = gdaltest.gpkg_dr.CreateDataSource(fname)
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
lyr = ds.CreateLayer("foo4326", srs=srs)
assert lyr is not None
with gdal.config_option("OGR_SQLITE_PRAGMA", "FOREIGN_KEYS=0"):
ds = gdaltest.gpkg_dr.CreateDataSource(fname)
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
lyr = ds.CreateLayer("foo4326", srs=srs)
assert lyr is not None

ds.ExecuteSQL("DROP TABLE gpkg_spatial_ref_sys")
ds.ExecuteSQL(
"CREATE TABLE gpkg_spatial_ref_sys (srs_name TEXT, "
"srs_id INTEGER, organization TEXT, "
"organization_coordsys_id INTEGER, definition TEXT)"
)
ds.ExecuteSQL(
"INSERT INTO gpkg_spatial_ref_sys "
"(srs_name,srs_id,organization,organization_coordsys_id,"
"definition) VALUES (NULL,4326,NULL,NULL,NULL)"
)
ds = None
ds.ExecuteSQL("DROP TABLE gpkg_spatial_ref_sys")
ds.ExecuteSQL(
"CREATE TABLE gpkg_spatial_ref_sys (srs_name TEXT, "
"srs_id INTEGER, organization TEXT, "
"organization_coordsys_id INTEGER, definition TEXT)"
)
ds.ExecuteSQL(
"INSERT INTO gpkg_spatial_ref_sys "
"(srs_name,srs_id,organization,organization_coordsys_id,"
"definition) VALUES (NULL,4326,NULL,NULL,NULL)"
)
ds = None

# Warning 1: null definition for srs_id '4326' in gpkg_spatial_ref_sys
with gdal.config_option(
Expand Down Expand Up @@ -3430,7 +3431,14 @@ def test_ogr_gpkg_35(tmp_vsimem, tmp_path):
):
f.DumpReadable()
pytest.fail()
lyr = None
lyr_nonspatial = None
ds = None

with gdaltest.config_option("OGR_SQLITE_PRAGMA", "FOREIGN_KEYS=0"):
ds = ogr.Open(dbname, update=1)
lyr = ds.GetLayerByName("test")
lyr_nonspatial = ds.GetLayerByName("test_nonspatial")
lyr.StartTransaction()
ret = lyr_nonspatial.DeleteField(1)
lyr.CommitTransaction()
Expand Down Expand Up @@ -5069,7 +5077,8 @@ def test_ogr_gpkg_57(tmp_vsimem):
out_filename = tmp_vsimem / "test_ogr_gpkg_57.gpkg"
ogr.GetDriverByName("GPKG").CreateDataSource(out_filename)

ds = ogr.Open(out_filename, update=1)
with gdal.config_option("OGR_SQLITE_PRAGMA", "FOREIGN_KEYS=0"):
ds = ogr.Open(out_filename, update=1)
ds.ExecuteSQL("DROP TABLE gpkg_contents")
ds.ExecuteSQL(
"CREATE TABLE gpkg_contents (table_name,data_type,identifier,description,last_change,min_x, min_y,max_x, max_y,srs_id)"
Expand Down Expand Up @@ -10004,3 +10013,11 @@ def test_ogr_gpkg_extent3d_on_2d_dataset_with_filters(tmp_vsimem):
assert ext3d == (3, 3, 4, 4, float("inf"), float("-inf"))

ds = None


@gdaltest.enable_exceptions()
def test_ogr_gpkg_creation_with_foreign_key_constraint_enabled(tmp_vsimem):

with gdaltest.config_option("OGR_SQLITE_PRAGMA", "FOREIGN_KEYS=1"):
out_filename = str(tmp_vsimem / "out.gpkg")
gdal.VectorTranslate(out_filename, "data/poly.shp")
3 changes: 3 additions & 0 deletions ogr/ogrsf_frmts/gpkg/ogr_geopackage.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ class GDALGeoPackageDataset final : public OGRSQLiteBaseDataSource,
mutable int m_nHasMetadataTables = -1; // -1 = unknown, 0 = false, 1 = true
int m_nCreateMetadataTables = -1; // -1 = on demand, 0 = false, 1 = true

// Set by CreateTileGriddedTable() and used by FinalizeRasterRegistration()
std::string m_osSQLInsertIntoGpkg2dGriddedCoverageAncillary{};

CPLString m_osIdentifier{};
bool m_bIdentifierAsCO = false;
CPLString m_osDescription{};
Expand Down
78 changes: 76 additions & 2 deletions ogr/ogrsf_frmts/gpkg/ogrgeopackagedatasource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@ bool GDALGeoPackageDataset::ConvertGpkgSpatialRefSysToExtensionWkt2(
if (!oResultTable)
return false;

// Temporary remove foreign key checks
const int nPragmaForeignKeysOldValue =
SQLGetInteger(hDB, "PRAGMA foreign_keys", nullptr);
if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 0"));
}

bool bRet = SoftStartTransaction() == OGRERR_NONE;

if (bRet)
Expand Down Expand Up @@ -527,6 +535,11 @@ bool GDALGeoPackageDataset::ConvertGpkgSpatialRefSysToExtensionWkt2(
SoftRollbackTransaction();
}

if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 1"));
}

return bRet;
}

Expand Down Expand Up @@ -3247,7 +3260,8 @@ CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration()
m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1];
double dfGDALMaxY = m_adfGeoTransform[3];

SoftStartTransaction();
if (SoftStartTransaction() != OGRERR_NONE)
return CE_Failure;

const char *pszCurrentDate =
CPLGetConfigOption("OGR_CURRENT_DATE", nullptr);
Expand All @@ -3270,7 +3284,10 @@ CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration()
eErr = SQLCommand(hDB, pszSQL);
sqlite3_free(pszSQL);
if (eErr != OGRERR_NONE)
{
SoftRollbackTransaction();
return CE_Failure;
}

double dfTMSMaxX = m_dfTMSMinX + nTileXCountZoomLevel0 * nTileWidth *
dfPixelXSizeZoomLevel0;
Expand All @@ -3286,7 +3303,10 @@ CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration()
eErr = SQLCommand(hDB, pszSQL);
sqlite3_free(pszSQL);
if (eErr != OGRERR_NONE)
{
SoftRollbackTransaction();
return CE_Failure;
}

m_papoOverviewDS = static_cast<GDALGeoPackageDataset **>(
CPLCalloc(sizeof(GDALGeoPackageDataset *), m_nZoomLevel));
Expand Down Expand Up @@ -3323,7 +3343,10 @@ CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration()
eErr = SQLCommand(hDB, pszSQL);
sqlite3_free(pszSQL);
if (eErr != OGRERR_NONE)
{
SoftRollbackTransaction();
return CE_Failure;
}

if (i < m_nZoomLevel)
{
Expand All @@ -3339,6 +3362,18 @@ CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration()
}
}

if (!m_osSQLInsertIntoGpkg2dGriddedCoverageAncillary.empty())
{
eErr = SQLCommand(
hDB, m_osSQLInsertIntoGpkg2dGriddedCoverageAncillary.c_str());
m_osSQLInsertIntoGpkg2dGriddedCoverageAncillary.clear();
if (eErr != OGRERR_NONE)
{
SoftRollbackTransaction();
return CE_Failure;
}
}

SoftCommitTransaction();

m_nOverviewCount = m_nZoomLevel;
Expand Down Expand Up @@ -5842,7 +5877,7 @@ bool GDALGeoPackageDataset::CreateTileGriddedTable(char **papszOptions)
m_dfOffset, m_dfPrecision, osGridCellEncoding.c_str(),
osUom.empty() ? nullptr : osUom.c_str(), osFieldName.c_str(),
osQuantityDefinition.c_str());
osSQL += pszSQL;
m_osSQLInsertIntoGpkg2dGriddedCoverageAncillary = pszSQL;
sqlite3_free(pszSQL);

// Requirement 3 /gpkg-spatial-ref-sys-row
Expand Down Expand Up @@ -6729,6 +6764,14 @@ int GDALGeoPackageDataset::FindLayerIndex(const char *pszLayerName)

OGRErr GDALGeoPackageDataset::DeleteLayerCommon(const char *pszLayerName)
{
// Temporary remove foreign key checks
const int nPragmaForeignKeysOldValue =
SQLGetInteger(hDB, "PRAGMA foreign_keys", nullptr);
if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 0"));
}

char *pszSQL = sqlite3_mprintf(
"DELETE FROM gpkg_contents WHERE lower(table_name) = lower('%q')",
pszLayerName);
Expand Down Expand Up @@ -6833,6 +6876,11 @@ OGRErr GDALGeoPackageDataset::DeleteLayerCommon(const char *pszLayerName)
sqlite3_free(pszSQL);
}

if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 1"));
}

// Check foreign key integrity
if (eErr == OGRERR_NONE)
{
Expand All @@ -6858,6 +6906,14 @@ OGRErr GDALGeoPackageDataset::DeleteLayer(int iLayer)

CPLDebug("GPKG", "DeleteLayer(%s)", osLayerName.c_str());

// Temporary remove foreign key checks
const int nPragmaForeignKeysOldValue =
SQLGetInteger(hDB, "PRAGMA foreign_keys", nullptr);
if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 0"));
}

OGRErr eErr = SoftStartTransaction();

if (eErr == OGRERR_NONE)
Expand Down Expand Up @@ -6915,6 +6971,11 @@ OGRErr GDALGeoPackageDataset::DeleteLayer(int iLayer)
SoftRollbackTransaction();
}

if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 1"));
}

return eErr;
}

Expand All @@ -6925,6 +6986,14 @@ OGRErr GDALGeoPackageDataset::DeleteLayer(int iLayer)
OGRErr GDALGeoPackageDataset::DeleteRasterLayer(const char *pszLayerName)
{

// Temporary remove foreign key checks
const int nPragmaForeignKeysOldValue =
SQLGetInteger(hDB, "PRAGMA foreign_keys", nullptr);
if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 0"));
}

OGRErr eErr = SoftStartTransaction();

if (eErr == OGRERR_NONE)
Expand Down Expand Up @@ -6979,6 +7048,11 @@ OGRErr GDALGeoPackageDataset::DeleteRasterLayer(const char *pszLayerName)
SoftRollbackTransaction();
}

if (nPragmaForeignKeysOldValue)
{
CPL_IGNORE_RET_VAL(SQLCommand(hDB, "PRAGMA foreign_keys = 1"));
}

return eErr;
}

Expand Down
Loading

0 comments on commit 23d37d7

Please sign in to comment.