Skip to content

Commit

Permalink
Merge pull request #9955 from rouault/backport_9939
Browse files Browse the repository at this point in the history
[Backport release-3_4] Backport #9939 (Fix opening of GeoPackage datasets with foreign key violation)
  • Loading branch information
rouault committed May 8, 2019
2 parents 87ebaf4 + c908306 commit f67eef3
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 63 deletions.
9 changes: 9 additions & 0 deletions src/providers/gdal/qgsgdalproviderbase.cpp
Expand Up @@ -261,7 +261,16 @@ QgsRectangle QgsGdalProviderBase::extent( GDALDatasetH gdalDataset )const

GDALDatasetH QgsGdalProviderBase::gdalOpen( const char *pszFilename, GDALAccess eAccess )
{
bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", "NO" );
}
GDALDatasetH hDS = GDALOpen( pszFilename, eAccess );
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
}
return hDS;
}

Expand Down
10 changes: 10 additions & 0 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -3694,11 +3694,21 @@ GDALDatasetH QgsOgrProviderUtils::GDALOpenWrapper( const char *pszPath, bool bUp
bIsLocalGpkg = true;
}

bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", "NO" );
}

const int nOpenFlags = GDAL_OF_VECTOR | ( bUpdate ? GDAL_OF_UPDATE : 0 );
GDALDatasetH hDS = GDALOpenEx( pszPath, nOpenFlags, nullptr, papszOpenOptions, nullptr );
CSLDestroy( papszOpenOptions );

CPLSetThreadLocalConfigOption( "OGR_SQLITE_JOURNAL", nullptr );
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
}

if ( !hDS )
{
Expand Down
81 changes: 18 additions & 63 deletions tests/src/python/test_provider_ogr_gpkg.py
Expand Up @@ -1230,69 +1230,6 @@ def testTransaction(self):
self.assertEqual(len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1)
del vl2_external

def testJson(self):
if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 4, 0):
return

tmpfile = os.path.join(self.basetestpath, 'test_json.gpkg')
testdata_path = unitTestDataPath('provider')
shutil.copy(os.path.join(unitTestDataPath('provider'), 'test_json.gpkg'), tmpfile)

vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile, 'foo', 'ogr'))
self.assertTrue(vl.isValid())

fields = vl.dataProvider().fields()
self.assertEqual(fields.at(fields.indexFromName('json_content')).type(), QVariant.Map)

fi = vl.getFeatures(QgsFeatureRequest())
f = QgsFeature()

#test reading dict value from attribute
while fi.nextFeature(f):
if f['fid'] == 1:
self.assertIsInstance(f['json_content'], dict)
self.assertEqual(f['json_content'], {'foo': 'bar'})
#test changing dict value in attribute
f['json_content'] = {'foo': 'baz'}
self.assertEqual(f['json_content'], {'foo': 'baz'})
#test changint dict to list
f['json_content'] = ['eins', 'zwei', 'drei']
self.assertEqual(f['json_content'], ['eins', 'zwei', 'drei'])
#test changing list value in attribute
f['json_content'] = ['eins', 'zwei', 'drei', 4]
self.assertEqual(f['json_content'], ['eins', 'zwei', 'drei', 4])
#test changing to complex json structure
f['json_content'] = {'name': 'Lily', 'age': '0', 'cars': {'car1': ['fiat tipo', 'fiat punto', 'davoser schlitten'], 'car2': 'bobbycar', 'car3': 'tesla'}}
self.assertEqual(f['json_content'], {'name': 'Lily', 'age': '0', 'cars': {'car1': ['fiat tipo', 'fiat punto', 'davoser schlitten'], 'car2': 'bobbycar', 'car3': 'tesla'}})

#test adding attribute
vl.startEditing()
self.assertTrue(vl.addAttribute(QgsField('json_content2', QVariant.Map, "JSON", 60, 0, 'no comment', QVariant.String)))
self.assertTrue(vl.commitChanges())

vl.startEditing()
self.assertTrue(vl.addAttribute(QgsField('json_content3', QVariant.Map, "JSON", 60, 0, 'no comment', QVariant.String)))
self.assertTrue(vl.commitChanges())

#test setting values to new attributes
while fi.nextFeature(f):
if f['fid'] == 2:
f['json_content'] = {'uno': 'foo'}
f['json_content2'] = ['uno', 'due', 'tre']
f['json_content3'] = {'uno': ['uno', 'due', 'tre']}
self.assertEqual(f['json_content'], {'foo': 'baz'})
self.assertEqual(f['json_content2'], ['uno', 'due', 'tre'])
self.assertEqual(f['json_content3'], {'uno': ['uno', 'due', 'tre']})

#test deleting attribute
vl.startEditing()
self.assertTrue(vl.deleteAttribute(vl.fields().indexFromName('json_content3')))
self.assertTrue(vl.commitChanges())

#test if index of existent field is not -1 and the one of the deleted is -1
self.assertNotEqual(vl.fields().indexFromName('json_content2'), -1)
self.assertEqual(vl.fields().indexFromName('json_content3'), -1)

def test_quote_identifier(self):
"""Regression #21100"""

Expand All @@ -1303,6 +1240,24 @@ def test_quote_identifier(self):
for i in range(1, len(vl.fields())):
self.assertEqual(vl.uniqueValues(i), {'a', 'b', 'c'})

def testForeignKeyViolation(self):
"""Test that we can open a dataset with a foreign key violation"""

tmpfile = os.path.join(self.basetestpath, 'testForeignKeyViolation.gpkg')
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)'))
lyr.CreateFeature(f)
ds.ExecuteSQL("PRAGMA foreign_keys = OFF")
ds.ExecuteSQL("CREATE TABLE foo(id INTEGER)")
ds.ExecuteSQL("CREATE TABLE bar(fkey INTEGER, CONSTRAINT fkey_constraint FOREIGN KEY (fkey) REFERENCES foo(id))")
ds.ExecuteSQL("INSERT INTO bar VALUES (1)")
ds = None
vl = QgsVectorLayer('{}'.format(tmpfile) + "|layername=" + "test", 'test', 'ogr')
self.assertTrue(vl.isValid())
fids = set([f['fid'] for f in vl.getFeatures()])
self.assertEqual(len(fids), 1)

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

0 comments on commit f67eef3

Please sign in to comment.