Skip to content

Commit

Permalink
Fix virtual layer construction from joined layer
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugo Mercier committed Jul 7, 2016
1 parent 564c02e commit 2257d71
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 6 deletions.
18 changes: 13 additions & 5 deletions src/core/qgsvirtuallayerdefinitionutils.cpp
Expand Up @@ -17,6 +17,7 @@ email : hugo dot mercier at oslandia dot com
#include "qgsvirtuallayerdefinitionutils.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsmaplayerregistry.h"

QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVectorLayer* layer )
{
Expand All @@ -25,8 +26,6 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
QStringList leftJoins;
QStringList columns;

columns << "t.*"; // columns from the main layer

// look for the uid
const QgsFields& fields = layer->dataProvider()->fields();
{
Expand All @@ -47,14 +46,21 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
def.setUid( uid );
}
}
Q_FOREACH ( const QgsField& f, layer->dataProvider()->fields() )
{
columns << "t." + f.name();
}

int joinIdx = 0;
Q_FOREACH ( const QgsVectorJoinInfo& join, layer->vectorJoins() )
{
QString joinName = QString( "j%1" ).arg( ++joinIdx );
QString prefix = join.prefix.isEmpty() ? layer->name() + "_" : join.prefix;
QgsVectorLayer* joinedLayer = static_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join.joinLayerId ) );
if ( !joinedLayer )
continue;
QString prefix = join.prefix.isEmpty() ? joinedLayer->name() + "_" : join.prefix;

leftJoins << QString( "LEFT JOIN %1 AS %2 ON t.\"%3\"=%2.\"%5\"" ).arg( join.joinLayerId, joinName, join.joinFieldName, join.targetFieldName );
leftJoins << QString( "LEFT JOIN %1 AS %2 ON t.\"%5\"=%2.\"%3\"" ).arg( join.joinLayerId, joinName, join.joinFieldName, join.targetFieldName );
if ( join.joinFieldNamesSubset() )
{
Q_FOREACH ( const QString& f, *join.joinFieldNamesSubset() )
Expand All @@ -64,8 +70,10 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe
}
else
{
Q_FOREACH ( const QgsField& f, layer->dataProvider()->fields() )
Q_FOREACH ( const QgsField& f, joinedLayer->fields() )
{
if ( f.name() == join.joinFieldName )
continue;
columns << joinName + "." + f.name() + " AS " + prefix + f.name();
}
}
Expand Down
57 changes: 56 additions & 1 deletion tests/src/python/test_provider_virtual.py
Expand Up @@ -22,8 +22,10 @@
QgsMapLayerRegistry,
QgsRectangle,
QgsVirtualLayerDefinition,
QgsVirtualLayerDefinitionUtils,
QgsWKBTypes,
QgsProject
QgsProject,
QgsVectorJoinInfo
)

from qgis.testing import start_app, unittest
Expand Down Expand Up @@ -731,6 +733,59 @@ def test_query_with_accents(self):
ids = [f.id() for f in vl2.getFeatures()]
self.assertEqual(ids, [])

def test_joined_layers_conversion(self):
v1 = QgsVectorLayer("Point?field=id:integer&field=b_id:integer&field=c_id:integer&field=name:string", "A", "memory")
self.assertEqual(v1.isValid(), True)
v2 = QgsVectorLayer("Point?field=id:integer&field=bname:string&field=bfield:integer", "B", "memory")
self.assertEqual(v2.isValid(), True)
v3 = QgsVectorLayer("Point?field=id:integer&field=cname:string", "C", "memory")
self.assertEqual(v3.isValid(), True)
QgsMapLayerRegistry.instance().addMapLayers([v1, v2, v3])
joinInfo = QgsVectorJoinInfo()
joinInfo.targetFieldName = "b_id"
joinInfo.joinLayerId = v2.id()
joinInfo.joinFieldName = "id"
#joinInfo.prefix = "B_";
v1.addJoin(joinInfo)
self.assertEqual(len(v1.fields()), 6)

df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
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()))

# with a field subset
v1.removeJoin(v2.id())
joinInfo.setJoinFieldNamesSubset(["bname"])
v1.addJoin(joinInfo)
self.assertEqual(len(v1.fields()), 5)
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
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()))
joinInfo.setJoinFieldNamesSubset(None)

# add a table prefix to the join
v1.removeJoin(v2.id())
joinInfo.prefix = "BB_"
v1.addJoin(joinInfo)
self.assertEqual(len(v1.fields()), 6)
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
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()))
joinInfo.prefix = ""
v1.removeJoin(v2.id())
v1.addJoin(joinInfo)

# add another join
joinInfo2 = QgsVectorJoinInfo()
joinInfo2.targetFieldName = "c_id"
joinInfo2.joinLayerId = v3.id()
joinInfo2.joinFieldName = "id"
v1.addJoin(joinInfo2)
self.assertEqual(len(v1.fields()), 7)
df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1)
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 ' +
'LEFT JOIN {} AS j1 ON t."b_id"=j1."id" ' +
'LEFT JOIN {} AS j2 ON t."c_id"=j2."id"').format(v1.id(), v2.id(), v3.id()))

QgsMapLayerRegistry.instance().removeMapLayers([v1, v2, v3])


if __name__ == '__main__':
unittest.main()

0 comments on commit 2257d71

Please sign in to comment.