Skip to content

Commit 05fda10

Browse files
Filter on joined fields: add geometry for spatial layer and handle sp…
…ecial field/layer names Fixes #19636 "Filter on joined fields" does not work properly in the following two circumstances: - when the (original) layer is a "spatial layer": the virtual layer created is an attribute only / non-spatial layer, instead of a spatial one - when at least a field name or a layer name starts with a digit or contains white spaces (or probably also other special characters): the virtual layer is not created at all (actually is created and instantly deleted) without warning the user This fixes both and also updates the related tests accordingly and adds another test.* Enclose field/layer names in double quotes. See also [QGIS-Developer] "Filter on joined fields" and Virtual layers not working as expected More details: https://issues.qgis.org/issues/19636#note-13 Projects and layers to test the bugs: https://issues.qgis.org/attachments/download/13196/test_filter_qgis.zip
1 parent 9710c18 commit 05fda10

File tree

2 files changed

+32
-14
lines changed

2 files changed

+32
-14
lines changed

src/core/qgsvirtuallayerdefinitionutils.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
2828
QStringList leftJoins;
2929
QStringList columns;
3030

31+
// add the geometry column if the layer is spatial
32+
if ( layer->isSpatial() )
33+
columns << "t.geometry";
34+
3135
// look for the uid
3236
QgsFields fields = layer->dataProvider()->fields();
3337
{
@@ -51,7 +55,7 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
5155
const QgsFields providerFields = layer->dataProvider()->fields();
5256
for ( const auto &f : providerFields )
5357
{
54-
columns << "t." + f.name();
58+
columns << "t.\"" + f.name() + "\"";
5559
}
5660

5761
int joinIdx = 0;
@@ -63,12 +67,12 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
6367
continue;
6468
QString prefix = join.prefix().isEmpty() ? joinedLayer->name() + "_" : join.prefix();
6569

66-
leftJoins << QStringLiteral( "LEFT JOIN %1 AS %2 ON t.\"%5\"=%2.\"%3\"" ).arg( joinedLayer->id(), joinName, join.joinFieldName(), join.targetFieldName() );
70+
leftJoins << QStringLiteral( "LEFT JOIN \"%1\" AS %2 ON t.\"%5\"=%2.\"%3\"" ).arg( joinedLayer->id(), joinName, join.joinFieldName(), join.targetFieldName() );
6771
if ( join.joinFieldNamesSubset() )
6872
{
6973
Q_FOREACH ( const QString &f, *join.joinFieldNamesSubset() )
7074
{
71-
columns << joinName + "." + f + " AS " + prefix + f;
75+
columns << joinName + ".\"" + f + "\" AS \"" + prefix + f + "\"";
7276
}
7377
}
7478
else
@@ -78,12 +82,12 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
7882
{
7983
if ( f.name() == join.joinFieldName() )
8084
continue;
81-
columns << joinName + "." + f.name() + " AS " + prefix + f.name();
85+
columns << joinName + ".\"" + f.name() + "\" AS \"" + prefix + f.name() + "\"";
8286
}
8387
}
8488
}
8589

86-
QString query = "SELECT " + columns.join( QStringLiteral( ", " ) ) + " FROM " + layer->id() + " AS t " + leftJoins.join( QStringLiteral( " " ) );
90+
QString query = "SELECT " + columns.join( QStringLiteral( ", " ) ) + " FROM \"" + layer->id() + "\" AS t " + leftJoins.join( QStringLiteral( " " ) );
8791
def.setQuery( query );
8892

8993
return def;

tests/src/python/test_provider_virtual.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,11 @@ def test_joined_layers_conversion(self):
822822
self.assertEqual(v2.isValid(), True)
823823
v3 = QgsVectorLayer("Point?field=id:integer&field=cname:string", "C", "memory")
824824
self.assertEqual(v3.isValid(), True)
825-
QgsProject.instance().addMapLayers([v1, v2, v3])
825+
tl1 = QgsVectorLayer("NoGeometry?field=id:integer&field=e_id:integer&field=0name:string", "D", "memory")
826+
self.assertEqual(tl1.isValid(), True)
827+
tl2 = QgsVectorLayer("NoGeometry?field=id:integer&field=ena me:string", "E", "memory")
828+
self.assertEqual(tl2.isValid(), True)
829+
QgsProject.instance().addMapLayers([v1, v2, v3, tl1, tl2])
826830
joinInfo = QgsVectorLayerJoinInfo()
827831
joinInfo.setTargetFieldName("b_id")
828832
joinInfo.setJoinLayer(v2)
@@ -832,15 +836,15 @@ def test_joined_layers_conversion(self):
832836
self.assertEqual(len(v1.fields()), 6)
833837

