Fix 'all' and 'single' functions; update their unit and TCK tests

Summary:
Semantics of 'all' and 'single' were updated to be
consistent with that of 'any' and 'none'

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2789
This commit is contained in:
jseljan 2020-06-23 15:18:57 +02:00
parent 8bfebfca9d
commit 9831f0396a
3 changed files with 206 additions and 9 deletions

View File

@ -475,6 +475,8 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
}
const auto &list = list_value.ValueList();
const auto &symbol = symbol_table_->at(*all.identifier_);
bool has_null_elements = false;
bool has_value = false;
for (const auto &element : list) {
frame_->at(symbol) = element;
auto result = all.where_->expression_->Accept(*this);
@ -483,11 +485,23 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
"Predicate of ALL must evaluate to boolean, got {}.",
result.type());
}
if (result.IsNull() || !result.ValueBool()) {
return result;
if (!result.IsNull()) {
has_value = true;
if (!result.ValueBool()) {
return TypedValue(false, ctx_->memory);
}
} else {
has_null_elements = true;
}
}
return TypedValue(true, ctx_->memory);
if (!has_value) {
return TypedValue(ctx_->memory);
}
if (has_null_elements) {
return TypedValue(false, ctx_->memory);
} else {
return TypedValue(true, ctx_->memory);
}
}
TypedValue Visit(Single &single) override {
@ -501,6 +515,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
}
const auto &list = list_value.ValueList();
const auto &symbol = symbol_table_->at(*single.identifier_);
bool has_value = false;
bool predicate_satisfied = false;
for (const auto &element : list) {
frame_->at(symbol) = element;
@ -510,6 +525,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
"Predicate of SINGLE must evaluate to boolean, got {}.",
result.type());
}
if (result.type() == TypedValue::Type::Bool) {
has_value = true;
}
if (result.IsNull() || !result.ValueBool()) {
continue;
}
@ -520,7 +538,11 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
predicate_satisfied = true;
}
}
return TypedValue(predicate_satisfied, ctx_->memory);
if (!has_value) {
return TypedValue(ctx_->memory);
} else {
return TypedValue(predicate_satisfied, ctx_->memory);
}
}
TypedValue Visit(Any &any) override {

View File

@ -687,6 +687,51 @@ Feature: Functions
"""
Then an error should be raised
Scenario: All test 04:
When executing query:
"""
RETURN all(x IN [Null, Null, Null] WHERE x = 0) AS a
"""
Then the result should be:
| a |
| null |
Scenario: All test 05:
When executing query:
"""
RETURN all(x IN [Null, Null, 0] WHERE x = 0) AS a
"""
Then the result should be:
| a |
| false |
Scenario: All test 06:
When executing query:
"""
RETURN all(x IN [Null, Null, 0] WHERE x > 0) AS a
"""
Then the result should be:
| a |
| false |
Scenario: All test 07:
When executing query:
"""
RETURN all(x IN [Null, Null, Null] WHERE x = Null) AS a
"""
Then the result should be:
| a |
| null |
Scenario: All test 08:
When executing query:
"""
RETURN all(x IN ["a", "b", "c"] WHERE x = Null) AS a
"""
Then the result should be:
| a |
| null |
Scenario: Single test 01:
When executing query:
"""
@ -712,6 +757,69 @@ Feature: Functions
"""
Then an error should be raised
Scenario: Single test 04:
When executing query:
"""
RETURN single(x IN [Null, Null, Null] WHERE x = 0) AS a
"""
Then the result should be:
| a |
| null |
Scenario: Single test 05:
When executing query:
"""
RETURN single(x IN [Null, Null, 0] WHERE x = 0) AS a
"""
Then the result should be:
| a |
| true |
Scenario: Single test 06:
When executing query:
"""
RETURN single(x IN [Null, 0, Null, 0] WHERE x = 0) AS a
"""
Then the result should be:
| a |
| false |
Scenario: Single test 07:
When executing query:
"""
RETURN single(x IN [Null, Null, 0] WHERE x > 0) AS a
"""
Then the result should be:
| a |
| false |
Scenario: Single test 08:
When executing query:
"""
RETURN single(x IN [Null, 1, Null, 0] WHERE x > 0) AS a
"""
Then the result should be:
| a |
| true |
Scenario: Single test 09:
When executing query:
"""
RETURN single(x IN [Null, Null, Null] WHERE x = Null) AS a
"""
Then the result should be:
| a |
| null |
Scenario: Single test 10:
When executing query:
"""
RETURN single(x IN ["a", "b", "c"] WHERE x = Null) AS a
"""
Then the result should be:
| a |
| null |
Scenario: Any test 01:
When executing query:
"""
@ -758,7 +866,7 @@ Feature: Functions
| a |
| true |
Scenario: Any test 06:
Scenario: Any test 06:
When executing query:
"""
RETURN any(x IN [Null, Null, 0] WHERE x > 0) AS a
@ -767,7 +875,7 @@ Feature: Functions
| a |
| false |
Scenario: Any test 07:
Scenario: Any test 07:
When executing query:
"""
RETURN any(x IN [Null, Null, Null] WHERE x = Null) AS a

View File

@ -703,7 +703,20 @@ TEST_F(ExpressionEvaluatorTest, ParameterLookup) {
EXPECT_EQ(value.ValueInt(), 42);
}
TEST_F(ExpressionEvaluatorTest, All) {
TEST_F(ExpressionEvaluatorTest, FunctionAll1) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *all =
ALL("x", LIST(LITERAL(1), LITERAL(1)), WHERE(EQ(ident_x, LITERAL(1))));
const auto x_sym = symbol_table.CreateSymbol("x", true);
all->identifier_->MapTo(x_sym);
ident_x->MapTo(x_sym);
auto value = Eval(all);
ASSERT_TRUE(value.IsBool());
EXPECT_TRUE(value.ValueBool());
}
TEST_F(ExpressionEvaluatorTest, FunctionAll2) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *all =
@ -725,6 +738,32 @@ TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) {
EXPECT_TRUE(value.IsNull());
}
TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList1) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *all = ALL("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())),
WHERE(EQ(ident_x, LITERAL(1))));
const auto x_sym = symbol_table.CreateSymbol("x", true);
all->identifier_->MapTo(x_sym);
ident_x->MapTo(x_sym);
auto value = Eval(all);
ASSERT_TRUE(value.IsBool());
EXPECT_FALSE(value.ValueBool());
}
TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList2) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *all = ALL("x", LIST(LITERAL(2), LITERAL(storage::PropertyValue())),
WHERE(EQ(ident_x, LITERAL(1))));
const auto x_sym = symbol_table.CreateSymbol("x", true);
all->identifier_->MapTo(x_sym);
ident_x->MapTo(x_sym);
auto value = Eval(all);
ASSERT_TRUE(value.IsBool());
EXPECT_FALSE(value.ValueBool());
}
TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) {
AstStorage storage;
auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2)));
@ -733,7 +772,7 @@ TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) {
EXPECT_THROW(Eval(all), QueryRuntimeException);
}
TEST_F(ExpressionEvaluatorTest, FunctionSingle) {
TEST_F(ExpressionEvaluatorTest, FunctionSingle1) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *single =
@ -769,7 +808,35 @@ TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) {
EXPECT_TRUE(value.IsNull());
}
TEST_F(ExpressionEvaluatorTest, FunctionAny) {
TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *single =
SINGLE("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())),
WHERE(EQ(ident_x, LITERAL(1))));
const auto x_sym = symbol_table.CreateSymbol("x", true);
single->identifier_->MapTo(x_sym);
ident_x->MapTo(x_sym);
auto value = Eval(single);
ASSERT_TRUE(value.IsBool());
EXPECT_TRUE(value.ValueBool());
}
TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *single =
SINGLE("x", LIST(LITERAL(2), LITERAL(storage::PropertyValue())),
WHERE(EQ(ident_x, LITERAL(1))));
const auto x_sym = symbol_table.CreateSymbol("x", true);
single->identifier_->MapTo(x_sym);
ident_x->MapTo(x_sym);
auto value = Eval(single);
ASSERT_TRUE(value.IsBool());
EXPECT_FALSE(value.ValueBool());
}
TEST_F(ExpressionEvaluatorTest, FunctionAny1) {
AstStorage storage;
auto *ident_x = IDENT("x");
auto *any =