Implement IS NULL and IS NOT NULL

Reviewers: teon.banek, florijan

Reviewed By: florijan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D254
This commit is contained in:
Mislav Bradac 2017-04-10 16:33:53 +02:00
parent fd2780155a
commit b7ee09785b
6 changed files with 76 additions and 5 deletions

View File

@ -347,6 +347,20 @@ class UnaryMinusOperator : public UnaryOperator {
using UnaryOperator::UnaryOperator;
};
class IsNullOperator : public UnaryOperator {
friend class AstTreeStorage;
public:
void Accept(TreeVisitorBase &visitor) override {
visitor.Visit(*this);
expression_->Accept(visitor);
visitor.PostVisit(*this);
}
protected:
using UnaryOperator::UnaryOperator;
};
class Literal : public Expression {
friend class AstTreeStorage;

View File

@ -29,6 +29,7 @@ class DivisionOperator;
class ModOperator;
class UnaryPlusOperator;
class UnaryMinusOperator;
class IsNullOperator;
class NotEqualOperator;
class EqualOperator;
class LessOperator;
@ -48,8 +49,8 @@ using TreeVisitorBase = ::utils::Visitor<
AdditionOperator, SubtractionOperator, MultiplicationOperator,
DivisionOperator, ModOperator, NotEqualOperator, EqualOperator,
LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator,
UnaryPlusOperator, UnaryMinusOperator, Identifier, Literal, PropertyLookup,
Aggregation, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom,
Delete, Where, SetProperty, SetProperties, SetLabels, RemoveProperty,
RemoveLabels>;
UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, Identifier, Literal,
PropertyLookup, Aggregation, Create, Match, Return, With, Pattern, NodeAtom,
EdgeAtom, Delete, Where, SetProperty, SetProperties, SetLabels,
RemoveProperty, RemoveLabels>;
}

View File

@ -496,7 +496,21 @@ antlrcpp::Any CypherMainVisitor::visitExpression3(
// that child is expression2. Other operations are not implemented at the
// moment.
// TODO: implement this.
if (ctx->children.size() > 1u) {
// This is a hack. Unfortunately, grammar for expression3 contains a lot of
// different expressions. We should break that production in parts so that
// we can easily implement its visitor.
Expression *expression = ctx->expression2()[0]->accept(this);
if (ctx->children.size() - ctx->SP().size() == 3U && ctx->IS().size() == 1U &&
ctx->CYPHERNULL().size() == 1U) {
return static_cast<Expression *>(
storage_.Create<IsNullOperator>(expression));
}
if (ctx->children.size() - ctx->SP().size() == 4U && ctx->IS().size() == 1U &&
ctx->NOT().size() == 1U && ctx->CYPHERNULL().size() == 1U) {
return static_cast<Expression *>(storage_.Create<NotOperator>(
storage_.Create<IsNullOperator>(expression)));
}
if (ctx->children.size() > 1U) {
throw NotYetImplemented();
}
return static_cast<Expression *>(visitChildren(ctx));

View File

@ -105,6 +105,12 @@ class ExpressionEvaluator : public TreeVisitorBase {
UNARY_OPERATOR_VISITOR(UnaryPlusOperator, +);
UNARY_OPERATOR_VISITOR(UnaryMinusOperator, -);
void PostVisit(IsNullOperator &) override {
auto expression = PopBack();
result_stack_.push_back(
TypedValue(expression.type() == TypedValue::Type::Null));
}
#undef BINARY_OPERATOR_VISITOR
#undef UNARY_OPERATOR_VISITOR

View File

@ -277,6 +277,30 @@ TEST(CypherMainVisitorTest, ComparisonOperators) {
#undef CHECK_COMPARISON
TEST(CypherMainVisitorTest, IsNull) {
AstGenerator ast_generator("RETURN 2 iS NulL");
auto *query = ast_generator.query_;
auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
auto *is_type_operator = dynamic_cast<IsNullOperator *>(
return_clause->named_expressions_[0]->expression_);
auto *operand1 = dynamic_cast<Literal *>(is_type_operator->expression_);
ASSERT_TRUE(operand1);
ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
}
TEST(CypherMainVisitorTest, IsNotNull) {
AstGenerator ast_generator("RETURN 2 iS nOT NulL");
auto *query = ast_generator.query_;
auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
auto *not_operator = dynamic_cast<NotOperator *>(
return_clause->named_expressions_[0]->expression_);
auto *is_type_operator =
dynamic_cast<IsNullOperator *>(not_operator->expression_);
auto *operand1 = dynamic_cast<Literal *>(is_type_operator->expression_);
ASSERT_TRUE(operand1);
ASSERT_EQ(operand1->value_.Value<int64_t>(), 2);
}
TEST(CypherMainVisitorTest, NotOperator) {
AstGenerator ast_generator("RETURN not true");
auto *query = ast_generator.query_;

View File

@ -232,3 +232,15 @@ TEST(ExpressionEvaluator, UnaryMinusOperator) {
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<int64_t>(), -5);
}
TEST(ExpressionEvaluator, IsNullOperator) {
AstTreeStorage storage;
NoContextExpressionEvaluator eval;
auto *op = storage.Create<IsNullOperator>(storage.Create<Literal>(1));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), false);
op =
storage.Create<IsNullOperator>(storage.Create<Literal>(TypedValue::Null));
op->Accept(eval.eval);
ASSERT_EQ(eval.eval.PopBack().Value<bool>(), true);
}