Skip to content

Commit

Permalink
[ogr] Improve decoding/encoding of URI
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored and nyalldawson committed Aug 13, 2020
1 parent eae949f commit dd0dbb2
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 14 deletions.
69 changes: 55 additions & 14 deletions src/core/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -3346,23 +3346,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 @@ -3394,16 +3426,25 @@ 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;
}

QString QgsOgrProviderMetadata::encodeUri( const QVariantMap &parts )
{
QString path = parts.value( QStringLiteral( "path" ) ).toString();
QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
QString layerId = parts.value( QStringLiteral( "layerId" ) ).toString();
return path + ( !layerName.isEmpty() ? QStringLiteral( "|layername=%1" ).arg( layerName ) : !layerId.isEmpty() ? QStringLiteral( "|layerid=%1" ).arg( layerId ) : QString() );
const QString path = parts.value( QStringLiteral( "path" ) ).toString();
const QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
const QString layerId = parts.value( QStringLiteral( "layerId" ) ).toString();
const QString subset = parts.value( QStringLiteral( "subset" ) ).toString();
const QString geometryType = parts.value( QStringLiteral( "geometryType" ) ).toString();
return path
+ ( !layerName.isEmpty() ? QStringLiteral( "|layername=%1" ).arg( layerName ) : !layerId.isEmpty() ? QStringLiteral( "|layerid=%1" ).arg( layerId ) : QString() )
+ ( !geometryType.isEmpty() ? QStringLiteral( "|geometrytype=%1" ).arg( geometryType ) : QString() )
+ ( !subset.isEmpty() ? QStringLiteral( "|subset=%1" ).arg( subset ) : QString() );
}

QString QgsOgrProviderUtils::fileVectorFilters()
Expand Down
91 changes: 91 additions & 0 deletions tests/src/core/testqgsogrprovider.cpp
Expand Up @@ -125,11 +125,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" ) );
}

void TestQgsOgrProvider::encodeUri()
Expand All @@ -151,6 +237,11 @@ void TestQgsOgrProvider::encodeUri()
parts.insert( QStringLiteral( "layerName" ), QStringLiteral( "test" ) );
QCOMPARE( QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), parts ), QStringLiteral( "/home/user/test.gpkg|layername=test" ) );

parts.insert( QStringLiteral( "geometryType" ), QStringLiteral( "point" ) );
QCOMPARE( QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), parts ), QStringLiteral( "/home/user/test.gpkg|layername=test|geometrytype=point" ) );

parts.insert( QStringLiteral( "subset" ), QStringLiteral( "\"a\"='b'" ) );
QCOMPARE( QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), parts ), QStringLiteral( "/home/user/test.gpkg|layername=test|geometrytype=point|subset=\"a\"='b'" ) );
}

class ReadVectorLayer : public QThread
Expand Down

0 comments on commit dd0dbb2

Please sign in to comment.