Skip to content

Commit e4ae11f

Browse files
committedAug 9, 2011
Improved handling of types in expressions:
- implicit conversions to string / int / number / TVL - error reporting on conversion errors - automatic conversion to numeric values for arithmetic operators - try to use numeric comparison by default, fallback to string comparison - functions do not need to explicitly care about NULL passed as an argument
1 parent 98a1c23 commit e4ae11f

File tree

2 files changed

+209
-139
lines changed

2 files changed

+209
-139
lines changed
 

‎src/core/qgsexpression.cpp

Lines changed: 192 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,28 @@ Q_DECLARE_METATYPE( TVL )
8686
///////////////////////////////////////////////
8787
// QVariant checks and conversions
8888

89-
inline bool isInt( const QVariant& v ) { return v.type() == QVariant::Int; }
90-
inline bool isDouble( const QVariant& v ) { return v.type() == QVariant::Double; }
91-
inline bool isNumeric( const QVariant& v ) { return v.type() == QVariant::Int || v.type() == QVariant::Double; }
92-
inline bool isString( const QVariant& v ) { return v.type() == QVariant::String; }
93-
inline bool isNull( const QVariant& v ) { return v.type() == QVariant::Invalid; }
94-
inline bool isLogic( const QVariant& v ) { return v.canConvert<TVL>(); }
95-
inline bool isNumericOrNull( const QVariant& v ) { return v.type() == QVariant::Int || v.type() == QVariant::Double; }
96-
97-
inline int qvInt( const QVariant& v ) { return v.toInt(); }
98-
inline double qvDouble( const QVariant& v ) { return v.toDouble(); }
99-
inline QString qvString( const QVariant& v ) { return v.toString(); }
100-
inline TVL qvLogic( const QVariant& v ) { return v.value<TVL>(); }
89+
inline bool isIntSafe( const QVariant& v )
90+
{
91+
if ( v.type() == QVariant::Int || v.canConvert<TVL>() ) return true;
92+
if ( v.type() == QVariant::Double ) return false;
93+
if ( v.type() == QVariant::String ) { bool ok; v.toString().toInt( &ok ); return ok; }
94+
return false;
95+
}
96+
inline bool isDoubleSafe( const QVariant& v )
97+
{
98+
if ( v.type() == QVariant::Double || v.type() == QVariant::Int || v.canConvert<TVL>() ) return true;
99+
if ( v.type() == QVariant::String ) { bool ok; v.toString().toDouble( &ok ); return ok; }
100+
return false;
101+
}
102+
103+
inline bool isNull( const QVariant& v ) { return v.type() == QVariant::Invalid || ( v.canConvert<TVL>() && v.value<TVL>().val == TVL::Unknown ); }
104+
inline bool isTVL( const QVariant & v ) { return v.canConvert<TVL>(); }
101105

102106
///////////////////////////////////////////////
103107
// evaluation error macros
104108

105-
#define ENSURE_NO_EVAL_ERROR { if (!parent->mEvalErrorString.isNull()) return QVariant(); }
106-
#define SET_EVAL_ERROR(x) { parent->mEvalErrorString = x; return QVariant(); }
109+
#define ENSURE_NO_EVAL_ERROR { if (parent->hasEvalError()) return QVariant(); }
110+
#define SET_EVAL_ERROR(x) { parent->setEvalErrorString(x); return QVariant(); }
107111

108112
///////////////////////////////////////////////
109113
// operators
@@ -124,131 +128,159 @@ const char* QgsExpression::UnaryOperatorText[] =
124128
///////////////////////////////////////////////
125129
// functions
126130

