0fb8e4116f
* Fix up REPLICA GetInfo and CreateSnapshot Subtle bug where these actions were using the incorrect transactional access while in REPLICA role. This casued timestamp to be incorrectly bumped, breaking REPLICA from doing replication. * Delay DNS resolution Rather than resolve at endpoint creation, we will instread resolve only on Socket connect. This allows k8s deployments to change their IP during pod restarts. * Minor sonarsource fixes --------- Co-authored-by: Andreja <andreja.tonev@memgraph.io> Co-authored-by: DavIvek <david.ivekovic@memgraph.io>
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::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);
|
|
}
|