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:
parent
8bfebfca9d
commit
9831f0396a
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
Loading…
Reference in New Issue
Block a user