127-
static int getIntArg( int i, const QVariantList& values, QgsExpression* parent )
131+
// implicit conversion to string
132+
static QString getStringValue( const QVariant& value, QgsExpression* )
128133
{
129-
QVariant v = values.at( i );
130-
if ( !isInt( v ) )
134+
if ( value.canConvert<TVL>() )
135+
{
136+
TVL::Value tvl = value.value<TVL>().val;
137+
Q_ASSERT( tvl != TVL::Unknown ); // null should be handled before calling this function
138+
return ( tvl == TVL::True ? "1" : "0" );
139+
}
140+
return value.toString();
141+
}
142+
143+
static double getDoubleValue( const QVariant& value, QgsExpression* parent )
144+
{
145+
if ( value.canConvert<TVL>() )
146+
{
147+
TVL::Value tvl = value.value<TVL>().val;
148+
Q_ASSERT( tvl != TVL::Unknown ); // null should be handled before calling this function
149+
return ( tvl == TVL::True ? 1 : 0 );
150+
}
151+
152+
bool ok;
153+
double x = value.toDouble( &ok );
154+
if ( !ok )
131155
{
132-
parent->setEvalErrorString( "Function needs integer value" );
156+
parent->setEvalErrorString( QString( "Cannot convert '%1' to double" ).arg( value.toString() ) );
133157
return 0;
134158
}
135-
return v.toInt();
159+
return x;
136160
}
137161

138-
static double getNumericArg( int i, const QVariantList& values, QgsExpression* parent )
162+
static int getIntValue( const QVariant& value, QgsExpression* parent )
139163
{
140-
QVariant v = values.at( i );
141-
if ( !isNumeric( v ) )
164+
if ( value.canConvert<TVL>() )
142165
{
143-
parent->setEvalErrorString( "Function needs numeric value" );
144-
return NAN;
166+
TVL::Value tvl = value.value<TVL>().val;
167+
Q_ASSERT( tvl != TVL::Unknown ); // null should be handled before calling this function
168+
return ( tvl == TVL::True ? 1 : 0 );
145169
}
146-
return v.toDouble();
170+
171+
bool ok;
172+
int x = value.toInt( &ok );
173+
if ( !ok )
174+
{
175+
parent->setEvalErrorString( QString( "Cannot convert '%1' to int" ).arg( value.toString() ) );
176+
return 0;
177+
}
178+
return x;
147179
}
148180

149-
static QString getStringArg( int i, const QVariantList& values, QgsExpression* parent )
181+
182+
// this handles also NULL values
183+
static TVL getTVLValue( const QVariant& value, QgsExpression* parent )
150184
{
151-
QVariant v = values.at( i );
152-
if ( !isString( v ) )
185+
if ( isTVL( value ) )
186+
return value.value<TVL>();
187+
188+
// we need to convert to TVL
189+
if ( value.type() == QVariant::Invalid )
190+
return TVL();
191+
192+
bool ok;
193+
double x = value.toDouble( &ok );
194+
if ( !ok )
153195
{
154-
parent->setEvalErrorString( "Function needs string value" );
155-
return QString();
196+
parent->setEvalErrorString( QString( "Cannot convert '%1' to boolean" ).arg( value.toString() ) );
197+
return TVL();
156198
}
157-
return v.toString();
199+
return x != 0 ? TVL( true ) : TVL( false );
158200
}
159201

