Skip to content

Commit 89b9b67

Browse files
committed
Handle type conversion failures for compiled expressions
1 parent 0386461 commit 89b9b67

File tree

10 files changed

+74
-44
lines changed

10 files changed

+74
-44
lines changed

src/providers/postgres/qgspostgresfeatureiterator.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,19 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
6161

6262
bool limitAtProvider = ( mRequest.limit() >= 0 );
6363

64+
bool useFallbackWhereClause = false;
65+
QString fallbackWhereClause;
66+
6467
if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() )
6568
{
6669
whereClause = whereClauseRect();
6770
}
6871

72+
if ( !mSource->mSqlWhereClause.isEmpty() )
73+
{
74+
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, '(' + mSource->mSqlWhereClause + ')' );
75+
}
76+
6977
if ( request.filterType() == QgsFeatureRequest::FilterFid )
7078
{
7179
QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
@@ -82,10 +90,13 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
8290
{
8391
if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
8492
{
93+
//IMPORTANT - this MUST be the last clause added!
8594
QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source );
8695

8796
if ( compiler.compile( request.filterExpression() ) == QgsSqlExpressionCompiler::Complete )
8897
{
98+
useFallbackWhereClause = true;
99+
fallbackWhereClause = whereClause;
89100
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() );
90101
mExpressionCompiled = true;
91102
}
@@ -100,19 +111,19 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
100111
}
101112
}
102113

103-
if ( !mSource->mSqlWhereClause.isEmpty() )
114+
bool success = declareCursor( whereClause, limitAtProvider ? mRequest.limit() : -1, false );
115+
if ( !success && useFallbackWhereClause )
104116
{
105-
if ( !whereClause.isEmpty() )
106-
whereClause += " AND ";
107-
108-
whereClause += '(' + mSource->mSqlWhereClause + ')';
117+
//try with the fallback where clause, eg for cases when using compiled expression failed to prepare
118+
mExpressionCompiled = false;
119+
success = declareCursor( fallbackWhereClause, -1, false );
109120
}
110121

111-
if ( !declareCursor( whereClause, limitAtProvider ? mRequest.limit() : -1 ) )
122+
if ( !success )
112123
{
124+
close();
113125
mClosed = true;
114126
iteratorClosed();
115-
return;
116127
}
117128

118129
mFetched = 0;
@@ -336,7 +347,7 @@ QString QgsPostgresFeatureIterator::whereClauseRect()
336347

337348

338349

339-
bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause, long limit )
350+
bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause, long limit, bool closeOnFail )
340351
{
341352
mFetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && !mSource->mGeometryColumn.isNull();
342353
#if 0
@@ -503,7 +514,8 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause, long
503514

504515
// reloading the fields might help next time around
505516
// TODO how to cleanly force reload of fields? P->loadFields();
506-
close();
517+
if ( closeOnFail )
518+
close();
507519
return false;
508520
}
509521

src/providers/postgres/qgspostgresfeatureiterator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Q
9797
QString whereClauseRect();
9898
bool getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature );
9999
void getFeatureAttribute( int idx, QgsPostgresResult& queryResult, int row, int& col, QgsFeature& feature );
100-
bool declareCursor( const QString& whereClause, long limit = -1 );
100+
bool declareCursor( const QString& whereClause, long limit = -1, bool closeOnFail = true );
101101

102102
QString mCursorName;
103103

src/providers/spatialite/qgsspatialitefeatureiterator.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
3737
mRowNumber = 0;
3838

3939
QStringList whereClauses;
40+
bool useFallbackWhereClause = false;
41+
QString fallbackWhereClause;
4042
QString whereClause;
4143

4244
//beware - limitAtProvider needs to be set to false if the request cannot be completely handled
@@ -53,6 +55,15 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
5355
}
5456
}
5557

58+
if ( !mSource->mSubsetString.isEmpty() )
59+
{
60+
whereClause = "( " + mSource->mSubsetString + ')';
61+
if ( ! whereClause.isEmpty() )
62+
{
63+
whereClauses.append( whereClause );
64+
}
65+
}
66+
5667
if ( request.filterType() == QgsFeatureRequest::FilterFid )
5768
{
5869
whereClause = whereClauseFid();
@@ -69,6 +80,7 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
6980
whereClauses.append( whereClause );
7081
}
7182
}
83+
//IMPORTANT - this MUST be the last clause added!
7284
else if ( request.filterType() == QgsFeatureRequest::FilterExpression )
7385
{
7486
if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
@@ -82,6 +94,8 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
8294
whereClause = compiler.result();
8395
if ( !whereClause.isEmpty() )
8496
{
97+
useFallbackWhereClause = true;
98+
fallbackWhereClause = whereClauses.join( " AND " );
8599
whereClauses.append( whereClause );
86100
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
87101
mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
@@ -99,24 +113,23 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
99113
}
100114
}
101115

