Skip to content

Commit 35d0dd3

Browse files
committedNov 18, 2015
[FEATURE] Expression compiler for SpatiaLite provider
1 parent 5f65f87 commit 35d0dd3

10 files changed

+182
-20
lines changed
 

‎src/core/qgssqlexpressioncompiler.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ QgsSqlExpressionCompiler::~QgsSqlExpressionCompiler()
3030
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExpression* exp )
3131
{
3232
if ( exp->rootNode() )
33-
return compile( exp->rootNode(), mResult );
33+
return compileNode( exp->rootNode(), mResult );
3434
else
3535
return Fail;
3636
}
@@ -69,7 +69,7 @@ QString QgsSqlExpressionCompiler::quotedValue( const QVariant& value )
6969
}
7070
}
7171

72-
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
72+
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
7373
{
7474
switch ( node->nodeType() )
7575
{
@@ -133,18 +133,26 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
133133

134134
case QgsExpression::boLike:
135135
op = "LIKE";
136+
partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
136137
break;
137138

138139
case QgsExpression::boILike:
139-
op = "ILIKE";
140+
if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
141+
op = "LIKE";
142+
else
143+
op = "ILIKE";
140144
break;
141145

142146
case QgsExpression::boNotLike:
143147
op = "NOT LIKE";
148+
partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive );
144149
break;
145150

146151
case QgsExpression::boNotILike:
147-
op = "NOT ILIKE";
152+
if ( mFlags.testFlag( LikeIsCaseInsensitive ) )
153+
op = "NOT LIKE";
154+
else
155+
op = "NOT ILIKE";
148156
break;
149157

150158
case QgsExpression::boOr:
@@ -198,10 +206,10 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
198206
return Fail;
199207

200208
QString left;
201-
Result lr( compile( n->opLeft(), left ) );
209+
Result lr( compileNode( n->opLeft(), left ) );
202210

203211
QString right;
204-
Result rr( compile( n->opRight(), right ) );
212+
Result rr( compileNode( n->opRight(), right ) );
205213

206214
result = left + ' ' + op + ' ' + right;
207215
if ( lr == Complete && rr == Complete )
@@ -251,7 +259,7 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
251259
Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
252260
{
253261
QString s;
254-
Result r = compile( ln, s );
262+
Result r = compileNode( ln, s );
255263
if ( r == Complete || r == Partial )
256264
{
257265
list << s;
@@ -263,7 +271,7 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExp
263271
}
264272

265273
QString nd;
266-
Result rn = compile( n->node(), nd );
274+
Result rn = compileNode( n->node(), nd );
267275
if ( rn != Complete && rn != Partial )
268276
return rn;
269277

‎src/core/qgssqlexpressioncompiler.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class CORE_EXPORT QgsSqlExpressionCompiler
4747
*/
4848
enum Flag
4949
{
50-
CaseInsensitiveStringMatch = 0x01, //!< Provider performs case-insensitive string matching
50+
CaseInsensitiveStringMatch = 0x01, //!< Provider performs case-insensitive string matching for all strings
51+
LikeIsCaseInsensitive = 0x02, //!< Provider treats LIKE as case-insensitive
5152
};
5253
Q_DECLARE_FLAGS( Flags, Flag )
5354

@@ -86,7 +87,7 @@ class CORE_EXPORT QgsSqlExpressionCompiler
8687
* @param str string representing compiled node should be stored in this parameter
8788
* @returns result of node compilation
8889
*/
89-
virtual Result compile( const QgsExpression::Node* node, QString& str );
90+
virtual Result compileNode( const QgsExpression::Node* node, QString& str );
9091

9192
QString mResult;
9293
QgsFields mFields;

‎src/providers/ogr/qgsogrexpressioncompiler.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ QgsOgrExpressionCompiler::QgsOgrExpressionCompiler( QgsOgrFeatureSource* source
2323
}
2424

2525

26-
QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression* exp )
26+
QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression* exp )
2727
{
2828
//for certain driver types, OGR forwards SQL through to the underlying provider. In these cases
2929
//the syntax may differ from OGR SQL, so we don't support compilation for these drivers
@@ -46,7 +46,7 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
4646
return QgsSqlExpressionCompiler::compile( exp );
4747
}
4848

