Skip to content
Permalink
Browse files
If corresponding gpkg_metadata row already exists, we need to update
that instead of adding a new row
  • Loading branch information
nyalldawson committed May 3, 2021
1 parent 8edbb69 commit d5896909c9ea92f54b115b07dfb04edd867e2600
Showing with 106 additions and 20 deletions.
  1. +66 −20 src/core/providers/ogr/qgsogrprovider.cpp
  2. +40 −0 tests/src/python/test_provider_ogr_gpkg.py
@@ -7329,39 +7329,85 @@ bool QgsOgrProviderMetadata::saveLayerMetadata( const QString &uri, const QgsLay
document.save( textStream, 2 );

// so far so good, ready to throw the whole of the QGIS layer XML into the metadata table!
QString sql = QStringLiteral( "INSERT INTO gpkg_metadata (md_scope, md_standard_uri, mime_type, metadata) VALUES (%1,%2,%3,%4);" )
.arg( QgsSqliteUtils::quotedString( QStringLiteral( "dataset" ) ),
QgsSqliteUtils::quotedString( QStringLiteral( "http://mrcc.com/qgis.dtd" ) ),
QgsSqliteUtils::quotedString( QStringLiteral( "text/xml" ) ),
QgsSqliteUtils::quotedString( metadataXml ) );
userLayer->ExecuteSQLNoReturn( sql.toLocal8Bit().constData() );

sql = QStringLiteral( "SELECT last_insert_rowid();" );
int lastRowId = -1;
if ( QgsOgrLayerUniquePtr l = userLayer->ExecuteSQL( sql.toLocal8Bit().constData() ) )

// first we need to check if there's already a corresponding entry in gpkg_metadata -- if so, we need to update it.
QString sql = QStringLiteral( "SELECT id from gpkg_metadata LEFT JOIN gpkg_metadata_reference ON "
"(gpkg_metadata_reference.table_name = %1 AND gpkg_metadata.id = gpkg_metadata_reference.md_file_id) "
"WHERE md_standard_uri = %2 and reference_scope = %3" ).arg(
QgsSqliteUtils::quotedString( layerName ),
QgsSqliteUtils::quotedString( QStringLiteral( "http://mrcc.com/qgis.dtd" ) ),
QgsSqliteUtils::quotedString( QStringLiteral( "table" ) ) );
int existingRowId = -1;
if ( QgsOgrLayerUniquePtr l = userLayer->ExecuteSQL( sql.toLocal8Bit().constData() ) )
{
// retrieve inserted row id
gdal::ogr_feature_unique_ptr f( l->GetNextFeature() );
if ( f )
{
bool ok = false;
QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(), QgsField( QString(), QVariant::String ), 0, nullptr, &ok );
if ( !ok )
if ( ok )
{
return false;
existingRowId = res.toInt( &ok );
if ( !ok )
existingRowId = -1;
}
lastRowId = res.toInt();
}
}

