Skip to content
Permalink
Browse files

Try to use provider native method for saving default metadata before

resorting to .qmd sidecar files

This ensures we correctly store metadata in the gpkg_metadata table,
linked to the associated table, for GPKG files instead of as sidecar files.

Fixes #31161, fixes #25119
  • Loading branch information
nyalldawson committed May 4, 2021
1 parent 4cbec54 commit 0a12fc74e9daae4db78c846a9661a43597c026e5
Showing with 97 additions and 0 deletions.
  1. +23 −0 src/core/qgsmaplayer.cpp
  2. +16 −0 tests/src/python/test_provider_ogr.py
  3. +58 −0 tests/src/python/test_provider_ogr_gpkg.py
@@ -59,6 +59,7 @@
#include "qgsmessagelog.h"
#include "qgsmaplayertemporalproperties.h"
#include "qgsmaplayerelevationproperties.h"
#include "qgsprovidermetadata.h"

QString QgsMapLayer::extensionPropertyType( QgsMapLayer::PropertyType type )
{
@@ -880,6 +881,28 @@ QString QgsMapLayer::metadataUri() const

QString QgsMapLayer::saveDefaultMetadata( bool &resultFlag )
{
if ( const QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( providerType() ) )
{
if ( metadata->providerCapabilities() & QgsProviderMetadata::SaveLayerMetadata )
{
try
{
QString errorMessage;
resultFlag = QgsProviderRegistry::instance()->saveLayerMetadata( providerType(), mDataSource, mMetadata, errorMessage );
if ( resultFlag )
return tr( "Successfully saved default layer metadata" );
else
return errorMessage;
}
catch ( QgsNotSupportedException &e )
{
resultFlag = false;
return e.what();
}
}
}

// fallback default metadata saving method, for providers which don't support (or implement) saveLayerMetadata
return saveNamedMetadata( metadataUri(), resultFlag );
}

@@ -1271,6 +1271,22 @@ def testSaveMetadataUnsupported(self):
with self.assertRaises(QgsNotSupportedException):
QgsProviderRegistry.instance().saveLayerMetadata('ogr', 'WFS:http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap', metadata)

def testSaveDefaultMetadataUnsupported(self):
"""
Test saving default metadata to an unsupported layer
"""
layer = QgsVectorLayer('WFS:http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap', 'test')
# now save some metadata
metadata = QgsLayerMetadata()
metadata.setAbstract('my abstract')
metadata.setIdentifier('my identifier')
metadata.setLicenses(['l1', 'l2'])
layer.setMetadata(metadata)
# save as default
msg, res = layer.saveDefaultMetadata()
self.assertFalse(res)
self.assertEqual(msg, 'Storing metadata for the specified uri is not supported')

def testEmbeddedSymbolsKml(self):
"""
Test retrieving embedded symbols from a KML file
@@ -1717,6 +1717,64 @@ def testGeopackageRestoreMetadata(self):
self.assertEqual(metadata2.identifier(), 'my identifier')
self.assertEqual(metadata2.licenses(), ['l1', 'l2'])

def testGeopackageSaveDefaultMetadata(self):
"""
Test saving layer metadata as default to a gpkg file
"""
tmpfile = os.path.join(self.basetestpath, 'testGeopackageSaveMetadataDefault.gpkg')
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon)
lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString))
lyr.CreateField(ogr.FieldDefn('str_field2', ogr.OFTString))
f = None
ds = None

uri = QgsProviderRegistry.instance().encodeUri('ogr', {'path': tmpfile, 'layerName': 'test'})
layer = QgsVectorLayer(uri, 'test')
self.assertTrue(layer.isValid())
# now save some metadata
metadata = QgsLayerMetadata()
metadata.setAbstract('my abstract')
metadata.setIdentifier('my identifier')
metadata.setLicenses(['l1', 'l2'])
layer.setMetadata(metadata)
# save as default
msg, res = layer.saveDefaultMetadata()
self.assertTrue(res)

# QMD sidecar should NOT exist -- metadata should be written to gpkg_metadata table
self.assertFalse(os.path.exists(os.path.join(self.basetestpath, 'testGeopackageSaveMetadataDefault.qmd')))

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', 'my abstract'])

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')
con.close()

# reload layer and check that metadata was restored
layer2 = QgsVectorLayer(uri, 'test')
self.assertTrue(layer2.isValid())
self.assertEqual(layer2.metadata().abstract(), 'my abstract')
self.assertEqual(layer2.metadata().identifier(), 'my identifier')
self.assertEqual(layer2.metadata().licenses(), ['l1', 'l2'])

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

0 comments on commit 0a12fc7

Please sign in to comment.