Add new implementation for runtime checking of merge

This commit is contained in:
Josip Mrden 2024-03-13 00:45:22 +01:00
parent bfd8921777
commit 0b762d37ba
7 changed files with 48 additions and 9 deletions

View File

@ -35,6 +35,10 @@ enum class TransactionStatus {
STARTED_ROLLBACK,
};
struct Scope {
bool in_merge{false};
};
struct EvaluationContext {
/// Memory for allocations during evaluation of a *single* Pull call.
///
@ -51,6 +55,7 @@ struct EvaluationContext {
/// All counters generated by `counter` function, mutable because the function
/// modifies the values
mutable std::unordered_map<std::string, int64_t> counters{};
Scope scope{};
};
inline std::vector<storage::PropertyId> NamesToProperties(const std::vector<std::string> &property_names,

View File

@ -745,14 +745,6 @@ bool SymbolGenerator::PreVisit(PatternComprehension &pc) {
return true;
}
bool SymbolGenerator::Visit(PrimitiveLiteral &primitive_literal) {
auto &scope = scopes_.back();
if (scope.in_merge && primitive_literal.value_.IsNull()) {
throw SemanticException("Cannot merge node or relationship entity because of null property value!");
}
return true;
}
bool SymbolGenerator::PostVisit(PatternComprehension & /*pc*/) { return true; }
void SymbolGenerator::VisitWithIdentifiers(Expression *expr, const std::vector<Identifier *> &identifiers) {

View File

@ -71,7 +71,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
// Expressions
ReturnType Visit(Identifier &) override;
ReturnType Visit(PrimitiveLiteral &) override;
ReturnType Visit(PrimitiveLiteral &) override { return true; }
bool PreVisit(MapLiteral &) override;
bool PostVisit(MapLiteral &) override { return true; };
ReturnType Visit(ParameterLookup &) override { return true; }

View File

@ -784,6 +784,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
TypedValue Visit(PrimitiveLiteral &literal) override {
// TODO: no need to evaluate constants, we can write it to frame in one
// of the previous phases.
if (ctx_->scope.in_merge && literal.value_.IsNull()) {
throw QueryRuntimeException("Can't have null literal properties inside merge!");
}
return TypedValue(literal.value_, ctx_->memory);
}

View File

@ -4297,6 +4297,9 @@ bool Merge::MergeCursor::Pull(Frame &frame, ExecutionContext &context) {
OOMExceptionEnabler oom_exception;
SCOPED_PROFILE_OP("Merge");
context.evaluation_context.scope.in_merge = true;
memgraph::utils::OnScopeExit merge_exit([&] { context.evaluation_context.scope.in_merge = false; });
while (true) {
if (pull_input_) {
if (input_cursor_->Pull(frame, context)) {

View File

@ -433,3 +433,21 @@ Feature: Merge feature
MERGE ()-[:TYPE {id:null}]->()
"""
Then an error should be raised
Scenario: Merge node with unwind with null property error
Given an empty graph
When executing query:
"""
UNWIND [1, 2, null, 3] as x
MERGE ({id: x})
"""
Then an error should be raised
Scenario: Merge edge with unwind with null property error
Given an empty graph
When executing query:
"""
UNWIND [1, 2, null, 3] as x
MERGE ()-[:TYPE {id:x}]->()
"""
Then an error should be raised

View File

@ -433,3 +433,21 @@ Feature: Merge feature
MERGE ()-[:TYPE {id:null}]->()
"""
Then an error should be raised
Scenario: Merge node with unwind with null property error
Given an empty graph
When executing query:
"""
UNWIND [1, 2, null, 3] as x
MERGE ({id: x})
"""
Then an error should be raised
Scenario: Merge edge with unwind with null property error
Given an empty graph
When executing query:
"""
UNWIND [1, 2, null, 3] as x
MERGE ()-[:TYPE {id:x}]->()
"""
Then an error should be raised