Skip to content

Commit

Permalink
Merge branch 'bugfix_relationquote' of https://github.com/pblottiere/…
Browse files Browse the repository at this point in the history
…QGIS into pblottiere-bugfix_relationquote
  • Loading branch information
nyalldawson committed Jul 6, 2017
2 parents 603168e + e306cb4 commit fcf3361
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 44 deletions.
11 changes: 11 additions & 0 deletions python/core/expression/qgsexpression.sip
Expand Up @@ -495,6 +495,17 @@ return index of the function in Functions array
:rtype: str
%End

static QString createFieldEqualityExpression( const QString &fieldName, const QVariant &value );
%Docstring
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
:return: the expression to evaluate field equality
.. versionadded:: 3.0
:rtype: str
%End

};


Expand Down
12 changes: 12 additions & 0 deletions src/core/expression/qgsexpression.cpp
Expand Up @@ -836,6 +836,18 @@ QString QgsExpression::formatPreviewString( const QVariant &value )
}
}

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

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

return expr;
}

const QgsExpressionNode *QgsExpression::rootNode() const
{
return d->mRootNode;
Expand Down
9 changes: 9 additions & 0 deletions src/core/expression/qgsexpression.h
Expand Up @@ -449,6 +449,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 3.0
*/
static QString createFieldEqualityExpression( const QString &fieldName, const QVariant &value );

private:
void initGeomCalculator();

Expand Down
28 changes: 2 additions & 26 deletions src/core/qgsrelation.cpp
Expand Up @@ -170,23 +170,8 @@ QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
{
int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );
QgsField referencingField = referencingLayer()->fields().at( referencingIdx );

QVariant val( feature.attribute( fieldPair.referencedField() ) );

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

return conditions.join( QStringLiteral( " AND " ) );
Expand All @@ -203,16 +188,7 @@ QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsAttributes

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

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

QgsFeatureRequest myRequest;
Expand Down
17 changes: 1 addition & 16 deletions src/gui/editorwidgets/qgsrelationreferencewidget.cpp
Expand Up @@ -861,22 +861,7 @@ void QgsRelationReferenceWidget::filterChanged()
if ( cb->currentIndex() != 0 )
{
const QString fieldName = cb->property( "Field" ).toString();

if ( cb->currentText() == nullValue.toString() )
{
filters << QStringLiteral( "\"%1\" IS NULL" ).arg( fieldName );
}
else
{
if ( mReferencedLayer->fields().field( fieldName ).type() == QVariant::String )
{
filters << QStringLiteral( "\"%1\" = '%2'" ).arg( fieldName, cb->currentText() );
}
else
{
filters << QStringLiteral( "\"%1\" = %2" ).arg( fieldName, cb->currentText() );
}
}
filters << QgsExpression::createFieldEqualityExpression( fieldName, cb->currentText() );
attrs << mReferencedLayer->fields().lookupField( fieldName );
}
}
Expand Down
34 changes: 34 additions & 0 deletions tests/src/python/test_qgsexpression.py
Expand Up @@ -14,6 +14,7 @@

import qgis # NOQA

from qgis.PyQt.QtCore import QVariant
from qgis.testing import unittest
from qgis.utils import qgsfunction
from qgis.core import QgsExpression, QgsFeatureRequest
Expand Down Expand Up @@ -196,6 +197,39 @@ def testValid(self):
e.setExpression('1')
self.assertTrue(e.isValid())

def testCreateFieldEqualityExpression(self):
e = QgsExpression()

# test when value is null
field = "myfield"
value = QVariant()
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 = QVariant()
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()
22 changes: 20 additions & 2 deletions tests/src/python/test_qgsrelation.py
Expand Up @@ -38,7 +38,11 @@ def createReferencingLayer():
f2.setFields(layer.pendingFields())
f2.setAttributes(["test2", 123])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(101, 201)))
assert pr.addFeatures([f1, f2])
f3 = QgsFeature()
f3.setFields(layer.pendingFields())
f3.setAttributes(["foobar'bar", 124])
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(101, 201)))
assert pr.addFeatures([f1, f2, f3])
return layer


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

def test_getRelatedFeaturesWithQuote(self):
rel = QgsRelation()

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

feat = self.referencedLayer.getFeature(3)

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

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

0 comments on commit fcf3361

Please sign in to comment.