diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp index 77891e155305..988d5ea8d243 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp @@ -238,35 +238,43 @@ void QgsGeoPackageProviderConnection::deleteSpatialIndex( const QString &schema, QList QgsGeoPackageProviderConnection::tables( const QString &schema, const TableFlags &flags ) const { -// List of GPKG quoted system and dummy tables names to be excluded from the tables listing + + // List of GPKG quoted system and dummy tables names to be excluded from the tables listing static const QStringList excludedTableNames { { QStringLiteral( "\"ogr_empty_table\"" ) } }; checkCapability( Capability::Tables ); + if ( ! schema.isEmpty() ) { QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info ); } + QList tableInfo; QString errCause; QList results; + try { const QString sql { QStringLiteral( "SELECT c.table_name, data_type, description, c.srs_id, g.geometry_type_name, g.column_name " "FROM gpkg_contents c LEFT JOIN gpkg_geometry_columns g ON (c.table_name = g.table_name) " "WHERE c.table_name NOT IN (%1)" ).arg( excludedTableNames.join( ',' ) ) }; results = executeSql( sql ); + for ( const auto &row : qgis::as_const( results ) ) { + if ( row.size() != 6 ) { throw QgsProviderConnectionException( QObject::tr( "Error listing tables from %1: wrong number of columns returned by query" ).arg( uri() ) ); } + QgsGeoPackageProviderConnection::TableProperty property; property.setTableName( row.at( 0 ).toString() ); property.setPrimaryKeyColumns( { QStringLiteral( "fid" ) } ); property.setGeometryColumnCount( 0 ); static const QStringList aspatialTypes = { QStringLiteral( "attributes" ), QStringLiteral( "aspatial" ) }; const QString dataType = row.at( 1 ).toString(); + // Table type if ( dataType == QLatin1String( "tiles" ) || dataType == QLatin1String( "2d-gridded-coverage" ) ) { @@ -278,6 +286,7 @@ QList QgsGeoPackageProviderConne property.setGeometryColumn( row.at( 5 ).toString() ); property.setGeometryColumnCount( 1 ); } + if ( aspatialTypes.contains( dataType ) ) { property.setFlag( QgsGeoPackageProviderConnection::Aspatial ); @@ -287,13 +296,16 @@ QList QgsGeoPackageProviderConne { bool ok; int srid = row.at( 3 ).toInt( &ok ); + if ( !ok ) { throw QgsProviderConnectionException( QObject::tr( "Error fetching srs_id table information: %1" ).arg( row.at( 3 ).toString() ) ); } + QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromEpsgId( srid ); property.addGeometryColumnType( QgsWkbTypes::parseType( row.at( 4 ).toString() ), crs ); } + property.setComment( row.at( 4 ).toString() ); tableInfo.push_back( property ); } diff --git a/src/providers/wfs/qgswfsdescribefeaturetype.cpp b/src/providers/wfs/qgswfsdescribefeaturetype.cpp index b799ad4c451e..f5df5d53f574 100644 --- a/src/providers/wfs/qgswfsdescribefeaturetype.cpp +++ b/src/providers/wfs/qgswfsdescribefeaturetype.cpp @@ -40,6 +40,8 @@ bool QgsWFSDescribeFeatureType::requestFeatureType( const QString &WFSVersion, } } + // Always add singular form for broken servers + // See: https://github.com/qgis/QGIS/issues/41087 query.addQueryItem( QStringLiteral( "TYPENAME" ), typeName ); if ( !namespaceValue.isEmpty() ) { diff --git a/src/providers/wfs/qgswfsfeatureiterator.cpp b/src/providers/wfs/qgswfsfeatureiterator.cpp index a46ee33c4dbc..cd54f5ac6aef 100644 --- a/src/providers/wfs/qgswfsfeatureiterator.cpp +++ b/src/providers/wfs/qgswfsfeatureiterator.cpp @@ -129,8 +129,13 @@ QUrl QgsWFSFeatureDownloaderImpl::buildURL( qint64 startIndex, int maxFeatures, } } if ( mShared->mWFSVersion.startsWith( QLatin1String( "2.0" ) ) ) + { query.addQueryItem( QStringLiteral( "TYPENAMES" ), typenames ); - query.addQueryItem( QStringLiteral( "TYPENAME" ), typenames ); + } + else + { + query.addQueryItem( QStringLiteral( "TYPENAME" ), typenames ); + } if ( forHits ) { diff --git a/src/providers/wfs/qgswfsshareddata.cpp b/src/providers/wfs/qgswfsshareddata.cpp index 5fbbcc556c69..99302ff35765 100644 --- a/src/providers/wfs/qgswfsshareddata.cpp +++ b/src/providers/wfs/qgswfsshareddata.cpp @@ -257,14 +257,22 @@ int QgsWFSFeatureHitsRequest::getFeatureCount( const QString &WFSVersion, { query.addQueryItem( QStringLiteral( "TYPENAMES" ), typeName ); } - query.addQueryItem( QStringLiteral( "TYPENAME" ), typeName ); + else + { + query.addQueryItem( QStringLiteral( "TYPENAME" ), typeName ); + } QString namespaceValue( caps.getNamespaceParameterValue( WFSVersion, typeName ) ); if ( !namespaceValue.isEmpty() ) { if ( WFSVersion.startsWith( QLatin1String( "2.0" ) ) ) + { query.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaceValue ); - query.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue ); + } + else + { + query.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue ); + } } if ( !filter.isEmpty() ) @@ -327,14 +335,16 @@ QgsRectangle QgsWFSSingleFeatureRequest::getExtent() query.addQueryItem( QStringLiteral( "VERSION" ), mShared->mWFSVersion ); if ( mShared->mWFSVersion .startsWith( QLatin1String( "2.0" ) ) ) query.addQueryItem( QStringLiteral( "TYPENAMES" ), mUri.typeName() ); - query.addQueryItem( QStringLiteral( "TYPENAME" ), mUri.typeName() ); + else + query.addQueryItem( QStringLiteral( "TYPENAME" ), mUri.typeName() ); QString namespaceValue( mShared->mCaps.getNamespaceParameterValue( mShared->mWFSVersion, mUri.typeName() ) ); if ( !namespaceValue.isEmpty() ) { if ( mShared->mWFSVersion.startsWith( QLatin1String( "2.0" ) ) ) query.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaceValue ); - query.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue ); + else + query.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue ); } if ( mShared->mWFSVersion .startsWith( QLatin1String( "2.0" ) ) ) diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index fc62710f81ea..0b011a392335 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -160,7 +160,7 @@ def setUpClass(cls): cls.source = cls.vl.dataProvider() with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" """.encode('UTF-8')) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&RESULTTYPE=hits'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&RESULTTYPE=hits'), 'wb') as f: f.write(""" """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&FILTER= cnt @@ -265,7 +265,7 @@ def setUpClass(cls): numberMatched="3" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= cnt @@ -317,7 +317,7 @@ def setUpClass(cls): """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&FILTER= cnt @@ -337,7 +337,7 @@ def setUpClass(cls): numberMatched="2" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= cnt @@ -378,7 +378,7 @@ def setUpClass(cls): """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&FILTER= name Apple @@ -392,7 +392,7 @@ def setUpClass(cls): numberMatched="1" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= name Apple @@ -418,7 +418,7 @@ def setUpClass(cls): """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&FILTER= name AppleBearOrangePear @@ -432,7 +432,7 @@ def setUpClass(cls): numberMatched="0" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> """.encode('UTF-8')) - with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= name AppleBearOrangePear @@ -1146,7 +1146,7 @@ def testWFS20Paging(self): """.encode('UTF-8')) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" """.encode('UTF-8')) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" """.encode('UTF-8')) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" server pagesize vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' pageSize='100'", 'test', 'WFS') @@ -1367,7 +1367,7 @@ def testWFS20PagingPageSizeOverride(self): self.assertEqual(vl.wkbType(), QgsWkbTypes.Point) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&STARTINDEX=0&COUNT=10&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=0&COUNT=10&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" + with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename,my:othertypename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= my:typename/id @@ -2520,7 +2520,7 @@ def testSelectDistinct(self): """.encode('UTF-8')) with open(sanitize(endpoint, - """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326"""), + """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326"""), 'wb') as f: f.write(""" """.encode('UTF-8')) with open(sanitize(endpoint, - """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=EPSG:32631"""), + """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=EPSG:32631"""), 'wb') as f: f.write(""" """ with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&TYPENAME=EC422&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(feature_content.encode('UTF-8')) with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&TYPENAME=EC422&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(feature_content.encode('UTF-8')) @@ -4441,7 +4446,7 @@ def testRetryLogicOnExceptionLackOfPrimaryKey(self): # Initial request: exception with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: f.write(""" @@ -4453,7 +4458,7 @@ def testRetryLogicOnExceptionLackOfPrimaryKey(self): # Retry with open(sanitize(endpoint, - '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&RETRY=1'), + '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&RETRY=1'), 'wb') as f: f.write("""