Skip to content

Commit d19adfe

Browse files
committed
[FEATURE] Compiler for OGR expressions
Allows much faster retrieval of features when a FilterExpression feature request is used. OGR SQL supports only basic expressions, so this is really only enabled for "attribute"='value' type expressions.
1 parent f184ec9 commit d19adfe

8 files changed

+340
-7
lines changed

src/providers/ogr/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
SET (OGR_SRCS qgsogrprovider.cpp qgsogrdataitems.cpp qgsogrfeatureiterator.cpp qgsogrgeometrysimplifier.cpp qgsogrconnpool.cpp)
2+
SET (OGR_SRCS qgsogrprovider.cpp qgsogrdataitems.cpp qgsogrfeatureiterator.cpp qgsogrgeometrysimplifier.cpp qgsogrconnpool.cpp qgsogrexpressioncompiler.cpp)
33

44
SET(OGR_MOC_HDRS qgsogrprovider.h qgsogrdataitems.h qgsogrconnpool.h)
55

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/***************************************************************************
2+
qgsogrexpressioncompiler.cpp
3+
----------------------------
4+
begin : November 2015
5+
copyright : (C) 2015 Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
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 "qgsogrexpressioncompiler.h"
17+
#include "qgsogrprovider.h"
18+
19+
QgsOgrExpressionCompiler::QgsOgrExpressionCompiler( QgsOgrFeatureSource* source )
20+
: mResult( None )
21+
, mSource( source )
22+
{
23+
}
24+
25+
QgsOgrExpressionCompiler::~QgsOgrExpressionCompiler()
26+
{
27+
28+
}
29+
30+
QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression* exp )
31+
{
32+
if ( exp->rootNode() )
33+
return compile( exp->rootNode(), mResult );
34+
else
35+
return Fail;
36+
}
37+
38+
QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
39+
{
40+
switch ( node->nodeType() )
41+
{
42+
case QgsExpression::ntUnaryOperator:
43+
{
44+
const QgsExpression::NodeUnaryOperator* n = static_cast<const QgsExpression::NodeUnaryOperator*>( node );
45+
switch ( n->op() )
46+
{
47+
case QgsExpression::uoNot:
48+
break;
49+
50+
case QgsExpression::uoMinus:
51+
break;
52+
}
53+
54+
break;
55+
}
56+
57+
case QgsExpression::ntBinaryOperator:
58+
{
59+
const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node );
60+
61+
QString op;
62+
switch ( n->op() )
63+
{
64+
case QgsExpression::boEQ:
65+
op = "=";
66+
break;
67+
68+
case QgsExpression::boGE:
69+
op = ">=";
70+
break;
71+
72+
case QgsExpression::boGT:
73+
op = ">";
74+
break;
75+
76+
case QgsExpression::boLE:
77+
op = "<=";
78+
break;
79+
80+
case QgsExpression::boLT:
81+
op = "<";
82+
break;
83+
84+
case QgsExpression::boIs:
85+
op = "IS";
86+
break;
87+
88+
case QgsExpression::boIsNot:
89+
op = "IS NOT";
90+
break;
91+
92+
case QgsExpression::boLike:
93+
op = "LIKE";
94+
break;
95+
96+
case QgsExpression::boILike:
97+
op = "ILIKE";
98+
return Fail; //disabled until https://trac.osgeo.org/gdal/ticket/5132 is fixed
99+
100+
case QgsExpression::boNotLike:
101+
op = "NOT LIKE";
102+
break;
103+
104+
case QgsExpression::boNotILike:
105+
op = "NOT ILIKE";
106+
return Fail; //disabled until https://trac.osgeo.org/gdal/ticket/5132 is fixed
107+
108+
case QgsExpression::boOr:
109+
op = "OR";
110+
break;
111+
112+
case QgsExpression::boAnd:
113+
op = "AND";
114+
break;
115+
116+
case QgsExpression::boNE:
117+
op = "<>";
118+
break;
119+
120+
case QgsExpression::boMul:
121+
case QgsExpression::boPlus:
122+
case QgsExpression::boMinus:
123+
case QgsExpression::boDiv:
124+
case QgsExpression::boMod:
125+
case QgsExpression::boConcat:
126+
case QgsExpression::boIntDiv:
127+
case QgsExpression::boPow:
128+
case QgsExpression::boRegexp:
129+
return Fail; //not supported
130+
}
131+
132+
if ( op.isNull() )
133+
return Fail;
134+
135+
QString left;
136+
Result lr( compile( n->opLeft(), left ) );
137+
138+
QString right;
139+
Result rr( compile( n->opRight(), right ) );
140+
141+
result = left + ' ' + op + ' ' + right;
142+
if ( lr == Complete && rr == Complete )
143+
return Complete;
144+
else if (( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
145+
return Partial;
146+
else
147+
return Fail;
148+
}
149+
150+
case QgsExpression::ntLiteral:
151+
{
152+
const QgsExpression::NodeLiteral* n = static_cast<const QgsExpression::NodeLiteral*>( node );
153+
result = QgsOgrUtils::quotedValue( n->value() );
154+
155+
// OGR SQL is case insensitive, so if literal was a string then we only have a Partial compilation and need to
156+
// double check results using QGIS' expression engine
157+
158+
if ( n->value().type() == QVariant::String )
159+
return Partial;
160+
else
161+
return Complete;
162+
}
163+
164+
case QgsExpression::ntColumnRef:
165+
{
166+
const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node );
167+
168+
if ( mSource->mFields.fieldNameIndex( n->name() ) == -1 )
169+
// Not a provider field
170+
return Fail;
171+
172+
result = mSource->mProvider->quotedIdentifier( n->name().toUtf8() );
173+
174+
return Complete;
175+
}
176+
177+
case QgsExpression::ntInOperator:
178+
{
179+
const QgsExpression::NodeInOperator* n = static_cast<const QgsExpression::NodeInOperator*>( node );
180+
QStringList list;
181+
182+
Result inResult = Complete;
183+
Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
184+
{
185+
QString s;
186+
Result r = compile( ln, s );
187+
if ( r == Complete || r == Partial )
188+
{
189+
list << s;
190+
if ( r == Partial )
191+
inResult = Partial;
192+
}
193+
else
194+
return r;
195+
}
196+
197+
QString nd;
198+
Result rn = compile( n->node(), nd );
199+
if ( rn != Complete && rn != Partial )
200+
return rn;
201+
202+
result = QString( "%1 %2IN(%3)" ).arg( nd, n->isNotIn() ? "NOT " : "", list.join( "," ) );
203+
return ( inResult == Partial || rn == Partial ) ? Partial : Complete;
204+
}
205+
206+
case QgsExpression::ntFunction:
207+
case QgsExpression::ntCondition:
208+
break;
209+
}
210+
211+
return Fail;
212+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/***************************************************************************
2+
qgsogrexpressioncompiler.h
3+
--------------------------
4+
begin : November 2015
5+
copyright : (C) 2015 Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
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 QGSOGREXPRESSIONCOMPILER_H
17+
#define QGSOGREXPRESSIONCOMPILER_H
18+
19+
#include "qgsexpression.h"
20+
#include "qgsogrfeatureiterator.h"
21+
22+
class QgsOgrExpressionCompiler
23+
{
24+
public:
25+
enum Result
26+
{
27+
None,
28+
Complete,
29+
Partial,
30+
Fail
31+
};
32+
33+
explicit QgsOgrExpressionCompiler( QgsOgrFeatureSource* source );
34+
~QgsOgrExpressionCompiler();
35+
36+
Result compile( const QgsExpression* exp );
37+
38+
const QString& result() { return mResult; }
39+
40+
private:
41+
Result compile( const QgsExpression::Node* node, QString& str );
42+
43+
private:
44+
QString mResult;
45+
QgsOgrFeatureSource* mSource;
46+
};
47+
48+
#endif // QGSOGREXPRESSIONCOMPILER_H

src/providers/ogr/qgsogrfeatureiterator.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "qgsogrprovider.h"
1818
#include "qgsogrgeometrysimplifier.h"
19+
#include "qgsogrexpressioncompiler.h"
1920

2021
#include "qgsapplication.h"
2122
#include "qgsgeometry.h"
@@ -38,6 +39,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
3839
, ogrLayer( 0 )
3940
, mSubsetStringSet( false )
4041
, mGeometrySimplifier( NULL )
42+
, mExpressionCompiled( false )
4143
{
4244
mFeatureFetched = false;
4345

@@ -82,6 +84,29 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
8284
OGR_L_SetSpatialFilter( ogrLayer, 0 );
8385
}
8486

87+
if ( request.filterType() == QgsFeatureRequest::FilterExpression )
88+
{
89+
QgsOgrExpressionCompiler compiler = QgsOgrExpressionCompiler( source );
90+
91+
QgsOgrExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
92+
93+
if ( result == QgsOgrExpressionCompiler::Complete || result == QgsOgrExpressionCompiler::Partial )
94+
{
95+
QString whereClause = compiler.result();
96+
OGR_L_SetAttributeFilter( ogrLayer, whereClause.toLocal8Bit().data() );
97+
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
98+
mExpressionCompiled = ( result == QgsOgrExpressionCompiler::Complete );
99+
}
100+
else
101+
{
102+
OGR_L_SetAttributeFilter( ogrLayer, 0 );
103+
}
104+
}
105+
else
106+
{
107+
OGR_L_SetAttributeFilter( ogrLayer, 0 );
108+
}
109+
85110
//start with first feature
86111
rewind();
87112
}
@@ -128,6 +153,14 @@ bool QgsOgrFeatureIterator::prepareSimplification( const QgsSimplifyMethod& simp
128153
return QgsAbstractFeatureIterator::prepareSimplification( simplifyMethod );
129154
}
130155

156+
bool QgsOgrFeatureIterator::nextFeatureFilterExpression( QgsFeature& f )
157+
{
158+
if ( !mExpressionCompiled )
159+
return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f );
160+
else
161+
return fetchFeature( f );
162+
}
163+
131164
bool QgsOgrFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
132165
{
133166
#if defined(GDAL_VERSION_NUM) && defined(GDAL_COMPUTE_VERSION)
@@ -333,6 +366,7 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
333366

334367

335368
QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider* p )
369+
: mProvider( p )
336370
{
337371
mFilePath = p->filePath();
338372
mLayerName = p->layerName();

src/providers/ogr/qgsogrfeatureiterator.h

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
3333
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ) override;
3434

3535
protected:
36+
const QgsOgrProvider* mProvider;
3637
QString mFilePath;
3738
QString mLayerName;
3839
int mLayerIndex;
@@ -43,6 +44,7 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
4344
QString mDriverName;
4445

4546
friend class QgsOgrFeatureIterator;
47+
friend class QgsOgrExpressionCompiler;
4648
};
4749

4850
class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>
@@ -65,6 +67,8 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgr
6567
//! Setup the simplification of geometries to fetch using the specified simplify method
6668
virtual bool prepareSimplification( const QgsSimplifyMethod& simplifyMethod ) override;
6769

70+
//! fetch next feature filter expression
71+
bool nextFeatureFilterExpression( QgsFeature& f ) override;
6872

6973
bool readFeature( OGRFeatureH fet, QgsFeature& feature );
7074

@@ -85,6 +89,8 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgr
8589
//! optional object to simplify OGR-geometries fecthed by this feature iterator
8690
QgsOgrAbstractGeometrySimplifier* mGeometrySimplifier;
8791

92+
bool mExpressionCompiled;
93+
8894
//! returns whether the iterator supports simplify geometries on provider side
8995
virtual bool providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const override;
9096
};

0 commit comments

Comments
 (0)