Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add capability for determining whether a spatial index exists (implem…
…ented

for GPKG, spatialite) and deleting a spatial index (implemented for GPKG)
to database connection
  • Loading branch information
nyalldawson committed Mar 17, 2020
1 parent e728dd2 commit cb883b0
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 4 deletions.
Expand Up @@ -232,6 +232,8 @@ This information is calculated from the geometry columns types.
TableExists,
Spatial,
CreateSpatialIndex,
SpatialIndexExists,
DeleteSpatialIndex,
};

typedef QFlags<QgsAbstractDatabaseProviderConnection::Capability> Capabilities;
Expand Down Expand Up @@ -397,6 +399,32 @@ The ``options`` argument can be used to provide extra options controlling the sp
Raises a QgsProviderConnectionException if any errors are encountered.

:raises :: py:class:`QgsProviderConnectionException`

.. versionadded:: 3.14
%End

virtual bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const throw( QgsProviderConnectionException );
%Docstring
Determines whether a spatial index exists for the database table with given ``schema``, ``name`` and ``geometryColumn`` (``schema`` and ``geometryColumn`` are
ignored if not supported by the backend).

Raises a QgsProviderConnectionException if any errors are encountered.

:raises :: py:class:`QgsProviderConnectionException`

.. versionadded:: 3.14
%End

virtual void deleteSpatialIndex( const QString &schema, const QString &name, const QString &geometryColumn ) const throw( QgsProviderConnectionException );
%Docstring
Deletes the existing spatial index for the database table with given ``schema``, ``name`` and ``geometryColumn`` (``schema`` and ``geometryColumn`` are
ignored if not supported by the backend).

Raises a QgsProviderConnectionException if any errors are encountered.

:raises :: py:class:`QgsProviderConnectionException`

.. versionadded:: 3.14
%End


Expand Down
30 changes: 27 additions & 3 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.cpp
Expand Up @@ -188,7 +188,29 @@ void QgsGeoPackageProviderConnection::createSpatialIndex( const QString &schema,
}
executeGdalSqlPrivate( QStringLiteral( "SELECT CreateSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ),
QgsSqliteUtils::quotedString( ( options.geometryColumnName ) ) ) );
}

bool QgsGeoPackageProviderConnection::spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const
{
checkCapability( Capability::CreateSpatialIndex );
if ( ! schema.isEmpty() )
{
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
}
const QList<QVariantList> res = executeGdalSqlPrivate( QStringLiteral( "SELECT HasSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ),
QgsSqliteUtils::quotedString( geometryColumn ) ) );
return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toBool();
}

void QgsGeoPackageProviderConnection::deleteSpatialIndex( const QString &schema, const QString &name, const QString &geometryColumn ) const
{
checkCapability( Capability::DeleteSpatialIndex );
if ( ! schema.isEmpty() )
{
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by GPKG, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
}
executeGdalSqlPrivate( QStringLiteral( "SELECT DisableSpatialIndex(%1, %2)" ).arg( QgsSqliteUtils::quotedString( name ),
QgsSqliteUtils::quotedString( geometryColumn ) ) );
}

QList<QgsGeoPackageProviderConnection::TableProperty> QgsGeoPackageProviderConnection::tables( const QString &schema, const TableFlags &flags ) const
Expand All @@ -207,8 +229,8 @@ QList<QgsGeoPackageProviderConnection::TableProperty> QgsGeoPackageProviderConne
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( ',' ) ) };
"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 ) )
{
Expand Down Expand Up @@ -291,7 +313,9 @@ void QgsGeoPackageProviderConnection::setDefaultCapabilities()
Capability::Spatial,
Capability::TableExists,
Capability::ExecuteSql,
Capability::CreateSpatialIndex
Capability::CreateSpatialIndex,
Capability::SpatialIndexExists,
Capability::DeleteSpatialIndex
};
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,4,0)
mCapabilities |= Capability::DropRasterTable;
Expand Down
2 changes: 2 additions & 0 deletions src/core/providers/ogr/qgsgeopackageproviderconnection.h
Expand Up @@ -41,6 +41,8 @@ class QgsGeoPackageProviderConnection : public QgsAbstractDatabaseProviderConnec
QList<QList<QVariant>> executeSql( const QString &sql ) const override;
void vacuum( const QString &schema, const QString &name ) const override;
void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override;
bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override;
void deleteSpatialIndex( const QString &schema, const QString &name, const QString &geometryColumn ) const override;
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema = QString(),
const TableFlags &flags = nullptr ) const override;
QIcon icon() const override;
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgsabstractdatabaseproviderconnection.cpp
Expand Up @@ -137,6 +137,17 @@ void QgsAbstractDatabaseProviderConnection::createSpatialIndex( const QString &,
checkCapability( Capability::CreateSpatialIndex );
}

void QgsAbstractDatabaseProviderConnection::deleteSpatialIndex( const QString &, const QString &, const QString & ) const
{
checkCapability( Capability::DeleteSpatialIndex );
}

bool QgsAbstractDatabaseProviderConnection::spatialIndexExists( const QString &, const QString &, const QString & ) const
{
checkCapability( Capability::SpatialIndexExists );
return false;
}

QList<QgsAbstractDatabaseProviderConnection::TableProperty> QgsAbstractDatabaseProviderConnection::tables( const QString &, const QgsAbstractDatabaseProviderConnection::TableFlags & ) const
{
checkCapability( Capability::Tables );
Expand Down
23 changes: 23 additions & 0 deletions src/core/qgsabstractdatabaseproviderconnection.h
Expand Up @@ -291,6 +291,8 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv
TableExists = 1 << 14, //!< Can check if table exists
Spatial = 1 << 15, //!< The connection supports spatial tables
CreateSpatialIndex = 1 << 16, //!< The connection can create spatial indices
SpatialIndexExists = 1 << 17, //!< The connection can determine if a spatial index exists
DeleteSpatialIndex = 1 << 18, //!< The connection can delete spatial indices for tables
};

