memgraph/tests/e2e/triggers/on_delete_triggers.cpp
János Benjamin Antal 537855a0b2
Fix usages of constexpr (#367)
* Fix usages of constexpr
2022-03-31 13:52:43 +02:00

314 lines
12 KiB
C++

// 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.
#include <string>
#include <string_view>
#include <unordered_set>
#include <gflags/gflags.h>
#include <mgclient.hpp>
#include "common.hpp"
#include "utils/logging.hpp"
inline constexpr std::string_view kTriggerDeletedVertexLabel{"DELETED_VERTEX"};
inline constexpr std::string_view kTriggerDeletedEdgeLabel{"DELETED_EDGE"};
inline constexpr std::string_view kTriggerDeletedObjectLabel{"DELETED_OBJECT"};
enum class AllowedTriggerType : uint8_t {
VERTEX,
EDGE,
OBJECT,
};
void DetachDeleteVertex(mg::Client &client, int vertex_id) {
mg::Map parameters{{"id", mg::Value{vertex_id}}};
client.Execute(fmt::format("MATCH (n: {} {{id: $id}}) DETACH DELETE n", kVertexLabel),
mg::ConstMap{parameters.ptr()});
client.DiscardAll();
}
void DeleteEdge(mg::Client &client, int edge_id) {
mg::Map parameters{{"id", mg::Value{edge_id}}};
client.Execute(fmt::format("MATCH ()-[r: {} {{id: $id}}]->() DELETE r", kEdgeLabel), mg::ConstMap{parameters.ptr()});
client.DiscardAll();
}
void CreateOnDeleteTriggers(mg::Client &client, bool is_before,
const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
const std::string_view before_or_after = is_before ? "BEFORE" : "AFTER";
const auto create_on_vertex_delete_trigger = [&, before_or_after] {
client.Execute(
fmt::format("CREATE TRIGGER DeletedVerticesTrigger ON () DELETE "
"{} COMMIT "
"EXECUTE "
"UNWIND deletedVertices as deletedVertex "
"CREATE (n: {} {{ id: deletedVertex.id }})",
before_or_after, kTriggerDeletedVertexLabel));
client.DiscardAll();
};
const auto create_on_edge_delete_trigger = [&, before_or_after] {
client.Execute(
fmt::format("CREATE TRIGGER DeletedEdgesTrigger ON --> DELETE "
"{} COMMIT "
"EXECUTE "
"UNWIND deletedEdges as deletedEdge "
"CREATE (n: {} {{ id: deletedEdge.id }})",
before_or_after, kTriggerDeletedEdgeLabel));
client.DiscardAll();
};
const auto create_on_object_delete_trigger = [&, before_or_after] {
client.Execute(
fmt::format("CREATE TRIGGER DeletedObjectsTrigger ON DELETE "
"{} COMMIT "
"EXECUTE "
"UNWIND deletedObjects as deletedObjectEvent "
"WITH CASE deletedObjectEvent.event_type WHEN \"deleted_vertex\" THEN deletedObjectEvent.vertex.id "
"ELSE deletedObjectEvent.edge.id END as id "
"CREATE (n: {} {{ id: id }})",
before_or_after, kTriggerDeletedObjectLabel));
client.DiscardAll();
};
for (const auto allowed_trigger_type : allowed_trigger_types) {
switch (allowed_trigger_type) {
case AllowedTriggerType::VERTEX:
create_on_vertex_delete_trigger();
break;
case AllowedTriggerType::EDGE:
create_on_edge_delete_trigger();
break;
case AllowedTriggerType::OBJECT:
create_on_object_delete_trigger();
break;
}
}
}
void DropOnDeleteTriggers(mg::Client &client, const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
for (const auto allowed_trigger_type : allowed_trigger_types) {
switch (allowed_trigger_type) {
case AllowedTriggerType::VERTEX: {
client.Execute("DROP TRIGGER DeletedVerticesTrigger");
client.DiscardAll();
break;
}
case AllowedTriggerType::EDGE: {
client.Execute("DROP TRIGGER DeletedEdgesTrigger");
client.DiscardAll();
break;
}
case AllowedTriggerType::OBJECT: {
client.Execute("DROP TRIGGER DeletedObjectsTrigger");
client.DiscardAll();
break;
}
}
}
}
struct EdgeInfo {
int from_vertex;
int to_vertex;
int edge_id;
};
void ValidateVertexExistance(mg::Client &client, const bool should_exist, const std::string_view label, const int id) {
should_exist ? CheckVertexExists(client, label, id) : CheckVertexMissing(client, label, id);
};
int main(int argc, char **argv) {
gflags::SetUsageMessage("Memgraph E2E ON DELETE Triggers");
gflags::ParseCommandLineFlags(&argc, &argv, true);
memgraph::logging::RedirectToStderr();
mg::Client::Init();
auto client = Connect();
const auto run_delete_trigger_tests = [&](const bool is_before,
const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
static constexpr std::array vertex_ids{1, 2, 3, 4};
static constexpr std::array edges{EdgeInfo{vertex_ids[0], vertex_ids[1], 5},
EdgeInfo{vertex_ids[2], vertex_ids[3], 6}};
{
CreateOnDeleteTriggers(*client, is_before, allowed_trigger_types);
client->BeginTransaction();
for (const auto vertex_id : vertex_ids) {
CreateVertex(*client, vertex_id);
}
for (const auto &edge : edges) {
CreateEdge(*client, edge.from_vertex, edge.to_vertex, edge.edge_id);
}
client->CommitTransaction();
CheckNumberOfAllVertices(*client, vertex_ids.size());
client->BeginTransaction();
DetachDeleteVertex(*client, vertex_ids[0]);
DeleteEdge(*client, edges[1].edge_id);
client->CommitTransaction();
// :VERTEX x 4
// deleted :VERTEX x -1
auto number_of_expected_vertices = 3;
for (const auto allowed_trigger_type : allowed_trigger_types) {
switch (allowed_trigger_type) {
case AllowedTriggerType::VERTEX:
number_of_expected_vertices += 1;
break;
case AllowedTriggerType::EDGE:
number_of_expected_vertices += 2;
break;
case AllowedTriggerType::OBJECT:
number_of_expected_vertices += 3;
break;
}
}
if (is_before) {
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
} else {
WaitForNumberOfAllVertices(*client, number_of_expected_vertices);
}
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
kTriggerDeletedVertexLabel, vertex_ids[0]);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, vertex_ids[0]);
for (const auto &edge : edges) {
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
kTriggerDeletedEdgeLabel, edge.edge_id);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, edge.edge_id);
}
DropOnDeleteTriggers(*client, allowed_trigger_types);
client->Execute("MATCH (n) DETACH DELETE n;");
client->DiscardAll();
}
};
const auto run_delete_trigger_write_procedure_tests =
[&](const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
ExecuteCreateVertex(*client, 2);
ExecuteCreateVertex(*client, 3);
client->Execute("MATCH (n {id:2}), (m {id:3}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
client->DiscardAll();
CreateOnDeleteTriggers(*client, true, allowed_trigger_types);
client->Execute("MATCH ()-[e]->() CALL write.delete_edge(e)");
client->DiscardAll();
client->Execute("MATCH (n {id:2}) CALL write.delete_vertex(n)");
client->DiscardAll();
auto number_of_expected_vertices = 1;
for (const auto allowed_trigger_type : allowed_trigger_types) {
switch (allowed_trigger_type) {
case AllowedTriggerType::VERTEX:
number_of_expected_vertices += 1;
break;
case AllowedTriggerType::EDGE:
number_of_expected_vertices += 1;
break;
case AllowedTriggerType::OBJECT:
number_of_expected_vertices += 2;
break;
}
}
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
kTriggerDeletedEdgeLabel, 1);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, 1);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
kTriggerDeletedVertexLabel, 2);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, 2);
DropOnDeleteTriggers(*client, allowed_trigger_types);
client->Execute("MATCH (n) DETACH DELETE n;");
client->DiscardAll();
};
const auto run_delete_trigger_write_procedure_delete_detach_test =
[&](const std::unordered_set<AllowedTriggerType> &allowed_trigger_types) {
ExecuteCreateVertex(*client, 2);
ExecuteCreateVertex(*client, 3);
client->Execute("MATCH (n {id:2}), (m {id:3}) CALL write.create_edge(n, m, 'edge') YIELD e RETURN e");
client->DiscardAll();
CreateOnDeleteTriggers(*client, true, allowed_trigger_types);
client->Execute("MATCH (v {id:2}) CALL write.detach_delete_vertex(v)");
client->DiscardAll();
auto number_of_expected_vertices = 1;
for (const auto allowed_trigger_type : allowed_trigger_types) {
switch (allowed_trigger_type) {
case AllowedTriggerType::VERTEX:
number_of_expected_vertices += 1;
break;
case AllowedTriggerType::EDGE:
number_of_expected_vertices += 1;
break;
case AllowedTriggerType::OBJECT:
number_of_expected_vertices += 2;
break;
}
}
CheckNumberOfAllVertices(*client, number_of_expected_vertices);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::EDGE),
kTriggerDeletedEdgeLabel, 1);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, 1);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::VERTEX),
kTriggerDeletedVertexLabel, 2);
ValidateVertexExistance(*client, allowed_trigger_types.contains(AllowedTriggerType::OBJECT),
kTriggerDeletedObjectLabel, 2);
DropOnDeleteTriggers(*client, allowed_trigger_types);
client->Execute("MATCH (n) DETACH DELETE n;");
client->DiscardAll();
};
const auto run_for_trigger_combinations = [&](const bool is_before) {
const std::array trigger_type_combinations{
std::unordered_set{AllowedTriggerType::VERTEX},
std::unordered_set{AllowedTriggerType::EDGE},
std::unordered_set{AllowedTriggerType::OBJECT},
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::EDGE},
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::OBJECT},
std::unordered_set{AllowedTriggerType::EDGE, AllowedTriggerType::OBJECT},
std::unordered_set{AllowedTriggerType::VERTEX, AllowedTriggerType::EDGE, AllowedTriggerType::OBJECT},
};
for (const auto &allowed_trigger_types : trigger_type_combinations) {
run_delete_trigger_tests(is_before, allowed_trigger_types);
run_delete_trigger_write_procedure_tests(allowed_trigger_types);
run_delete_trigger_write_procedure_delete_detach_test(allowed_trigger_types);
}
};
static constexpr bool kBeforeCommit = true;
static constexpr bool kAfterCommit = false;
run_for_trigger_combinations(kBeforeCommit);
run_for_trigger_combinations(kAfterCommit);
return 0;
}