49-
QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExpression::Node* node, QString& result )
49+
QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
5050
{
5151
switch ( node->nodeType() )
5252
{
@@ -68,7 +68,7 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
6868

6969
default:
7070
//fallback to default handling
71-
return QgsSqlExpressionCompiler::compile( node, result );
71+
return QgsSqlExpressionCompiler::compileNode( node, result );
7272
}
7373
}
7474

@@ -78,10 +78,10 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
7878
return Fail;
7979

8080
default:
81-
return QgsSqlExpressionCompiler::compile( node, result );
81+
return QgsSqlExpressionCompiler::compileNode( node, result );
8282
}
8383

84-
return QgsSqlExpressionCompiler::compile( node, result );
84+
return QgsSqlExpressionCompiler::compileNode( node, result );
8585
}
8686

8787
QString QgsOgrExpressionCompiler::quotedIdentifier( const QString& identifier )

‎src/providers/ogr/qgsogrexpressioncompiler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ class QgsOgrExpressionCompiler : public QgsSqlExpressionCompiler
2626

2727
explicit QgsOgrExpressionCompiler( QgsOgrFeatureSource* source );
2828

29-
Result compile( const QgsExpression* exp ) override;
29+
virtual Result compile( const QgsExpression* exp ) override;
3030

3131
protected:
3232

33-
Result compile( const QgsExpression::Node* node, QString& str ) override;
33+
virtual Result compileNode( const QgsExpression::Node* node, QString& str ) override;
3434
virtual QString quotedIdentifier( const QString& identifier ) override;
3535
virtual QString quotedValue( const QVariant& value ) override;
3636