sql = QStringLiteral( "INSERT INTO gpkg_metadata_reference (reference_scope, table_name, md_file_id) VALUES (%1,%2,%3);" )
.arg( QgsSqliteUtils::quotedString( QStringLiteral( "table" ) ),
QgsSqliteUtils::quotedString( layerName ) )
.arg( lastRowId );
userLayer->ExecuteSQLNoReturn( sql.toLocal8Bit().constData() );
if ( existingRowId >= 0 )
{
// update existing row
sql = QStringLiteral( "UPDATE gpkg_metadata SET metadata=%1 where id=%2;" ).arg(
QgsSqliteUtils::quotedString( metadataXml ) ).arg( existingRowId );
userLayer->ExecuteSQLNoReturn( sql.toLocal8Bit().constData() );
if ( CPLGetLastErrorType() != CE_None )
{
errorMessage = QStringLiteral( "%1 (%2): %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() );
return false;
}
else
{
return true;
}
}
errorMessage = QStringLiteral( "Could not retrieve gpkg_metadata row id" );
return false;
else
{
// insert new details in metadata tables
sql = QStringLiteral( "INSERT INTO gpkg_metadata (md_scope, md_standard_uri, mime_type, metadata) VALUES (%1,%2,%3,%4);" )
.arg( QgsSqliteUtils::quotedString( QStringLiteral( "dataset" ) ),
QgsSqliteUtils::quotedString( QStringLiteral( "http://mrcc.com/qgis.dtd" ) ),
QgsSqliteUtils::quotedString( QStringLiteral( "text/xml" ) ),
QgsSqliteUtils::quotedString( metadataXml ) );
userLayer->ExecuteSQLNoReturn( sql.toLocal8Bit().constData() );

sql = QStringLiteral( "SELECT last_insert_rowid();" );
int lastRowId = -1;
if ( QgsOgrLayerUniquePtr l = userLayer->ExecuteSQL( sql.toLocal8Bit().constData() ) )
{
// retrieve inserted row id
gdal::ogr_feature_unique_ptr f( l->GetNextFeature() );
if ( f )
{
bool ok = false;
QVariant res = QgsOgrUtils::getOgrFeatureAttribute( f.get(), QgsField( QString(), QVariant::String ), 0, nullptr, &ok );
if ( !ok )
{
return false;
}
lastRowId = res.toInt();

sql = QStringLiteral( "INSERT INTO gpkg_metadata_reference (reference_scope, table_name, md_file_id) VALUES (%1,%2,%3);" )
.arg( QgsSqliteUtils::quotedString( QStringLiteral( "table" ) ),
QgsSqliteUtils::quotedString( layerName ) )
.arg( lastRowId );
userLayer->ExecuteSQLNoReturn( sql.toLocal8Bit().constData() );
return true;
}
}
errorMessage = QStringLiteral( "Could not retrieve gpkg_metadata row id" );
return false;
}
}
else
{
@@ -1650,6 +1650,46 @@ def testGeopackageSaveMetadata(self):
self.assertEqual(metadata2.identifier(), 'my identifier')
self.assertEqual(metadata2.licenses(), ['l1', 'l2'])

# try updating existing metadata -- current row must be updated, not a new row added
metadata2.setAbstract('my abstract 2')
metadata2.setIdentifier('my identifier 2')
metadata2.setHistory(['h1', 'h2'])
ok, err = QgsProviderRegistry.instance().saveLayerMetadata('ogr', QgsProviderRegistry.instance().encodeUri('ogr', {'path': tmpfile, 'layerName': 'test'}), metadata2)
self.assertTrue(ok)

con = spatialite_connect(tmpfile, isolation_level=None)
cur = con.cursor()

# check that main gpkg_contents metadata columns have been updated
rs = cur.execute("SELECT identifier, description FROM gpkg_contents WHERE table_name='test'")
rows = [r for r in rs]
self.assertEqual(len(rows), 1)
self.assertCountEqual(rows[0], ['my identifier 2', 'my abstract 2'])

rs = cur.execute("SELECT md_file_id FROM gpkg_metadata_reference WHERE table_name='test'")
rows = [r for r in rs]
file_ids = [row[0] for row in rows]
self.assertTrue(file_ids)

rs = cur.execute("SELECT id, md_scope, mime_type, metadata FROM gpkg_metadata WHERE md_standard_uri='http://mrcc.com/qgis.dtd'")
res = [row for row in rs]
self.assertEqual(len(res), 1)
# id must match md_file_id from gpkg_metadata_reference
self.assertIn(res[0][0], file_ids)
self.assertEqual(res[0][1], 'dataset')
self.assertEqual(res[0][2], 'text/xml')
metadata_xml = res[0][3]
con.close()

metadata3 = QgsLayerMetadata()
doc = QDomDocument()
doc.setContent(metadata_xml)
self.assertTrue(metadata3.readMetadataXml(doc.documentElement()))
self.assertEqual(metadata3.abstract(), 'my abstract 2')
self.assertEqual(metadata3.identifier(), 'my identifier 2')
self.assertEqual(metadata3.licenses(), ['l1', 'l2'])
self.assertEqual(metadata3.history(), ['h1', 'h2'])

def testUniqueValuesOnFidColumn(self):
"""Test regression #21311 OGR provider returns an empty set for GPKG uniqueValues"""

0 comments on commit d589690

Please sign in to comment.