diff --git a/python/core/qgsexpression.sip b/python/core/qgsexpression.sip index 067e5d4b55f..1fc7c3515fc 100644 --- a/python/core/qgsexpression.sip +++ b/python/core/qgsexpression.sip @@ -194,7 +194,14 @@ class QgsExpression class Function { public: - Function( const QString& fnname, int params, const QString& group, const QString& helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList(), bool lazyEval = false ); + Function( const QString& fnname, + int params, + QString group, + QString helpText = QString(), + bool usesGeometry = false, + QStringList referencedColumns = QStringList(), + bool lazyEval = false, + bool handlesNull = false ); virtual ~Function(); @@ -225,11 +232,11 @@ class QgsExpression const QString helptext(); virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) = 0; + + virtual bool handlesNull() const; }; - static const QList& Functions(); - static const QStringList& BuiltinFunctions(); static bool registerFunction( Function* function ); @@ -310,6 +317,7 @@ class QgsExpression public: NodeList(); ~NodeList(); + /** Takes ownership of the provided node */ void append( QgsExpression::Node* node /Transfer/ ); int count(); const QList& list(); diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 5feacd20347..02714881001 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -1741,7 +1741,7 @@ const QList& QgsExpression::Functions() << new StaticFunction( "to_date", 1, fcnToDate, "Conversions", QString(), false, QStringList(), false, QStringList() << "todate" ) << new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "totime" ) << new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QStringList(), false, QStringList() << "tointerval" ) - << new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals" ) + << new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QStringList(), false, QStringList(), true ) << new StaticFunction( "if", 3, fcnIf, "Conditionals", "", False, QStringList(), true ) << new StaticFunction( "regexp_match", 2, fcnRegexpMatch, "Conditionals" ) << new StaticFunction( "now", 0, fcnNow, "Date and Time", QString(), false, QStringList(), false, QStringList() << "$now" ) @@ -1763,7 +1763,7 @@ const QList& QgsExpression::Functions() << new StaticFunction( "regexp_replace", 3, fcnRegexpReplace, "String" ) << new StaticFunction( "regexp_substr", 2, fcnRegexpSubstr, "String" ) << new StaticFunction( "substr", 3, fcnSubstr, "String" ) - << new StaticFunction( "concat", -1, fcnConcat, "String" ) + << new StaticFunction( "concat", -1, fcnConcat, "String", QString(), false, QStringList(), false, QStringList(), true ) << new StaticFunction( "strpos", 2, fcnStrpos, "String" ) << new StaticFunction( "left", 2, fcnLeft, "String" ) << new StaticFunction( "right", 2, fcnRight, "String" ) @@ -2683,7 +2683,7 @@ QVariant QgsExpression::NodeFunction::eval( QgsExpression* parent, const QgsFeat { v = n->eval( parent, f ); ENSURE_NO_EVAL_ERROR; - if ( isNull( v ) && fd->name() != "coalesce" ) + if ( isNull( v ) && !fd->handlesNull() ) return QVariant(); // all "normal" functions return NULL, when any parameter is NULL (so coalesce is abnormal) } argValues.append( v ); diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h index daf09ca2980..257d15fc92c 100644 --- a/src/core/qgsexpression.h +++ b/src/core/qgsexpression.h @@ -286,8 +286,23 @@ class CORE_EXPORT QgsExpression class CORE_EXPORT Function { public: - Function( const QString& fnname, int params, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList(), bool lazyEval = false ) - : mName( fnname ), mParams( params ), mUsesGeometry( usesGeometry ), mGroup( group ), mHelpText( helpText ), mReferencedColumns( referencedColumns ), mLazyEval( lazyEval ) {} + Function( const QString& fnname, + int params, + QString group, + QString helpText = QString(), + bool usesGeometry = false, + QStringList referencedColumns = QStringList(), + bool lazyEval = false, + bool handlesNull = false ) + : mName( fnname ) + , mParams( params ) + , mUsesGeometry( usesGeometry ) + , mGroup( group ) + , mHelpText( helpText ) + , mReferencedColumns( referencedColumns ) + , mLazyEval( lazyEval ) + , mHandlesNull( handlesNull ) + {} virtual ~Function() {} @@ -327,6 +342,8 @@ class CORE_EXPORT QgsExpression return false; } + virtual bool handlesNull() const { return mHandlesNull; } + private: QString mName; int mParams; @@ -335,13 +352,26 @@ class CORE_EXPORT QgsExpression QString mHelpText; QStringList mReferencedColumns; bool mLazyEval; + bool mHandlesNull; }; class StaticFunction : public Function { public: - StaticFunction( QString fnname, int params, FcnEval fcn, QString group, QString helpText = QString(), bool usesGeometry = false, QStringList referencedColumns = QStringList(), bool lazyEval = false, const QStringList& aliases = QStringList() ) - : Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval ), mFnc( fcn ), mAliases( aliases ) {} + StaticFunction( QString fnname, + int params, + FcnEval fcn, + QString group, + QString helpText = QString(), + bool usesGeometry = false, + QStringList referencedColumns = QStringList(), + bool lazyEval = false, + const QStringList& aliases = QStringList(), + bool handlesNull = false ) + : Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull ) + , mFnc( fcn ) + , mAliases( aliases ) + {} virtual ~StaticFunction() {} diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 555752d4b4f..14f9a9db3da 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -387,7 +387,8 @@ class TestQgsExpression: public QObject QTest::newRow( "wordwrap" ) << "wordwrap('university of qgis\nsupports many multiline',-5,' ')" << false << QVariant( "university\nof qgis\nsupports\nmany multiline" ); QTest::newRow( "format" ) << "format('%1 %2 %3 %1', 'One', 'Two', 'Three')" << false << QVariant( "One Two Three One" ); QTest::newRow( "concat" ) << "concat('a', 'b', 'c', 'd')" << false << QVariant( "abcd" ); - QTest::newRow( "concat single" ) << "concat('a')" << false << QVariant( "a" ); + QTest::newRow( "concat function single" ) << "concat('a')" << false << QVariant( "a" ); + QTest::newRow( "concat function with NULL" ) << "concat(NULL,'a','b')" << false << QVariant( "ab" ); // implicit conversions QTest::newRow( "implicit int->text" ) << "length(123)" << false << QVariant( 3 );