Skip to content

Commit

Permalink
Use ad-hoc WHERE clause for TopoGeometry layers
Browse files Browse the repository at this point in the history
This is aimed at using spatial indexes on primitive tables.
Only affects non-hierarchical TopoGeometry layers access.

Thanks Laurențiu Nicola (grayshade) for excellent feedback and
Salvatore Fiandaca (pigreco) for testing !
  • Loading branch information
strk committed Jan 24, 2023
1 parent 7c1dc49 commit c513bad
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 8 deletions.
53 changes: 49 additions & 4 deletions src/providers/postgres/qgspostgresfeatureiterator.cpp
Expand Up @@ -506,10 +506,54 @@ QString QgsPostgresFeatureIterator::whereClauseRect()
bool castToGeometry = mSource->mSpatialColType == SctGeography ||
mSource->mSpatialColType == SctPcPatch;

QString whereClause = QStringLiteral( "%1%2 && %3" )
.arg( QgsPostgresConn::quotedIdentifier( mSource->mBoundingBoxColumn ),
castToGeometry ? "::geometry" : "",
qBox );
QString whereClause;
if ( mSource->mSpatialColType == SctTopoGeometry &&
mSource->mTopoLayerInfo.layerLevel == 0 )
{
whereClause = QStringLiteral( R"SQL(
id(%1) in (
WITH elems AS (
SELECT n.node_id id, 1 typ
FROM %3.node n
WHERE %5 in (1,4) AND n.geom && %2
UNION ALL
SELECT edge_id, 2
FROM %3.edge
WHERE %5 in (2,4) AND geom && %2
UNION ALL
SELECT face_id, 3
FROM %3.face
WHERE %5 in (3,4) AND mbr && %2
)
select r.topogeo_id
FROM %3.relation r, elems e
WHERE r.layer_id = %4
AND r.element_type = e.typ
AND r.element_id = e.id
)
)SQL" )
// Should we bother with mBoundingBoxColumn ?
.arg(
QgsPostgresConn::quotedIdentifier( mSource->mGeometryColumn ),
qBox,
QgsPostgresConn::quotedIdentifier( mSource->mTopoLayerInfo.topologyName )
)
.arg( mSource->mTopoLayerInfo.layerId )
.arg( mSource->mTopoLayerInfo.featureType );
}
else
{
whereClause = QStringLiteral( "%1%2 && %3" )
.arg( QgsPostgresConn::quotedIdentifier( mSource->mBoundingBoxColumn ),
castToGeometry ? "::geometry" : "",
qBox );
}

// For geography type, using a && filter with the geography column cast as
// geometry prevents the use of a spatial index. So for "small" filtering
Expand Down Expand Up @@ -1034,6 +1078,7 @@ QgsPostgresFeatureSource::QgsPostgresFeatureSource( const QgsPostgresProvider *p
, mQuery( p->mQuery )
, mCrs( p->crs() )
, mShared( p->mShared )
, mTopoLayerInfo( p->mTopoLayerInfo )
{
if ( mSqlWhereClause.startsWith( QLatin1String( " WHERE " ) ) )
mSqlWhereClause = mSqlWhereClause.mid( 7 );
Expand Down
2 changes: 2 additions & 0 deletions src/providers/postgres/qgspostgresfeatureiterator.h
Expand Up @@ -63,6 +63,8 @@ class QgsPostgresFeatureSource final: public QgsAbstractFeatureSource
* connection has since been destroyed. */
QgsPostgresConn *mTransactionConnection = nullptr;

QgsPostgresProvider::TopoLayerInfo mTopoLayerInfo;

friend class QgsPostgresFeatureIterator;
friend class QgsPostgresExpressionCompiler;
};
Expand Down
34 changes: 30 additions & 4 deletions src/providers/postgres/qgspostgresprovider.cpp
Expand Up @@ -2403,10 +2403,19 @@ QString QgsPostgresProvider::paramValue( const QString &fieldValue, const QStrin
/* private */
bool QgsPostgresProvider::getTopoLayerInfo()
{
QString sql = QString( "SELECT t.name, l.layer_id "
"FROM topology.layer l, topology.topology t "
"WHERE l.topology_id = t.id AND l.schema_name=%1 "
"AND l.table_name=%2 AND l.feature_column=%3" )
QString sql = QStringLiteral( R"SQL(
SELECT
t.name,
l.layer_id,
l.level,
l.feature_type
FROM topology.layer l
JOIN topology.topology t ON (
l.topology_id = t.id
)
WHERE l.schema_name=%1
AND l.table_name=%2 AND l.feature_column=%3
)SQL" )
.arg( quotedValue( mSchemaName ),
quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
Expand All @@ -2426,6 +2435,23 @@ bool QgsPostgresProvider::getTopoLayerInfo()
}
mTopoLayerInfo.topologyName = result.PQgetvalue( 0, 0 );
mTopoLayerInfo.layerId = result.PQgetvalue( 0, 1 ).toLong();
mTopoLayerInfo.layerLevel = result.PQgetvalue( 0, 2 ).toInt();
switch ( result.PQgetvalue( 0, 3 ).toInt() )
{
case 1:
mTopoLayerInfo.featureType = TopoLayerInfo::Puntal;
break;
case 2:
mTopoLayerInfo.featureType = TopoLayerInfo::Lineal;
break;
case 3:
mTopoLayerInfo.featureType = TopoLayerInfo::Polygonal;
break;
case 4:
default:
mTopoLayerInfo.featureType = TopoLayerInfo::Mixed;
break;
}
return true;
}

Expand Down
8 changes: 8 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Expand Up @@ -432,6 +432,14 @@ class QgsPostgresProvider final: public QgsVectorDataProvider
{
QString topologyName;
long layerId;
int layerLevel;
enum TopoFeatureType
{
Puntal = 1,
Lineal = 2,
Polygonal = 3,
Mixed = 4
} featureType;
};

TopoLayerInfo mTopoLayerInfo;
Expand Down

0 comments on commit c513bad

Please sign in to comment.