Fix returning NULL on map projection from a null value (#1119)
This commit is contained in:
parent
9e4babcdbb
commit
0403b67073
@ -1105,6 +1105,8 @@ class MapProjectionLiteral : public memgraph::query::BaseLiteral {
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
map_variable_->Accept(visitor);
|
||||
|
||||
for (auto pair : elements_) {
|
||||
if (!pair.second) continue;
|
||||
|
||||
|
@ -763,20 +763,27 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
|
||||
TypedValue::TMap result(ctx_->memory);
|
||||
TypedValue::TMap all_properties_lookup(ctx_->memory);
|
||||
|
||||
auto map_variable = literal.map_variable_->Accept(*this);
|
||||
if (map_variable.IsNull()) {
|
||||
return TypedValue(ctx_->memory);
|
||||
}
|
||||
|
||||
for (const auto &[property_key, property_value] : literal.elements_) {
|
||||
if (property_key.name == kAllPropertiesSelector.data()) {
|
||||
auto maybe_all_properties_lookup = property_value->Accept(*this);
|
||||
|
||||
if (maybe_all_properties_lookup.type() != TypedValue::Type::Map) {
|
||||
throw QueryRuntimeException("Expected a map from AllPropertiesLookup, got {}.",
|
||||
maybe_all_properties_lookup.type());
|
||||
LOG_FATAL("Expected a map from AllPropertiesLookup, got {}.", maybe_all_properties_lookup.type());
|
||||
}
|
||||
|
||||
all_properties_lookup = std::move(maybe_all_properties_lookup.ValueMap());
|
||||
continue;
|
||||
}
|
||||
|
||||
result.emplace(property_key.name, property_value->Accept(*this));
|
||||
}
|
||||
|
||||
if (!all_properties_lookup.empty()) result.merge(all_properties_lookup);
|
||||
|
||||
return TypedValue(result, ctx_->memory);
|
||||
|
@ -98,6 +98,11 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
||||
auto it = has_aggregation_.end();
|
||||
auto elements_it = literal.elements_.begin();
|
||||
std::advance(it, -literal.elements_.size());
|
||||
if (literal.GetTypeInfo() == MapProjectionLiteral::kType) {
|
||||
// Erase the map variable. Grammar-wise, it’s a variable and thus never has aggregations.
|
||||
std::advance(it, -1);
|
||||
it = has_aggregation_.erase(it);
|
||||
}
|
||||
while (it != has_aggregation_.end()) {
|
||||
if (*it) {
|
||||
has_aggr = true;
|
||||
@ -446,9 +451,9 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
||||
std::vector<Expression *> group_by_;
|
||||
std::unordered_set<Symbol> group_by_used_symbols_;
|
||||
// Flag stack indicating whether an expression contains an aggregation. A
|
||||
// stack is needed so that we differentiate the case where a child
|
||||
// sub-expression has an aggregation, while the other child doesn't. For
|
||||
// example AST, (+ (sum x) y)
|
||||
// stack is needed to address the case where one child sub-expression has
|
||||
// an aggregation, while the other child does not.
|
||||
// For example, the AST (+ (sum x) y) is as follows:
|
||||
// * (sum x) -- Has an aggregation.
|
||||
// * y -- Doesn't, we need to group by this.
|
||||
// * (+ (sum x) y) -- The whole expression has an aggregation, so we don't
|
||||
|
@ -4,7 +4,7 @@ Feature: Map projection
|
||||
When executing query:
|
||||
"""
|
||||
WITH {} AS map
|
||||
RETURN map {} as result
|
||||
RETURN map {} AS result
|
||||
"""
|
||||
Then the result should be:
|
||||
| result |
|
||||
@ -26,6 +26,17 @@ Feature: Map projection
|
||||
| result |
|
||||
| {age: 85, lastName: 'Freeman', name: 'Morgan', oscars: 1} |
|
||||
|
||||
Scenario: Projecting from a null value
|
||||
When executing query:
|
||||
"""
|
||||
WITH "value" AS var
|
||||
OPTIONAL MATCH (n:Nonexistent)
|
||||
RETURN n {.*} AS result0, n {.prop} AS result1, n {prop: "value"} AS result2, n {var} AS result3;
|
||||
"""
|
||||
Then the result should be:
|
||||
| result0 | result1 | result2 | result3 |
|
||||
| null | null | null | null |
|
||||
|
||||
Scenario: Projecting a nonexistent property
|
||||
When executing query:
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user