Skip to content

Commit

Permalink
Merge pull request #4833 from pblottiere/bugfix_relationquote_218
Browse files Browse the repository at this point in the history
[bugfix] Fixes relation widget reference when filter value contains a quote #16399 (backport)
  • Loading branch information
nyalldawson committed Jul 11, 2017
2 parents eec19de + 0a13019 commit bd1f9bf
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 37 deletions.
9 changes: 9 additions & 0 deletions python/core/qgsexpression.sip
Expand Up @@ -1008,6 +1008,15 @@ class QgsExpression
*/
static QString formatPreviewString( const QVariant& value );

/** Create an expression allowing to evaluate if a field is equal to a
* value. The value may be null.
* @param fieldName the name of the field
* @param value the value of the field
* @returns the expression to evaluate field equality
* @since QGIS 2.18
*/
static QString createFieldEqualityExpression( const QString &fieldName, const QVariant &value );

protected:
void initGeomCalculator();
};
12 changes: 12 additions & 0 deletions src/core/qgsexpression.cpp
Expand Up @@ -5324,6 +5324,18 @@ QVariant QgsExpression::StaticFunction::func( const QVariantList &values, const
Q_NOWARN_DEPRECATED_POP
}

QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
{
QString expr;

if ( value.isNull() )
expr = QString( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
else
expr = QString( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );

return expr;
}

const QgsExpression::Node* QgsExpression::rootNode() const
{
return d->mRootNode;
Expand Down
9 changes: 9 additions & 0 deletions src/core/qgsexpression.h
Expand Up @@ -1488,6 +1488,15 @@ class CORE_EXPORT QgsExpression
*/
static QString formatPreviewString( const QVariant& value );

/** Create an expression allowing to evaluate if a field is equal to a
* value. The value may be null.
* @param fieldName the name of the field
* @param value the value of the field
* @returns the expression to evaluate field equality
* @since QGIS 2.18
*/
static QString createFieldEqualityExpression( const QString &fieldName, const QVariant &value );

protected:
void initGeomCalculator();

Expand Down
30 changes: 3 additions & 27 deletions src/core/qgsrelation.cpp
Expand Up @@ -167,19 +167,8 @@ QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature& feature ) const

Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mFieldPairs )
{
int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );
QgsField referencingField = referencingLayer()->fields().at( referencingIdx );

if ( referencingField.type() == QVariant::String )
{
// Use quotes
conditions << QString( "\"%1\" = '%2'" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
}
else
{
// No quotes
conditions << QString( "\"%1\" = %2" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
}
QVariant val( feature.attribute( fieldPair.referencedField() ) );
conditions << QgsExpression::createFieldEqualityExpression( fieldPair.referencingField(), val );
}

return conditions.join( " AND " );
Expand All @@ -191,21 +180,8 @@ QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsAttributes&

Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mFieldPairs )
{
int referencedIdx = referencedLayer()->fields().indexFromName( fieldPair.referencedField() );
int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );

QgsField referencedField = referencedLayer()->fields().at( referencedIdx );

if ( referencedField.type() == QVariant::String )
{
// Use quotes
conditions << QString( "\"%1\" = '%2'" ).arg( fieldPair.referencedField(), attributes.at( referencingIdx ).toString() );
}
else
{
// No quotes
conditions << QString( "\"%1\" = %2" ).arg( fieldPair.referencedField(), attributes.at( referencingIdx ).toString() );
}
conditions << QgsExpression::createFieldEqualityExpression( fieldPair.referencedField(), attributes.at( referencingIdx ) );
}

QgsFeatureRequest myRequest;
Expand Down
9 changes: 1 addition & 8 deletions src/gui/editorwidgets/qgsrelationreferencewidget.cpp
Expand Up @@ -846,14 +846,7 @@ void QgsRelationReferenceWidget::filterChanged()
}
else
{
if ( mReferencedLayer->fields().field( fieldName ).type() == QVariant::String )
{
filters << QString( "\"%1\" = '%2'" ).arg( fieldName, cb->currentText() );
}
else
{
filters << QString( "\"%1\" = %2" ).arg( fieldName, cb->currentText() );
}
filters << QgsExpression::createFieldEqualityExpression( fieldName, cb->currentText() );
}
attrs << mReferencedLayer->fieldNameIndex( fieldName );
}
Expand Down
34 changes: 34 additions & 0 deletions tests/src/python/test_qgsexpression.py
Expand Up @@ -195,5 +195,39 @@ def testValid(self):
e.setExpression('1')
self.assertTrue(e.isValid())

def testCreateFieldEqualityExpression(self):
e = QgsExpression()

# test when value is null
field = "myfield"
value = None
res = '"myfield" IS NULL'
self.assertEqual(e.createFieldEqualityExpression(field, value), res)

# test when value is null and field name has a quote
field = "my'field"
value = None
res = '"my\'field" IS NULL'
self.assertEqual(e.createFieldEqualityExpression(field, value), res)

# test when field name has a quote and value is an int
field = "my'field"
value = 5
res = '"my\'field" = 5'
self.assertEqual(e.createFieldEqualityExpression(field, value), res)

# test when field name has a quote and value is a string
field = "my'field"
value = '5'
res = '"my\'field" = \'5\''
self.assertEqual(e.createFieldEqualityExpression(field, value), res)

# test when field name has a quote and value is a boolean
field = "my'field"
value = True
res = '"my\'field" = TRUE'
self.assertEqual(e.createFieldEqualityExpression(field, value), res)


if __name__ == "__main__":
unittest.main()
23 changes: 21 additions & 2 deletions tests/src/python/test_qgsrelation.py
Expand Up @@ -17,6 +17,7 @@
from qgis.PyQt.QtCore import QFileInfo
from qgis.core import (QgsVectorLayer,
QgsFeature,
QgsFeatureRequest,
QgsRelation,
QgsGeometry,
QgsPoint,
Expand All @@ -43,7 +44,11 @@ def createReferencingLayer():
f2.setFields(layer.pendingFields())
f2.setAttributes(["test2", 123])
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(101, 201)))
assert pr.addFeatures([f1, f2])
f3 = QgsFeature()
f3.setFields(layer.pendingFields())
f3.setAttributes(["foobar'bar", 124])
f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(101, 201)))
assert pr.addFeatures([f1, f2, f3])
return layer


Expand All @@ -62,7 +67,7 @@ def createReferencedLayer():
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
f3 = QgsFeature()
f3.setFields(layer.pendingFields())
f3.setAttributes(["foobar", 789, 554])
f3.setAttributes(["foobar'bar", 789, 554])
f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 3)))
assert pr.addFeatures([f1, f2, f3])
return layer
Expand Down Expand Up @@ -117,6 +122,20 @@ def test_getRelatedFeatures(self):
it = rel.getRelatedFeatures(feat)
self.assertEqual([a.attributes() for a in it], [[u'test1', 123], [u'test2', 123]])

def test_getRelatedFeaturesWithQuote(self):
rel = QgsRelation()

rel.setRelationId('rel1')
rel.setRelationName('Relation Number One')
rel.setReferencingLayer(self.referencingLayer.id())
rel.setReferencedLayer(self.referencedLayer.id())
rel.addFieldPair('fldtxt', 'x')

feat = next(self.referencedLayer.getFeatures(QgsFeatureRequest().setFilterFid(3)))

it = rel.getRelatedFeatures(feat)
assert next(it).attributes() == ["foobar'bar", 124]

def test_getReferencedFeature(self):
rel = QgsRelation()
rel.setRelationId('rel1')
Expand Down

0 comments on commit bd1f9bf

Please sign in to comment.