diff --git a/src/core/qgssearchstringlexer.ll b/src/core/qgssearchstringlexer.ll index cbc1feb776a..3e6ffe6b5d8 100644 --- a/src/core/qgssearchstringlexer.ll +++ b/src/core/qgssearchstringlexer.ll @@ -59,6 +59,10 @@ string "'"{str_char}*"'" "AND" { return AND; } "OR" { return OR; } +"NULL" { return NULLVALUE; } + +"IS" { return IS; } + "=" { yylval.op = QgsSearchTreeNode::opEQ; return COMPARISON; } "!=" { yylval.op = QgsSearchTreeNode::opNE; return COMPARISON; } "<=" { yylval.op = QgsSearchTreeNode::opLE; return COMPARISON; } diff --git a/src/core/qgssearchstringparser.yy b/src/core/qgssearchstringparser.yy index 715d14eb440..32dcc4250ad 100644 --- a/src/core/qgssearchstringparser.yy +++ b/src/core/qgssearchstringparser.yy @@ -62,8 +62,10 @@ void addToTmpNodes(QgsSearchTreeNode* node); %token NUMBER %token COMPARISON %token FUNCTION +%token IS %token AREA %token LENGTH +%token NULLVALUE %token STRING %token COLUMN_REF @@ -119,12 +121,14 @@ predicate: ; comp_predicate: - scalar_exp COMPARISON scalar_exp { $$ = new QgsSearchTreeNode($2, $1, $3); joinTmpNodes($$,$1,$3); } + scalar_exp IS NULLVALUE { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opISNULL, $1, 0); joinTmpNodes($$,$1,0); } + | scalar_exp IS NOT NULLVALUE { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opISNOTNULL, $1, 0); joinTmpNodes($$,$1,0); } + | scalar_exp COMPARISON scalar_exp { $$ = new QgsSearchTreeNode($2, $1, $3); joinTmpNodes($$,$1,$3); } ; scalar_exp: - FUNCTION '(' scalar_exp ')' {$$ = new QgsSearchTreeNode($1, $3, 0); joinTmpNodes($$, $3, 0);} - | scalar_exp '^' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPOW, $1, $3); joinTmpNodes($$, $1, $3); } + FUNCTION '(' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, 0); joinTmpNodes($$, $3, 0);} + | scalar_exp '^' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPOW, $1, $3); joinTmpNodes($$, $1, $3); } | scalar_exp '*' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMUL, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '/' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opDIV, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '+' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPLUS, $1, $3); joinTmpNodes($$,$1,$3); } @@ -134,7 +138,7 @@ scalar_exp: | '-' scalar_exp %prec UMINUS { $$ = $2; if ($$->type() == QgsSearchTreeNode::tNumber) $$->setNumber(- $$->number()); } | AREA { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAREA, 0, 0); addToTmpNodes($$); } | LENGTH { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opLENGTH, 0, 0); addToTmpNodes($$); } - | NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); } + | NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); } | STRING { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 0); addToTmpNodes($$); } | COLUMN_REF { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 1); addToTmpNodes($$); } ; diff --git a/src/core/qgssearchtreenode.cpp b/src/core/qgssearchtreenode.cpp index 5d9522b38eb..a1872c9a8fa 100644 --- a/src/core/qgssearchtreenode.cpp +++ b/src/core/qgssearchtreenode.cpp @@ -237,6 +237,20 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, const QgsAttrib return true; return mRight->checkAgainst( fields, attributes ); + case opISNULL: + case opISNOTNULL: + if ( !getValue( value1, mLeft, fields, attributes ) ) + return false; + + if ( mOp == opISNULL ) + { + return value1.isNull(); + } + else if ( mOp == opISNOTNULL ) + { + return !value1.isNull(); + } + case opEQ: case opNE: case opGT: @@ -247,6 +261,12 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, const QgsAttrib if ( !getValue( value1, mLeft, fields, attributes ) || !getValue( value2, mRight, fields, attributes ) ) return false; + if ( value1.isNull() || value2.isNull() ) + { + // NULL values never match + return false; + } + res = QgsSearchTreeValue::compare( value1, value2 ); switch ( mOp ) @@ -365,7 +385,12 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, c // get the value QVariant val = attributes[it.key()]; - if ( val.type() == QVariant::Bool || val.type() == QVariant::Int || val.type() == QVariant::Double ) + if ( val.isNull() ) + { + QgsDebugMsgLevel( " NULL", 2 ); + return QgsSearchTreeValue(); + } + else if ( val.type() == QVariant::Bool || val.type() == QVariant::Int || val.type() == QVariant::Double ) { QgsDebugMsgLevel( " number: " + QString::number( val.toDouble() ), 2 ); return QgsSearchTreeValue( val.toDouble() ); diff --git a/src/core/qgssearchtreenode.h b/src/core/qgssearchtreenode.h index 48ac54a1f78..af69426ab76 100644 --- a/src/core/qgssearchtreenode.h +++ b/src/core/qgssearchtreenode.h @@ -78,6 +78,8 @@ class CORE_EXPORT QgsSearchTreeNode opAREA, // comparison + opISNULL, // IS NULL + opISNOTNULL, // IS NOT NULL opEQ, // = opNE, // != resp. <> opGT, // > @@ -174,11 +176,12 @@ class CORE_EXPORT QgsSearchTreeValue { valError, valString, - valNumber + valNumber, + valNull }; - QgsSearchTreeValue() { } - QgsSearchTreeValue( QString string ) { mType = valString; mString = string; } + QgsSearchTreeValue() { mType = valNull; } + QgsSearchTreeValue( QString string ) { mType = string.isNull() ? valNull : valString; mString = string; } QgsSearchTreeValue( double number ) { mType = valNumber; mNumber = number; } QgsSearchTreeValue( int error, QString errorMsg ) { mType = valError; mNumber = error; mString = errorMsg; } @@ -187,6 +190,7 @@ class CORE_EXPORT QgsSearchTreeValue bool isNumeric() { return mType == valNumber; } bool isError() { return mType == valError; } + bool isNull() { return mType == valNull; } QString& string() { return mString; } double number() { return mNumber; }