diff --git a/src/query/frontend/ast/ast.lcp b/src/query/frontend/ast/ast.lcp index 33d397754..f8641f4c3 100644 --- a/src/query/frontend/ast/ast.lcp +++ b/src/query/frontend/ast/ast.lcp @@ -398,7 +398,7 @@ cpp<# ,@(loop for op in '(or-operator xor-operator and-operator addition-operator subtraction-operator multiplication-operator division-operator - mod-operator not-equal-operator equal-operator less-operator + mod-operator not-equal-operator less-operator greater-operator less-equal-operator greater-equal-operator in-list-operator subscript-operator) collecting @@ -458,6 +458,33 @@ cpp<# (:clone)))))) (define-unary-operators)) +(lcp:define-class equal-operator (binary-operator) + ((isNullCheckRequired "bool" :initval "false" :scope :public)) + (:public + #>cpp + DEFVISITABLE(ExpressionVisitor); + DEFVISITABLE(ExpressionVisitor); + + bool Accept(HierarchicalTreeVisitor &visitor) override { + if (visitor.PreVisit(*this)) { + expression1_->Accept(visitor) && expression2_->Accept(visitor); + } + return visitor.PostVisit(*this); + } + cpp<#) + (:protected + #>cpp + using BinaryOperator::BinaryOperator; + EqualOperator(Expression *expression1, Expression *expression2, bool is_nullcheck_required = false) + : BinaryOperator(expression1, expression2), isnullcheckrequired_(is_nullcheck_required) {} + cpp<#) + (:private + #>cpp + friend class AstStorage; + cpp<#) + (:serialize (:slk)) + (:clone)) + (lcp:define-class aggregation (binary-operator) ((op "Op" :scope :public) (symbol-pos :int32_t :initval -1 :scope :public diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index f4a269dfd..ba1a82b5b 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -2267,9 +2267,9 @@ antlrcpp::Any CypherMainVisitor::visitCaseExpression(MemgraphCypher::CaseExpress Expression *else_expression = ctx->else_expression ? ctx->else_expression->accept(this).as() : storage_->Create(TypedValue()); for (auto *alternative : alternatives) { - Expression *condition = - test_expression ? storage_->Create(test_expression, alternative->when_expression->accept(this)) - : alternative->when_expression->accept(this).as(); + Expression *condition = test_expression ? storage_->Create( + test_expression, alternative->when_expression->accept(this), true) + : alternative->when_expression->accept(this).as(); Expression *then_expression = alternative->then_expression->accept(this); else_expression = storage_->Create(condition, then_expression, else_expression); } diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp index b5ca8ab84..867cd796e 100644 --- a/src/query/interpret/eval.hpp +++ b/src/query/interpret/eval.hpp @@ -81,7 +81,7 @@ class ExpressionEvaluator : public ExpressionVisitor { BINARY_OPERATOR_VISITOR(DivisionOperator, /, /); BINARY_OPERATOR_VISITOR(ModOperator, %, %); BINARY_OPERATOR_VISITOR(NotEqualOperator, !=, <>); - BINARY_OPERATOR_VISITOR(EqualOperator, ==, =); + // BINARY_OPERATOR_VISITOR(EqualOperator, ==, =); BINARY_OPERATOR_VISITOR(LessOperator, <, <); BINARY_OPERATOR_VISITOR(GreaterOperator, >, >); BINARY_OPERATOR_VISITOR(LessEqualOperator, <=, <=); @@ -94,6 +94,29 @@ class ExpressionEvaluator : public ExpressionVisitor { #undef BINARY_OPERATOR_VISITOR #undef UNARY_OPERATOR_VISITOR + TypedValue Visit(EqualOperator &op) override { + auto val1 = op.expression1_->Accept(*this); + auto val2 = op.expression2_->Accept(*this); + + // if(val1.IsNull()) + // { + // val1.V + // } + + // if(op.isoperandnull_) + // throw QueryRuntimeException("Use the generic form when checking against NULL."); + + try { + if (op.isnullcheckrequired_ && val2.IsNull()) { + throw QueryRuntimeException("Use the generic form when checking against NULL."); + } + + return val1 == val2; + } catch (const TypedValueException &) { + throw QueryRuntimeException("Invalid types: {} and {} for '='.", val1.type(), val2.type()); + } + } + TypedValue Visit(AndOperator &op) override { auto value1 = op.expression1_->Accept(*this); if (value1.IsBool() && !value1.ValueBool()) { diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 814f3fa7c..4da29e39a 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -790,6 +790,26 @@ TEST_P(CypherMainVisitorTest, CaseSimpleForm) { ast_generator.CheckLiteral(if_operator->else_expression_, TypedValue()); } +TEST_P(CypherMainVisitorTest, CaseSimpleFormNullcheckIsSet) { + auto &ast_generator = *GetParam(); + auto *query = dynamic_cast(ast_generator.ParseQuery("RETURN CASE 5 WHEN 10 THEN 1 END")); + ASSERT_TRUE(query); + ASSERT_TRUE(query->single_query_); + auto *single_query = query->single_query_; + auto *return_clause = dynamic_cast(single_query->clauses_[0]); + auto *if_operator = dynamic_cast(return_clause->body_.named_expressions[0]->expression_); + auto *condition = dynamic_cast(if_operator->condition_); + ASSERT_TRUE(condition); + ast_generator.CheckLiteral(condition->expression1_, 5); + ast_generator.CheckLiteral(condition->expression2_, 10); + + // This might be the only line thats different. + ASSERT_TRUE(condition->isnullcheckrequired_); + + ast_generator.CheckLiteral(if_operator->then_expression_, 1); + ast_generator.CheckLiteral(if_operator->else_expression_, TypedValue()); +} + TEST_P(CypherMainVisitorTest, IsNull) { auto &ast_generator = *GetParam(); auto *query = dynamic_cast(ast_generator.ParseQuery("RETURN 2 iS NulL"));