Skip to content
Permalink
Browse files

Fix virtual layers using a where filter which has field names which

must be quoted

E.g. field names with special characters or which duplicate a
QGIS expression function name
  • Loading branch information
nyalldawson committed Jan 31, 2018
1 parent 73293e8 commit a9c62673329bed4258cab017bb6997c01fbe3750
Showing with 36 additions and 4 deletions.
  1. +3 −4 src/providers/virtual/qgsvirtuallayersqlitemodule.cpp
  2. +33 −0 tests/src/python/test_provider_virtual.py
@@ -218,7 +218,7 @@ struct VTable
typeName = QStringLiteral( "text" );
break;
}
sqlFields << QStringLiteral( "\"%1\" %2" ).arg( field.name(), typeName );
sqlFields << QStringLiteral( "%1 %2" ).arg( QgsExpression::quotedColumnRef( field.name() ), typeName );
}

QgsVectorDataProvider *provider = mLayer ? mLayer->dataProvider() : mProvider;
@@ -508,7 +508,7 @@ int vtableBestIndex( sqlite3_vtab *pvtab, sqlite3_index_info *indexInfo )
indexInfo->idxNum = 3; // expression filter
indexInfo->estimatedCost = 2.0; // probably better than no index

QString expr = vtab->fields().at( indexInfo->aConstraint[i].iColumn - 1 ).name();
QString expr = QgsExpression::quotedColumnRef( vtab->fields().at( indexInfo->aConstraint[i].iColumn - 1 ).name() );
switch ( indexInfo->aConstraint[i].op )
{
case SQLITE_INDEX_CONSTRAINT_EQ:
@@ -585,7 +585,6 @@ int vtableClose( sqlite3_vtab_cursor *cursor )
int vtableFilter( sqlite3_vtab_cursor *cursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv )
{
Q_UNUSED( argc );
Q_UNUSED( idxStr );

QgsFeatureRequest request;
if ( idxNum == 1 )
@@ -619,7 +618,7 @@ int vtableFilter( sqlite3_vtab_cursor *cursor, int idxNum, const char *idxStr, i
int n = sqlite3_value_bytes( argv[0] );
const char *t = reinterpret_cast<const char *>( sqlite3_value_text( argv[0] ) );
QString str = QString::fromUtf8( t, n );
expr += "'" + str.replace( QLatin1String( "'" ), QLatin1String( "''" ) ) + "'";
expr += QgsExpression::quotedString( str );
break;
}
case SQLITE_NULL:
@@ -855,10 +855,13 @@ def testFieldsWithSpecialCharacters(self):

ml.startEditing()
self.assertTrue(ml.addAttribute(QgsField('abc:123', QVariant.String)))
self.assertTrue(ml.addAttribute(QgsField('map', QVariant.String))) # matches QGIS expression function name
f1 = QgsFeature(ml.fields())
f1.setGeometry(QgsGeometry.fromWkt('POINT(0 0)'))
f1.setAttributes([1, 'a', 'b'])
f2 = QgsFeature(ml.fields())
f2.setGeometry(QgsGeometry.fromWkt('POINT(1 1)'))
f2.setAttributes([2, 'c', 'd'])
ml.addFeatures([f1, f2])
ml.commitChanges()

@@ -869,6 +872,36 @@ def testFieldsWithSpecialCharacters(self):

self.assertEqual(vl.featureCount(), 2)

features = [f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('"abc:123"=\'c\''))]
self.assertEqual(len(features), 1)
self.assertEqual(features[0].attributes(), [2, 'c', 'd'])

features = [f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('"map"=\'b\''))]
self.assertEqual(len(features), 1)
self.assertEqual(features[0].attributes(), [1, 'a', 'b'])

vl2 = QgsVectorLayer("?query=select * from mem_with_nontext_fieldnames where \"abc:123\"='c'", "vl", "virtual")
self.assertEqual(vl2.isValid(), True)
self.assertEqual(vl2.fields().at(0).name(), '123')
self.assertEqual(vl2.fields().at(1).name(), 'abc:123')

self.assertEqual(vl2.featureCount(), 1)

features = [f for f in vl2.getFeatures()]
self.assertEqual(len(features), 1)
self.assertEqual(features[0].attributes(), [2, 'c', 'd'])

vl3 = QgsVectorLayer("?query=select * from mem_with_nontext_fieldnames where \"map\"='b'", "vl", "virtual")
self.assertEqual(vl3.isValid(), True)
self.assertEqual(vl3.fields().at(0).name(), '123')
self.assertEqual(vl3.fields().at(1).name(), 'abc:123')

self.assertEqual(vl3.featureCount(), 1)

features = [f for f in vl3.getFeatures()]
self.assertEqual(len(features), 1)
self.assertEqual(features[0].attributes(), [1, 'a', 'b'])

QgsProject.instance().removeMapLayer(ml)


0 comments on commit a9c6267

Please sign in to comment.
You can’t perform that action at this time.