Navigation Menu

Skip to content

Commit

Permalink
[ogr] Add support for retrieving system tables when querying sublayers
Browse files Browse the repository at this point in the history
Fairly rudimentary for now, requires GDAL API update for more format
support
  • Loading branch information
nyalldawson committed Aug 26, 2021
1 parent 44d3ac8 commit 10287f0
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 19 deletions.
16 changes: 11 additions & 5 deletions src/core/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -599,9 +599,14 @@ QStringList subLayerDetailsToStringList( const QList< QgsProviderSublayerDetails
QStringList QgsOgrProvider::subLayers() const
{
const bool withFeatureCount = ( mReadFlags & QgsDataProvider::SkipFeatureCount ) == 0;
return subLayerDetailsToStringList( _subLayers( withFeatureCount
? ( Qgis::SublayerQueryFlag::CountFeatures | Qgis::SublayerQueryFlag::ResolveGeometryType )
: Qgis::SublayerQueryFlag::ResolveGeometryType ) );

Qgis::SublayerQueryFlags flags = withFeatureCount
? ( Qgis::SublayerQueryFlag::CountFeatures | Qgis::SublayerQueryFlag::ResolveGeometryType )
: Qgis::SublayerQueryFlag::ResolveGeometryType;
if ( mIsSubLayer )
flags |= Qgis::SublayerQueryFlag::IncludeSystemTables;

return subLayerDetailsToStringList( _subLayers( flags ) );
}

QgsLayerMetadata QgsOgrProvider::layerMetadata() const
Expand All @@ -625,7 +630,7 @@ QList<QgsProviderSublayerDetails> QgsOgrProvider::_subLayers( Qgis::SublayerQuer
const size_t totalLayerCount = layerCount();
if ( mOgrLayer && ( mIsSubLayer || totalLayerCount == 1 ) )
{
mSubLayerList << QgsOgrProviderUtils::querySubLayerList( mLayerIndex, mOgrLayer, mGDALDriverName, flags, mIsSubLayer, dataSourceUri(), totalLayerCount == 1 );
mSubLayerList << QgsOgrProviderUtils::querySubLayerList( mLayerIndex, mOgrLayer, mGDALDriverName, flags, dataSourceUri(), totalLayerCount == 1 );
}
else
{
Expand All @@ -648,13 +653,14 @@ QList<QgsProviderSublayerDetails> QgsOgrProvider::_subLayers( Qgis::SublayerQuer
if ( !layer )
continue;

mSubLayerList << QgsOgrProviderUtils::querySubLayerList( i, layer.get(), mGDALDriverName, flags, mIsSubLayer, dataSourceUri(), totalLayerCount == 1 );
mSubLayerList << QgsOgrProviderUtils::querySubLayerList( i, layer.get(), mGDALDriverName, flags, dataSourceUri(), totalLayerCount == 1 );
if ( firstLayer == nullptr )
{
firstLayer = std::move( layer );
}
}
}

return mSubLayerList;
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/providers/ogr/qgsogrprovidermetadata.cpp
Expand Up @@ -1198,7 +1198,7 @@ QList<QgsProviderSublayerDetails> QgsOgrProviderMetadata::querySublayers( const
QList<QgsProviderSublayerDetails> res;
if ( layerCount == 1 )
{
res << QgsOgrProviderUtils::querySubLayerList( 0, firstLayer.get(), driverName, flags, false, uri, true, feedback );
res << QgsOgrProviderUtils::querySubLayerList( 0, firstLayer.get(), driverName, flags, uri, true, feedback );
}
else
{
Expand Down Expand Up @@ -1239,7 +1239,7 @@ QList<QgsProviderSublayerDetails> QgsOgrProviderMetadata::querySublayers( const
if ( !originalUriLayerName.isEmpty() && layerName != originalUriLayerName )
continue;

res << QgsOgrProviderUtils::querySubLayerList( i, sublayer, driverName, flags, false, uri, false, feedback );
res << QgsOgrProviderUtils::querySubLayerList( i, sublayer, driverName, flags, uri, false, feedback );
}
}

Expand Down
25 changes: 14 additions & 11 deletions src/core/providers/ogr/qgsogrproviderutils.cpp
Expand Up @@ -2313,26 +2313,27 @@ bool QgsOgrProviderUtils::canDriverShareSameDatasetAmongLayers( const QString &d
!( updateMode && dsName.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) );
}

QList< QgsProviderSublayerDetails > QgsOgrProviderUtils::querySubLayerList( int i, QgsOgrLayer *layer, const QString &driverName, Qgis::SublayerQueryFlags flags, bool isSubLayer, const QString &baseUri, bool hasSingleLayerOnly, QgsFeedback *feedback )
QList< QgsProviderSublayerDetails > QgsOgrProviderUtils::querySubLayerList( int i, QgsOgrLayer *layer, const QString &driverName, Qgis::SublayerQueryFlags flags, const QString &baseUri, bool hasSingleLayerOnly, QgsFeedback *feedback )
{
const QString layerName = QString::fromUtf8( layer->name() );

if ( !isSubLayer && ( layerName == QLatin1String( "layer_styles" ) ||
layerName == QLatin1String( "qgis_projects" ) ) )
QStringList privateLayerNames { QStringLiteral( "layer_styles" ),
QStringLiteral( "qgis_projects" )};
if ( driverName == QLatin1String( "SQLite" ) )
{
// Ignore layer_styles (coming from QGIS styling support) and
// qgis_projects (coming from http://plugins.qgis.org/plugins/QgisGeopackage/)
return {};
privateLayerNames.append( QgsSqliteUtils::systemTables() );
}

QStringList skippedLayerNames;
if ( driverName == QLatin1String( "SQLite" ) )
Qgis::SublayerFlags layerFlags;
if ( ( driverName == QLatin1String( "SQLite" ) && layerName.contains( QRegularExpression( QStringLiteral( "idx_.*_geom(etry)?($|_.*)" ), QRegularExpression::PatternOption::CaseInsensitiveOption ) ) )
|| privateLayerNames.contains( layerName ) )
{
skippedLayerNames = QgsSqliteUtils::systemTables();
layerFlags |= Qgis::SublayerFlag::SystemTable;
}
if ( ( driverName == QLatin1String( "SQLite" ) && layerName.contains( QRegularExpression( QStringLiteral( "idx_.*_geom(etry)?($|_.*)" ), QRegularExpression::PatternOption::CaseInsensitiveOption ) ) )
|| skippedLayerNames.contains( layerName ) )

if ( !( flags & Qgis::SublayerQueryFlag::IncludeSystemTables ) && ( layerFlags & Qgis::SublayerFlag::SystemTable ) )
{
// layer is a system table, and we are not scanning for them
return {};
}

Expand Down Expand Up @@ -2389,6 +2390,7 @@ QList< QgsProviderSublayerDetails > QgsOgrProviderUtils::querySubLayerList( int
details.setDescription( longDescription );
details.setProviderKey( QStringLiteral( "ogr" ) );
details.setDriverName( driverName );
details.setFlags( layerFlags );

const QString uri = QgsOgrProviderMetadata().encodeUri( parts );
details.setUri( uri );
Expand Down Expand Up @@ -2479,6 +2481,7 @@ QList< QgsProviderSublayerDetails > QgsOgrProviderUtils::querySubLayerList( int
details.setDescription( longDescription );
details.setProviderKey( QStringLiteral( "ogr" ) );
details.setDriverName( driverName );
details.setFlags( layerFlags );

// if we had to iterate through the table to find geometry types, make sure to include these
// in the uri for the sublayers (otherwise we'll be forced to re-do this iteration whenever
Expand Down
2 changes: 1 addition & 1 deletion src/core/providers/ogr/qgsogrproviderutils.h
Expand Up @@ -260,7 +260,7 @@ class CORE_EXPORT QgsOgrProviderUtils
bool updateMode,
const QString &dsName );

static QList<QgsProviderSublayerDetails> querySubLayerList( int i, QgsOgrLayer *layer, const QString &driverName, Qgis::SublayerQueryFlags flags, bool isSubLayer,
static QList<QgsProviderSublayerDetails> querySubLayerList( int i, QgsOgrLayer *layer, const QString &driverName, Qgis::SublayerQueryFlags flags,
const QString &baseUri, bool hasSingleLayerOnly, QgsFeedback *feedback = nullptr );

/**
Expand Down
43 changes: 43 additions & 0 deletions tests/src/python/test_provider_ogr.py
Expand Up @@ -2045,6 +2045,49 @@ def test_provider_sublayer_details(self):
'driverName': 'SQLite',
'geomColName': ''}])

# sqlite
res = metadata.querySublayers(
os.path.join(TEST_DATA_DIR, "valuerelation_widget_wrapper_test.spatialite.sqlite"))
self.assertCountEqual([{'name': r.name(),
'systemTable': bool(r.flags() & Qgis.SublayerFlag.SystemTable)} for r in res],
[{'name': 'authors', 'systemTable': False},
{'name': 'json', 'systemTable': False}])

# retrieve system tables
res = metadata.querySublayers(
os.path.join(TEST_DATA_DIR, "valuerelation_widget_wrapper_test.spatialite.sqlite"),
Qgis.SublayerQueryFlag.IncludeSystemTables)
self.assertCountEqual([{'name': r.name(),
'systemTable': bool(r.flags() & Qgis.SublayerFlag.SystemTable)} for r in res],
[{'name': 'ElementaryGeometries', 'systemTable': True},
{'name': 'SpatialIndex', 'systemTable': True},
{'name': 'authors', 'systemTable': False},
{'name': 'geom_cols_ref_sys', 'systemTable': True},
{'name': 'geometry_columns', 'systemTable': True},
{'name': 'geometry_columns_auth', 'systemTable': True},
{'name': 'geometry_columns_field_infos', 'systemTable': True},
{'name': 'geometry_columns_statistics', 'systemTable': True},
{'name': 'geometry_columns_time', 'systemTable': True},
{'name': 'json', 'systemTable': False},
{'name': 'spatial_ref_sys', 'systemTable': True},
{'name': 'spatial_ref_sys_all', 'systemTable': True},
{'name': 'spatial_ref_sys_aux', 'systemTable': True},
{'name': 'spatialite_history', 'systemTable': True},
{'name': 'sql_statements_log', 'systemTable': True},
{'name': 'sqlite_sequence', 'systemTable': True},
{'name': 'vector_layers', 'systemTable': True},
{'name': 'vector_layers_auth', 'systemTable': True},
{'name': 'vector_layers_field_infos', 'systemTable': True},
{'name': 'vector_layers_statistics', 'systemTable': True},
{'name': 'views_geometry_columns', 'systemTable': True},
{'name': 'views_geometry_columns_auth', 'systemTable': True},
{'name': 'views_geometry_columns_field_infos', 'systemTable': True},
{'name': 'views_geometry_columns_statistics', 'systemTable': True},
{'name': 'virts_geometry_columns', 'systemTable': True},
{'name': 'virts_geometry_columns_auth', 'systemTable': True},
{'name': 'virts_geometry_columns_field_infos', 'systemTable': True},
{'name': 'virts_geometry_columns_statistics', 'systemTable': True}])

@unittest.skipIf(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 4, 0), "GDAL 3.4 required")
def test_provider_sublayer_details_hierarchy(self):
"""
Expand Down

0 comments on commit 10287f0

Please sign in to comment.