Q_ENUM( Capability )
Expand Down Expand Up @@ -430,9 +432,30 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv
*
* Raises a QgsProviderConnectionException if any errors are encountered.
* \throws QgsProviderConnectionException
* \since QGIS 3.14
*/
virtual void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const SIP_THROW( QgsProviderConnectionException );

/**
* Determines whether a spatial index exists for the database table with given \a schema, \a name and \a geometryColumn (\a schema and \a geometryColumn are
* ignored if not supported by the backend).
*
* Raises a QgsProviderConnectionException if any errors are encountered.
* \throws QgsProviderConnectionException
* \since QGIS 3.14
*/
virtual bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const SIP_THROW( QgsProviderConnectionException );

/**
* Deletes the existing spatial index for the database table with given \a schema, \a name and \a geometryColumn (\a schema and \a geometryColumn are
* ignored if not supported by the backend).
*
* Raises a QgsProviderConnectionException if any errors are encountered.
* \throws QgsProviderConnectionException
* \since QGIS 3.14
*/
virtual void deleteSpatialIndex( const QString &schema, const QString &name, const QString &geometryColumn ) const SIP_THROW( QgsProviderConnectionException );

/**
* Returns information on the tables in the given schema.
* Raises a QgsProviderConnectionException if any errors are encountered.
Expand Down
14 changes: 14 additions & 0 deletions src/providers/spatialite/qgsspatialiteproviderconnection.cpp
Expand Up @@ -208,6 +208,19 @@ void QgsSpatiaLiteProviderConnection::createSpatialIndex( const QString &schema,
QgsSqliteUtils::quotedString( ( options.geometryColumnName ) ) ) );
}

bool QgsSpatiaLiteProviderConnection::spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const
{
checkCapability( Capability::CreateSpatialIndex );
if ( ! schema.isEmpty() )
{
QgsMessageLog::logMessage( QStringLiteral( "Schema is not supported by Spatialite, ignoring" ), QStringLiteral( "OGR" ), Qgis::Info );
}
const QList<QVariantList> res = executeSqlPrivate( QStringLiteral( "SELECT spatial_index_enabled FROM geometry_columns WHERE lower(f_table_name) = lower(%1) AND lower(f_geometry_column) = lower(%2)" )
.arg( QgsSqliteUtils::quotedString( name ),
QgsSqliteUtils::quotedString( geometryColumn ) ) );
return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toInt() == 1;
}

QList<QgsSpatiaLiteProviderConnection::TableProperty> QgsSpatiaLiteProviderConnection::tables( const QString &schema, const TableFlags &flags ) const
{
checkCapability( Capability::Tables );
Expand Down Expand Up @@ -346,6 +359,7 @@ void QgsSpatiaLiteProviderConnection::setDefaultCapabilities()
Capability::TableExists,
Capability::ExecuteSql,
Capability::CreateSpatialIndex,
Capability::SpatialIndexExists,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/providers/spatialite/qgsspatialiteproviderconnection.h
Expand Up @@ -41,6 +41,7 @@ class QgsSpatiaLiteProviderConnection : public QgsAbstractDatabaseProviderConnec
QList<QList<QVariant>> executeSql( const QString &sql ) const override;
void vacuum( const QString &schema, const QString &name ) const override;
void createSpatialIndex( const QString &schema, const QString &name, const QgsAbstractDatabaseProviderConnection::SpatialIndexOptions &options = QgsAbstractDatabaseProviderConnection::SpatialIndexOptions() ) const override;
bool spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const override;
QList<QgsAbstractDatabaseProviderConnection::TableProperty> tables( const QString &schema = QString(),
const TableFlags &flags = nullptr ) const override;
QIcon icon() const override;
Expand Down
21 changes: 20 additions & 1 deletion tests/src/python/test_qgsproviderconnection_base.py
Expand Up @@ -273,12 +273,31 @@ def _test_operations(self, md, conn):
if capabilities & QgsAbstractDatabaseProviderConnection.Vacuum:
conn.vacuum('myNewSchema', 'myNewTable')

# Create spatial index
# Spatial index
spatial_index_exists = False
# we don't initially know if a spatial index exists -- some formats may create them by default, others not
if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists:
spatial_index_exists = conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom')
if capabilities & QgsAbstractDatabaseProviderConnection.DeleteSpatialIndex:
if spatial_index_exists:
conn.deleteSpatialIndex('myNewSchema', 'myNewTable', 'geom')
if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists:
self.assertFalse(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom'))

if capabilities & QgsAbstractDatabaseProviderConnection.CreateSpatialIndex:
options = QgsAbstractDatabaseProviderConnection.SpatialIndexOptions()
options.geometryColumnName = 'geom'
conn.createSpatialIndex('myNewSchema', 'myNewTable', options)

if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists:
self.assertTrue(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom'))

# now we know for certain a spatial index exists, let's retry dropping it
if capabilities & QgsAbstractDatabaseProviderConnection.DeleteSpatialIndex:
conn.deleteSpatialIndex('myNewSchema', 'myNewTable', 'geom')
if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists:
self.assertFalse(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom'))

if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema:
# Drop schema (should fail)
with self.assertRaises(QgsProviderConnectionException) as ex:
Expand Down

0 comments on commit cb883b0

Please sign in to comment.