202+
//////
203+
160204
QVariant fcnSqrt( const QVariantList& values, QgsFeature* /*f*/, QgsExpression* parent )
161205
{
162-
double x = getNumericArg( 0, values, parent );
163-
return isnan( x ) ? QVariant() : QVariant( sqrt( x ) );
206+
double x = getDoubleValue( values.at( 0 ), parent );
207+
return QVariant( sqrt( x ) );
164208
}
165209
QVariant fcnSin( const QVariantList& values, QgsFeature* , QgsExpression* parent )
166210
{
167-
double x = getNumericArg( 0, values, parent );
168-
return isnan( x ) ? QVariant() : QVariant( sin( x ) );
211+
double x = getDoubleValue( values.at( 0 ), parent );
212+
return QVariant( sin( x ) );
169213
}
170214
QVariant fcnCos( const QVariantList& values, QgsFeature* , QgsExpression* parent )
171215
{
172-
double x = getNumericArg( 0, values, parent );
173-
return isnan( x ) ? QVariant() : QVariant( cos( x ) );
216+
double x = getDoubleValue( values.at( 0 ), parent );
217+
return QVariant( cos( x ) );
174218
}
175219
QVariant fcnTan( const QVariantList& values, QgsFeature* , QgsExpression* parent )
176220
{
177-
double x = getNumericArg( 0, values, parent );
178-
return isnan( x ) ? QVariant() : QVariant( tan( x ) );
221+
double x = getDoubleValue( values.at( 0 ), parent );
222+
return QVariant( tan( x ) );
179223
}
180224
QVariant fcnAsin( const QVariantList& values, QgsFeature* , QgsExpression* parent )
181225
{
182-
double x = getNumericArg( 0, values, parent );
183-
return isnan( x ) ? QVariant() : QVariant( asin( x ) );
226+
double x = getDoubleValue( values.at( 0 ), parent );
227+
return QVariant( asin( x ) );
184228
}
185229
QVariant fcnAcos( const QVariantList& values, QgsFeature* , QgsExpression* parent )
186230
{
187-
double x = getNumericArg( 0, values, parent );
188-
return isnan( x ) ? QVariant() : QVariant( acos( x ) );
231+
double x = getDoubleValue( values.at( 0 ), parent );
232+
return QVariant( acos( x ) );
189233
}
190234
QVariant fcnAtan( const QVariantList& values, QgsFeature* , QgsExpression* parent )
191235
{
192-
double x = getNumericArg( 0, values, parent );
193-
return isnan( x ) ? QVariant() : QVariant( atan( x ) );
236+
double x = getDoubleValue( values.at( 0 ), parent );
237+
return QVariant( atan( x ) );
194238
}
195239
QVariant fcnAtan2( const QVariantList& values, QgsFeature* , QgsExpression* parent )
196240
{
197-
double y = getNumericArg( 0, values, parent );
198-
double x = getNumericArg( 1, values, parent );
199-
if ( isnan( y ) || isnan( x ) ) return QVariant();
241+
double y = getDoubleValue( values.at( 0 ), parent );
242+
double x = getDoubleValue( values.at( 1 ), parent );
200243
return QVariant( atan2( y, x ) );
201244
}
202-
QVariant fcnToInt( const QVariantList& values, QgsFeature* , QgsExpression* /*parent*/ )
245+
QVariant fcnToInt( const QVariantList& values, QgsFeature* , QgsExpression* parent )
203246
{
204-
QVariant v = values.at( 0 );
205-
if ( v.type() == QVariant::Invalid ) return QVariant();
206-
return QVariant( v.toInt() );
247+
return QVariant( getIntValue( values.at( 0 ), parent ) );
207248
}
208-
QVariant fcnToReal( const QVariantList& values, QgsFeature* , QgsExpression* /*parent*/ )
249+
QVariant fcnToReal( const QVariantList& values, QgsFeature* , QgsExpression* parent )
209250
{
210-
QVariant v = values.at( 0 );
211-
if ( v.type() == QVariant::Invalid ) return QVariant();
212-
return QVariant( v.toDouble() );
251+
return QVariant( getDoubleValue( values.at( 0 ), parent ) );
213252
}
214-
QVariant fcnToString( const QVariantList& values, QgsFeature* , QgsExpression* /*parent*/ )
253+
QVariant fcnToString( const QVariantList& values, QgsFeature* , QgsExpression* parent )
215254
{
216-
QVariant v = values.at( 0 );
217-
if ( v.type() == QVariant::Invalid ) return QVariant();
218-
return QVariant( v.toString() );
255+
return QVariant( getStringValue( values.at( 0 ), parent ) );
219256
}
220257
QVariant fcnLower( const QVariantList& values, QgsFeature* , QgsExpression* parent )
221258
{
222-
QString str = getStringArg( 0, values, parent );
223-
if ( str.isNull() ) return QVariant(); // error or null string
259+
QString str = getStringValue( values.at( 0 ), parent );
224260
return QVariant( str.toLower() );
225261
}
226262
QVariant fcnUpper( const QVariantList& values, QgsFeature* , QgsExpression* parent )
227263
{
228-
QString str = getStringArg( 0, values, parent );
229-
if ( str.isNull() ) return QVariant(); // error or null string
264+
QString str = getStringValue( values.at( 0 ), parent );
230265
return QVariant( str.toUpper() );
231266
}
232267
QVariant fcnLength( const QVariantList& values, QgsFeature* , QgsExpression* parent )
233268
{
234-
QString str = getStringArg( 0, values, parent );
235-
if ( str.isNull() ) return QVariant(); // error or null string
269+
QString str = getStringValue( values.at( 0 ), parent );
236270
return QVariant( str.length() );
237271
}
238272
QVariant fcnReplace( const QVariantList& values, QgsFeature* , QgsExpression* parent )
239273
{
240-
QString str = getStringArg( 0, values, parent );
241-
QString before = getStringArg( 1, values, parent );
242-
QString after = getStringArg( 2, values, parent );
243-
if ( str.isNull() || before.isNull() || after.isNull() ) return QVariant(); // error or null string
274+
QString str = getStringValue( values.at( 0 ), parent );
275+
QString before = getStringValue( values.at( 1 ), parent );
276+
QString after = getStringValue( values.at( 2 ), parent );
244277
return QVariant( str.replace( before, after ) );
245278
}
246279
QVariant fcnRegexpReplace( const QVariantList& values, QgsFeature* , QgsExpression* parent )
247280
{
248-
QString str = getStringArg( 0, values, parent );
249-
QString regexp = getStringArg( 1, values, parent );
250-
QString after = getStringArg( 2, values, parent );
251-
if ( str.isNull() || regexp.isNull() || after.isNull() ) return QVariant(); // error or null string
281+
QString str = getStringValue( values.at( 0 ), parent );
282+
QString regexp = getStringValue( values.at( 1 ), parent );
283+
QString after = getStringValue( values.at( 2 ), parent );
252284

253285
QRegExp re( regexp );
254286
if ( !re.isValid() )
@@ -260,12 +292,9 @@ QVariant fcnRegexpReplace( const QVariantList& values, QgsFeature* , QgsExpressi
260292
}
261293
QVariant fcnSubstr( const QVariantList& values, QgsFeature* , QgsExpression* parent )
262294
{
263-
QString str = getStringArg( 0, values, parent );
264-
if ( str.isNull() ) return QVariant(); // error or null string
265-
int from = getIntArg( 1, values, parent );
266-
if ( parent->hasEvalError() ) return QVariant();
267-
int len = getIntArg( 2, values, parent );
268-
if ( parent->hasEvalError() ) return QVariant();
295+
QString str = getStringValue( values.at( 0 ), parent );
296+
int from = getIntValue( values.at( 1 ), parent );
297+
int len = getIntValue( values.at( 2 ), parent );
269298
return QVariant( str.mid( from -1, len ) );
270299
}
271300

@@ -298,7 +327,7 @@ QVariant fcnY( const QVariantList& , QgsFeature* f, QgsExpression* )
298327

299328
static QVariant pointAt( const QVariantList& values, QgsFeature* f, QgsExpression* parent ) // helper function
300329
{
301-
int idx = getIntArg( 0, values, parent );
330+
int idx = getIntValue( values.at( 0 ), parent );
302331
ENSURE_GEOM_TYPE( f, g, QGis::Line );
303332
QgsPolyline polyline = g->asPolyline();
304333
if ( idx < 0 )
@@ -532,25 +561,24 @@ QVariant QgsExpression::NodeUnaryOperator::eval( QgsExpression* parent, QgsFeatu
532561
switch ( mOp )
533562
{
534563
case uoNot:
535-
if ( isLogic( val ) )
536-
val.setValue( ! qvLogic( val ) );
537-
else
538-
SET_EVAL_ERROR( "NOT applicable only on boolean" );
539-
break;
564+
{
565+
TVL tvl = getTVLValue( val, parent );
566+
ENSURE_NO_EVAL_ERROR;
567+
return QVariant::fromValue( ! tvl );
568+
}
540569

541570
case uoMinus:
542-
if ( isInt( val ) )
543-
val.setValue( - qvInt( val ) );
544-
else if ( isDouble( val ) )
545-
val.setValue( - qvDouble( val ) );
571+
if ( isIntSafe( val ) )
572+
return QVariant( - getIntValue( val, parent ) );
573+
else if ( isDoubleSafe( val ) )
574+
return QVariant( - getDoubleValue( val, parent ) );
546575
else
547576
SET_EVAL_ERROR( "Unary minus only for numeric values." );
548577
break;
549578
default:
550579
Q_ASSERT( 0 && "unknown unary operation" );
551580
}
552-
553-
return val;
581+
return QVariant();
554582
}
555583

556584
bool QgsExpression::NodeUnaryOperator::prepare( QgsExpression* parent, const QgsFieldMap& fields )
@@ -581,37 +609,47 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
581609
case boMod:
582610
if ( isNull( vL ) || isNull( vR ) )
583611
return QVariant();
584-
else if ( isNumeric( vL ) && isNumeric( vR ) )
612+
else if ( isIntSafe( vL ) && isIntSafe( vR ) )
585613
{
586-
if ( mOp == boDiv && qvDouble( vR ) == 0 )
587-
return QVariant(); // silently handle division by zero and return NULL
588-
if ( isInt( vL ) && isInt( vR ) )
589-
return QVariant( computeInt( qvInt( vL ), qvInt( vR ) ) );
590-
else
591-
return QVariant( computeDouble( qvDouble( vL ), qvDouble( vR ) ) );
614+
// both are integers - let's use integer arithmetics
615+
int iL = getIntValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
616+
int iR = getIntValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
617+
if ( mOp == boDiv && iR == 0 ) return QVariant(); // silently handle division by zero and return NULL
618+
return QVariant( computeInt( iL, iR ) );
592619
}
593620
else
594-
SET_EVAL_ERROR( "Arithmetic possible only with numeric values" );
621+
{
622+
// general floating point arithmetic
623+
double fL = getDoubleValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
624+
double fR = getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
625+
if ( mOp == boDiv && fR == 0 )
626+
return QVariant(); // silently handle division by zero and return NULL
627+
return QVariant( computeDouble( fL, fR ) );
628+
}
595629

596630
case boPow:
597631
if ( isNull( vL ) || isNull( vR ) )
598632
return QVariant();
599-
else if ( isNumeric( vL ) && isNumeric( vR ) )
600-
return QVariant( pow( qvDouble( vL ), qvDouble( vR ) ) );
601633
else
602-
SET_EVAL_ERROR( "Arithmetic possible only with numeric values" );
634+
{
635+
double fL = getDoubleValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
636+
double fR = getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
637+
return QVariant( pow( fL, fR ) );
638+
}
603639

604640
case boAnd:
605-
if ( isLogic( vL ) && isLogic( vR ) )
606-
return QVariant::fromValue( qvLogic( vL ) & qvLogic( vR ) );
607-
else
608-
SET_EVAL_ERROR( "AND applicable only on boolean" );
641+
{
642+
TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent );
643+
ENSURE_NO_EVAL_ERROR;
644+
return QVariant::fromValue( tvlL & tvlR );
645+
}
609646

610647
case boOr:
611-
if ( isLogic( vL ) && isLogic( vR ) )
612-
return QVariant::fromValue( qvLogic( vL ) | qvLogic( vR ) );
613-
else
614-
SET_EVAL_ERROR( "OR applicable only on boolean" );
648+
{
649+
TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent );
650+
ENSURE_NO_EVAL_ERROR;
651+
return QVariant::fromValue( tvlL | tvlR );
652+
}
615653

