Skip to content

Commit a99deb8

Browse files
committed
[FEATURE] Send filter expressions to postgres provider
This commit allows to filter features already on server side. Only supported expressions will be sent to the database. Expressions using unsupported operators or functions will gracefully fallback to local evaluation. To make use of this feature * Enable it in options: data sources * QgsFeatureRequest().setFilterExpression( expression ) * or QgsVectorLayer::getFeatures( expression )
1 parent 9b6e23d commit a99deb8

5 files changed

+244
-3
lines changed

src/providers/postgres/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ SET(PG_SRCS
1313
qgspgnewconnection.cpp
1414
qgspgtablemodel.cpp
1515
qgscolumntypethread.cpp
16+
qgspostgresexpressioncompiler.cpp
1617
)
1718
SET(PG_MOC_HDRS
1819
qgspostgresprovider.h
@@ -27,6 +28,7 @@ SET(PG_MOC_HDRS
2728

2829
SET(PG_HDRS
2930
qgspostgrestransaction.h
31+
qgspostgresexpressioncompiler.h
3032
)
3133

3234
########################################################
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/***************************************************************************
2+
3+
----------------------------------------------------
4+
date : 22.4.2015
5+
copyright : (C) 2015 by Matthias Kuhn
6+
email : matthias (at) opengis.ch
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgspostgresexpressioncompiler.h"
17+
18+
QgsPostgresExpressionCompiler::QgsPostgresExpressionCompiler( QgsPostgresFeatureSource* source )
19+
: mResult( None )
20+
, mSource( source )
21+
{
22+
}
23+
24+
QgsPostgresExpressionCompiler::~QgsPostgresExpressionCompiler()
25+
{
26+
27+
}
28+
29+
QgsPostgresExpressionCompiler::Result QgsPostgresExpressionCompiler::compile( const QgsExpression* exp )
30+
{
31+
if ( exp->rootNode() )
32+
return compile ( exp->rootNode(), mResult );
33+
else
34+
return Fail;
35+
}
36+
37+
QgsPostgresExpressionCompiler::Result QgsPostgresExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
38+
{
39+
switch( node->nodeType() )
40+
{
41+
case QgsExpression::ntUnaryOperator:
42+
{
43+
const QgsExpression::NodeUnaryOperator* n = static_cast<const QgsExpression::NodeUnaryOperator*>( node );
44+
switch( n->op() )
45+
{
46+
case QgsExpression::uoNot:
47+
break;
48+
49+
case QgsExpression::uoMinus:
50+
break;
51+
}
52+
53+
break;
54+
}
55+
56+
case QgsExpression::ntBinaryOperator:
57+
{
58+
QString op;
59+
QString left;
60+
QString right;
61+
Result lr;
62+
Result rr;
63+
64+
const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node );
65+
66+
switch ( n->op() )
67+
{
68+
case QgsExpression::boEQ:
69+
op = "=";
70+
break;
71+
72+
case QgsExpression::boGE:
73+
op = ">=";
74+
break;
75+
76+
case QgsExpression::boGT:
77+
op = ">";
78+
break;
79+
80+
case QgsExpression::boLE:
81+
op = "<=";
82+
break;
83+
84+
case QgsExpression::boLT:
85+
op = "<";
86+
break;
87+
88+
case QgsExpression::boLike:
89+
op = "LIKE";
90+
break;
91+
92+
case QgsExpression::boILike:
93+
op = "ILIKE";
94+
break;
95+
96+
case QgsExpression::boOr:
97+
op = "OR";
98+
break;
99+
100+
case QgsExpression::boAnd:
101+
op = "AND";
102+
break;
103+
104+
case QgsExpression::boNE:
105+
op = "<>";
106+
break;
107+
}
108+
109+
if ( !op.isNull() )
110+
{
111+
lr = compile( n->opLeft(), left );
112+
rr = compile( n->opRight(), right );
113+
result = left + " " + op + " " + right;
114+
return ( lr == Complete && rr == Complete ) ? Complete : Fail;
115+
}
116+
else
117+
{
118+
return Fail;
119+
}
120+
121+
break;
122+
}
123+
124+
case QgsExpression::ntFunction:
125+
return Fail;
126+
break;
127+
128+
case QgsExpression::ntLiteral:
129+
{
130+
const QgsExpression::NodeLiteral* n = static_cast<const QgsExpression::NodeLiteral*>( node );
131+
result = QgsPostgresConn::quotedValue( n->value() );
132+
return Complete;
133+
}
134+
135+
case QgsExpression::ntColumnRef:
136+
{
137+
const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node );
138+
139+
if ( mSource->mFields.indexFromName( n->name() ) != -1 )
140+
{
141+
result = QgsPostgresConn::quotedIdentifier( n->name() );
142+
return Complete;
143+
}
144+
else
145+
{
146+
// Not a provider field
147+
return Fail;
148+
}
149+
}
150+
}
151+
return Fail;
152+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/***************************************************************************
2+
3+
----------------------------------------------------
4+
date : 22.4.2015
5+
copyright : (C) 2015 by Matthias Kuhn
6+
email : matthias (at) opengis.ch
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSPOSTGRESEXPRESSIONCOMPILER_H
17+
#define QGSPOSTGRESEXPRESSIONCOMPILER_H
18+
19+
#include "qgsexpression.h"
20+
#include "qgspostgresfeatureiterator.h"
21+
22+
class QgsPostgresExpressionCompiler
23+
{
24+
public:
25+
enum Result {
26+
None,
27+
Complete,
28+
Partial,
29+
Fail
30+
};
31+
32+
QgsPostgresExpressionCompiler( QgsPostgresFeatureSource* source );
33+
~QgsPostgresExpressionCompiler();
34+
35+
Result compile( const QgsExpression* exp );
36+
37+
inline Result success() { return mSuccess; }
38+
39+
const QString& result() { return mResult; }
40+
41+
private:
42+
Result compile( const QgsExpression::Node* node, QString& str );
43+
44+
private:
45+
Result mSuccess;
46+
QString mResult;
47+
QgsPostgresFeatureSource* mSource;
48+
};
49+
50+
#endif // QGSPOSTGRESEXPRESSIONCOMPILER_H

src/providers/postgres/qgspostgresfeatureiterator.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
* (at your option) any later version. *
1313
* *
1414
***************************************************************************/
15+
#include "qgsgeometry.h"
16+
#include "qgspostgresconnpool.h"
17+
#include "qgspostgresexpressioncompiler.h"
1518
#include "qgspostgresfeatureiterator.h"
1619
#include "qgspostgresprovider.h"
17-
#include "qgspostgresconnpool.h"
1820
#include "qgspostgrestransaction.h"
19-
#include "qgsgeometry.h"
2021

2122
#include "qgslogger.h"
2223
#include "qgsmessagelog.h"
2324

2425
#include <QObject>
26+
#include <QSettings>
2527

2628

2729
const int QgsPostgresFeatureIterator::sFeatureQueueSize = 2000;
@@ -32,6 +34,7 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
3234
, mFeatureQueueSize( sFeatureQueueSize )
3335
, mFetched( 0 )
3436
, mFetchGeometry( false )
37+
, mExpressionCompiled( false)
3538
{
3639
if ( !source->mTransactionConnection )
3740
{
@@ -67,6 +70,17 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
6770
{
6871
whereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
6972
}
73+
else if ( request.filterType() == QgsFeatureRequest::FilterExpression
74+
&& QSettings().value( "/qgis/postgres/compileExpressions", false ).toBool() )
75+
{
76+
QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source );
77+
78+
if ( compiler.compile( request.filterExpression() ) == QgsPostgresExpressionCompiler::Complete )
79+
{
80+
whereClause = compiler.result();
81+
mExpressionCompiled = true;
82+
}
83+
}
7084

7185
if ( !mSource->mSqlWhereClause.isEmpty() )
7286
{
@@ -166,6 +180,14 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
166180
return true;
167181
}
168182

183+
bool QgsPostgresFeatureIterator::nextFeatureFilterExpression( QgsFeature& f )
184+
{
185+
if ( !mExpressionCompiled )
186+
return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f );
187+
else
188+
return fetchFeature( f );
189+
}
190+
169191
bool QgsPostgresFeatureIterator::prepareSimplification( const QgsSimplifyMethod& simplifyMethod )
170192
{
171193
// setup simplification of geometries to fetch
@@ -233,6 +255,11 @@ bool QgsPostgresFeatureIterator::close()
233255
return true;
234256
}
235257

258+
int QgsPostgresFeatureIterator::count()
259+
{
260+
261+
}
262+
236263
///////////////
237264

238265
QString QgsPostgresFeatureIterator::whereClauseRect()
@@ -407,6 +434,7 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause )
407434

408435
if ( !mConn->openCursor( mCursorName, query ) )
409436
{
437+
410438
// reloading the fields might help next time around
411439
// TODO how to cleanly force reload of fields? P->loadFields();
412440
close();

src/providers/postgres/qgspostgresfeatureiterator.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ class QgsPostgresFeatureSource : public QgsAbstractFeatureSource
5555
QSharedPointer<QgsPostgresSharedData> mShared;
5656

5757
/* The transaction connection (if any) gets refed/unrefed when creating/
58-
* destroying the QgsPostfresFeatureSource, to ensure that the transaction
58+
* destroying the QgsPostgresFeatureSource, to ensure that the transaction
5959
* connection remains valid during the life time of the feature source
6060
* even if the QgsPostgresTransaction object which initially created the
6161
* connection has since been destroyed. */
6262
QgsPostgresConn* mTransactionConnection;
6363

6464
friend class QgsPostgresFeatureIterator;
65+
friend class QgsPostgresExpressionCompiler;
6566
};
6667

6768

@@ -80,10 +81,16 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Q
8081
//! end of iterating: free the resources / lock
8182
virtual bool close() override;
8283

84+
//! the number of features
85+
virtual int count() override;
86+
8387
protected:
8488
//! fetch next feature, return true on success
8589
virtual bool fetchFeature( QgsFeature& feature ) override;
8690

91+
//! fetch next feature filter expression
92+
bool nextFeatureFilterExpression(QgsFeature& f);
93+
8794
//! Setup the simplification of geometries to fetch using the specified simplify method
8895
virtual bool prepareSimplification( const QgsSimplifyMethod& simplifyMethod ) override;
8996

@@ -119,6 +126,8 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Q
119126
private:
120127
//! returns whether the iterator supports simplify geometries on provider side
121128
virtual bool providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const override;
129+
130+
bool mExpressionCompiled;
122131
};
123132

124133
#endif // QGSPOSTGRESFEATUREITERATOR_H

0 commit comments

Comments
 (0)