Skip to content

Commit

Permalink
[ogr] Improve decoding/encoding of URI
Browse files Browse the repository at this point in the history
- Correctly handle |subset= and |geometrytype= components
- More robust logic to handle complex cases
- More unit tests

(cherry picked from commit 62ce9ab)
(cherry picked from commit 60bcaca)
  • Loading branch information
nyalldawson committed Sep 11, 2020
1 parent ed47dd3 commit bc248a7
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 10 deletions.
56 changes: 46 additions & 10 deletions src/core/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -3335,23 +3335,55 @@ QVariantMap QgsOgrProviderMetadata::decodeUri( const QString &uri )
{
QString path = uri;
QString layerName;
QString subset;
QString geometryType;

int layerId = -1;

int pipeIndex = path.indexOf( '|' );
if ( pipeIndex != -1 )
if ( path.contains( '|' ) )
{
if ( path.indexOf( QLatin1String( "|layername=" ) ) != -1 )
const QRegularExpression geometryTypeRegex( QStringLiteral( "\\|geometrytype=([a-zA-Z0-9]*)" ) );
const QRegularExpression layerNameRegex( QStringLiteral( "\\|layername=([^|]*)" ) );
const QRegularExpression layerIdRegex( QStringLiteral( "\\|layerid=([^|]*)" ) );
const QRegularExpression subsetRegex( QStringLiteral( "\\|subset=(.*)$" ) );


// we first try to split off the geometry type component, if that's present. That's a known quantity which
// will never be more than a-z characters
QRegularExpressionMatch match = geometryTypeRegex.match( path );
if ( match.hasMatch() )
{
QRegularExpression regex( QStringLiteral( "\\|layername=([^|]*)" ) );
layerName = regex.match( path ).captured( 1 );
geometryType = match.captured( 1 );
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
}
else if ( path.indexOf( QLatin1String( "|layerid=" ) ) )

// next, we try to find and strip out the layerid/layername component. (This logic is based on the assumption
// that a layer name doesn't contain a | character!)
// we prefer layer names over layer ids, since they are persistent..
match = layerNameRegex.match( path );
if ( match.hasMatch() )
{
QRegularExpression regex( QStringLiteral( "\\|layerid=([^|]*)" ) );
layerId = regex.match( path ).captured( 1 ).toInt();
layerName = match.captured( 1 );
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
}

path = path.left( pipeIndex );
match = layerIdRegex.match( path );
if ( match.hasMatch() )
{
layerId = match.captured( 1 ).toInt();
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
}

// lastly, try to parse out the subset component. This is the biggest unknown, because it's
// quite possible that a subset string will contain a | character. If we've already parsed
// out all the other known |xxx=yyy tags, then we can safely assume that everything from "|subset=" to the
// end of the path is part of the subset filter
match = subsetRegex.match( path );
if ( match.hasMatch() )
{
subset = match.captured( 1 );
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
}
}

// Handles DB connections extracting database name if possible
Expand Down Expand Up @@ -3383,7 +3415,11 @@ QVariantMap QgsOgrProviderMetadata::decodeUri( const QString &uri )
QVariantMap uriComponents;
uriComponents.insert( QStringLiteral( "path" ), path );
uriComponents.insert( QStringLiteral( "layerName" ), layerName );
uriComponents.insert( QStringLiteral( "layerId" ), layerId > -1 ? layerId : QVariant() ) ;
uriComponents.insert( QStringLiteral( "layerId" ), layerId > -1 && layerName.isEmpty() ? layerId : QVariant() ) ;
if ( !subset.isEmpty() )
uriComponents.insert( QStringLiteral( "subset" ), subset );
if ( !geometryType.isEmpty() )
uriComponents.insert( QStringLiteral( "geometryType" ), geometryType );
return uriComponents;
}

Expand Down
86 changes: 86 additions & 0 deletions tests/src/core/testqgsogrprovider.cpp
Expand Up @@ -128,11 +128,97 @@ void TestQgsOgrProvider::setupProxy()
void TestQgsOgrProvider::decodeUri()
{
auto parts( QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "MySQL:database_name,host=localhost,port=3306 authcfg='f8wwfx8'" ) ) );
QCOMPARE( parts.size(), 3 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "database_name" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "MySQL:database_name,host=localhost,port=3306 authcfg='f8wwfx8'" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "MYSQL:westholland,user=root,password=psv9570,port=3306,tables=bedrijven" ) );
QCOMPARE( parts.size(), 3 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "westholland" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "MYSQL:westholland,user=root,password=psv9570,port=3306,tables=bedrijven" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layername=a_layer" ) );
QCOMPARE( parts.size(), 3 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layerid=4" ) );
QCOMPARE( parts.size(), 3 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString() );
QCOMPARE( parts.value( QStringLiteral( "layerId" ) ).toInt(), 4 );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layerid=4|layername=a_layer4" ) );
QCOMPARE( parts.size(), 3 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer4" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() ); // layername should take preference
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layername=a_layer|geometrytype=point" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|geometrytype=point|layername=a_layer" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|geometrytype=point|layerid=4" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString() );
QCOMPARE( parts.value( QStringLiteral( "layerId" ) ).toInt(), 4 );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|geometrytype=point|layerid=4|layername=a_layer" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|geometrytype=point|layername=a_layer_with_geometrytype" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "a_layer_with_geometrytype" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|geometrytype=point" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString() );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "point" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|subset=A IN (3,4,5) or \"b\"='x|y'" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString() );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "subset" ) ).toString(), QString( "A IN (3,4,5) or \"b\"='x|y'" ) );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layername=my_subset|subset=A IN (3,4,5) or \"b\"='x|layerid'" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "my_subset" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "subset" ) ).toString(), QString( "A IN (3,4,5) or \"b\"='x|layerid'" ) );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|subset=A IN (3,4,5) or \"b\"='x|layerid'|layername=my_subset" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "my_subset" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "subset" ) ).toString(), QString( "A IN (3,4,5) or \"b\"='x|layerid'" ) );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layerid=4|subset=A IN (3,4,5) or \"b\"='x|layerid'|layername=my_subset" ) );
QCOMPARE( parts.size(), 4 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "my_subset" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "subset" ) ).toString(), QString( "A IN (3,4,5) or \"b\"='x|layerid'" ) );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
parts = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), QStringLiteral( "/path/to/a/geopackage.gpkg|layerid=4|subset=A IN (3,4,5) or \"b\"='x|layerid'|geometrytype=polygonz|layername=my_subset" ) );
QCOMPARE( parts.size(), 5 );
QCOMPARE( parts.value( QStringLiteral( "layerName" ) ).toString(), QString( "my_subset" ) );
QCOMPARE( parts.value( QStringLiteral( "geometryType" ) ).toString(), QString( "polygonz" ) );
QVERIFY( !parts.value( QStringLiteral( "layerId" ) ).isValid() );
QCOMPARE( parts.value( QStringLiteral( "subset" ) ).toString(), QString( "A IN (3,4,5) or \"b\"='x|layerid'" ) );
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QString( "/path/to/a/geopackage.gpkg" ) );
}


Expand Down

0 comments on commit bc248a7

Please sign in to comment.