616654
case boEQ:
617655
case boNE:
@@ -623,18 +661,21 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
623661
{
624662
return TVL_Unknown;
625663
}
626-
else if ( isNumeric( vL ) && isNumeric( vR ) )
664+
else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) )
627665
{
628-
double diff = qvDouble( vL ) - qvDouble( vR );
629-
return compare( diff ) ? TVL_True : TVL_False;
666+
// do numeric comparison if both operators can be converted to numbers
667+
double fL = getDoubleValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
668+
double fR = getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
669+
return compare( fL - fR ) ? TVL_True : TVL_False;
630670
}
631-
else if ( isString( vL ) && isString( vR ) )
671+
else
632672
{
633-
int diff = QString::compare( qvString( vL ), qvString( vR ) );
673+
// do string comparison otherwise
674+
QString sL = getStringValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
675+
QString sR = getStringValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
676+
int diff = QString::compare( sL, sR );
634677
return compare( diff ) ? TVL_True : TVL_False;
635678
}
636-
else
637-
SET_EVAL_ERROR( "Invalid arguments for comparison" );
638679

639680
case boIs:
640681
case boIsNot:
@@ -645,12 +686,18 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
645686
else // both operators non-null
646687
{
647688
bool equal = false;
648-
if ( isNumeric( vL ) && isNumeric( vR ) )
649-
equal = qvDouble( vL ) == qvDouble( vR );
650-
else if ( isString( vL ) && isString( vR ) )
651-
equal = QString::compare( qvString( vL ), qvString( vR ) ) == 0;
689+
if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) )
690+
{
691+
double fL = getDoubleValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
692+
double fR = getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
693+
equal = fL == fR;
694+
}
652695
else
653-
SET_EVAL_ERROR( "Invalid arguments for comparison" );
696+
{
697+
QString sL = getStringValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
698+
QString sR = getStringValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
699+
equal = QString::compare( sL, sR ) == 0;
700+
}
654701
if ( equal )
655702
return mOp == boIs ? TVL_True : TVL_False;
656703
else
@@ -662,9 +709,10 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
662709
case boILike:
663710
if ( isNull( vL ) || isNull( vR ) )
664711
return TVL_Unknown;
665-
else if ( isString( vL ) && isString( vR ) )
712+
else
666713
{
667-
QString str = qvString( vL ), regexp = qvString( vR );
714+
QString str = getStringValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
715+
QString regexp = getStringValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
668716
// TODO: cache QRegExp in case that regexp is a literal string (i.e. it will stay constant)
669717
bool matches;
670718
if ( mOp == boLike || mOp == boILike ) // change from LIKE syntax to regexp
@@ -680,18 +728,16 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression* parent, QgsFeat
680728
}
681729
return matches ? TVL_True : TVL_False;
682730
}
683-
else
684-
SET_EVAL_ERROR( "Invalid arguments for regexp" );
685731