102-
if ( !mSource->mSubsetString.isEmpty() )
103-
{
104-
whereClause = "( " + mSource->mSubsetString + ')';
105-
if ( ! whereClause.isEmpty() )
106-
{
107-
whereClauses.append( whereClause );
108-
}
109-
}
110116

111117
whereClause = whereClauses.join( " AND " );
112118

113119
// preparing the SQL statement
114-
if ( !prepareStatement( whereClause, limitAtProvider ? mRequest.limit() : -1 ) )
120+
bool success = prepareStatement( whereClause, limitAtProvider ? mRequest.limit() : -1 );
121+
if ( !success && useFallbackWhereClause )
122+
{
123+
//try with the fallback where clause, eg for cases when using compiled expression failed to prepare
124+
mExpressionCompiled = false;
125+
success = prepareStatement( fallbackWhereClause, -1 );
126+
}
127+
128+
if ( !success )
115129
{
116130
// some error occurred
117131
sqliteStatement = NULL;
118132
close();
119-
return;
120133
}
121134
}
122135

tests/src/python/providertestbase.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ def runGetFeatureTests(self, provider):
9999
self.assert_query(provider, 'not name = \'Apple\' and not pk = 4', [1, 3])
100100
self.assert_query(provider, 'not pk IN (1, 2, 4, 8)', [3, 5])
101101

102+
#type conversion - QGIS expressions do not mind that we are comparing a string
103+
#against numeric literals
104+
self.assert_query(provider, 'num_char IN (2, 4, 5)', [2, 4, 5])
105+
102106
def testGetFeaturesUncompiled(self):
103107
try:
104108
self.disableCompiler()

tests/src/python/test_provider_memory.py

Lines changed: 6 additions & 6 deletions
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)&field=name2: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)&field=num_char:string&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, 'NuLl'])
48+
f1.setAttributes([5, -200, NULL, 'NuLl', '5'])
4949
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
5050

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

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

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

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

6666
cls.provider.addFeatures([f1, f2, f3, f4, f5])
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
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)
1+
pk,cnt,name,name2,num_char,wkt
2+
5,-200,,NuLl,5,Point(-71.123 78.23)
3+
3,300,Pear,3,PEaR,
4+
1,100,Orange,oranGe,1,Point(-70.332 66.33)
5+
2,200,Apple,Apple,2,Point(-68.2 70.8)
6+
4,400,Honey,Honey,4,Point(-65.32 78.3)
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
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
1+
pk,cnt,name,name2,num_char,X,Y
2+
5,-200,,NuLl,5,-71.123,78.23
3+
3,300,Pear,PEaR,3,,
4+
1,100,Orange,oranGe,1,-70.332,66.33
5+
2,200,Apple,Apple,2,-68.2,70.8
6+
4,400,Honey,Honey,4,-65.32,78.3
82 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

tests/testdata/provider/testdata.sql

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ CREATE TABLE qgis_test."someData" (
3838
cnt integer,
3939
name text DEFAULT 'qgis',
4040
name2 text DEFAULT 'qgis',
41+
num_char text,
4142
geom public.geometry(Point,4326)
4243
);
4344

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

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')
52+
INSERT INTO qgis_test."someData" (pk, cnt, name, name2, num_char, geom) VALUES
53+
(5, -200, NULL, 'NuLl', '5', '0101000020E61000001D5A643BDFC751C01F85EB51B88E5340'),
54+
(3, 300, 'Pear', 'PEaR', '3', NULL),
55+
(1, 100, 'Orange', 'oranGe', '1', '0101000020E61000006891ED7C3F9551C085EB51B81E955040'),
56+
(2, 200, 'Apple', 'Apple', '2', '0101000020E6100000CDCCCCCCCC0C51C03333333333B35140'),
57+
(4, 400, 'Honey', 'Honey', '4', '0101000020E610000014AE47E17A5450C03333333333935340')
5758
;
5859

5960

0 commit comments

Comments
 (0)