Skip to content
Permalink
Browse files
Merge pull request #4284 from arnaud-morvan/expression_compare_arrays
Add arrays comparison in QgsExpression
  • Loading branch information
nyalldawson committed Apr 23, 2017
2 parents 268acab + 8385cd9 commit f7b7254527a2380f86bd16d0273e8c341b9b4277
Showing with 128 additions and 0 deletions.
  1. +84 −0 src/core/qgsexpression.cpp
  2. +44 −0 tests/src/core/testqgsexpression.cpp
@@ -176,6 +176,11 @@ inline bool isNull( const QVariant &v )
return v.isNull();
}

inline bool isList( const QVariant &v )
{
return v.type() == QVariant::List;
}

///////////////////////////////////////////////
// evaluation error macros

@@ -4257,6 +4262,16 @@ QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type )
case QVariant::Bool:
return value.toBool() ? "TRUE" : "FALSE";

case QVariant::List:
{
QStringList quotedValues;
Q_FOREACH ( const QVariant &v, value.toList() )
{
quotedValues += quotedValue( v );
}
return QStringLiteral( "array( %1 )" ).arg( quotedValues.join( ", " ) );
}

default:
case QVariant::String:
return quotedString( value.toString() );
@@ -4834,6 +4849,75 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression *parent, const Q
{
return TVL_Unknown;
}
else if ( isList( vL ) || isList( vR ) )
{
// verify that we have two lists
if ( !isList( vL ) || !isList( vR ) )
return TVL_Unknown;

// and search for not equal respective items
QVariantList lL = vL.toList();
QVariantList lR = vR.toList();
for ( int i = 0; i < lL.length() && i < lR.length(); i++ )
{
if ( isNull( lL.at( i ) ) && isNull( lR.at( i ) ) )
continue; // same behavior as PostgreSQL

if ( isNull( lL.at( i ) ) || isNull( lR.at( i ) ) )
{
switch ( mOp )
{
case boEQ:
return false;
case boNE:
return true;
case boLT:
case boLE:
return isNull( lR.at( i ) );
case boGT:
case boGE:
return isNull( lL.at( i ) );
default:
Q_ASSERT( false );
return TVL_Unknown;
}
}

QgsExpression::NodeLiteral nL( lL.at( i ) );
QgsExpression::NodeLiteral nR( lR.at( i ) );
QgsExpression::NodeBinaryOperator eqNode( boEQ, nL.clone(), nR.clone() );
QVariant eq = eqNode.eval( parent, context );
ENSURE_NO_EVAL_ERROR;
if ( eq == TVL_False )
{
// return the two items comparison
QgsExpression::NodeBinaryOperator node( mOp, nL.clone(), nR.clone() );
QVariant v = node.eval( parent, context );
ENSURE_NO_EVAL_ERROR;
return v;
}
}

// default to length comparison
switch ( mOp )
{
case boEQ:
return lL.length() == lR.length();
case boNE:
return lL.length() != lR.length();
case boLT:
return lL.length() < lR.length();
case boGT:
return lL.length() > lR.length();
case boLE:
return lL.length() <= lR.length();
case boGE:
return lL.length() >= lR.length();
default:
Q_ASSERT( false );
return TVL_Unknown;
}
}
else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) &&
( vL.type() != QVariant::String || vR.type() != QVariant::String ) )
{
@@ -2447,6 +2447,48 @@ class TestQgsExpression: public QObject
QCOMPARE( badArray.evalErrorString(), QString( "Cannot convert 'not an array' to array" ) );
}

void compare_arrays()
{
QCOMPARE( QgsExpression( "array() = array()" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(NULL) = array(NULL)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array() = array(NULL)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array(1, NULL) = array(NULL, 1)" ).evaluate(), QVariant( false ) );

QCOMPARE( QgsExpression( "array('hello') = array('hello')" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array('hello') = array('hello2')" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') = array('h', 'e', 'l', 'l', 'o')" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') = array('h', 'e', 'l', 'l')" ).evaluate(), QVariant( false ) );

QCOMPARE( QgsExpression( "array('1') = array(1)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array('1.2') = array(1.2)" ).evaluate(), QVariant( true ) );

QCOMPARE( QgsExpression( "array() != array()" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array(NULL) != array(NULL)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array() != array(NULL)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array('hello') != array('hello')" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('hello') != array('hello2')" ).evaluate(), QVariant( true ) );

QCOMPARE( QgsExpression( "array() < array(1)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1) < array(NULL)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1) < array(1)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array(1) < array(2)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(2) < array(1)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array(1) < array(1, 2)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1, 2) < array(1)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') < array('h', 'e', 'l', 'l')" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') > array('h', 'e', 'l', 'l')" ).evaluate(), QVariant( true ) );

QCOMPARE( QgsExpression( "array() <= array(1)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1) <= array(NULL)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1) <= array(1)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1) <= array(2)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(2) <= array(1)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array(1) <= array(1, 2)" ).evaluate(), QVariant( true ) );
QCOMPARE( QgsExpression( "array(1, 2) <= array(1)" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') <= array('h', 'e', 'l', 'l')" ).evaluate(), QVariant( false ) );
QCOMPARE( QgsExpression( "array('h', 'e', 'l', 'l', 'o') >= array('h', 'e', 'l', 'l')" ).evaluate(), QVariant( true ) );
}

void eval_map()
{
QgsFeature f( 100 );
@@ -2543,6 +2585,8 @@ class TestQgsExpression: public QObject
QCOMPARE( QgsExpression::quotedValue( QVariant( true ), QVariant::String ), QString( "'true'" ) );
QCOMPARE( QgsExpression::quotedValue( QVariant() ), QString( "NULL" ) );
QCOMPARE( QgsExpression::quotedValue( QVariant(), QVariant::String ), QString( "NULL" ) );
QVariantList array = QVariantList() << QVariant( 1 ) << QVariant( "a" ) << QVariant();
QCOMPARE( QgsExpression::quotedValue( array ), QString( "array( 1, 'a', NULL )" ) );
}

void reentrant()

0 comments on commit f7b7254

Please sign in to comment.