‎src/providers/ogr/qgsogrfeatureiterator.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
8888
{
8989
QgsOgrExpressionCompiler compiler = QgsOgrExpressionCompiler( source );
9090

91-
QgsOgrExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
91+
QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
9292

93-
if ( result == QgsOgrExpressionCompiler::Complete || result == QgsOgrExpressionCompiler::Partial )
93+
if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
9494
{
9595
QString whereClause = compiler.result();
9696
if ( OGR_L_SetAttributeFilter( ogrLayer, whereClause.toLocal8Bit().data() ) == OGRERR_NONE )
9797
{
9898
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
99-
mExpressionCompiled = ( result == QgsOgrExpressionCompiler::Complete );
99+
mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
100100
}
101101
}
102102
else

‎src/providers/spatialite/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SET(SPATIALITE_SRCS
1010
qgsspatialitedataitems.cpp
1111
qgsspatialiteconnection.cpp
1212
qgsspatialiteconnpool.cpp
13+
qgsspatialiteexpressioncompiler.cpp
1314
qgsspatialitefeatureiterator.cpp
1415
qgsspatialitesourceselect.cpp
1516
qgsspatialitetablemodel.cpp
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/***************************************************************************
2+
qgsspatialiteexpressioncompiler.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 "qgsspatialiteexpressioncompiler.h"
17+
#include "qgssqlexpressioncompiler.h"
18+
#include "qgsspatialiteprovider.h"
19+
20+
QgsSpatiaLiteExpressionCompiler::QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source )
21+
: QgsSqlExpressionCompiler( source->mFields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
22+
{
23+
}
24+
25+
QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
26+
{
27+
switch ( node->nodeType() )
28+
{
29+
case QgsExpression::ntBinaryOperator:
30+
{
31+
switch ( static_cast<const QgsExpression::NodeBinaryOperator*>( node )->op() )
32+
{
33+
case QgsExpression::boPow:
34+
case QgsExpression::boRegexp:
35+
return Fail; //not supported by SQLite
36+
37+
default:
38+
//fallback to default handling
39+
return QgsSqlExpressionCompiler::compileNode( node, result );
40+
}
41+
}
42+
43+
default:
44+
return QgsSqlExpressionCompiler::compileNode( node, result );
45+
}
46+
47+
return QgsSqlExpressionCompiler::compileNode( node, result );
48+
}
49+
50+
QString QgsSpatiaLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
51+
{
52+
return QgsSpatiaLiteProvider::quotedIdentifier( identifier );
53+
}
54+
55+
QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value )
56+
{
57+
if ( value.isNull() )
58+
return "NULL";
59+
60+
switch ( value.type() )
61+
{
62+
case QVariant::Int:
63+
case QVariant::LongLong:
64+
case QVariant::Double:
65+
return value.toString();
66+
67+
case QVariant::Bool:
68+
//SQLite has no boolean literals
69+
return value.toBool() ? "1" : "0";
70+
71+
default:
72+
case QVariant::String:
73+
QString v = value.toString();
74+
v.replace( '\'', "''" );
75+
if ( v.contains( '\\' ) )
76+
return v.replace( '\\', "\\\\" ).prepend( "E'" ).append( '\'' );
77+
else
78+
return v.prepend( '\'' ).append( '\'' );
79+
}
80+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/***************************************************************************
2+
qgsspatialiteexpressioncompiler.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 QGSSPATIALITEEXPRESSIONCOMPILER_H
17+
#define QGSSPATIALITEEXPRESSIONCOMPILER_H
18+
19+
#include "qgssqlexpressioncompiler.h"
20+
#include "qgsexpression.h"
21+
#include "qgsspatialitefeatureiterator.h"
22+
23+
class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
24+
{
25+
public:
26+
27+
explicit QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source );
28+
29+
protected:
30+
31+
virtual Result compileNode( const QgsExpression::Node* node, QString& str ) override;
32+
virtual QString quotedIdentifier( const QString& identifier ) override;
33+
virtual QString quotedValue( const QVariant& value ) override;
34+
35+
};
36+
37+
#endif // QGSSPATIALITEEXPRESSIONCOMPILER_H

‎src/providers/spatialite/qgsspatialitefeatureiterator.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "qgsspatialiteconnection.h"
1818
#include "qgsspatialiteconnpool.h"
1919
#include "qgsspatialiteprovider.h"
20+
#include "qgsspatialiteexpressioncompiler.h"
2021

2122
#include "qgslogger.h"
2223
#include "qgsmessagelog.h"
@@ -26,6 +27,7 @@
2627
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
2728
: QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request )
2829
, sqliteStatement( NULL )
30+
, mExpressionCompiled( false )
2931
{
3032

3133
mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath );
@@ -62,6 +64,23 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
6264
whereClauses.append( whereClause );
6365
}
6466
}
67+
else if ( request.filterType() == QgsFeatureRequest::FilterExpression )
68+
{
69+
QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source );
70+
71+
QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
72+
73+
if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
74+
{
75+
whereClause = compiler.result();
76+
if ( !whereClause.isEmpty() )
77+
{
78+
whereClauses.append( whereClause );
79+
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
80+
mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
81+
}
82+
}
83+
}
6584

6685
if ( !mSource->mSubsetString.isEmpty() )
6786
{
@@ -116,6 +135,14 @@ bool QgsSpatiaLiteFeatureIterator::fetchFeature( QgsFeature& feature )
116135
return true;
117136
}
118137

138+
bool QgsSpatiaLiteFeatureIterator::nextFeatureFilterExpression( QgsFeature& f )
139+
{
140+
if ( !mExpressionCompiled )
141+
return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f );
142+
else
143+
return fetchFeature( f );
144+
}
145+
119146

120147
bool QgsSpatiaLiteFeatureIterator::rewind()
121148
{

‎src/providers/spatialite/qgsspatialitefeatureiterator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class QgsSpatiaLiteFeatureSource : public QgsAbstractFeatureSource
4949
QString mSqlitePath;
5050

5151
friend class QgsSpatiaLiteFeatureIterator;
52+
friend class QgsSpatiaLiteExpressionCompiler;
5253
};
5354

5455
class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>
@@ -69,6 +70,9 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource
6970
//! fetch next feature, return true on success
7071
virtual bool fetchFeature( QgsFeature& feature ) override;
7172

73+
//! fetch next feature filter expression
74+
bool nextFeatureFilterExpression( QgsFeature& f ) override;
75+
7276
QString whereClauseRect();
7377
QString whereClauseFid();
7478
QString whereClauseFids();
@@ -96,6 +100,10 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource
96100

97101
bool mHasPrimaryKey;
98102
QgsFeatureId mRowNumber;
103+
104+
private:
105+
106+
bool mExpressionCompiled;
99107
};
100108

101109
#endif // QGSSPATIALITEFEATUREITERATOR_H

0 commit comments

Comments
 (0)
Please sign in to comment.