Fix PROFILE infinite loop (#1431)

This commit is contained in:
Ante Pušić 2023-10-28 15:34:52 +02:00 committed by GitHub
parent 011caf3bf1
commit b1c3168308
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 10 deletions

View File

@ -2456,17 +2456,16 @@ Filter::FilterCursor::FilterCursor(const Filter &self, utils::MemoryResource *me
bool Filter::FilterCursor::Pull(Frame &frame, ExecutionContext &context) {
OOMExceptionEnabler oom_exception;
SCOPED_PROFILE_OP_BY_REF(self_);
// Like all filters, newly set values should not affect filtering of old
// nodes and edges.
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::OLD, context.frame_change_collector);
while (input_cursor_->Pull(frame, context) || UNLIKELY(context.is_profile_query)) {
while (input_cursor_->Pull(frame, context)) {
for (const auto &pattern_filter_cursor : pattern_filter_cursors_) {
pattern_filter_cursor->Pull(frame, context);
}
if (EvaluateFilter(evaluator, self_.expression_)) return true;
if (UNLIKELY(context.is_profile_query)) return false;
}
return false;
}
@ -2790,13 +2789,15 @@ SetProperties::SetPropertiesCursor::SetPropertiesCursor(const SetProperties &sel
namespace {
template <typename T>
concept AccessorWithProperties = requires(T value, storage::PropertyId property_id,
storage::PropertyValue property_value,
std::map<storage::PropertyId, storage::PropertyValue> properties) {
{ value.ClearProperties() } -> std::same_as<storage::Result<std::map<storage::PropertyId, storage::PropertyValue>>>;
{value.SetProperty(property_id, property_value)};
{value.UpdateProperties(properties)};
};
concept AccessorWithProperties =
requires(T value, storage::PropertyId property_id, storage::PropertyValue property_value,
std::map<storage::PropertyId, storage::PropertyValue> properties) {
{
value.ClearProperties()
} -> std::same_as<storage::Result<std::map<storage::PropertyId, storage::PropertyValue>>>;
{ value.SetProperty(property_id, property_value) };
{ value.UpdateProperties(properties) };
};
/// Helper function that sets the given values on either a Vertex or an Edge.
///

View File

@ -69,6 +69,7 @@ add_subdirectory(transaction_rollback)
add_subdirectory(index_hints)
add_subdirectory(query_modules)
add_subdirectory(constraints)
add_subdirectory(inspect_query)
copy_e2e_python_files(pytest_runner pytest_runner.sh "")
copy_e2e_python_files(x x.sh "")

View File

@ -0,0 +1,6 @@
function(copy_inspect_query_e2e_python_files FILE_NAME)
copy_e2e_python_files(inspect_query ${FILE_NAME})
endfunction()
copy_inspect_query_e2e_python_files(common.py)
copy_inspect_query_e2e_python_files(inspect_query.py)

View File

@ -0,0 +1,45 @@
# Copyright 2023 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import typing
import mgclient
import pytest
from gqlalchemy import Memgraph
def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = {}) -> typing.List[tuple]:
cursor.execute(query, params)
return cursor.fetchall()
@pytest.fixture
def connect(**kwargs) -> mgclient.Connection:
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
connection.autocommit = True
cursor = connection.cursor()
execute_and_fetch_all(cursor, "USE DATABASE memgraph")
try:
execute_and_fetch_all(cursor, "DROP DATABASE clean")
except:
pass
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
yield connection
@pytest.fixture
def memgraph(**kwargs) -> Memgraph:
memgraph = Memgraph()
yield memgraph
memgraph.drop_database()
memgraph.drop_indexes()

View File

@ -0,0 +1,47 @@
# Copyright 2023 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import sys
import mgclient
import pytest
from common import memgraph
def test_label_index_hint(memgraph):
explain_query = "EXPLAIN MATCH (n:Node) RETURN n.property1;"
profile_query = "PROFILE MATCH (n:Node) RETURN n.property1;"
expected_profile = ["* Produce {n.property1}", "* Filter (n :Node)", "* ScanAll (n)", "* Once"]
expected_explain = [" * Produce {n.property1}", " * Filter (n :Node)", " * ScanAll (n)", " * Once"]
explain_empty = [row["QUERY PLAN"] for row in memgraph.execute_and_fetch(explain_query)]
profile_empty = [row["OPERATOR"] for row in memgraph.execute_and_fetch(profile_query)]
assert explain_empty == expected_explain and profile_empty == expected_profile
memgraph.execute("CREATE (n:WrongLabel {property1: 2});")
explain_none_matched = [row["QUERY PLAN"] for row in memgraph.execute_and_fetch(explain_query)]
profile_none_matched = [row["OPERATOR"] for row in memgraph.execute_and_fetch(profile_query)]
assert explain_none_matched == expected_explain and profile_none_matched == expected_profile
memgraph.execute("CREATE (n:Node {property1: 2});")
explain_has_match = [row["QUERY PLAN"] for row in memgraph.execute_and_fetch(explain_query)]
profile_has_match = [row["OPERATOR"] for row in memgraph.execute_and_fetch(profile_query)]
assert explain_has_match == expected_explain and profile_has_match == expected_profile
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -0,0 +1,13 @@
inspect_query_cluster: &inspect_query_cluster
cluster:
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "inspect_query.log"
setup_queries: []
validation_queries: []
workloads:
- name: "Verify that query inspection works"
binary: "tests/e2e/pytest_runner.sh"
args: ["inspect_query/inspect_query.py"]
<<: *inspect_query_cluster