memgraph/tests/e2e/triggers/triggers_properties_false.py
Andi 156e2cd095
On delete triggers invalid edge reference (#717)
* Added check if there is invalid reference to the underlying edge

* Added fix and e2e tests

* Isolation levels tracking based on from_vertex_

* Added explicit transaction test + edge accessor changes based on the vertex_edge

* Autocommit on tests, initialize deleted by checking out_edges

Co-authored-by: Marko Budiselić <marko.budiselic@memgraph.com>
2023-01-18 15:05:10 +01:00

171 lines
6.8 KiB
Python

# Copyright 2022 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 connect, execute_and_fetch_all
@pytest.mark.parametrize("ba_commit", ["BEFORE COMMIT", "AFTER COMMIT"])
def test_create_on_create(ba_commit, connect):
"""
Args:
ba_commit (str): BEFORE OR AFTER commit
"""
cursor = connect.cursor()
QUERY_TRIGGER_CREATE = f"""
CREATE TRIGGER CreateTriggerEdgesCount
ON --> CREATE
{ba_commit}
EXECUTE
CREATE (n:CreatedEdge {{count: size(createdEdges)}})
"""
execute_and_fetch_all(cursor, QUERY_TRIGGER_CREATE)
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(res) == 2
res2 = execute_and_fetch_all(cursor, "MATCH (n:CreatedEdge) RETURN n")
assert len(res2) == 0
QUERY_CREATE_EDGE = """
MATCH (n:Node {id: 1}), (m:Node {id: 2})
CREATE (n)-[r:TYPE]->(m);
"""
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
# See if trigger was triggered
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(nodes) == 2
created_edges = execute_and_fetch_all(cursor, "MATCH (n:CreatedEdge) RETURN n")
assert len(created_edges) == 1
# execute_and_fetch_all(cursor, "DROP TRIGGER CreateTriggerEdgesCount")
# execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
@pytest.mark.parametrize("ba_commit", ["AFTER COMMIT", "BEFORE COMMIT"])
def test_create_on_delete(ba_commit, connect):
"""
Args:
ba_commit (str): BEFORE OR AFTER commit
"""
cursor = connect.cursor()
QUERY_TRIGGER_CREATE = f"""
CREATE TRIGGER DeleteTriggerEdgesCount
ON --> DELETE
{ba_commit}
EXECUTE
CREATE (n:DeletedEdge {{count: size(deletedEdges)}})
"""
# Setup queries
execute_and_fetch_all(cursor, QUERY_TRIGGER_CREATE)
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 3})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 4})")
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(res) == 4
res2 = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
assert len(res2) == 0
# create an edge that will be deleted
QUERY_CREATE_EDGE = """
MATCH (n:Node {id: 1}), (m:Node {id: 2})
CREATE (n)-[r:TYPE]->(m);
"""
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
# create an edge that won't be deleted
QUERY_CREATE_EDGE_NO_DELETE = """
MATCH (n:Node {id: 3}), (m:Node {id: 4})
CREATE (n)-[r:NO_DELETE_EDGE]->(m);
"""
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE_NO_DELETE)
# Delete only one type of the edger
QUERY_DELETE_EDGE = """
MATCH ()-[r:TYPE]->()
DELETE r;
"""
execute_and_fetch_all(cursor, QUERY_DELETE_EDGE)
# See if trigger was triggered
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(nodes) == 4
# Check how many edges got deleted
deleted_edges = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
assert len(deleted_edges) == 1
# execute_and_fetch_all(cursor, "DROP TRIGGER DeleteTriggerEdgesCount")
# execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")``
@pytest.mark.parametrize("ba_commit", ["BEFORE COMMIT", "AFTER COMMIT"])
def test_create_on_delete_explicit_transaction(ba_commit):
"""
Args:
ba_commit (str): BEFORE OR AFTER commit
"""
connection_with_autocommit = mgclient.connect(host="localhost", port=7687)
connection_with_autocommit.autocommit = True
cursor_autocommit = connection_with_autocommit.cursor()
QUERY_TRIGGER_CREATE = f"""
CREATE TRIGGER DeleteTriggerEdgesCountExplicit
ON --> DELETE
{ba_commit}
EXECUTE
CREATE (n:DeletedEdge {{count: size(deletedEdges)}})
"""
# Setup queries
execute_and_fetch_all(cursor_autocommit, QUERY_TRIGGER_CREATE)
# Start explicit transaction on the execution of the first command
connection_without_autocommit = mgclient.connect(host="localhost", port=7687)
connection_without_autocommit.autocommit = False
cursor = connection_without_autocommit.cursor()
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 1})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 2})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 3})")
execute_and_fetch_all(cursor, "CREATE (n:Node {id: 4})")
res = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(res) == 4
res2 = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n;")
assert len(res2) == 0
QUERY_CREATE_EDGE = """
MATCH (n:Node {id: 1}), (m:Node {id: 2})
CREATE (n)-[r:TYPE]->(m);
"""
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE)
# create an edge that won't be deleted
QUERY_CREATE_EDGE_NO_DELETE = """
MATCH (n:Node {id: 3}), (m:Node {id: 4})
CREATE (n)-[r:NO_DELETE_EDGE]->(m);
"""
execute_and_fetch_all(cursor, QUERY_CREATE_EDGE_NO_DELETE)
# Delete only one type of the edger
QUERY_DELETE_EDGE = """
MATCH ()-[r:TYPE]->()
DELETE r;
"""
execute_and_fetch_all(cursor, QUERY_DELETE_EDGE)
connection_without_autocommit.commit() # finish explicit transaction
nodes = execute_and_fetch_all(cursor, "MATCH (n:Node) RETURN n")
assert len(nodes) == 4
# Check how many edges got deleted
deleted_nodes_edges = execute_and_fetch_all(cursor, "MATCH (n:DeletedEdge) RETURN n")
assert len(deleted_nodes_edges) == 0
# Delete with the original cursor because triggers aren't allowed in multi-transaction environment
execute_and_fetch_all(cursor_autocommit, "DROP TRIGGER DeleteTriggerEdgesCountExplicit")
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
connection_without_autocommit.commit() # finish explicit transaction
nodes = execute_and_fetch_all(cursor_autocommit, "MATCH (n) RETURN n")
assert len(nodes) == 0
connection_with_autocommit.close()
connection_without_autocommit.close()
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))