834838
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
835-
self.assertEqual(df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname, j1.bfield AS B_bfield FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
839+
self.assertEqual(df.query(), 'SELECT t.geometry, t.rowid AS uid, t."id", t."b_id", t."c_id", t."name", j1."bname" AS "B_bname", j1."bfield" AS "B_bfield" FROM "{}" AS t LEFT JOIN "{}" AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
836840

837841
# with a field subset
838842
v1.removeJoin(v2.id())
839843
joinInfo.setJoinFieldNamesSubset(["bname"])
840844
v1.addJoin(joinInfo)
841845
self.assertEqual(len(v1.fields()), 5)
842846
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
843-
self.assertEqual(df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
847+
self.assertEqual(df.query(), 'SELECT t.geometry, t.rowid AS uid, t."id", t."b_id", t."c_id", t."name", j1."bname" AS "B_bname" FROM "{}" AS t LEFT JOIN "{}" AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
844848
joinInfo.setJoinFieldNamesSubset(None)
845849

846850
# add a table prefix to the join
@@ -849,7 +853,7 @@ def test_joined_layers_conversion(self):
849853
v1.addJoin(joinInfo)
850854
self.assertEqual(len(v1.fields()), 6)
851855
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
852-
self.assertEqual(df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS BB_bname, j1.bfield AS BB_bfield FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
856+
self.assertEqual(df.query(), 'SELECT t.geometry, t.rowid AS uid, t."id", t."b_id", t."c_id", t."name", j1."bname" AS "BB_bname", j1."bfield" AS "BB_bfield" FROM "{}" AS t LEFT JOIN "{}" AS j1 ON t."b_id"=j1."id"'.format(v1.id(), v2.id()))
853857
joinInfo.setPrefix("")
854858
v1.removeJoin(v2.id())
855859
v1.addJoin(joinInfo)
@@ -862,11 +866,21 @@ def test_joined_layers_conversion(self):
862866
v1.addJoin(joinInfo2)
863867
self.assertEqual(len(v1.fields()), 7)
864868
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
865-
self.assertEqual(df.query(), ('SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname, j1.bfield AS B_bfield, j2.cname AS C_cname FROM {} AS t ' +
866-
'LEFT JOIN {} AS j1 ON t."b_id"=j1."id" ' +
867-
'LEFT JOIN {} AS j2 ON t."c_id"=j2."id"').format(v1.id(), v2.id(), v3.id()))
868-
869-
QgsProject.instance().removeMapLayers([v1.id(), v2.id(), v3.id()])
869+
self.assertEqual(df.query(), ('SELECT t.geometry, t.rowid AS uid, t."id", t."b_id", t."c_id", t."name", j1."bname" AS "B_bname", j1."bfield" AS "B_bfield", j2."cname" AS "C_cname" FROM "{}" AS t ' +
870+
'LEFT JOIN "{}" AS j1 ON t."b_id"=j1."id" ' +
871+
'LEFT JOIN "{}" AS j2 ON t."c_id"=j2."id"').format(v1.id(), v2.id(), v3.id()))
872+
873+
# test NoGeometry joined layers with field names starting with a digit or containing white spaces
874+
joinInfo3 = QgsVectorLayerJoinInfo()
875+
joinInfo3.setTargetFieldName("e_id")
876+
joinInfo3.setJoinLayer(tl2)
877+
joinInfo3.setJoinFieldName("id")
878+
tl1.addJoin(joinInfo3)
879+
self.assertEqual(len(tl1.fields()), 4)
880+
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(tl1)
881+
self.assertEqual(df.query(), 'SELECT t.rowid AS uid, t."id", t."e_id", t."0name", j1."ena me" AS "E_ena me" FROM "{}" AS t LEFT JOIN "{}" AS j1 ON t."e_id"=j1."id"'.format(tl1.id(), tl2.id()))
882+
883+
QgsProject.instance().removeMapLayers([v1.id(), v2.id(), v3.id(), tl1.id(), tl2.id()])
870884

871885
def testFieldsWithSpecialCharacters(self):
872886
ml = QgsVectorLayer("Point?srid=EPSG:4326&field=123:int", "mem_with_nontext_fieldnames", "memory")

0 commit comments

Comments
 (0)