Added edge filtering to storage, need to add filtering in simple Expand in operator.cpp
This commit is contained in:
@@ -296,10 +296,12 @@ FineGrainedAccessHandler::FineGrainedAccessHandler(const FineGrainedAccessPermis
|
||||
: label_permissions_(labelPermissions), edge_type_permissions_(edgeTypePermissions) {}
|
||||
|
||||
const FineGrainedAccessPermissions &FineGrainedAccessHandler::label_permissions() const { return label_permissions_; }
|
||||
FineGrainedAccessPermissions &FineGrainedAccessHandler::label_permissions() { return label_permissions_; }
|
||||
|
||||
const FineGrainedAccessPermissions &FineGrainedAccessHandler::edge_type_permissions() const {
|
||||
return edge_type_permissions_;
|
||||
}
|
||||
FineGrainedAccessPermissions &FineGrainedAccessHandler::edge_type_permissions() { return edge_type_permissions_; }
|
||||
|
||||
nlohmann::json FineGrainedAccessHandler::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
@@ -312,8 +314,8 @@ FineGrainedAccessHandler FineGrainedAccessHandler::Deserialize(const nlohmann::j
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
if (!data["fine_grained_access_handler"].is_object()) {
|
||||
throw AuthException("Couldn't load FineGrainedAccessHandler data!");
|
||||
if (!data["label_permissions"].is_object() && !data["edge_type_permissions"].is_object()) {
|
||||
throw AuthException("Couldn't load label_permissions or edge_type_permissions data!");
|
||||
}
|
||||
auto label_permissions = FineGrainedAccessPermissions::Deserialize(data["label_permissions"]);
|
||||
auto edge_type_permissions = FineGrainedAccessPermissions::Deserialize(data["edge_type_permissions"]);
|
||||
|
||||
@@ -840,7 +840,7 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
||||
edit_fun(&role->permissions(), permission);
|
||||
}
|
||||
for (const auto &label : labels) {
|
||||
edit_fun(&user->fine_grained_access_handler().edge_type_permissions(), label);
|
||||
edit_fun(&user->fine_grained_access_handler().label_permissions(), label);
|
||||
}
|
||||
for (const auto &edgeType : edgeTypes) {
|
||||
edit_fun(&role->fine_grained_access_handler().edge_type_permissions(), edgeType);
|
||||
|
||||
@@ -11,14 +11,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include <cppitertools/filter.hpp>
|
||||
#include <cppitertools/imap.hpp>
|
||||
|
||||
#include "query/context.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/fine_grained_access_checker.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/result.hpp"
|
||||
@@ -132,34 +131,48 @@ class VertexAccessor final {
|
||||
return impl_.ClearProperties();
|
||||
}
|
||||
|
||||
auto InEdges(storage::View view, const ExecutionContex &execution_context) const
|
||||
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const FineGrainedAccessChecker *fine_grained_access_checker) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
|
||||
auto maybe_edges = impl_.InEdges(view, &execution_context.fine_grained_access_checker);
|
||||
auto maybe_edges = impl_.InEdges(view, edge_types, fine_grained_access_checker);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
auto InEdges(storage::View view) const { return InEdges(view, {}); }
|
||||
|
||||
auto InEdges(storage::View view, const ExecutionContex &execution_context, const VertexAccessor &dest) const
|
||||
auto InEdges(storage::View view) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
|
||||
auto maybe_edges = impl_.InEdges(view, &execution_context.fine_grained_access_checker, &dest.impl_);
|
||||
auto maybe_edges = impl_.InEdges(view, {}, nullptr);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
auto OutEdges(storage::View view, const ExecutionContex &execution_context) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, &execution_context.fine_grained_access_checker);
|
||||
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const FineGrainedAccessChecker *fine_grained_access_checker, const VertexAccessor &dest) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
|
||||
auto maybe_edges = impl_.InEdges(view, edge_types, fine_grained_access_checker, &dest.impl_);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
auto OutEdges(storage::View view) const { return OutEdges(view, {}); }
|
||||
|
||||
auto OutEdges(storage::View view, const ExecutionContex &execution_context, const VertexAccessor &dest) const
|
||||
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const FineGrainedAccessChecker *fine_grained_access_checker) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, &execution_context.fine_grained_access_checker, &dest.impl_);
|
||||
auto maybe_edges = impl_.OutEdges(view, edge_types, fine_grained_access_checker);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
auto OutEdges(storage::View view) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, {}, nullptr);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const FineGrainedAccessChecker *fine_grained_access_checker, const VertexAccessor &dest) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, edge_types, fine_grained_access_checker, &dest.impl_);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
@@ -362,7 +375,6 @@ class DbAccessor final {
|
||||
} // namespace memgraph::query
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::VertexAccessor> {
|
||||
size_t operator()(const memgraph::query::VertexAccessor &v) const { return std::hash<decltype(v.impl_)>{}(v.impl_); }
|
||||
|
||||
@@ -2267,8 +2267,8 @@ cpp<#
|
||||
#>cpp
|
||||
AuthQuery(Action action, std::string user, std::string role,
|
||||
std::string user_or_role, Expression *password,
|
||||
std::vector<Privilege> privileges,
|
||||
std::vector<std::string> labels, std::vector<std::string> edgeTypes)
|
||||
std::vector<Privilege> privileges, std::vector<std::string> labels,
|
||||
std::vector<std::string> edgeTypes)
|
||||
: action_(action),
|
||||
user_(user),
|
||||
role_(role),
|
||||
|
||||
@@ -1298,7 +1298,9 @@ antlrcpp::Any CypherMainVisitor::visitDenyPrivilege(MemgraphCypher::DenyPrivileg
|
||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
||||
if (ctx->privilegeList()) {
|
||||
for (auto *privilege : ctx->privilegeList()->privilege()) {
|
||||
if (privilege->LABELS()) {
|
||||
if (privilege->EDGE_TYPES()) {
|
||||
auth->edgetypes_ = privilege->edgeTypeList()->accept(this).as<std::vector<std::string>>();
|
||||
} else if (privilege->LABELS()) {
|
||||
auth->labels_ = privilege->labelList()->accept(this).as<std::vector<std::string>>();
|
||||
} else {
|
||||
auth->privileges_.push_back(privilege->accept(this));
|
||||
@@ -1320,7 +1322,9 @@ antlrcpp::Any CypherMainVisitor::visitRevokePrivilege(MemgraphCypher::RevokePriv
|
||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
||||
if (ctx->privilegeList()) {
|
||||
for (auto *privilege : ctx->privilegeList()->privilege()) {
|
||||
if (privilege->LABELS()) {
|
||||
if (privilege->EDGE_TYPES()) {
|
||||
auth->edgetypes_ = privilege->edgeTypeList()->accept(this).as<std::vector<std::string>>();
|
||||
} else if (privilege->LABELS()) {
|
||||
auth->labels_ = privilege->labelList()->accept(this).as<std::vector<std::string>>();
|
||||
} else {
|
||||
auth->privileges_.push_back(privilege->accept(this));
|
||||
@@ -1387,7 +1391,6 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
|
||||
if (ctx->MODULE_READ()) return AuthQuery::Privilege::MODULE_READ;
|
||||
if (ctx->MODULE_WRITE()) return AuthQuery::Privilege::MODULE_WRITE;
|
||||
if (ctx->WEBSOCKET()) return AuthQuery::Privilege::WEBSOCKET;
|
||||
if (ctx->EDGE_TYPES()) return AuthQuery::Privilege::EDGE_TYPES;
|
||||
LOG_FATAL("Should not get here - unknown privilege!");
|
||||
}
|
||||
|
||||
|
||||
@@ -262,12 +262,12 @@ privilege : CREATE
|
||||
|
||||
privilegeList : privilege ( ',' privilege )* ;
|
||||
|
||||
|
||||
edgeTypeList : '*' | listOfEdgeTypes ;
|
||||
|
||||
listOfEdgeTypes : edgeType ( ',' edgeType )* ;
|
||||
|
||||
edgeType : COLON symbolicName ;
|
||||
|
||||
labelList : '*' | listOfLabels ;
|
||||
|
||||
listOfLabels : label ( ',' label )* ;
|
||||
|
||||
@@ -206,9 +206,8 @@ const trie::Trie kKeywords = {"union",
|
||||
"version",
|
||||
"websocket",
|
||||
"foreach",
|
||||
"labels",
|
||||
"edge_types"};
|
||||
"labels"
|
||||
};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(
|
||||
|
||||
@@ -319,10 +319,11 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
||||
AuthQuery::Action::REVOKE_PRIVILEGE, AuthQuery::Action::SHOW_PRIVILEGES, AuthQuery::Action::SHOW_USERS_FOR_ROLE,
|
||||
AuthQuery::Action::SHOW_ROLE_FOR_USER};
|
||||
|
||||
if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) {
|
||||
throw utils::BasicException(
|
||||
utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication features"));
|
||||
}
|
||||
// if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) {
|
||||
// throw utils::BasicException(
|
||||
// utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication
|
||||
// features"));
|
||||
// }
|
||||
|
||||
switch (auth_query->action_) {
|
||||
case AuthQuery::Action::CREATE_USER:
|
||||
@@ -336,7 +337,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
||||
// If the license is not valid we create users with admin access
|
||||
if (!valid_enterprise_license) {
|
||||
spdlog::warn("Granting all the privileges to {}.", username);
|
||||
auth->GrantPrivilege(username, kPrivilegesAll, {"*"});
|
||||
auth->GrantPrivilege(username, kPrivilegesAll, {"*"}, {"*"});
|
||||
}
|
||||
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
@@ -968,7 +969,7 @@ PullPlan::PullPlan(const std::shared_ptr<CachedPlan> plan, const Parameters &par
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (username.has_value()) {
|
||||
memgraph::auth::User *user = interpreter_context->auth->GetUser(*username);
|
||||
ctx_.fine_grained_access_checker = new FineGrainedAccessChecker{user};
|
||||
ctx_.fine_grained_access_checker = new FineGrainedAccessChecker{user, dba};
|
||||
}
|
||||
#endif
|
||||
if (interpreter_context->config.execution_timeout_sec > 0) {
|
||||
|
||||
@@ -739,11 +739,13 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
|
||||
// old_node_value may be Null when using optional matching
|
||||
if (!existing_node.IsNull()) {
|
||||
ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
|
||||
in_edges_.emplace(UnwrapEdgesResult(
|
||||
vertex.InEdges(self_.view_, context.fine_grained_access_checker, existing_node.ValueVertex())));
|
||||
in_edges_.emplace(
|
||||
UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types,
|
||||
context.fine_grained_access_checker, existing_node.ValueVertex())));
|
||||
}
|
||||
} else {
|
||||
in_edges_.emplace(UnwrapEdgesResult(vertex.InEdges(self_.view_, context.fine_grained_access_checker)));
|
||||
in_edges_.emplace(UnwrapEdgesResult(
|
||||
vertex.InEdges(self_.view_, self_.common_.edge_types, context.fine_grained_access_checker)));
|
||||
}
|
||||
if (in_edges_) {
|
||||
in_edges_it_.emplace(in_edges_->begin());
|
||||
@@ -756,11 +758,13 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
|
||||
// old_node_value may be Null when using optional matching
|
||||
if (!existing_node.IsNull()) {
|
||||
ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
|
||||
out_edges_.emplace(UnwrapEdgesResult(
|
||||
vertex.OutEdges(self_.view_, context.fine_grained_access_checker, existing_node.ValueVertex())));
|
||||
out_edges_.emplace(
|
||||
UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types,
|
||||
context.fine_grained_access_checker, existing_node.ValueVertex())));
|
||||
}
|
||||
} else {
|
||||
out_edges_.emplace(UnwrapEdgesResult(vertex.OutEdges(self_.view_, context.fine_grained_access_checker)));
|
||||
out_edges_.emplace(UnwrapEdgesResult(
|
||||
vertex.OutEdges(self_.view_, self_.common_.edge_types, context.fine_grained_access_checker)));
|
||||
}
|
||||
if (out_edges_) {
|
||||
out_edges_it_.emplace(out_edges_->begin());
|
||||
@@ -817,7 +821,8 @@ namespace {
|
||||
* @return See above.
|
||||
*/
|
||||
auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction direction,
|
||||
const std::vector<storage::EdgeTypeId> &edge_types, utils::MemoryResource *memory) {
|
||||
const std::vector<storage::EdgeTypeId> &edge_types, utils::MemoryResource *memory,
|
||||
const ExecutionContext &context) {
|
||||
// wraps an EdgeAccessor into a pair <accessor, direction>
|
||||
auto wrapper = [](EdgeAtom::Direction direction, auto &&edges) {
|
||||
return iter::imap([direction](const auto &edge) { return std::make_pair(edge, direction); },
|
||||
@@ -825,16 +830,18 @@ auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction directio
|
||||
};
|
||||
|
||||
storage::View view = storage::View::OLD;
|
||||
utils::pmr::vector<decltype(wrapper(direction, *vertex.InEdges(view, edge_types)))> chain_elements(memory);
|
||||
utils::pmr::vector<decltype(wrapper(direction,
|
||||
*vertex.InEdges(view, edge_types, context.fine_grained_access_checker)))>
|
||||
chain_elements(memory);
|
||||
|
||||
if (direction != EdgeAtom::Direction::OUT) {
|
||||
auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types));
|
||||
auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types, context.fine_grained_access_checker));
|
||||
if (edges.begin() != edges.end()) {
|
||||
chain_elements.emplace_back(wrapper(EdgeAtom::Direction::IN, std::move(edges)));
|
||||
}
|
||||
}
|
||||
if (direction != EdgeAtom::Direction::IN) {
|
||||
auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types));
|
||||
auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types, context.fine_grained_access_checker));
|
||||
if (edges.begin() != edges.end()) {
|
||||
chain_elements.emplace_back(wrapper(EdgeAtom::Direction::OUT, std::move(edges)));
|
||||
}
|
||||
@@ -899,8 +906,9 @@ class ExpandVariableCursor : public Cursor {
|
||||
|
||||
// a stack of edge iterables corresponding to the level/depth of
|
||||
// the expansion currently being Pulled
|
||||
using ExpandEdges = decltype(ExpandFromVertex(std::declval<VertexAccessor>(), EdgeAtom::Direction::IN,
|
||||
self_.common_.edge_types, utils::NewDeleteResource()));
|
||||
using ExpandEdges =
|
||||
decltype(ExpandFromVertex(std::declval<VertexAccessor>(), EdgeAtom::Direction::IN, self_.common_.edge_types,
|
||||
utils::NewDeleteResource(), std::declval<ExecutionContext>()));
|
||||
|
||||
utils::pmr::vector<ExpandEdges> edges_;
|
||||
// an iterator indicating the position in the corresponding edges_ element
|
||||
@@ -941,7 +949,8 @@ class ExpandVariableCursor : public Cursor {
|
||||
|
||||
if (upper_bound_ > 0) {
|
||||
auto *memory = edges_.get_allocator().GetMemoryResource();
|
||||
edges_.emplace_back(ExpandFromVertex(vertex, self_.common_.direction, self_.common_.edge_types, memory));
|
||||
edges_.emplace_back(
|
||||
ExpandFromVertex(vertex, self_.common_.direction, self_.common_.edge_types, memory, context));
|
||||
edges_it_.emplace_back(edges_.back().begin());
|
||||
}
|
||||
|
||||
@@ -1041,7 +1050,7 @@ class ExpandVariableCursor : public Cursor {
|
||||
if (upper_bound_ > static_cast<int64_t>(edges_.size())) {
|
||||
auto *memory = edges_.get_allocator().GetMemoryResource();
|
||||
edges_.emplace_back(
|
||||
ExpandFromVertex(current_vertex, self_.common_.direction, self_.common_.edge_types, memory));
|
||||
ExpandFromVertex(current_vertex, self_.common_.direction, self_.common_.edge_types, memory, context));
|
||||
edges_it_.emplace_back(edges_.back().begin());
|
||||
}
|
||||
|
||||
@@ -1184,7 +1193,8 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
|
||||
for (const auto &vertex : source_frontier) {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto out_edges = UnwrapEdgesResult(
|
||||
vertex.OutEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : out_edges) {
|
||||
if (ShouldExpand(edge.To(), edge, frame, evaluator) && !Contains(in_edge, edge.To())) {
|
||||
in_edge.emplace(edge.To(), edge);
|
||||
@@ -1201,7 +1211,8 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
}
|
||||
}
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto in_edges = UnwrapEdgesResult(
|
||||
vertex.InEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : in_edges) {
|
||||
if (ShouldExpand(edge.From(), edge, frame, evaluator) && !Contains(in_edge, edge.From())) {
|
||||
in_edge.emplace(edge.From(), edge);
|
||||
@@ -1232,7 +1243,8 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
// reversed.
|
||||
for (const auto &vertex : sink_frontier) {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto out_edges = UnwrapEdgesResult(
|
||||
vertex.OutEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : out_edges) {
|
||||
if (ShouldExpand(vertex, edge, frame, evaluator) && !Contains(out_edge, edge.To())) {
|
||||
out_edge.emplace(edge.To(), edge);
|
||||
@@ -1249,7 +1261,8 @@ class STShortestPathCursor : public query::plan::Cursor {
|
||||
}
|
||||
}
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto in_edges = UnwrapEdgesResult(
|
||||
vertex.InEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : in_edges) {
|
||||
if (ShouldExpand(vertex, edge, frame, evaluator) && !Contains(out_edge, edge.From())) {
|
||||
out_edge.emplace(edge.From(), edge);
|
||||
@@ -1323,13 +1336,15 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
// populates the to_visit_next_ structure with expansions
|
||||
// from the given vertex. skips expansions that don't satisfy
|
||||
// the "where" condition.
|
||||
auto expand_from_vertex = [this, &expand_pair](const auto &vertex) {
|
||||
auto expand_from_vertex = [this, &expand_pair, &context](const auto &vertex) {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto out_edges = UnwrapEdgesResult(
|
||||
vertex.OutEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : out_edges) expand_pair(edge, edge.To());
|
||||
}
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto in_edges = UnwrapEdgesResult(
|
||||
vertex.InEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : in_edges) expand_pair(edge, edge.From());
|
||||
}
|
||||
};
|
||||
@@ -1505,16 +1520,18 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
// Populates the priority queue structure with expansions
|
||||
// from the given vertex. skips expansions that don't satisfy
|
||||
// the "where" condition.
|
||||
auto expand_from_vertex = [this, &expand_pair](const VertexAccessor &vertex, const TypedValue &weight,
|
||||
int64_t depth) {
|
||||
auto expand_from_vertex = [this, &expand_pair, &context](const VertexAccessor &vertex, const TypedValue &weight,
|
||||
int64_t depth) {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto out_edges = UnwrapEdgesResult(
|
||||
vertex.OutEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : out_edges) {
|
||||
expand_pair(edge, edge.To(), weight, depth);
|
||||
}
|
||||
}
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
|
||||
auto in_edges = UnwrapEdgesResult(
|
||||
vertex.InEdges(storage::View::OLD, self_.common_.edge_types, context.fine_grained_access_checker));
|
||||
for (const auto &edge : in_edges) {
|
||||
expand_pair(edge, edge.From(), weight, depth);
|
||||
}
|
||||
|
||||
@@ -403,8 +403,9 @@ uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *
|
||||
if (!from_vertex) throw utils::BasicException("Invalid transaction!");
|
||||
auto to_vertex = transaction->FindVertex(delta.edge_create_delete.to_vertex, storage::View::NEW);
|
||||
if (!to_vertex) throw utils::BasicException("Invalid transaction!");
|
||||
auto edges = from_vertex->OutEdges(
|
||||
storage::View::NEW, {transaction->NameToEdgeType(delta.edge_create_delete.edge_type)}, &*to_vertex);
|
||||
auto edges =
|
||||
from_vertex->OutEdges(storage::View::NEW, {transaction->NameToEdgeType(delta.edge_create_delete.edge_type)},
|
||||
nullptr, &*to_vertex);
|
||||
if (edges.HasError()) throw utils::BasicException("Invalid transaction!");
|
||||
if (edges->size() != 1) throw utils::BasicException("Invalid transaction!");
|
||||
auto &edge = (*edges)[0];
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "query/context.hpp"
|
||||
#include "query/fine_grained_access_checker.hpp"
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
@@ -342,8 +341,8 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
|
||||
}
|
||||
|
||||
Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
View view, const query::FineGrainedAccessChecker *fine_grained_access_checker,
|
||||
const VertexAccessor *destination) const {
|
||||
View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const query::FineGrainedAccessChecker *fine_grained_access_checker, const VertexAccessor *destination) const {
|
||||
MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!");
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
@@ -358,62 +357,71 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
for (const auto &item : vertex_->in_edges) {
|
||||
const auto &[edge_type, from_vertex, edge] = item;
|
||||
if (destination && from_vertex != destination->vertex_) continue;
|
||||
if (!fine_grained_access_checker && fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(from_vertex->labels))
|
||||
if (!edge_types.empty() && std::find(edge_types.begin(), edge_types.end(), edge_type) == edge_types.end())
|
||||
continue;
|
||||
if (fine_grained_access_checker && (!fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(from_vertex->labels)))
|
||||
continue;
|
||||
in_edges.push_back(item);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&exists, &deleted, &in_edges, &fine_grained_access_checker, &destination](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_IN_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!fine_grained_access_checker &&
|
||||
fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels))
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{
|
||||
delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it == in_edges.end(), "Invalid database state!");
|
||||
in_edges.push_back(link);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_IN_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!fine_grained_access_checker &&
|
||||
fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels))
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{
|
||||
delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it != in_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *in_edges.rbegin());
|
||||
in_edges.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &in_edges, &edge_types, &fine_grained_access_checker, &destination](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_IN_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!edge_types.empty() &&
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
if (fine_grained_access_checker &&
|
||||
(!fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)))
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex,
|
||||
delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it == in_edges.end(), "Invalid database state!");
|
||||
in_edges.push_back(link);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_IN_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!edge_types.empty() &&
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
if (fine_grained_access_checker &&
|
||||
(!fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)))
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex,
|
||||
delta.vertex_edge.edge};
|
||||
auto it = std::find(in_edges.begin(), in_edges.end(), link);
|
||||
MG_ASSERT(it != in_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *in_edges.rbegin());
|
||||
in_edges.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
std::vector<EdgeAccessor> ret;
|
||||
@@ -426,8 +434,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(
|
||||
}
|
||||
|
||||
Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
View view, const query::FineGrainedAccessChecker *fine_grained_access_checker,
|
||||
const VertexAccessor *destination) const {
|
||||
View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const query::FineGrainedAccessChecker *fine_grained_access_checker, const VertexAccessor *destination) const {
|
||||
MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!");
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
@@ -442,62 +450,71 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(
|
||||
for (const auto &item : vertex_->out_edges) {
|
||||
const auto &[edge_type, to_vertex, edge] = item;
|
||||
if (destination && to_vertex != destination->vertex_) continue;
|
||||
if (!fine_grained_access_checker && fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(to_vertex->labels))
|
||||
if (!edge_types.empty() && std::find(edge_types.begin(), edge_types.end(), edge_type) == edge_types.end())
|
||||
continue;
|
||||
if (fine_grained_access_checker && (!fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(to_vertex->labels)))
|
||||
continue;
|
||||
out_edges.push_back(item);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&exists, &deleted, &out_edges, &fine_grained_access_checker, &destination](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_OUT_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!fine_grained_access_checker &&
|
||||
fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels))
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{
|
||||
delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it == out_edges.end(), "Invalid database state!");
|
||||
out_edges.push_back(link);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_OUT_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!fine_grained_access_checker &&
|
||||
fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) &&
|
||||
fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels))
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{
|
||||
delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it != out_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *out_edges.rbegin());
|
||||
out_edges.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view,
|
||||
[&exists, &deleted, &out_edges, &edge_types, &fine_grained_access_checker, &destination](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_OUT_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!edge_types.empty() &&
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
if (fine_grained_access_checker &&
|
||||
(!fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)))
|
||||
break;
|
||||
// Add the edge because we don't see the removal.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex,
|
||||
delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it == out_edges.end(), "Invalid database state!");
|
||||
out_edges.push_back(link);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_OUT_EDGE: {
|
||||
if (destination && delta.vertex_edge.vertex != destination->vertex_) break;
|
||||
if (!edge_types.empty() &&
|
||||
std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end())
|
||||
break;
|
||||
if (fine_grained_access_checker &&
|
||||
(!fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) ||
|
||||
!fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)))
|
||||
break;
|
||||
// Remove the label because we don't see the addition.
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex,
|
||||
delta.vertex_edge.edge};
|
||||
auto it = std::find(out_edges.begin(), out_edges.end(), link);
|
||||
MG_ASSERT(it != out_edges.end(), "Invalid database state!");
|
||||
std::swap(*it, *out_edges.rbegin());
|
||||
out_edges.pop_back();
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!exists) return Error::NONEXISTENT_OBJECT;
|
||||
if (deleted) return Error::DELETED_OBJECT;
|
||||
std::vector<EdgeAccessor> ret;
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include "query/fine_grained_access_checker.hpp"
|
||||
#include "storage/v2/vertex.hpp"
|
||||
|
||||
#include "storage/v2/config.hpp"
|
||||
#include "storage/v2/result.hpp"
|
||||
#include "storage/v2/transaction.hpp"
|
||||
#include "storage/v2/vertex.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
@@ -82,14 +81,18 @@ class VertexAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if the resulting vector exceeds
|
||||
/// std::vector::max_size().
|
||||
Result<std::vector<EdgeAccessor>> InEdges(View view, const ExecutionContex *execution_context = nullptr,
|
||||
const VertexAccessor *destination = nullptr) const;
|
||||
Result<std::vector<EdgeAccessor>> InEdges(
|
||||
View view, const std::vector<storage::EdgeTypeId> &edge_types = {},
|
||||
const query::FineGrainedAccessChecker *fine_grained_access_checker = nullptr,
|
||||
const VertexAccessor *destination = nullptr) const;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
/// @throw std::length_error if the resulting vector exceeds
|
||||
/// std::vector::max_size().
|
||||
Result<std::vector<EdgeAccessor>> OutEdges(View view, const ExecutionContex *execution_context = nullptr,
|
||||
const VertexAccessor *destination = nullptr) const;
|
||||
Result<std::vector<EdgeAccessor>> OutEdges(
|
||||
View view, const std::vector<storage::EdgeTypeId> &edge_types = {},
|
||||
const query::FineGrainedAccessChecker *fine_grained_access_checker = nullptr,
|
||||
const VertexAccessor *destination = nullptr) const;
|
||||
|
||||
Result<size_t> InDegree(View view) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user