Skip to content

Commit

Permalink
expression: let concat handle NULL like empty string (like in postgres)
Browse files Browse the repository at this point in the history
  • Loading branch information
jef-n committed Jun 6, 2015
1 parent f3c781e commit 3387d6f
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 11 deletions.
14 changes: 11 additions & 3 deletions python/core/qgsexpression.sip
Expand Up @@ -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();

Expand Down Expand Up @@ -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<QgsExpression::Function *>& Functions();

static const QStringList& BuiltinFunctions();

static bool registerFunction( Function* function );
Expand Down Expand Up @@ -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<QgsExpression::Node*>& list();
Expand Down
6 changes: 3 additions & 3 deletions src/core/qgsexpression.cpp
Expand Up @@ -1741,7 +1741,7 @@ const QList<QgsExpression::Function*>& 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" )
Expand All @@ -1763,7 +1763,7 @@ const QList<QgsExpression::Function*>& 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" )
Expand Down Expand Up @@ -2638,7 +2638,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 );
Expand Down
38 changes: 34 additions & 4 deletions src/core/qgsexpression.h
Expand Up @@ -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() {}

Expand Down Expand Up @@ -327,6 +342,8 @@ class CORE_EXPORT QgsExpression
return false;
}

virtual bool handlesNull() const { return mHandlesNull; }

private:
QString mName;
int mParams;
Expand All @@ -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() {}

Expand Down
3 changes: 2 additions & 1 deletion tests/src/core/testqgsexpression.cpp
Expand Up @@ -393,7 +393,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 );
Expand Down

0 comments on commit 3387d6f

Please sign in to comment.