686732
case boConcat:
687733
if ( isNull( vL ) || isNull( vR ) )
688734
return QVariant();
689-
else if ( isString( vL ) || isString( vR ) )
735+
else
690736
{
691-
return QVariant( qvString( vL ) + qvString( vR ) );
737+
QString sL = getStringValue( vL, parent ); ENSURE_NO_EVAL_ERROR;
738+
QString sR = getStringValue( vR, parent ); ENSURE_NO_EVAL_ERROR;
739+
return QVariant( sL + sR );
692740
}
693-
else
694-
SET_EVAL_ERROR( "Invalid arguments for concatenation" );
695741

696742
default: break;
697743
}
@@ -775,12 +821,18 @@ QVariant QgsExpression::NodeInOperator::eval( QgsExpression* parent, QgsFeature*
775821
{
776822
bool equal = false;
777823
// check whether they are equal
778-
if ( isNumeric( v1 ) && isNumeric( v2 ) )
779-
equal = ( qvDouble( v1 ) == qvDouble( v2 ) );
780-
else if ( isString( v1 ) && isString( v2 ) )
781-
equal = ( QString::compare( qvString( v1 ), qvString( v2 ) ) == 0 );
824+
if ( isDoubleSafe( v1 ) && isDoubleSafe( v2 ) )
825+
{
826+
double f1 = getDoubleValue( v1, parent ); ENSURE_NO_EVAL_ERROR;
827+
double f2 = getDoubleValue( v2, parent ); ENSURE_NO_EVAL_ERROR;
828+
equal = f1 == f2;
829+
}
782830
else
783-
SET_EVAL_ERROR( "Invalid arguments for comparison (IN operator)" );
831+
{
832+
QString s1 = getStringValue( v1, parent ); ENSURE_NO_EVAL_ERROR;
833+
QString s2 = getStringValue( v2, parent ); ENSURE_NO_EVAL_ERROR;
834+
equal = QString::compare( s1, s2 ) == 0;
835+
}
784836

785837
if ( equal ) // we know the result
786838
return mNotIn ? TVL_False : TVL_True;
@@ -821,8 +873,11 @@ QVariant QgsExpression::NodeFunction::eval( QgsExpression* parent, QgsFeature* f
821873
{
822874
foreach( Node* n, mArgs->list() )
823875
{
824-
argValues.append( n->eval( parent, f ) );
876+
QVariant v = n->eval( parent, f );
825877
ENSURE_NO_EVAL_ERROR;
878+
if ( isNull( v ) )
879+
return QVariant(); // all "normal" functions return NULL when any parameter is NULL
880+
argValues.append( v );
826881
}
827882
}
828883

‎tests/src/core/testqgsexpression.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,15 @@ class TestQgsExpression: public QObject
109109
QTest::newRow( "div double" ) << "11.0/2" << false << QVariant( 5.5 );
110110
QTest::newRow( "mod double" ) << "6.1 % 2.5" << false << QVariant( 1.1 );
111111
QTest::newRow( "pow" ) << "2^8" << false << QVariant( 256. );
112+
QTest::newRow( "division by zero" ) << "1/0" << false << QVariant();
113+
QTest::newRow( "division by zero" ) << "1.0/0.0" << false << QVariant();
112114

113115
// comparison
114116
QTest::newRow( "eq int" ) << "1+1 = 2" << false << QVariant( 1 );
115117
QTest::newRow( "eq double" ) << "3.2 = 2.2+1" << false << QVariant( 1 );
116118
QTest::newRow( "eq string" ) << "'a' = 'b'" << false << QVariant( 0 );
117119
QTest::newRow( "eq null" ) << "2 = null" << false << QVariant();
118-
QTest::newRow( "eq invalid" ) << "'a' = 1" << true << QVariant();
120+
QTest::newRow( "eq mixed" ) << "'a' = 1" << false << QVariant( 0 );
119121
QTest::newRow( "ne int 1" ) << "3 != 4" << false << QVariant( 1 );
120122
QTest::newRow( "ne int 2" ) << "3 != 3" << false << QVariant( 0 );
121123
QTest::newRow( "lt int 1" ) << "3 < 4" << false << QVariant( 1 );
@@ -176,7 +178,7 @@ class TestQgsExpression: public QObject
176178
QTest::newRow( "concat with int" ) << "'a' || 1" << false << QVariant( "a1" );
177179
QTest::newRow( "concat with int" ) << "2 || 'b'" << false << QVariant( "2b" );
178180
QTest::newRow( "concat with null" ) << "'a' || null" << false << QVariant();
179-
QTest::newRow( "invalid concat" ) << "1 || 2" << true << QVariant();
181+
QTest::newRow( "concat numbers" ) << "1 || 2" << false << QVariant( "12" );
180182

181183
// math functions
182184
QTest::newRow( "sqrt" ) << "sqrt(16)" << false << QVariant( 4. );
@@ -210,6 +212,15 @@ class TestQgsExpression: public QObject
210212
QTest::newRow( "regexp_replace invalid" ) << "regexp_replace('HeLLo','[[[', '-')" << true << QVariant();
211213
QTest::newRow( "substr" ) << "substr('HeLLo', 3,2)" << false << QVariant( "LL" );
212214
QTest::newRow( "substr outside" ) << "substr('HeLLo', -5,2)" << false << QVariant( "" );
215+
216+
// implicit conversions
217+
QTest::newRow( "implicit int->text" ) << "length(123)" << false << QVariant( 3 );
218+
QTest::newRow( "implicit double->text" ) << "length(1.23)" << false << QVariant( 4 );
219+
QTest::newRow( "implicit int->bool" ) << "1 or 0" << false << QVariant( 1 );
220+
QTest::newRow( "implicit double->bool" ) << "0.1 or 0" << false << QVariant( 1 );
221+
QTest::newRow( "implicit text->int" ) << "'5'+2" << false << QVariant( 7 );
222+
QTest::newRow( "implicit text->double" ) << "'5.1'+2" << false << QVariant( 7.1 );
223+
QTest::newRow( "implicit text->bool" ) << "'0.1' or 0" << false << QVariant( 1 );
213224
}
214225

215226
void evaluation()
@@ -224,6 +235,10 @@ class TestQgsExpression: public QObject
224235
QVariant res = exp.evaluate();
225236
if ( exp.hasEvalError() )
226237
qDebug() << exp.evalErrorString();
238+
if ( res.type() != result.type() )
239+
{
240+
qDebug() << "got " << res.typeName() << " instead of " << result.typeName();
241+
}
227242
//qDebug() << res.type() << " " << result.type();
228243
//qDebug() << "type " << res.typeName();
229244
QCOMPARE( exp.hasEvalError(), evalError );

0 commit comments

Comments
 (0)
Please sign in to comment.