Skip to content

Commit 26f7683

Browse files
committed
OGR expression compiler fixes
- Allow simple operators as supported by OGR (+, -, * ) - Only return a partial compilation success if expression contains "column"="column2". Since OGR SQL performs case insensitive comparisons, we need to double check the result using QGIS' expressions to ensure that case matches. Add unit tests for this to provider tests.
1 parent d19adfe commit 26f7683

9 files changed

+51
-28
lines changed

src/providers/ogr/qgsogrexpressioncompiler.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,16 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
5959
const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node );
6060

6161
QString op;
62+
bool partialCompilation = false;
6263
switch ( n->op() )
6364
{
6465
case QgsExpression::boEQ:
66+
if ( n->opLeft()->nodeType() == QgsExpression::ntColumnRef && n->opRight()->nodeType() == QgsExpression::ntColumnRef )
67+
{
68+
// equality between column refs results in a partial compilation, since OGR will case-insensitive match strings
69+
// in columns
70+
partialCompilation = true;
71+
}
6572
op = "=";
6673
break;
6774

@@ -118,8 +125,17 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
118125
break;
119126

120127
case QgsExpression::boMul:
128+
op = "*";
129+
break;
130+
121131
case QgsExpression::boPlus:
132+
op = "+";
133+
break;
134+
122135
case QgsExpression::boMinus:
136+
op = "-";
137+
break;
138+
123139
case QgsExpression::boDiv:
124140
case QgsExpression::boMod:
125141
case QgsExpression::boConcat:
@@ -140,7 +156,7 @@ QgsOgrExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
140156

141157
result = left + ' ' + op + ' ' + right;
142158
if ( lr == Complete && rr == Complete )
143-
return Complete;
159+
return ( partialCompilation ? Partial : Complete );
144160
else if (( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) )
145161
return Partial;
146162
else

src/providers/ogr/qgsogrfeatureiterator.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,11 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
9393
if ( result == QgsOgrExpressionCompiler::Complete || result == QgsOgrExpressionCompiler::Partial )
9494
{
9595
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 );
96+
if ( OGR_L_SetAttributeFilter( ogrLayer, whereClause.toLocal8Bit().data() ) == OGRERR_NONE )
97+
{
98+
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
99+
mExpressionCompiled = ( result == QgsOgrExpressionCompiler::Complete );
100+
}
99101
}
100102
else
101103
{

tests/src/python/providertestbase.py

+4
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,14 @@ def runGetFeatureTests(self, provider):
5353
self.assert_query(provider, 'pk IN (1, 2, 4, 8)', [1, 2, 4])
5454
self.assert_query(provider, 'cnt = 50 * 2', [1])
5555
self.assert_query(provider, 'cnt = 99 + 1', [1])
56+
self.assert_query(provider, 'cnt = 101 - 1', [1])
57+
self.assert_query(provider, 'cnt - 1 = 99', [1])
58+
self.assert_query(provider, 'cnt + 1 = 101', [1])
5659
self.assert_query(provider, 'cnt = 1100 % 1000', [1])
5760
self.assert_query(provider, '"name" || \' \' || "cnt" = \'Orange 100\'', [1])
5861
self.assert_query(provider, 'cnt = 10 ^ 2', [1])
5962
self.assert_query(provider, '"name" ~ \'[OP]ra[gne]+\'', [1])
63+
self.assert_query(provider, '"name"="name2"', [2, 4]) # mix of matched and non-matched case sensitive names
6064
self.assert_query(provider, 'true', [1, 2, 3, 4, 5])
6165

6266
def testGetFeaturesUncompiled(self):

tests/src/python/test_provider_memory.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,28 @@ class TestPyQgsMemoryProvider(TestCase, ProviderTestCase):
3939
def setUpClass(cls):
4040
"""Run before all tests"""
4141
# Create test layer
42-
cls.vl = QgsVectorLayer(u'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&key=pk',
42+
cls.vl = QgsVectorLayer(u'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&key=pk',
4343
u'test', u'memory')
4444
assert (cls.vl.isValid())
4545
cls.provider = cls.vl.dataProvider()
4646

4747
f1 = QgsFeature()
48-
f1.setAttributes([5, -200, NULL])
48+
f1.setAttributes([5, -200, NULL, 'NuLl'])
4949
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
5050

5151
f2 = QgsFeature()
52-
f2.setAttributes([3, 300, 'Pear'])
52+
f2.setAttributes([3, 300, 'Pear', 'PEaR'])
5353

5454
f3 = QgsFeature()
55-
f3.setAttributes([1, 100, 'Orange'])
55+
f3.setAttributes([1, 100, 'Orange', 'oranGe'])
5656
f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
5757

5858
f4 = QgsFeature()
59-
f4.setAttributes([2, 200, 'Apple'])
59+
f4.setAttributes([2, 200, 'Apple', 'Apple'])
6060
f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
6161

6262
f5 = QgsFeature()
63-
f5.setAttributes([4, 400, 'Honey'])
63+
f5.setAttributes([4, 400, 'Honey', 'Honey'])
6464
f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
6565

6666
cls.provider.addFeatures([f1, f2, f3, f4, f5])
+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
pk,cnt,name,wkt
2-
5,-200,,"Point(-71.123 78.23)"
3-
3,300,Pear,
4-
1,100,Orange,"Point(-70.332 66.33)"
5-
2,200,Apple,"Point(-68.2 70.8)"
6-
4,400,Honey,"Point(-65.32 78.3)"
1+
pk,cnt,name,name2,wkt
2+
5,-200,,NuLl,Point(-71.123 78.23)
3+
3,300,Pear,PEaR,
4+
1,100,Orange,oranGe,Point(-70.332 66.33)
5+
2,200,Apple,Apple,Point(-68.2 70.8)
6+
4,400,Honey,Honey,Point(-65.32 78.3)
+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
pk,cnt,name,X,Y
2-
5,-200,,-71.123,78.23
3-
3,300,Pear,,
4-
1,100,Orange,-70.332,66.33
5-
2,200,Apple,-68.2,70.8
6-
4,400,Honey,-65.32,78.3
1+
pk,cnt,name,name2,X,Y
2+
5,-200,,NuLl,-71.123,78.23
3+
3,300,Pear,PEaR,,
4+
1,100,Orange,oranGe,-70.332,66.33
5+
2,200,Apple,Apple,-68.2,70.8
6+
4,400,Honey,Honey,-65.32,78.3

tests/testdata/provider/shapefile.dbf

182 Bytes
Binary file not shown.

tests/testdata/provider/spatialite.db

0 Bytes
Binary file not shown.

tests/testdata/provider/testdata.sql

+7-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ CREATE TABLE qgis_test."someData" (
3737
pk SERIAL NOT NULL,
3838
cnt integer,
3939
name text DEFAULT 'qgis',
40+
name2 text DEFAULT 'qgis',
4041
geom public.geometry(Point,4326)
4142
);
4243

@@ -47,12 +48,12 @@ CREATE TABLE qgis_test."someData" (
4748
-- Data for Name: someData; Type: TABLE DATA; Schema: qgis_test; Owner: postgres
4849
--
4950

50-
INSERT INTO qgis_test."someData" (pk, cnt, name, geom) VALUES
51-
(5, -200, NULL, '0101000020E61000001D5A643BDFC751C01F85EB51B88E5340'),
52-
(3, 300, 'Pear', NULL),
53-
(1, 100, 'Orange', '0101000020E61000006891ED7C3F9551C085EB51B81E955040'),
54-
(2, 200, 'Apple', '0101000020E6100000CDCCCCCCCC0C51C03333333333B35140'),
55-
(4, 400, 'Honey', '0101000020E610000014AE47E17A5450C03333333333935340')
51+
INSERT INTO qgis_test."someData" (pk, cnt, name, name2, geom) VALUES
52+
(5, -200, NULL, 'NuLl', '0101000020E61000001D5A643BDFC751C01F85EB51B88E5340'),
53+
(3, 300, 'Pear', 'PEaR', NULL),
54+
(1, 100, 'Orange', 'oranGe', '0101000020E61000006891ED7C3F9551C085EB51B81E955040'),
55+
(2, 200, 'Apple', 'Apple', '0101000020E6100000CDCCCCCCCC0C51C03333333333B35140'),
56+
(4, 400, 'Honey', 'Honey', '0101000020E610000014AE47E17A5450C03333333333935340')
5657
;
5758

5859

0 commit comments

Comments
 (0)