78a88737f8
Co-authored-by: antoniofilipovic <filipovicantonio1998@gmail.com>
2693 lines
120 KiB
C++
2693 lines
120 KiB
C++
// Copyright 2024 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 <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include "auth/models.hpp"
|
|
#include "disk_test_utils.hpp"
|
|
#include "glue/auth_checker.hpp"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "license/license.hpp"
|
|
#include "query/context.hpp"
|
|
#include "query/exceptions.hpp"
|
|
#include "query/frontend/ast/ast.hpp"
|
|
#include "query/interpret/frame.hpp"
|
|
#include "query/plan/operator.hpp"
|
|
|
|
#include "query_plan_common.hpp"
|
|
#include "storage/v2/disk/storage.hpp"
|
|
#include "storage/v2/id_types.hpp"
|
|
#include "storage/v2/inmemory/storage.hpp"
|
|
#include "storage/v2/property_value.hpp"
|
|
#include "storage/v2/storage.hpp"
|
|
#include "storage/v2/vertex_accessor.hpp"
|
|
|
|
using namespace memgraph::query;
|
|
using namespace memgraph::query::plan;
|
|
using memgraph::replication_coordination_glue::ReplicationRole;
|
|
|
|
template <typename StorageType>
|
|
class QueryPlanTest : public testing::Test {
|
|
public:
|
|
const std::string testSuite = "query_plan_create_set_remove_delete";
|
|
memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
|
|
std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<StorageType>(config);
|
|
AstStorage storage;
|
|
|
|
void TearDown() override {
|
|
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
|
|
disk_test_utils::RemoveRocksDbDirs(testSuite);
|
|
}
|
|
}
|
|
};
|
|
|
|
using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
|
|
TYPED_TEST_CASE(QueryPlanTest, StorageTypes);
|
|
|
|
TYPED_TEST(QueryPlanTest, CreateNodeWithAttributes) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
memgraph::storage::LabelId label = dba.NameToLabel("Person");
|
|
auto property = PROPERTY_PAIR(dba, "prop");
|
|
|
|
SymbolTable symbol_table;
|
|
NodeCreationInfo node;
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
|
node.labels.emplace_back(label);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(node.properties)
|
|
.emplace_back(property.second, LITERAL(42));
|
|
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*create, &context);
|
|
dba.AdvanceCommand();
|
|
|
|
// count the number of vertices
|
|
int vertex_count = 0;
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
vertex_count++;
|
|
auto maybe_labels = vertex.Labels(memgraph::storage::View::OLD);
|
|
ASSERT_TRUE(maybe_labels.HasValue());
|
|
const auto &labels = *maybe_labels;
|
|
EXPECT_EQ(labels.size(), 1);
|
|
EXPECT_EQ(*labels.begin(), label);
|
|
auto maybe_properties = vertex.Properties(memgraph::storage::View::OLD);
|
|
ASSERT_TRUE(maybe_properties.HasValue());
|
|
const auto &properties = *maybe_properties;
|
|
EXPECT_EQ(properties.size(), 1);
|
|
auto maybe_prop = vertex.GetProperty(memgraph::storage::View::OLD, property.second);
|
|
ASSERT_TRUE(maybe_prop.HasValue());
|
|
auto prop_eq = TypedValue(*maybe_prop) == TypedValue(42);
|
|
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
|
|
EXPECT_TRUE(prop_eq.ValueBool());
|
|
}
|
|
EXPECT_EQ(vertex_count, 1);
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
TYPED_TEST(QueryPlanTest, FineGrainedCreateNodeWithAttributes) {
|
|
memgraph::license::global_license_checker.EnableTesting();
|
|
memgraph::query::SymbolTable symbol_table;
|
|
auto dba = this->db->Access(ReplicationRole::MAIN);
|
|
DbAccessor execution_dba(dba.get());
|
|
const auto label = dba->NameToLabel("label1");
|
|
const auto property = memgraph::storage::PropertyId::FromInt(1);
|
|
|
|
memgraph::query::plan::NodeCreationInfo node;
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(node.properties)
|
|
.emplace_back(property, this->storage.template Create<PrimitiveLiteral>(42));
|
|
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
|
node.labels.emplace_back(label);
|
|
|
|
const auto test_create = [&](memgraph::auth::User &user) {
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &execution_dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &execution_dba, &auth_checker);
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
|
|
|
return PullAll(*create, &context);
|
|
};
|
|
|
|
// Granted label
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
EXPECT_EQ(test_create(user), 1);
|
|
}
|
|
|
|
// Denied label
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label1",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
ASSERT_THROW(test_create(user), QueryRuntimeException);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, CreateReturn) {
|
|
// test CREATE (n:Person {age: 42}) RETURN n, n.age
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
memgraph::storage::LabelId label = dba.NameToLabel("Person");
|
|
auto property = PROPERTY_PAIR(dba, "property");
|
|
|
|
SymbolTable symbol_table;
|
|
NodeCreationInfo node;
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
|
node.labels.emplace_back(label);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(node.properties)
|
|
.emplace_back(property.second, LITERAL(42));
|
|
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
|
auto named_expr_n =
|
|
NEXPR("n", IDENT("n")->MapTo(node.symbol))->MapTo(symbol_table.CreateSymbol("named_expr_n", true));
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(node.symbol), property);
|
|
auto named_expr_n_p = NEXPR("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("named_expr_n_p", true));
|
|
|
|
auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
auto results = CollectProduce(*produce, &context);
|
|
EXPECT_EQ(1, results.size());
|
|
EXPECT_EQ(2, results[0].size());
|
|
EXPECT_EQ(TypedValue::Type::Vertex, results[0][0].type());
|
|
auto maybe_labels = results[0][0].ValueVertex().Labels(memgraph::storage::View::NEW);
|
|
EXPECT_EQ(1, maybe_labels->size());
|
|
EXPECT_EQ(label, (*maybe_labels)[0]);
|
|
EXPECT_EQ(TypedValue::Type::Int, results[0][1].type());
|
|
EXPECT_EQ(42, results[0][1].ValueInt());
|
|
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
TYPED_TEST(QueryPlanTest, FineGrainedCreateReturn) {
|
|
memgraph::license::global_license_checker.EnableTesting();
|
|
|
|
// test CREATE (n:Person {age: 42}) RETURN n, n.age
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
const auto label = dba.NameToLabel("label");
|
|
const auto property = PROPERTY_PAIR(dba, "property");
|
|
|
|
SymbolTable symbol_table;
|
|
NodeCreationInfo node;
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
|
node.labels.emplace_back(label);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(node.properties)
|
|
.emplace_back(property.second, LITERAL(42));
|
|
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
|
auto named_expr_n =
|
|
NEXPR("n", IDENT("n")->MapTo(node.symbol))->MapTo(symbol_table.CreateSymbol("named_expr_n", true));
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(node.symbol), property);
|
|
auto named_expr_n_p = NEXPR("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("named_expr_n_p", true));
|
|
|
|
auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
|
|
|
|
// Granted label
|
|
{
|
|
memgraph::auth::User user{"Test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
auto results = CollectProduce(*produce, &context);
|
|
EXPECT_EQ(1, results.size());
|
|
EXPECT_EQ(2, results[0].size());
|
|
EXPECT_EQ(TypedValue::Type::Vertex, results[0][0].type());
|
|
auto maybe_labels = results[0][0].ValueVertex().Labels(memgraph::storage::View::NEW);
|
|
EXPECT_EQ(1, maybe_labels->size());
|
|
EXPECT_EQ(label, (*maybe_labels)[0]);
|
|
EXPECT_EQ(TypedValue::Type::Int, results[0][1].type());
|
|
EXPECT_EQ(42, results[0][1].ValueInt());
|
|
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
}
|
|
|
|
// Denied label
|
|
{
|
|
memgraph::auth::User user{"Test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, CreateExpand) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
memgraph::storage::LabelId label_node_1 = dba.NameToLabel("Node1");
|
|
memgraph::storage::LabelId label_node_2 = dba.NameToLabel("Node2");
|
|
auto property = PROPERTY_PAIR(dba, "property");
|
|
memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
|
|
int before_v = CountIterable(dba.Vertices(memgraph::storage::View::OLD));
|
|
int before_e = CountEdges(&dba, memgraph::storage::View::OLD);
|
|
|
|
// data for the first node
|
|
NodeCreationInfo n;
|
|
n.symbol = symbol_table.CreateSymbol("n", true);
|
|
n.labels.emplace_back(label_node_1);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(n.properties)
|
|
.emplace_back(property.second, LITERAL(1));
|
|
|
|
// data for the second node
|
|
NodeCreationInfo m;
|
|
m.symbol = cycle ? n.symbol : symbol_table.CreateSymbol("m", true);
|
|
m.labels.emplace_back(label_node_2);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(m.properties)
|
|
.emplace_back(property.second, LITERAL(2));
|
|
|
|
EdgeCreationInfo r;
|
|
r.symbol = symbol_table.CreateSymbol("r", true);
|
|
r.edge_type = edge_type;
|
|
std::get<0>(r.properties).emplace_back(property.second, LITERAL(3));
|
|
|
|
auto create_op = std::make_shared<CreateNode>(nullptr, n);
|
|
auto create_expand = std::make_shared<CreateExpand>(m, r, create_op, n.symbol, cycle);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*create_expand, &context);
|
|
dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)) - before_v, expected_nodes_created);
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD) - before_e, expected_edges_created);
|
|
};
|
|
|
|
test_create_path(false, 2, 1);
|
|
test_create_path(true, 1, 1);
|
|
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
auto maybe_labels = vertex.Labels(memgraph::storage::View::OLD);
|
|
MG_ASSERT(maybe_labels.HasValue());
|
|
const auto &labels = *maybe_labels;
|
|
EXPECT_EQ(labels.size(), 1);
|
|
memgraph::storage::LabelId label = labels[0];
|
|
if (label == label_node_1) {
|
|
// node created by first op
|
|
EXPECT_EQ(vertex.GetProperty(memgraph::storage::View::OLD, property.second)->ValueInt(), 1);
|
|
} else if (label == label_node_2) {
|
|
// node create by expansion
|
|
EXPECT_EQ(vertex.GetProperty(memgraph::storage::View::OLD, property.second)->ValueInt(), 2);
|
|
} else {
|
|
// should not happen
|
|
FAIL();
|
|
}
|
|
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
|
|
MG_ASSERT(maybe_edges.HasValue());
|
|
for (auto edge : maybe_edges->edges) {
|
|
EXPECT_EQ(edge.EdgeType(), edge_type);
|
|
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, property.second)->ValueInt(), 3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
template <typename StorageType>
|
|
class CreateExpandWithAuthFixture : public QueryPlanTest<StorageType> {
|
|
protected:
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
|
|
void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
|
|
|
|
void ExecuteCreateExpand(bool cycle, memgraph::auth::User &user) {
|
|
const auto label_node_1 = dba.NameToLabel("Node1");
|
|
const auto label_node_2 = dba.NameToLabel("Node2");
|
|
const auto property = PROPERTY_PAIR(dba, "property");
|
|
const auto edge_type = dba.NameToEdgeType("edge_type");
|
|
|
|
// data for the first node
|
|
NodeCreationInfo n;
|
|
n.symbol = symbol_table.CreateSymbol("n", true);
|
|
n.labels.emplace_back(label_node_1);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(n.properties)
|
|
.emplace_back(property.second, LITERAL(1));
|
|
|
|
// data for the second node
|
|
NodeCreationInfo m;
|
|
m.symbol = cycle ? n.symbol : symbol_table.CreateSymbol("m", true);
|
|
m.labels.emplace_back(label_node_2);
|
|
std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(m.properties)
|
|
.emplace_back(property.second, LITERAL(2));
|
|
|
|
EdgeCreationInfo r;
|
|
r.symbol = symbol_table.CreateSymbol("r", true);
|
|
r.edge_type = edge_type;
|
|
std::get<0>(r.properties).emplace_back(property.second, LITERAL(3));
|
|
|
|
auto create_op = std::make_shared<CreateNode>(nullptr, n);
|
|
auto create_expand = std::make_shared<CreateExpand>(m, r, create_op, n.symbol, cycle);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
PullAll(*create_expand, &context);
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void TestCreateExpandHypothesis(int expected_nodes_created, int expected_edges_created) {
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::NEW)), expected_nodes_created);
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::NEW), expected_edges_created);
|
|
}
|
|
};
|
|
|
|
TYPED_TEST_CASE(CreateExpandWithAuthFixture, StorageTypes);
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithNoGrantsOnCreateDelete) {
|
|
// All labels denied, All edge types denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithLabelsGrantedOnly) {
|
|
// All labels granted, All edge types denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithEdgeTypesGrantedOnly) {
|
|
// All labels denied, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithFirstLabelGranted) {
|
|
// First label granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("Node1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("Node2", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("Node2", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithSecondLabelGranted) {
|
|
// Second label granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("Node2",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("Node1", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithoutCycleWithEverythingGranted) {
|
|
// All labels granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteCreateExpand(false, user);
|
|
this->TestCreateExpandHypothesis(2, 1);
|
|
}
|
|
|
|
TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithCycleWithEverythingGranted) {
|
|
// All labels granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteCreateExpand(true, user);
|
|
this->TestCreateExpandHypothesis(1, 1);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, MatchCreateNode) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// add three nodes we'll match and expand-create from
|
|
dba.InsertVertex();
|
|
dba.InsertVertex();
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
// first node
|
|
auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
// second node
|
|
NodeCreationInfo m;
|
|
m.symbol = symbol_table.CreateSymbol("m", true);
|
|
// creation op
|
|
auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
|
|
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)), 3);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*create_node, &context);
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)), 6);
|
|
}
|
|
|
|
template <typename StorageType>
|
|
class MatchCreateNodeWithAuthFixture : public QueryPlanTest<StorageType> {
|
|
protected:
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
|
|
void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
|
|
|
|
void InitGraph() {
|
|
// add three nodes we'll match and expand-create from
|
|
memgraph::query::VertexAccessor v1{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v2{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v3{dba.InsertVertex()};
|
|
ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("l1")).HasValue());
|
|
ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("l2")).HasValue());
|
|
ASSERT_TRUE(v3.AddLabel(dba.NameToLabel("l3")).HasValue());
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void ExecuteMatchCreate(memgraph::auth::User &user) {
|
|
auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
// second node
|
|
NodeCreationInfo m{};
|
|
|
|
m.symbol = symbol_table.CreateSymbol("m", true);
|
|
std::vector<memgraph::storage::LabelId> labels{dba.NameToLabel("l2")};
|
|
m.labels = labels;
|
|
// creation op
|
|
auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*create_node, &context);
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void MatchCreateAssertion(int expected_result_size) {
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)), expected_result_size);
|
|
}
|
|
|
|
void ExecuteMatchCreateTestSuite(memgraph::auth::User &user, int expected_result_size) {
|
|
InitGraph();
|
|
ExecuteMatchCreate(user);
|
|
MatchCreateAssertion(expected_result_size);
|
|
}
|
|
};
|
|
|
|
TYPED_TEST_CASE(MatchCreateNodeWithAuthFixture, StorageTypes);
|
|
|
|
TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsDeniedThrows) {
|
|
// All labels denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsGrantedExecutes) {
|
|
// All labels granteddenieddenieddenied
|
|
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteMatchCreateTestSuite(user, 6);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithOneLabelDeniedThrows) {
|
|
// Label2 denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("l1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l3",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, MatchCreateExpand) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// add three nodes we'll match and expand-create from
|
|
dba.InsertVertex();
|
|
dba.InsertVertex();
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
|
|
// memgraph::storage::LabelId label_node_1 = dba.NameToLabel("Node1");
|
|
// memgraph::storage::LabelId label_node_2 = dba.NameToLabel("Node2");
|
|
// memgraph::storage::PropertyId property = dba.NameToLabel("prop");
|
|
memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
|
|
SymbolTable symbol_table;
|
|
|
|
auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
|
|
int before_v = CountIterable(dba.Vertices(memgraph::storage::View::OLD));
|
|
int before_e = CountEdges(&dba, memgraph::storage::View::OLD);
|
|
|
|
// data for the first node
|
|
auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
// data for the second node
|
|
NodeCreationInfo m;
|
|
m.symbol = cycle ? n_scan_all.sym_ : symbol_table.CreateSymbol("m", true);
|
|
|
|
EdgeCreationInfo r;
|
|
r.symbol = symbol_table.CreateSymbol("r", true);
|
|
r.direction = EdgeAtom::Direction::OUT;
|
|
r.edge_type = edge_type;
|
|
|
|
auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_, n_scan_all.sym_, cycle);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*create_expand, &context);
|
|
dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)) - before_v, expected_nodes_created);
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD) - before_e, expected_edges_created);
|
|
};
|
|
|
|
test_create_path(false, 3, 3);
|
|
test_create_path(true, 0, 6);
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
template <typename StorageType>
|
|
class MatchCreateExpandWithAuthFixture : public QueryPlanTest<StorageType> {
|
|
protected:
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
|
|
void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
|
|
|
|
void InitGraph() {
|
|
// add three nodes we'll match and expand-create from
|
|
memgraph::query::VertexAccessor v1{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v2{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v3{dba.InsertVertex()};
|
|
ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("l1")).HasValue());
|
|
ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("l2")).HasValue());
|
|
ASSERT_TRUE(v3.AddLabel(dba.NameToLabel("l3")).HasValue());
|
|
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void ExecuteMatchCreateExpand(memgraph::auth::User &user, bool cycle) {
|
|
// data for the first node
|
|
auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
// data for the second node
|
|
NodeCreationInfo m;
|
|
m.symbol = cycle ? n_scan_all.sym_ : symbol_table.CreateSymbol("m", true);
|
|
std::vector<memgraph::storage::LabelId> labels{dba.NameToLabel("l2")};
|
|
m.labels = labels;
|
|
|
|
EdgeCreationInfo r;
|
|
r.symbol = symbol_table.CreateSymbol("r", true);
|
|
r.direction = EdgeAtom::Direction::OUT;
|
|
r.edge_type = dba.NameToEdgeType("edge_type");
|
|
;
|
|
|
|
auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_, n_scan_all.sym_, cycle);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
PullAll(*create_expand, &context);
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void MatchCreateExpandAssertion(int expected_nodes_size, int expected_edges_size) {
|
|
EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::NEW)), expected_nodes_size);
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::NEW), expected_edges_size);
|
|
}
|
|
|
|
void ExecuteMatchCreateExpandTestSuite(bool cycle, int expected_nodes_size, int expected_edges_size,
|
|
memgraph::auth::User &user) {
|
|
InitGraph();
|
|
ExecuteMatchCreateExpand(user, cycle);
|
|
MatchCreateExpandAssertion(expected_nodes_size, expected_edges_size);
|
|
}
|
|
};
|
|
|
|
TYPED_TEST_CASE(MatchCreateExpandWithAuthFixture, StorageTypes);
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEverything) {
|
|
// All labels denied, All edge types denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEdgeTypes) {
|
|
// All labels granted, All edge types denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedLabels) {
|
|
// All labels denied, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedOneLabel) {
|
|
// First two label granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("l1", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::READ);
|
|
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
|
|
ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedSpecificallyEverything) {
|
|
// All label granted, Specific edge type granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"edge_type", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedSpecificallyEverything) {
|
|
// All label granted, Specific edge type granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"edge_type", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedEverything) {
|
|
// All labels granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
|
|
}
|
|
|
|
TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedEverything) {
|
|
// All labels granted, All edge types granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, Delete) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// make a fully-connected (one-direction, no cycles) with 4 nodes
|
|
std::vector<memgraph::query::VertexAccessor> vertices;
|
|
for (int i = 0; i < 4; ++i) vertices.push_back(dba.InsertVertex());
|
|
auto type = dba.NameToEdgeType("type");
|
|
for (int j = 0; j < 4; ++j)
|
|
for (int k = j + 1; k < 4; ++k) ASSERT_TRUE(dba.InsertEdge(&vertices[j], &vertices[k], type).HasValue());
|
|
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(6, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
|
|
SymbolTable symbol_table;
|
|
// attempt to delete a vertex, and fail
|
|
{
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_THROW(PullAll(*delete_op, &context), QueryRuntimeException);
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(6, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
}
|
|
|
|
// detach delete a single vertex
|
|
// delete will not happen as we are deleting in bulk
|
|
{
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
|
|
Frame frame(symbol_table.max_position());
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
delete_op->MakeCursor(memgraph::utils::NewDeleteResource())->Pull(frame, context);
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(6, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
}
|
|
|
|
// delete all remaining edges
|
|
{
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
|
|
memgraph::storage::View::NEW);
|
|
auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{r_get}, false);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*delete_op, &context);
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(0, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
}
|
|
|
|
// delete all remaining vertices
|
|
{
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*delete_op, &context);
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(0, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
}
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
template <typename StorageType>
|
|
class DeleteOperatorWithAuthFixture : public QueryPlanTest<StorageType> {
|
|
protected:
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
|
|
void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
|
|
|
|
void InitGraph() {
|
|
std::vector<memgraph::query::VertexAccessor> vertices;
|
|
for (int i = 0; i < 4; ++i) {
|
|
memgraph::query::VertexAccessor v{dba.InsertVertex()};
|
|
auto label = "l" + std::to_string(i);
|
|
ASSERT_TRUE(v.AddLabel(dba.NameToLabel(label)).HasValue());
|
|
vertices.push_back(v);
|
|
}
|
|
for (int j = 0; j < 4; ++j) {
|
|
auto edge_type = "type" + std::to_string(j);
|
|
auto type = dba.NameToEdgeType(edge_type);
|
|
for (int k = j + 1; k < 4; ++k) ASSERT_TRUE(dba.InsertEdge(&vertices[j], &vertices[k], type).HasValue());
|
|
}
|
|
|
|
dba.AdvanceCommand();
|
|
|
|
assertInitGraphValid();
|
|
}
|
|
|
|
void TestDeleteNodesHypothesis(int expected_result_size) {
|
|
EXPECT_EQ(expected_result_size, CountIterable(dba.Vertices(memgraph::storage::View::NEW)));
|
|
};
|
|
|
|
void DeleteAllNodes(memgraph::auth::User &user) {
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
Frame frame(symbol_table.max_position());
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
|
|
PullAll(*delete_op, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteDeleteNodesTestSuite(memgraph::auth::User &user, int expected_nodes) {
|
|
// make a fully-connected (one-direction, no cycles) with 4 nodes
|
|
InitGraph();
|
|
DeleteAllNodes(user);
|
|
TestDeleteNodesHypothesis(expected_nodes);
|
|
};
|
|
|
|
void TestDeleteEdgesHypothesis(int expected_result_size) {
|
|
EXPECT_EQ(expected_result_size, CountEdges(&dba, memgraph::storage::View::NEW));
|
|
};
|
|
|
|
void DeleteAllEdges(memgraph::auth::User &user) {
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
|
|
memgraph::storage::View::NEW);
|
|
auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{r_get}, false);
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
PullAll(*delete_op, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteDeleteEdgesTestSuite(memgraph::auth::User &user, int expected_edges) {
|
|
// make a fully-connected (one-direction, no cycles) with 4 nodes
|
|
InitGraph();
|
|
DeleteAllEdges(user);
|
|
TestDeleteEdgesHypothesis(expected_edges);
|
|
};
|
|
|
|
private:
|
|
void assertInitGraphValid() {
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(6, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
}
|
|
};
|
|
|
|
TYPED_TEST_CASE(DeleteOperatorWithAuthFixture, StorageTypes);
|
|
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenAllLabelsDenied) {
|
|
// All labels denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenPartialLabelsGranted) {
|
|
// One Label granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("l1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeExecutesWhenGrantedAllLabels) {
|
|
// All labels granted
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
this->ExecuteDeleteNodesTestSuite(user, 0);
|
|
}
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenEdgeTypesNotGranted) {
|
|
// All labels granted,All edge types denied
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
|
|
}
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteEdgesThrowsErrorWhenPartialGrant) {
|
|
// Specific label granted, Specific edge types granted
|
|
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("l1", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().label_permissions().Grant("l4", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"type0", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"type1", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("type2",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("type3",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
ASSERT_THROW(this->ExecuteDeleteEdgesTestSuite(user, 0), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeAndDeleteEdgePerformWhenGranted) {
|
|
// All labels granted, All edge_types granted
|
|
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->InitGraph();
|
|
this->DeleteAllNodes(user);
|
|
this->TestDeleteNodesHypothesis(0);
|
|
this->TestDeleteEdgesHypothesis(0);
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteTwiceDeleteBlockingEdge) {
|
|
// test deleting the same vertex and edge multiple times
|
|
//
|
|
// also test vertex deletion succeeds if the prohibiting
|
|
// edge is deleted in the same logical op
|
|
//
|
|
// we test both with the following queries (note the
|
|
// undirected edge in MATCH):
|
|
//
|
|
// CREATE ()-[:T]->()
|
|
// MATCH (n)-[r]-(m) [DETACH] DELETE n, r, m
|
|
|
|
auto test_delete = [this](bool detach) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
auto v1 = dba.InsertVertex();
|
|
auto v2 = dba.InsertVertex();
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("T")).HasValue());
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(1, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
|
|
memgraph::storage::View::OLD);
|
|
|
|
// getter expressions for deletion
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
|
|
auto m_get = this->storage.template Create<Identifier>("m")->MapTo(r_m.node_sym_);
|
|
|
|
auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{n_get, r_get, m_get}, detach);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*delete_op, &context));
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(0, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
};
|
|
|
|
test_delete(true);
|
|
test_delete(false);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteReturn) {
|
|
// MATCH (n) DETACH DELETE n RETURN n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// graph with 4 vertices
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
for (int i = 0; i < 4; ++i) {
|
|
auto va = dba.InsertVertex();
|
|
ASSERT_TRUE(va.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
|
|
}
|
|
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
EXPECT_EQ(0, CountEdges(&dba, memgraph::storage::View::OLD));
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
|
|
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(delete_op, delete_op->ModifiedSymbols(symbol_table), true);
|
|
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteNull) {
|
|
// test (simplified) WITH Null as x delete x
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
|
|
auto once = std::make_shared<Once>();
|
|
auto delete_op = std::make_shared<plan::Delete>(once, std::vector<Expression *>{LITERAL(TypedValue())}, false);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*delete_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteAdvance) {
|
|
// test queries on empty DB:
|
|
// CREATE (n)
|
|
// MATCH (n) DELETE n WITH n ...
|
|
// this fails only if the deleted record `n` is actually used in subsequent
|
|
// clauses, which is compatible with Neo's behavior.
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto advance = std::make_shared<Accumulate>(delete_op, std::vector<Symbol>{n.sym_}, true);
|
|
auto res_sym = symbol_table.CreateSymbol("res", true);
|
|
{
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
auto produce = MakeProduce(advance, NEXPR("res", LITERAL(42))->MapTo(res_sym));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*produce, &context));
|
|
}
|
|
{
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
auto n_prop = PROPERTY_LOOKUP(dba, n_get, dba.NameToProperty("prop"));
|
|
auto produce = MakeProduce(advance, NEXPR("res", n_prop)->MapTo(res_sym));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_THROW(PullAll(*produce, &context), QueryRuntimeException);
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetProperty) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// graph with 4 vertices in connected pairs
|
|
// the origin vertex in each par and both edges
|
|
// have a property set
|
|
auto v1 = dba.InsertVertex();
|
|
auto v2 = dba.InsertVertex();
|
|
auto v3 = dba.InsertVertex();
|
|
auto v4 = dba.InsertVertex();
|
|
auto edge_type = dba.NameToEdgeType("edge_type");
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
|
|
ASSERT_TRUE(dba.InsertEdge(&v2, &v4, edge_type).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
// scan (n)-[r]->(m)
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
|
|
memgraph::storage::View::OLD);
|
|
|
|
// set prop1 to 42 on n and r
|
|
auto prop1 = dba.NameToProperty("prop1");
|
|
auto literal = LITERAL(42);
|
|
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
|
|
auto set_n_p = std::make_shared<plan::SetProperty>(r_m.op_, prop1, n_p, literal);
|
|
|
|
auto r_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(r_m.edge_sym_), prop1);
|
|
auto set_r_p = std::make_shared<plan::SetProperty>(set_n_p, prop1, r_p, literal);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*set_r_p, &context));
|
|
dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 2);
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
|
|
ASSERT_TRUE(maybe_edges.HasValue());
|
|
for (auto edge : maybe_edges->edges) {
|
|
ASSERT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->ValueInt(), 42);
|
|
auto from = edge.From();
|
|
auto to = edge.To();
|
|
ASSERT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop1)->ValueInt(), 42);
|
|
ASSERT_EQ(to.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Null);
|
|
}
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetProperties) {
|
|
auto test_set_properties = [this](bool update) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// graph: ({a: 0})-[:R {b:1}]->({c:2})
|
|
auto prop_a = dba.NameToProperty("a");
|
|
auto prop_b = dba.NameToProperty("b");
|
|
auto prop_c = dba.NameToProperty("c");
|
|
auto v1 = dba.InsertVertex();
|
|
auto v2 = dba.InsertVertex();
|
|
auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("R"));
|
|
ASSERT_TRUE(v1.SetProperty(prop_a, memgraph::storage::PropertyValue(0)).HasValue());
|
|
ASSERT_TRUE(e->SetProperty(prop_b, memgraph::storage::PropertyValue(1)).HasValue());
|
|
ASSERT_TRUE(v2.SetProperty(prop_c, memgraph::storage::PropertyValue(2)).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
// scan (n)-[r]->(m)
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
|
|
memgraph::storage::View::OLD);
|
|
|
|
auto op = update ? plan::SetProperties::Op::UPDATE : plan::SetProperties::Op::REPLACE;
|
|
|
|
// set properties on r to n, and on r to m
|
|
auto r_ident = IDENT("r")->MapTo(r_m.edge_sym_);
|
|
auto m_ident = IDENT("m")->MapTo(r_m.node_sym_);
|
|
auto set_r_to_n = std::make_shared<plan::SetProperties>(r_m.op_, n.sym_, r_ident, op);
|
|
auto set_m_to_r = std::make_shared<plan::SetProperties>(set_r_to_n, r_m.edge_sym_, m_ident, op);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*set_m_to_r, &context));
|
|
dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 1);
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
|
|
ASSERT_TRUE(maybe_edges.HasValue());
|
|
for (auto edge : maybe_edges->edges) {
|
|
auto from = edge.From();
|
|
EXPECT_EQ(from.Properties(memgraph::storage::View::OLD)->size(), update ? 2 : 1);
|
|
if (update) {
|
|
ASSERT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop_a)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop_a)->ValueInt(), 0);
|
|
}
|
|
ASSERT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop_b)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop_b)->ValueInt(), 1);
|
|
|
|
EXPECT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), update ? 2 : 1);
|
|
if (update) {
|
|
ASSERT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop_b)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop_b)->ValueInt(), 1);
|
|
}
|
|
ASSERT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop_c)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop_c)->ValueInt(), 2);
|
|
|
|
auto to = edge.To();
|
|
EXPECT_EQ(to.Properties(memgraph::storage::View::OLD)->size(), 1);
|
|
ASSERT_EQ(to.GetProperty(memgraph::storage::View::OLD, prop_c)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(to.GetProperty(memgraph::storage::View::OLD, prop_c)->ValueInt(), 2);
|
|
}
|
|
}
|
|
};
|
|
|
|
test_set_properties(true);
|
|
test_set_properties(false);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetLabels) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
ASSERT_TRUE(dba.InsertVertex().AddLabel(label1).HasValue());
|
|
ASSERT_TRUE(dba.InsertVertex().AddLabel(label1).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto label_set =
|
|
std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{label2, label3});
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*label_set, &context));
|
|
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
EXPECT_EQ(3, vertex.Labels(memgraph::storage::View::NEW)->size());
|
|
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
|
|
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label3));
|
|
}
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
TYPED_TEST(QueryPlanTest, SetLabelsWithFineGrained) {
|
|
memgraph::license::global_license_checker.EnableTesting();
|
|
auto set_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
|
|
std::vector<memgraph::storage::LabelId> labels) {
|
|
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
|
|
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto label_set =
|
|
std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[1], labels[2]});
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*label_set, &context);
|
|
};
|
|
|
|
// All labels granted
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3});
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
EXPECT_EQ(3, vertex.Labels(memgraph::storage::View::NEW)->size());
|
|
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
|
|
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label3));
|
|
}
|
|
}
|
|
|
|
// All labels denied
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
ASSERT_THROW(set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
|
|
QueryRuntimeException);
|
|
}
|
|
|
|
// label2 denied
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("label2",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("label3",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
ASSERT_THROW(set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
|
|
QueryRuntimeException);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, RemoveProperty) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// graph with 4 vertices in connected pairs
|
|
// the origin vertex in each par and both edges
|
|
// have a property set
|
|
auto prop1 = dba.NameToProperty("prop1");
|
|
auto v1 = dba.InsertVertex();
|
|
auto v2 = dba.InsertVertex();
|
|
auto v3 = dba.InsertVertex();
|
|
auto v4 = dba.InsertVertex();
|
|
auto edge_type = dba.NameToEdgeType("edge_type");
|
|
{
|
|
auto e = dba.InsertEdge(&v1, &v3, edge_type);
|
|
ASSERT_TRUE(e.HasValue());
|
|
ASSERT_TRUE(e->SetProperty(prop1, memgraph::storage::PropertyValue(42)).HasValue());
|
|
}
|
|
ASSERT_TRUE(dba.InsertEdge(&v2, &v4, edge_type).HasValue());
|
|
ASSERT_TRUE(v2.SetProperty(prop1, memgraph::storage::PropertyValue(42)).HasValue());
|
|
ASSERT_TRUE(v3.SetProperty(prop1, memgraph::storage::PropertyValue(42)).HasValue());
|
|
ASSERT_TRUE(v4.SetProperty(prop1, memgraph::storage::PropertyValue(42)).HasValue());
|
|
auto prop2 = dba.NameToProperty("prop2");
|
|
ASSERT_TRUE(v1.SetProperty(prop2, memgraph::storage::PropertyValue(0)).HasValue());
|
|
ASSERT_TRUE(v2.SetProperty(prop2, memgraph::storage::PropertyValue(0)).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
// scan (n)-[r]->(m)
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
|
|
memgraph::storage::View::OLD);
|
|
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
|
|
auto set_n_p = std::make_shared<plan::RemoveProperty>(r_m.op_, prop1, n_p);
|
|
|
|
auto r_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(r_m.edge_sym_), prop1);
|
|
auto set_r_p = std::make_shared<plan::RemoveProperty>(set_n_p, prop1, r_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*set_r_p, &context));
|
|
dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 2);
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
|
|
ASSERT_TRUE(maybe_edges.HasValue());
|
|
for (auto edge : maybe_edges->edges) {
|
|
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Null);
|
|
auto from = edge.From();
|
|
auto to = edge.To();
|
|
EXPECT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Null);
|
|
EXPECT_EQ(from.GetProperty(memgraph::storage::View::OLD, prop2)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
EXPECT_EQ(to.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
}
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, RemoveLabels) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
auto v1 = dba.InsertVertex();
|
|
ASSERT_TRUE(v1.AddLabel(label1).HasValue());
|
|
ASSERT_TRUE(v1.AddLabel(label2).HasValue());
|
|
ASSERT_TRUE(v1.AddLabel(label3).HasValue());
|
|
auto v2 = dba.InsertVertex();
|
|
ASSERT_TRUE(v2.AddLabel(label1).HasValue());
|
|
ASSERT_TRUE(v2.AddLabel(label3).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto label_remove =
|
|
std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{label1, label2});
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*label_remove, &context));
|
|
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
EXPECT_EQ(1, vertex.Labels(memgraph::storage::View::NEW)->size());
|
|
EXPECT_FALSE(*vertex.HasLabel(memgraph::storage::View::NEW, label1));
|
|
EXPECT_FALSE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
|
|
}
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
TYPED_TEST(QueryPlanTest, RemoveLabelsFineGrainedFiltering) {
|
|
memgraph::license::global_license_checker.EnableTesting();
|
|
auto remove_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
|
|
std::vector<memgraph::storage::LabelId> labels) {
|
|
auto v1 = dba.InsertVertex();
|
|
ASSERT_TRUE(v1.AddLabel(labels[0]).HasValue());
|
|
ASSERT_TRUE(v1.AddLabel(labels[1]).HasValue());
|
|
ASSERT_TRUE(v1.AddLabel(labels[2]).HasValue());
|
|
auto v2 = dba.InsertVertex();
|
|
ASSERT_TRUE(v2.AddLabel(labels[0]).HasValue());
|
|
ASSERT_TRUE(v2.AddLabel(labels[2]).HasValue());
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto label_remove = std::make_shared<plan::RemoveLabels>(
|
|
n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[0], labels[1]});
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*label_remove, &context);
|
|
};
|
|
|
|
// All labels granted
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3});
|
|
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
|
|
EXPECT_EQ(1, vertex.Labels(memgraph::storage::View::NEW)->size());
|
|
EXPECT_FALSE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
|
|
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label3));
|
|
}
|
|
}
|
|
|
|
// All labels denied
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
ASSERT_THROW(remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
|
|
QueryRuntimeException);
|
|
}
|
|
|
|
// label2 denied
|
|
{
|
|
memgraph::auth::User user{"test"};
|
|
user.fine_grained_access_handler().label_permissions().Grant("label1",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("label2",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("label3",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
auto label3 = dba.NameToLabel("label3");
|
|
ASSERT_THROW(remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
|
|
QueryRuntimeException);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TYPED_TEST(QueryPlanTest, NodeFilterSet) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
|
|
auto v1 = dba.InsertVertex();
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
ASSERT_TRUE(v1.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
|
|
auto v2 = dba.InsertVertex();
|
|
auto v3 = dba.InsertVertex();
|
|
auto edge_type = dba.NameToEdgeType("Edge");
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
|
|
dba.AdvanceCommand();
|
|
// Create operations which match (v1 {prop: 42}) -- (v) and increment the
|
|
// v1.prop. The expected result is two incremenentations, since v1 is matched
|
|
// twice for 2 edges it has.
|
|
SymbolTable symbol_table;
|
|
// MATCH (n {prop: 42}) -[r]- (m)
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
std::get<0>(scan_all.node_->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
|
|
auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {},
|
|
"m", false, memgraph::storage::View::OLD);
|
|
auto *filter_expr = EQ(this->storage.template Create<PropertyLookup>(scan_all.node_->identifier_,
|
|
this->storage.GetPropertyIx(prop.first)),
|
|
LITERAL(42));
|
|
auto node_filter = std::make_shared<Filter>(expand.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
|
|
// SET n.prop = n.prop + 1
|
|
auto set_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
|
|
auto add = ADD(set_prop, LITERAL(1));
|
|
auto set = std::make_shared<plan::SetProperty>(node_filter, prop.second, set_prop, add);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*set, &context));
|
|
dba.AdvanceCommand();
|
|
auto prop_eq = TypedValue(*v1.GetProperty(memgraph::storage::View::OLD, prop.second)) == TypedValue(42 + 2);
|
|
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
|
|
EXPECT_TRUE(prop_eq.ValueBool());
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, FilterRemove) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
|
|
auto v1 = dba.InsertVertex();
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
ASSERT_TRUE(v1.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
|
|
auto v2 = dba.InsertVertex();
|
|
auto v3 = dba.InsertVertex();
|
|
auto edge_type = dba.NameToEdgeType("Edge");
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
|
|
dba.AdvanceCommand();
|
|
// Create operations which match (v1 {prop: 42}) -- (v) and remove v1.prop.
|
|
// The expected result is two matches, for each edge of v1.
|
|
SymbolTable symbol_table;
|
|
// MATCH (n) -[r]- (m) WHERE n.prop < 43
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
std::get<0>(scan_all.node_->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
|
|
auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {},
|
|
"m", false, memgraph::storage::View::OLD);
|
|
auto filter_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
|
|
auto filter = std::make_shared<Filter>(expand.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
|
|
LESS(filter_prop, LITERAL(43)));
|
|
// REMOVE n.prop
|
|
auto rem_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
|
|
auto rem = std::make_shared<plan::RemoveProperty>(filter, prop.second, rem_prop);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(2, PullAll(*rem, &context));
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(v1.GetProperty(memgraph::storage::View::OLD, prop.second)->type(),
|
|
memgraph::storage::PropertyValue::Type::Null);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetRemove) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto v = dba.InsertVertex();
|
|
auto label1 = dba.NameToLabel("label1");
|
|
auto label2 = dba.NameToLabel("label2");
|
|
dba.AdvanceCommand();
|
|
// Create operations which match (v) and set and remove v :label.
|
|
// The expected result is single (v) as it was at the start.
|
|
SymbolTable symbol_table;
|
|
// MATCH (n) SET n :label1 :label2 REMOVE n :label1 :label2
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto set = std::make_shared<plan::SetLabels>(scan_all.op_, scan_all.sym_,
|
|
std::vector<memgraph::storage::LabelId>{label1, label2});
|
|
auto rem =
|
|
std::make_shared<plan::RemoveLabels>(set, scan_all.sym_, std::vector<memgraph::storage::LabelId>{label1, label2});
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*rem, &context));
|
|
dba.AdvanceCommand();
|
|
EXPECT_FALSE(*v.HasLabel(memgraph::storage::View::OLD, label1));
|
|
EXPECT_FALSE(*v.HasLabel(memgraph::storage::View::OLD, label2));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, Merge) {
|
|
// test setup:
|
|
// - three nodes, two of them connected with T
|
|
// - merge input branch matches all nodes
|
|
// - merge_match branch looks for an expansion (any direction)
|
|
// and sets some property (for result validation)
|
|
// - merge_create branch just sets some other property
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto v1 = dba.InsertVertex();
|
|
auto v2 = dba.InsertVertex();
|
|
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")).HasValue());
|
|
auto v3 = dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
// merge_match branch
|
|
auto r_m = MakeExpand(this->storage, symbol_table, std::make_shared<Once>(), n.sym_, "r", EdgeAtom::Direction::BOTH,
|
|
{}, "m", false, memgraph::storage::View::OLD);
|
|
auto m_p = PROPERTY_LOOKUP(dba, IDENT("m")->MapTo(r_m.node_sym_), prop);
|
|
auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, prop.second, m_p, LITERAL(1));
|
|
|
|
// merge_create branch
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_set = std::make_shared<plan::SetProperty>(std::make_shared<Once>(), prop.second, n_p, LITERAL(2));
|
|
|
|
auto merge = std::make_shared<plan::Merge>(n.op_, m_set, n_set);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_EQ(3, PullAll(*merge, &context));
|
|
dba.AdvanceCommand();
|
|
|
|
ASSERT_EQ(v1.GetProperty(memgraph::storage::View::OLD, prop.second)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
ASSERT_EQ(v1.GetProperty(memgraph::storage::View::OLD, prop.second)->ValueInt(), 1);
|
|
ASSERT_EQ(v2.GetProperty(memgraph::storage::View::OLD, prop.second)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
ASSERT_EQ(v2.GetProperty(memgraph::storage::View::OLD, prop.second)->ValueInt(), 1);
|
|
ASSERT_EQ(v3.GetProperty(memgraph::storage::View::OLD, prop.second)->type(),
|
|
memgraph::storage::PropertyValue::Type::Int);
|
|
ASSERT_EQ(v3.GetProperty(memgraph::storage::View::OLD, prop.second)->ValueInt(), 2);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, MergeNoInput) {
|
|
// merge with no input, creates a single node
|
|
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
|
|
NodeCreationInfo node;
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
|
auto merge = std::make_shared<plan::Merge>(nullptr, create, create);
|
|
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*merge, &context));
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetPropertyWithCaching) {
|
|
// SET (Null).prop = 42
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto null = LITERAL(TypedValue());
|
|
auto literal = LITERAL(42);
|
|
auto n_prop = PROPERTY_LOOKUP(dba, null, prop);
|
|
auto once = std::make_shared<Once>();
|
|
auto set_op = std::make_shared<plan::SetProperty>(once, prop.second, n_prop, literal);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*set_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetPropertyOnNull) {
|
|
// SET (Null).prop = 42
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto null = LITERAL(TypedValue());
|
|
auto literal = LITERAL(42);
|
|
auto n_prop = PROPERTY_LOOKUP(dba, null, prop);
|
|
auto once = std::make_shared<Once>();
|
|
auto set_op = std::make_shared<plan::SetProperty>(once, prop.second, n_prop, literal);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*set_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetPropertiesOnNull) {
|
|
// OPTIONAL MATCH (n) SET n = n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_ident = IDENT("n")->MapTo(n.sym_);
|
|
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
|
|
auto set_op = std::make_shared<plan::SetProperties>(optional, n.sym_, n_ident, plan::SetProperties::Op::REPLACE);
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*set_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, UpdateSetPropertiesFromMap) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex. ( {property: 43})
|
|
auto vertex_accessor = dba.InsertVertex();
|
|
auto old_value = vertex_accessor.SetProperty(dba.NameToProperty("property"), memgraph::storage::PropertyValue{43});
|
|
EXPECT_EQ(old_value.HasError(), false);
|
|
EXPECT_EQ(*old_value, memgraph::storage::PropertyValue());
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
// MATCH (n) SET n += {property: "updated", new_property:"a"}
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
auto prop_property = PROPERTY_PAIR(dba, "property");
|
|
auto prop_new_property = PROPERTY_PAIR(dba, "new_property");
|
|
|
|
std::unordered_map<PropertyIx, Expression *> prop_map;
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop_property.first), LITERAL("updated"));
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop_new_property.first), LITERAL("a"));
|
|
auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
|
|
|
|
auto op_type{plan::SetProperties::Op::UPDATE};
|
|
auto set_op = std::make_shared<plan::SetProperties>(n.op_, n.sym_, rhs, op_type);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*set_op, &context);
|
|
dba.AdvanceCommand();
|
|
auto new_properties = vertex_accessor.Properties(memgraph::storage::View::OLD);
|
|
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> expected_properties;
|
|
expected_properties.emplace(dba.NameToProperty("property"), memgraph::storage::PropertyValue("updated"));
|
|
expected_properties.emplace(dba.NameToProperty("new_property"), memgraph::storage::PropertyValue("a"));
|
|
EXPECT_EQ(new_properties.HasError(), false);
|
|
EXPECT_EQ(*new_properties, expected_properties);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetPropertiesFromMapWithCaching) {
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
|
|
// Add a single vertex. ({prop1: 43, prop2: 44})
|
|
auto vertex_accessor = dba.InsertVertex();
|
|
auto old_value = vertex_accessor.SetProperty(dba.NameToProperty("prop1"), memgraph::storage::PropertyValue{43});
|
|
old_value = vertex_accessor.SetProperty(dba.NameToProperty("prop2"), memgraph::storage::PropertyValue{44});
|
|
EXPECT_EQ(old_value.HasError(), false);
|
|
EXPECT_EQ(*old_value, memgraph::storage::PropertyValue());
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
|
|
SymbolTable symbol_table;
|
|
// MATCH (n) SET n += {new_prop1: n.prop1, new_prop2: n.prop2};
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto prop_new_prop1 = PROPERTY_PAIR(dba, "new_prop1");
|
|
auto prop_new_prop2 = PROPERTY_PAIR(dba, "new_prop2");
|
|
std::unordered_map<PropertyIx, Expression *> prop_map;
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop_new_prop1.first), LITERAL(43));
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop_new_prop2.first), LITERAL(44));
|
|
auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
|
|
|
|
auto op_type{plan::SetProperties::Op::UPDATE};
|
|
auto set_op = std::make_shared<plan::SetProperties>(n.op_, n.sym_, rhs, op_type);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
PullAll(*set_op, &context);
|
|
dba.AdvanceCommand();
|
|
|
|
auto new_properties = vertex_accessor.Properties(memgraph::storage::View::OLD);
|
|
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> expected_properties;
|
|
expected_properties.emplace(dba.NameToProperty("prop1"), memgraph::storage::PropertyValue(43));
|
|
expected_properties.emplace(dba.NameToProperty("prop2"), memgraph::storage::PropertyValue(44));
|
|
expected_properties.emplace(dba.NameToProperty("new_prop1"), memgraph::storage::PropertyValue(43));
|
|
expected_properties.emplace(dba.NameToProperty("new_prop2"), memgraph::storage::PropertyValue(44));
|
|
EXPECT_EQ(new_properties.HasError(), false);
|
|
EXPECT_EQ(*new_properties, expected_properties);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, SetLabelsOnNull) {
|
|
// OPTIONAL MATCH (n) SET n :label
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label = dba.NameToLabel("label");
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
|
|
auto set_op = std::make_shared<plan::SetLabels>(optional, n.sym_, std::vector<memgraph::storage::LabelId>{label});
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*set_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, RemovePropertyOnNull) {
|
|
// REMOVE (Null).prop
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
SymbolTable symbol_table;
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto null = LITERAL(TypedValue());
|
|
auto n_prop = PROPERTY_LOOKUP(dba, null, prop);
|
|
auto once = std::make_shared<Once>();
|
|
auto remove_op = std::make_shared<plan::RemoveProperty>(once, prop.second, n_prop);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*remove_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, RemoveLabelsOnNull) {
|
|
// OPTIONAL MATCH (n) REMOVE n :label
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
auto label = dba.NameToLabel("label");
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
|
|
auto remove_op =
|
|
std::make_shared<plan::RemoveLabels>(optional, n.sym_, std::vector<memgraph::storage::LabelId>{label});
|
|
EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
EXPECT_EQ(1, PullAll(*remove_op, &context));
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteSetProperty) {
|
|
// MATCH (n) DELETE n SET n.property = 42 RETURN n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex.
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto n_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto set_op = std::make_shared<plan::SetProperty>(delete_op, prop.second, n_prop, LITERAL(42));
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(set_op, set_op->ModifiedSymbols(symbol_table), true);
|
|
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteSetPropertiesFromMap) {
|
|
// MATCH (n) DELETE n SET n = {property: 42} return n
|
|
// MATCH (n) DELETE n SET n += {property: 42} return n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex.
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
std::unordered_map<PropertyIx, Expression *> prop_map;
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(42));
|
|
auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
|
|
for (auto op_type : {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
|
|
auto set_op = std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(set_op, set_op->ModifiedSymbols(symbol_table), false);
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteSetPropertiesFrom) {
|
|
// MATCH (n) DELETE n SET n = n RETURN n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex.
|
|
{
|
|
auto v = dba.InsertVertex();
|
|
ASSERT_TRUE(v.SetProperty(dba.NameToProperty("property"), memgraph::storage::PropertyValue(1)).HasValue());
|
|
}
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto *rhs = IDENT("n")->MapTo(n.sym_);
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
for (auto op_type : {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
|
|
auto set_op = std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(set_op, set_op->ModifiedSymbols(symbol_table), false);
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteRemoveLabels) {
|
|
// MATCH (n) DELETE n REMOVE n :label return n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex.
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
std::vector<memgraph::storage::LabelId> labels{dba.NameToLabel("label")};
|
|
auto rem_op = std::make_shared<plan::RemoveLabels>(delete_op, n.sym_, labels);
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(rem_op, rem_op->ModifiedSymbols(symbol_table), true);
|
|
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
|
|
TYPED_TEST(QueryPlanTest, DeleteRemoveProperty) {
|
|
// MATCH (n) DELETE n REMOVE n.property RETURN n
|
|
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
|
|
memgraph::query::DbAccessor dba(storage_dba.get());
|
|
// Add a single vertex.
|
|
dba.InsertVertex();
|
|
dba.AdvanceCommand();
|
|
EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
|
|
SymbolTable symbol_table;
|
|
auto n = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
|
|
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
|
|
auto prop = PROPERTY_PAIR(dba, "property");
|
|
auto n_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto rem_op = std::make_shared<plan::RemoveProperty>(delete_op, prop.second, n_prop);
|
|
auto accumulate_op = std::make_shared<plan::Accumulate>(rem_op, rem_op->ModifiedSymbols(symbol_table), true);
|
|
|
|
auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
|
|
auto n_p =
|
|
this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
|
|
auto produce = MakeProduce(accumulate_op, n_p);
|
|
auto context = MakeContext(this->storage, symbol_table, &dba);
|
|
ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
//// FINE GRAINED AUTHORIZATION /////
|
|
//////////////////////////////////////////////
|
|
#ifdef MG_ENTERPRISE
|
|
template <typename StorageType>
|
|
class UpdatePropertiesWithAuthFixture : public QueryPlanTest<StorageType> {
|
|
protected:
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
|
|
const std::string vertex_label_name = "l1";
|
|
const memgraph::storage::LabelId vertex_label = dba.NameToLabel(vertex_label_name);
|
|
|
|
const std::string entity_prop_name = "prop";
|
|
const memgraph::storage::PropertyId entity_prop{dba.NameToProperty(entity_prop_name)};
|
|
const memgraph::storage::PropertyValue entity_prop_value{1};
|
|
|
|
const std::string label_name_1 = "l1";
|
|
const memgraph::storage::LabelId label_1{dba.NameToLabel(label_name_1)};
|
|
const std::string label_name_2 = "l1";
|
|
const memgraph::storage::LabelId label_2{dba.NameToLabel(label_name_2)};
|
|
|
|
const std::string edge_prop_name = "prop";
|
|
const memgraph::storage::PropertyId edge_prop{dba.NameToProperty(edge_prop_name)};
|
|
const memgraph::storage::PropertyValue edge_prop_value{1};
|
|
|
|
void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
|
|
|
|
void SetVertexProperty(memgraph::query::VertexAccessor vertex) {
|
|
static_cast<void>(vertex.SetProperty(entity_prop, entity_prop_value));
|
|
}
|
|
|
|
void SetEdgeProperty(memgraph::query::EdgeAccessor edge) {
|
|
static_cast<void>(edge.SetProperty(entity_prop, entity_prop_value));
|
|
}
|
|
|
|
void ExecuteSetPropertyOnVertex(memgraph::auth::User user, int new_property_value) {
|
|
// MATCH (n) SET n.prop = 2
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
auto literal = LITERAL(new_property_value);
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), entity_prop);
|
|
auto set_property = std::make_shared<plan::SetProperty>(scan_all.op_, entity_prop, n_p, literal);
|
|
|
|
// produce the node
|
|
auto output =
|
|
NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
|
|
auto produce = MakeProduce(set_property, output);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*produce, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteSetPropertyOnEdge(memgraph::auth::User user, int new_property_value) {
|
|
// MATCH (n)-[r]->(m) SET r.prop = 2
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
|
|
{}, "m", false, memgraph::storage::View::OLD);
|
|
// set property to 2 on n
|
|
auto literal = LITERAL(new_property_value);
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(expand.edge_sym_), entity_prop);
|
|
auto set_property = std::make_shared<plan::SetProperty>(expand.op_, entity_prop, n_p, literal);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*set_property, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteSetPropertiesOnVertex(memgraph::auth::User user, int new_property_value) {
|
|
// MATCH (n) SET n = {prop: 2};
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
auto prop = PROPERTY_PAIR(dba, entity_prop_name);
|
|
std::unordered_map<PropertyIx, Expression *> prop_map;
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
|
|
auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
|
|
auto set_properties =
|
|
std::make_shared<plan::SetProperties>(scan_all.op_, scan_all.sym_, rhs, plan::SetProperties::Op::UPDATE);
|
|
|
|
auto output =
|
|
NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
|
|
auto produce = MakeProduce(set_properties, output);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*produce, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteSetPropertiesOnEdge(memgraph::auth::User user, int new_property_value) {
|
|
// MATCH (n)-[r]->(m) SET r = {prop: 2};
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
|
|
{}, "m", false, memgraph::storage::View::OLD);
|
|
|
|
auto prop = PROPERTY_PAIR(dba, entity_prop_name);
|
|
std::unordered_map<PropertyIx, Expression *> prop_map;
|
|
prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
|
|
auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
|
|
auto set_properties =
|
|
std::make_shared<plan::SetProperties>(expand.op_, expand.edge_sym_, rhs, plan::SetProperties::Op::UPDATE);
|
|
|
|
auto output =
|
|
NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
|
|
auto produce = MakeProduce(set_properties, output);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*produce, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteRemovePropertyOnVertex(memgraph::auth::User user) {
|
|
// MATCH (n) REMOVE n.prop
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), entity_prop);
|
|
auto remove_property = std::make_shared<plan::RemoveProperty>(scan_all.op_, entity_prop, n_p);
|
|
|
|
// produce the node
|
|
auto output =
|
|
NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
|
|
auto produce = MakeProduce(remove_property, output);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*produce, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
|
|
void ExecuteRemovePropertyOnEdge(memgraph::auth::User user) {
|
|
// MATCH (n)-[r]->(m) REMOVE r.prop
|
|
auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
|
|
auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
|
|
{}, "m", false, memgraph::storage::View::OLD);
|
|
// set property to 2 on n
|
|
auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(expand.edge_sym_), entity_prop);
|
|
auto remove_property = std::make_shared<plan::RemoveProperty>(expand.op_, entity_prop, n_p);
|
|
|
|
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
|
|
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
|
|
|
|
PullAll(*remove_property, &context);
|
|
dba.AdvanceCommand();
|
|
};
|
|
};
|
|
|
|
TYPED_TEST_CASE(UpdatePropertiesWithAuthFixture, StorageTypes);
|
|
|
|
TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
|
|
// Add a single vertex
|
|
auto v = this->dba.InsertVertex();
|
|
ASSERT_TRUE(v.AddLabel(this->vertex_label).HasValue());
|
|
ASSERT_TRUE(v.SetProperty(this->entity_prop, this->entity_prop_value).HasValue());
|
|
this->dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(1, CountIterable(this->dba.Vertices(memgraph::storage::View::OLD)));
|
|
|
|
auto test_hypothesis = [&](int expected_property_value) {
|
|
auto vertex = *this->dba.Vertices(memgraph::storage::View::NEW).begin();
|
|
auto maybe_properties = vertex.Properties(memgraph::storage::View::NEW);
|
|
ASSERT_TRUE(maybe_properties.HasValue());
|
|
const auto &properties = *maybe_properties;
|
|
EXPECT_EQ(properties.size(), 1);
|
|
auto maybe_prop = vertex.GetProperty(memgraph::storage::View::NEW, this->entity_prop);
|
|
ASSERT_TRUE(maybe_prop.HasValue());
|
|
ASSERT_EQ(maybe_prop->ValueInt(), expected_property_value);
|
|
};
|
|
|
|
auto test_remove_hypothesis = [&](int properties_size) {
|
|
auto vertex = *this->dba.Vertices(memgraph::storage::View::NEW).begin();
|
|
auto maybe_properties = vertex.Properties(memgraph::storage::View::NEW);
|
|
ASSERT_TRUE(maybe_properties.HasValue());
|
|
const auto &properties = *maybe_properties;
|
|
EXPECT_EQ(properties.size(), properties_size);
|
|
};
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"denied_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"denied_label"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_label"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_read_label"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::READ);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
ASSERT_THROW(this->ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_update_label_denied_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_update_global_denied_read_label"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_create_delete_label_denied_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_create_delete_global_denied_read_label"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
user.fine_grained_access_handler().label_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertyOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteSetPropertiesOnVertex(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetVertexProperty(v);
|
|
this->ExecuteRemovePropertyOnVertex(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
|
|
// Add a single vertex
|
|
auto v1 = this->dba.InsertVertex();
|
|
ASSERT_TRUE(v1.AddLabel(this->label_1).HasValue());
|
|
|
|
auto v2 = this->dba.InsertVertex();
|
|
ASSERT_TRUE(v2.AddLabel(this->label_2).HasValue());
|
|
|
|
auto edge_type_name = "edge_type";
|
|
auto edge_type_id = this->dba.NameToEdgeType(edge_type_name);
|
|
auto edge = this->dba.InsertEdge(&v1, &v2, edge_type_id);
|
|
ASSERT_TRUE(edge.HasValue());
|
|
ASSERT_TRUE(edge->SetProperty(this->entity_prop, this->entity_prop_value).HasValue());
|
|
this->dba.AdvanceCommand();
|
|
|
|
EXPECT_EQ(2, CountIterable(this->dba.Vertices(memgraph::storage::View::OLD)));
|
|
|
|
auto test_hypothesis = [&](int expected_property_value) {
|
|
for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
|
|
if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
|
|
for (auto edge : maybe_edges->edges) {
|
|
EXPECT_EQ(edge.EdgeType(), edge_type_id);
|
|
auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);
|
|
ASSERT_TRUE(maybe_properties.HasValue());
|
|
const auto &properties = *maybe_properties;
|
|
EXPECT_EQ(properties.size(), 1);
|
|
auto maybe_prop = edge.GetProperty(memgraph::storage::View::NEW, this->entity_prop);
|
|
ASSERT_TRUE(maybe_prop.HasValue());
|
|
ASSERT_EQ(maybe_prop->ValueInt(), expected_property_value);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
auto test_remove_hypothesis = [&](int properties_size) {
|
|
for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
|
|
if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
|
|
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
|
|
for (auto edge : maybe_edges->edges) {
|
|
EXPECT_EQ(edge.EdgeType(), edge_type_id);
|
|
auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);
|
|
ASSERT_TRUE(maybe_properties.HasValue());
|
|
const auto &properties = *maybe_properties;
|
|
EXPECT_EQ(properties.size(), properties_size);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"denied_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"denied_edge_type"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_edge_type"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_update_edge_type_denied_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_read_edge_type"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::READ);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
ASSERT_THROW(this->ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_update_global_denied_read_edge_type"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::UPDATE);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_create_delete_edge_type_denied_read_global"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
edge_type_name, memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(2);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(0);
|
|
}
|
|
|
|
{
|
|
auto user = memgraph::auth::User{"granted_create_delete_global_denied_read_edge_type"};
|
|
|
|
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
|
|
memgraph::auth::FineGrainedPermission::NOTHING);
|
|
user.fine_grained_access_handler().edge_type_permissions().Grant(
|
|
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertyOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteSetPropertiesOnEdge(user, 2);
|
|
test_hypothesis(1);
|
|
|
|
this->SetEdgeProperty(edge.GetValue());
|
|
this->ExecuteRemovePropertyOnEdge(user);
|
|
test_remove_hypothesis(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template <typename StorageType>
|
|
class DynamicExpandFixture : public testing::Test {
|
|
protected:
|
|
const std::string testSuite = "query_plan_create_set_remove_delete_dynamic_expand";
|
|
memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
|
|
std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
|
|
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access(ReplicationRole::MAIN)};
|
|
memgraph::query::DbAccessor dba{storage_dba.get()};
|
|
SymbolTable symbol_table;
|
|
AstStorage storage;
|
|
|
|
// make 2 nodes connected to the third node
|
|
memgraph::query::VertexAccessor v1{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v2{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v3{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v4{dba.InsertVertex()};
|
|
memgraph::query::VertexAccessor v5{dba.InsertVertex()};
|
|
memgraph::storage::EdgeTypeId edge_type{db->NameToEdgeType("Edge")};
|
|
memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v5, edge_type)};
|
|
memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&v2, &v5, edge_type)};
|
|
memgraph::query::EdgeAccessor r3{*dba.InsertEdge(&v3, &v5, edge_type)};
|
|
memgraph::query::EdgeAccessor r4{*dba.InsertEdge(&v4, &v5, edge_type)};
|
|
|
|
memgraph::storage::LabelId node_label{dba.NameToLabel("Node")};
|
|
memgraph::storage::LabelId supernode_label{dba.NameToLabel("Supernode")};
|
|
|
|
void SetUp() override {
|
|
ASSERT_TRUE(v1.AddLabel(node_label).HasValue());
|
|
ASSERT_TRUE(v2.AddLabel(node_label).HasValue());
|
|
ASSERT_TRUE(v3.AddLabel(node_label).HasValue());
|
|
ASSERT_TRUE(v4.AddLabel(node_label).HasValue());
|
|
ASSERT_TRUE(v5.AddLabel(supernode_label).HasValue());
|
|
memgraph::license::global_license_checker.EnableTesting();
|
|
|
|
dba.AdvanceCommand();
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
|
|
disk_test_utils::RemoveRocksDbDirs(testSuite);
|
|
}
|
|
}
|
|
};
|
|
|
|
using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
|
|
TYPED_TEST_CASE(DynamicExpandFixture, StorageTypes);
|
|
|
|
TYPED_TEST(DynamicExpandFixture, Expand) {
|
|
using ExpandCursor = memgraph::query::plan::Expand::ExpandCursor;
|
|
|
|
auto scan_node_by_label = MakeScanAllByLabel(this->storage, this->symbol_table, "n", this->node_label);
|
|
auto scan_supernode_by_label =
|
|
MakeScanAllByLabel(this->storage, this->symbol_table, "s", this->supernode_label, scan_node_by_label.op_);
|
|
|
|
auto once = std::make_shared<Once>();
|
|
|
|
auto edge_sym = this->symbol_table.CreateSymbol("r", true);
|
|
auto my_expand = std::make_shared<Expand>(
|
|
scan_supernode_by_label.op_, scan_supernode_by_label.sym_, scan_node_by_label.sym_, edge_sym,
|
|
EdgeAtom::Direction::OUT, std::vector<memgraph::storage::EdgeTypeId>{}, true, memgraph::storage::View::OLD);
|
|
|
|
auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
|
|
|
|
Frame frame{context.symbol_table.max_position()};
|
|
frame[scan_supernode_by_label.sym_] = this->v4;
|
|
frame[scan_node_by_label.sym_] = this->v1;
|
|
|
|
auto *mem = memgraph::utils::NewDeleteResource();
|
|
auto initial_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, -1, -1, mem);
|
|
auto *initial_cursor = dynamic_cast<ExpandCursor *>(initial_cursor_ptr.get());
|
|
auto expansion_info = initial_cursor->GetExpansionInfo(frame);
|
|
|
|
ASSERT_EQ(expansion_info.input_node.value(), this->v4);
|
|
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::OUT);
|
|
ASSERT_EQ(expansion_info.existing_node.value(), this->v1);
|
|
|
|
auto expanded_first_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 1, -1, mem);
|
|
auto *expanded_first_cursor = dynamic_cast<ExpandCursor *>(expanded_first_cursor_ptr.get());
|
|
expansion_info = expanded_first_cursor->GetExpansionInfo(frame);
|
|
|
|
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
|
|
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
|
|
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
|
|
|
|
auto expanded_both_take_first_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 1, 100, mem);
|
|
auto *expanded_both_take_first = dynamic_cast<ExpandCursor *>(expanded_both_take_first_cursor_ptr.get());
|
|
expansion_info = expanded_both_take_first->GetExpansionInfo(frame);
|
|
|
|
ASSERT_EQ(expansion_info.input_node.value(), this->v4);
|
|
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::OUT);
|
|
ASSERT_EQ(expansion_info.existing_node.value(), this->v1);
|
|
|
|
auto expanded_both_take_second_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 100, 1, mem);
|
|
auto *expanded_both_take_second = dynamic_cast<ExpandCursor *>(expanded_both_take_second_cursor_ptr.get());
|
|
expansion_info = expanded_both_take_second->GetExpansionInfo(frame);
|
|
|
|
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
|
|
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
|
|
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
|
|
|
|
auto expanded_equal_take_second_cursror_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 5, 5, mem);
|
|
auto *expanded_equal_take_second = dynamic_cast<ExpandCursor *>(expanded_equal_take_second_cursror_ptr.get());
|
|
expansion_info = expanded_equal_take_second->GetExpansionInfo(frame);
|
|
|
|
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
|
|
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
|
|
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
|
|
}
|