Support label creation via property values (#1762)

This commit is contained in:
DavIvek 2024-03-12 13:55:40 +01:00 committed by GitHub
parent a282542666
commit de2e2048ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 440 additions and 133 deletions

View File

@ -1249,6 +1249,8 @@ class AllPropertiesLookup : public memgraph::query::Expression {
friend class AstStorage;
};
using QueryLabelType = std::variant<LabelIx, Expression *>;
class LabelsTest : public memgraph::query::Expression {
public:
static const utils::TypeInfo kType;
@ -1281,6 +1283,16 @@ class LabelsTest : public memgraph::query::Expression {
protected:
LabelsTest(Expression *expression, const std::vector<LabelIx> &labels) : expression_(expression), labels_(labels) {}
LabelsTest(Expression *expression, const std::vector<QueryLabelType> &labels) : expression_(expression) {
labels_.reserve(labels.size());
for (const auto &label : labels) {
if (const auto *label_ix = std::get_if<LabelIx>(&label)) {
labels_.push_back(*label_ix);
} else {
throw SemanticException("You can't use labels in filter expressions.");
}
}
}
private:
friend class AstStorage;
@ -1771,7 +1783,7 @@ class NodeAtom : public memgraph::query::PatternAtom {
return visitor.PostVisit(*this);
}
std::vector<memgraph::query::LabelIx> labels_;
std::vector<QueryLabelType> labels_;
std::variant<std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *>,
memgraph::query::ParameterLookup *>
properties_;
@ -1781,7 +1793,11 @@ class NodeAtom : public memgraph::query::PatternAtom {
object->identifier_ = identifier_ ? identifier_->Clone(storage) : nullptr;
object->labels_.resize(labels_.size());
for (auto i = 0; i < object->labels_.size(); ++i) {
object->labels_[i] = storage->GetLabelIx(labels_[i].name);
if (const auto *label = std::get_if<LabelIx>(&labels_[i])) {
object->labels_[i] = storage->GetLabelIx(label->name);
} else {
object->labels_[i] = std::get<Expression *>(labels_[i])->Clone(storage);
}
}
if (const auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&properties_)) {
auto &new_obj_properties = std::get<std::unordered_map<PropertyIx, Expression *>>(object->properties_);
@ -2657,20 +2673,25 @@ class SetLabels : public memgraph::query::Clause {
}
memgraph::query::Identifier *identifier_{nullptr};
std::vector<memgraph::query::LabelIx> labels_;
std::vector<QueryLabelType> labels_;
SetLabels *Clone(AstStorage *storage) const override {
SetLabels *object = storage->Create<SetLabels>();
object->identifier_ = identifier_ ? identifier_->Clone(storage) : nullptr;
object->labels_.resize(labels_.size());
for (auto i = 0; i < object->labels_.size(); ++i) {
object->labels_[i] = storage->GetLabelIx(labels_[i].name);
if (const auto *label = std::get_if<LabelIx>(&labels_[i])) {
object->labels_[i] = storage->GetLabelIx(label->name);
} else {
object->labels_[i] = std::get<Expression *>(labels_[i])->Clone(storage);
}
}
return object;
}
protected:
SetLabels(Identifier *identifier, const std::vector<LabelIx> &labels) : identifier_(identifier), labels_(labels) {}
SetLabels(Identifier *identifier, std::vector<QueryLabelType> labels)
: identifier_(identifier), labels_(std::move(labels)) {}
private:
friend class AstStorage;
@ -2720,20 +2741,25 @@ class RemoveLabels : public memgraph::query::Clause {
}
memgraph::query::Identifier *identifier_{nullptr};
std::vector<memgraph::query::LabelIx> labels_;
std::vector<QueryLabelType> labels_;
RemoveLabels *Clone(AstStorage *storage) const override {
RemoveLabels *object = storage->Create<RemoveLabels>();
object->identifier_ = identifier_ ? identifier_->Clone(storage) : nullptr;
object->labels_.resize(labels_.size());
for (auto i = 0; i < object->labels_.size(); ++i) {
object->labels_[i] = storage->GetLabelIx(labels_[i].name);
if (const auto *label = std::get_if<LabelIx>(&labels_[i])) {
object->labels_[i] = storage->GetLabelIx(label->name);
} else {
object->labels_[i] = std::get<Expression *>(labels_[i])->Clone(storage);
}
}
return object;
}
protected:
RemoveLabels(Identifier *identifier, const std::vector<LabelIx> &labels) : identifier_(identifier), labels_(labels) {}
RemoveLabels(Identifier *identifier, std::vector<QueryLabelType> labels)
: identifier_(identifier), labels_(std::move(labels)) {}
private:
friend class AstStorage;

View File

@ -1933,7 +1933,7 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(MemgraphCypher::NodePatternCon
anonymous_identifiers.push_back(&node->identifier_);
}
if (ctx->nodeLabels()) {
node->labels_ = std::any_cast<std::vector<LabelIx>>(ctx->nodeLabels()->accept(this));
node->labels_ = std::any_cast<std::vector<QueryLabelType>>(ctx->nodeLabels()->accept(this));
}
if (ctx->properties()) {
// This can return either properties or parameters
@ -1947,16 +1947,27 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(MemgraphCypher::NodePatternCon
}
antlrcpp::Any CypherMainVisitor::visitNodeLabels(MemgraphCypher::NodeLabelsContext *ctx) {
std::vector<LabelIx> labels;
std::vector<QueryLabelType> labels;
for (auto *node_label : ctx->nodeLabel()) {
if (node_label->labelName()->symbolicName()) {
auto *label_name = node_label->labelName();
if (label_name->symbolicName()) {
labels.emplace_back(AddLabel(std::any_cast<std::string>(node_label->accept(this))));
} else {
} else if (label_name->parameter()) {
// If we have a parameter, we have to resolve it.
const auto *param_lookup = std::any_cast<ParameterLookup *>(node_label->accept(this));
const auto label_name = parameters_->AtTokenPosition(param_lookup->token_position_).ValueString();
labels.emplace_back(storage_->GetLabelIx(label_name));
query_info_.is_cacheable = false; // We can't cache queries with label parameters.
} else {
auto variable = std::any_cast<std::string>(label_name->variable()->accept(this));
users_identifiers.insert(variable);
auto *expression = static_cast<Expression *>(storage_->Create<Identifier>(variable));
for (auto *lookup : label_name->propertyLookup()) {
auto key = std::any_cast<PropertyIx>(lookup->accept(this));
auto *property_lookup = storage_->Create<PropertyLookup>(expression, key);
expression = property_lookup;
}
labels.emplace_back(expression);
}
}
return labels;
@ -2504,7 +2515,7 @@ antlrcpp::Any CypherMainVisitor::visitListIndexingOrSlicing(MemgraphCypher::List
antlrcpp::Any CypherMainVisitor::visitExpression2a(MemgraphCypher::Expression2aContext *ctx) {
auto *expression = std::any_cast<Expression *>(ctx->expression2b()->accept(this));
if (ctx->nodeLabels()) {
auto labels = std::any_cast<std::vector<LabelIx>>(ctx->nodeLabels()->accept(this));
auto labels = std::any_cast<std::vector<QueryLabelType>>(ctx->nodeLabels()->accept(this));
expression = storage_->Create<LabelsTest>(expression, labels);
}
return expression;
@ -2830,7 +2841,7 @@ antlrcpp::Any CypherMainVisitor::visitSetItem(MemgraphCypher::SetItemContext *ct
// SetLabels
auto *set_labels = storage_->Create<SetLabels>();
set_labels->identifier_ = storage_->Create<Identifier>(std::any_cast<std::string>(ctx->variable()->accept(this)));
set_labels->labels_ = std::any_cast<std::vector<LabelIx>>(ctx->nodeLabels()->accept(this));
set_labels->labels_ = std::any_cast<std::vector<QueryLabelType>>(ctx->nodeLabels()->accept(this));
return static_cast<Clause *>(set_labels);
}
@ -2853,7 +2864,7 @@ antlrcpp::Any CypherMainVisitor::visitRemoveItem(MemgraphCypher::RemoveItemConte
// RemoveLabels
auto *remove_labels = storage_->Create<RemoveLabels>();
remove_labels->identifier_ = storage_->Create<Identifier>(std::any_cast<std::string>(ctx->variable()->accept(this)));
remove_labels->labels_ = std::any_cast<std::vector<LabelIx>>(ctx->nodeLabels()->accept(this));
remove_labels->labels_ = std::any_cast<std::vector<QueryLabelType>>(ctx->nodeLabels()->accept(this));
return static_cast<Clause *>(remove_labels);
}

View File

@ -193,7 +193,10 @@ nodeLabels : nodeLabel ( nodeLabel )* ;
nodeLabel : ':' labelName ;
labelName : symbolicName | parameter;
labelName : symbolicName
| parameter
| variable ( propertyLookup )+
;
relTypeName : symbolicName ;

View File

@ -568,6 +568,44 @@ bool SymbolGenerator::PostVisit(SetProperty & /*set_property*/) {
return true;
}
bool SymbolGenerator::PreVisit(SetLabels &set_labels) {
auto &scope = scopes_.back();
scope.in_set_labels = true;
for (auto &label : set_labels.labels_) {
if (auto *expression = std::get_if<Expression *>(&label)) {
(*expression)->Accept(*this);
}
}
return true;
}
bool SymbolGenerator::PostVisit(SetLabels & /*set_labels*/) {
auto &scope = scopes_.back();
scope.in_set_labels = false;
return true;
}
bool SymbolGenerator::PreVisit(RemoveLabels &remove_labels) {
auto &scope = scopes_.back();
scope.in_remove_labels = true;
for (auto &label : remove_labels.labels_) {
if (auto *expression = std::get_if<Expression *>(&label)) {
(*expression)->Accept(*this);
}
}
return true;
}
bool SymbolGenerator::PostVisit(RemoveLabels & /*remove_labels*/) {
auto &scope = scopes_.back();
scope.in_remove_labels = false;
return true;
}
// Pattern and its subparts.
bool SymbolGenerator::PreVisit(Pattern &pattern) {
@ -602,6 +640,15 @@ bool SymbolGenerator::PreVisit(NodeAtom &node_atom) {
};
scope.in_node_atom = true;
if (scope.in_create) { // you can use expressions with labels only in create
for (auto &label : node_atom.labels_) {
if (auto *expression = std::get_if<Expression *>(&label)) {
(*expression)->Accept(*this);
}
}
}
if (auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&node_atom.properties_)) {
bool props_or_labels = !properties->empty() || !node_atom.labels_.empty();

View File

@ -68,6 +68,10 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
bool PostVisit(Foreach &) override;
bool PreVisit(SetProperty & /*set_property*/) override;
bool PostVisit(SetProperty & /*set_property*/) override;
bool PreVisit(SetLabels &) override;
bool PostVisit(SetLabels & /*set_labels*/) override;
bool PreVisit(RemoveLabels &) override;
bool PostVisit(RemoveLabels & /*remove_labels*/) override;
// Expressions
ReturnType Visit(Identifier &) override;
@ -130,6 +134,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
bool in_set_property{false};
bool in_call_subquery{false};
bool has_return{false};
bool in_set_labels{false};
bool in_remove_labels{false};
// True when visiting a pattern atom (node or edge) identifier, which can be
// reused or created in the pattern itself.
bool in_pattern_atom_identifier{false};

View File

@ -48,6 +48,7 @@
#include "query/procedure/module.hpp"
#include "query/typed_value.hpp"
#include "range/v3/all.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/view.hpp"
#include "utils/algorithm.hpp"
@ -179,6 +180,20 @@ inline void AbortCheck(ExecutionContext const &context) {
if (auto const reason = MustAbort(context); reason != AbortReason::NO_ABORT) throw HintedAbortError(reason);
}
std::vector<storage::LabelId> EvaluateLabels(const std::vector<StorageLabelType> &labels,
ExpressionEvaluator &evaluator, DbAccessor *dba) {
std::vector<storage::LabelId> result;
result.reserve(labels.size());
for (const auto &label : labels) {
if (const auto *label_atom = std::get_if<storage::LabelId>(&label)) {
result.emplace_back(*label_atom);
} else {
result.emplace_back(dba->NameToLabel(std::get<Expression *>(label)->Accept(evaluator).ValueString()));
}
}
return result;
}
} // namespace
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
@ -214,12 +229,13 @@ CreateNode::CreateNode(const std::shared_ptr<LogicalOperator> &input, NodeCreati
// Creates a vertex on this GraphDb. Returns a reference to vertex placed on the
// frame.
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, ExecutionContext &context) {
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, ExecutionContext &context,
std::vector<storage::LabelId> &labels, ExpressionEvaluator &evaluator) {
auto &dba = *context.db_accessor;
auto new_node = dba.InsertVertex();
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
for (auto label : node_info.labels) {
auto maybe_error = new_node.AddLabel(label);
for (const auto &label : labels) {
auto maybe_error = std::invoke([&] { return new_node.AddLabel(label); });
if (maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
@ -234,10 +250,6 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *fram
}
context.execution_stats[ExecutionStats::Key::CREATED_LABELS] += 1;
}
// Evaluator should use the latest accessors, as modified in this query, when
// setting properties on new nodes.
ExpressionEvaluator evaluator(frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
// TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory
// when we update PropertyValue with custom allocator.
std::map<storage::PropertyId, storage::PropertyValue> properties;
@ -277,16 +289,21 @@ CreateNode::CreateNodeCursor::CreateNodeCursor(const CreateNode &self, utils::Me
bool CreateNode::CreateNodeCursor::Pull(Frame &frame, ExecutionContext &context) {
OOMExceptionEnabler oom_exception;
SCOPED_PROFILE_OP("CreateNode");
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
!context.auth_checker->Has(self_.node_info_.labels,
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Vertex not created due to not having enough permission!");
}
#endif
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
if (input_cursor_->Pull(frame, context)) {
auto created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
// we have to resolve the labels before we can check for permissions
auto labels = EvaluateLabels(self_.node_info_.labels, evaluator, context.db_accessor);
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
!context.auth_checker->Has(labels, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Vertex not created due to not having enough permission!");
}
#endif
auto created_vertex = CreateLocalVertex(self_.node_info_, &frame, context, labels, evaluator);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
@ -370,6 +387,9 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
SCOPED_PROFILE_OP_BY_REF(self_);
if (!input_cursor_->Pull(frame, context)) return false;
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
auto labels = EvaluateLabels(self_.node_info_.labels, evaluator, context.db_accessor);
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast()) {
@ -381,7 +401,7 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
if (context.auth_checker &&
!(context.auth_checker->Has(self_.edge_info_.edge_type,
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE) &&
context.auth_checker->Has(self_.node_info_.labels, fine_grained_permission))) {
context.auth_checker->Has(labels, fine_grained_permission))) {
throw QueryRuntimeException("Edge not created due to not having enough permission!");
}
}
@ -391,14 +411,8 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &v1 = vertex_value.ValueVertex();
// Similarly to CreateNode, newly created edges and nodes should use the
// storage::View::NEW.
// E.g. we pickup new properties: `CREATE (n {p: 42}) -[:r {ep: n.p}]-> ()`
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
// get the destination vertex (possibly an existing node)
auto &v2 = OtherVertex(frame, context);
auto &v2 = OtherVertex(frame, context, labels, evaluator);
// create an edge between the two nodes
auto *dba = context.db_accessor;
@ -429,13 +443,15 @@ void CreateExpand::CreateExpandCursor::Shutdown() { input_cursor_->Shutdown(); }
void CreateExpand::CreateExpandCursor::Reset() { input_cursor_->Reset(); }
VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, ExecutionContext &context) {
VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, ExecutionContext &context,
std::vector<storage::LabelId> &labels,
ExpressionEvaluator &evaluator) {
if (self_.existing_node_) {
TypedValue &dest_node_value = frame[self_.node_info_.symbol];
ExpectType(self_.node_info_.symbol, dest_node_value, TypedValue::Type::Vertex);
return dest_node_value.ValueVertex();
} else {
auto &created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
auto &created_vertex = CreateLocalVertex(self_.node_info_, &frame, context, labels, evaluator);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
@ -3208,8 +3224,8 @@ void SetProperties::SetPropertiesCursor::Shutdown() { input_cursor_->Shutdown();
void SetProperties::SetPropertiesCursor::Reset() { input_cursor_->Reset(); }
SetLabels::SetLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
const std::vector<storage::LabelId> &labels)
: input_(input), input_symbol_(std::move(input_symbol)), labels_(labels) {}
std::vector<StorageLabelType> labels)
: input_(input), input_symbol_(std::move(input_symbol)), labels_(std::move(labels)) {}
ACCEPT_WITH_INPUT(SetLabels)
@ -3229,16 +3245,18 @@ SetLabels::SetLabelsCursor::SetLabelsCursor(const SetLabels &self, utils::Memory
bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
OOMExceptionEnabler oom_exception;
SCOPED_PROFILE_OP("SetLabels");
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
if (!input_cursor_->Pull(frame, context)) return false;
auto labels = EvaluateLabels(self_.labels_, evaluator, context.db_accessor);
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
!context.auth_checker->Has(labels, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Couldn't set label due to not having enough permission!");
}
#endif
if (!input_cursor_->Pull(frame, context)) return false;
TypedValue &vertex_value = frame[self_.input_symbol_];
// Skip setting labels on Null (can occur in optional match).
if (vertex_value.IsNull()) return true;
@ -3253,7 +3271,7 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
}
#endif
for (auto label : self_.labels_) {
for (auto label : labels) {
auto maybe_value = vertex.AddLabel(label);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
@ -3368,8 +3386,8 @@ void RemoveProperty::RemovePropertyCursor::Shutdown() { input_cursor_->Shutdown(
void RemoveProperty::RemovePropertyCursor::Reset() { input_cursor_->Reset(); }
RemoveLabels::RemoveLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
const std::vector<storage::LabelId> &labels)
: input_(input), input_symbol_(std::move(input_symbol)), labels_(labels) {}
std::vector<StorageLabelType> labels)
: input_(input), input_symbol_(std::move(input_symbol)), labels_(std::move(labels)) {}
ACCEPT_WITH_INPUT(RemoveLabels)
@ -3389,16 +3407,18 @@ RemoveLabels::RemoveLabelsCursor::RemoveLabelsCursor(const RemoveLabels &self, u
bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
OOMExceptionEnabler oom_exception;
SCOPED_PROFILE_OP("RemoveLabels");
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
if (!input_cursor_->Pull(frame, context)) return false;
auto labels = EvaluateLabels(self_.labels_, evaluator, context.db_accessor);
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
!context.auth_checker->Has(labels, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Couldn't remove label due to not having enough permission!");
}
#endif
if (!input_cursor_->Pull(frame, context)) return false;
TypedValue &vertex_value = frame[self_.input_symbol_];
// Skip removing labels on Null (can occur in optional match).
if (vertex_value.IsNull()) return true;
@ -3413,7 +3433,7 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
}
#endif
for (auto label : self_.labels_) {
for (auto label : labels) {
auto maybe_value = vertex.RemoveLabel(label);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {

View File

@ -285,6 +285,7 @@ class Once : public memgraph::query::plan::LogicalOperator {
};
using PropertiesMapList = std::vector<std::pair<storage::PropertyId, Expression *>>;
using StorageLabelType = std::variant<storage::LabelId, Expression *>;
struct NodeCreationInfo {
static const utils::TypeInfo kType;
@ -292,18 +293,18 @@ struct NodeCreationInfo {
NodeCreationInfo() = default;
NodeCreationInfo(Symbol symbol, std::vector<storage::LabelId> labels,
NodeCreationInfo(Symbol symbol, std::vector<StorageLabelType> labels,
std::variant<PropertiesMapList, ParameterLookup *> properties)
: symbol{std::move(symbol)}, labels{std::move(labels)}, properties{std::move(properties)} {};
NodeCreationInfo(Symbol symbol, std::vector<storage::LabelId> labels, PropertiesMapList properties)
NodeCreationInfo(Symbol symbol, std::vector<StorageLabelType> labels, PropertiesMapList properties)
: symbol{std::move(symbol)}, labels{std::move(labels)}, properties{std::move(properties)} {};
NodeCreationInfo(Symbol symbol, std::vector<storage::LabelId> labels, ParameterLookup *properties)
NodeCreationInfo(Symbol symbol, std::vector<StorageLabelType> labels, ParameterLookup *properties)
: symbol{std::move(symbol)}, labels{std::move(labels)}, properties{properties} {};
Symbol symbol;
std::vector<storage::LabelId> labels;
std::vector<StorageLabelType> labels;
std::variant<PropertiesMapList, ParameterLookup *> properties;
NodeCreationInfo Clone(AstStorage *storage) const {
@ -506,7 +507,8 @@ class CreateExpand : public memgraph::query::plan::LogicalOperator {
const UniqueCursorPtr input_cursor_;
// Get the existing node (if existing_node_ == true), or create a new node
VertexAccessor &OtherVertex(Frame &frame, ExecutionContext &context);
VertexAccessor &OtherVertex(Frame &frame, ExecutionContext &context,
std::vector<memgraph::storage::LabelId> &labels, ExpressionEvaluator &evaluator);
};
};
@ -1477,8 +1479,7 @@ class SetLabels : public memgraph::query::plan::LogicalOperator {
SetLabels() = default;
SetLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
const std::vector<storage::LabelId> &labels);
SetLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol, std::vector<StorageLabelType> labels);
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
@ -1489,7 +1490,7 @@ class SetLabels : public memgraph::query::plan::LogicalOperator {
std::shared_ptr<memgraph::query::plan::LogicalOperator> input_;
Symbol input_symbol_;
std::vector<storage::LabelId> labels_;
std::vector<StorageLabelType> labels_;
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<SetLabels>();
@ -1567,7 +1568,7 @@ class RemoveLabels : public memgraph::query::plan::LogicalOperator {
RemoveLabels() = default;
RemoveLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
const std::vector<storage::LabelId> &labels);
std::vector<StorageLabelType> labels);
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
@ -1578,7 +1579,7 @@ class RemoveLabels : public memgraph::query::plan::LogicalOperator {
std::shared_ptr<memgraph::query::plan::LogicalOperator> input_;
Symbol input_symbol_;
std::vector<storage::LabelId> labels_;
std::vector<StorageLabelType> labels_;
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<RemoveLabels>();

View File

@ -358,11 +358,17 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
};
auto add_node_filter = [&](NodeAtom *node) {
const auto &node_symbol = symbol_table.at(*node->identifier_);
if (!node->labels_.empty()) {
// Create a LabelsTest and store it.
auto *labels_test = storage.Create<LabelsTest>(node->identifier_, node->labels_);
std::vector<LabelIx> labels;
for (auto label : node->labels_) {
if (const auto *label_node = std::get_if<Expression *>(&label)) {
throw SemanticException("Property lookup not supported in MATCH/MERGE clause!");
}
labels.push_back(std::get<LabelIx>(label));
}
if (!labels.empty()) {
auto *labels_test = storage.Create<LabelsTest>(node->identifier_, labels);
auto label_filter = FilterInfo{FilterInfo::Type::Label, labels_test, std::unordered_set<Symbol>{node_symbol}};
label_filter.labels = node->labels_;
label_filter.labels = labels;
all_filters_.emplace_back(label_filter);
}
add_properties(node);

View File

@ -340,7 +340,7 @@ json ToJson(NamedExpression *nexpr) {
return json;
}
json ToJson(const std::vector<std::pair<storage::PropertyId, Expression *>> &properties, const DbAccessor &dba) {
json ToJson(const PropertiesMapList &properties, const DbAccessor &dba) {
json json;
for (const auto &prop_pair : properties) {
json.emplace(ToJson(prop_pair.first, dba), ToJson(prop_pair.second));
@ -348,6 +348,18 @@ json ToJson(const std::vector<std::pair<storage::PropertyId, Expression *>> &pro
return json;
}
json ToJson(const std::vector<StorageLabelType> &labels, const DbAccessor &dba) {
json json;
for (const auto &label : labels) {
if (const auto *label_node = std::get_if<Expression *>(&label)) {
json.emplace_back(ToJson(*label_node));
} else {
json.emplace_back(ToJson(std::get<storage::LabelId>(label), dba));
}
}
return json;
}
json ToJson(const NodeCreationInfo &node_info, const DbAccessor &dba) {
json self;
self["symbol"] = ToJson(node_info.symbol);
@ -654,7 +666,6 @@ bool PlanToJsonVisitor::PreVisit(SetLabels &op) {
self["name"] = "SetLabels";
self["input_symbol"] = ToJson(op.input_symbol_);
self["labels"] = ToJson(op.labels_, *dba_);
op.input_->Accept(*this);
self["input"] = PopOutput();

View File

@ -293,6 +293,19 @@ class RuleBasedPlanner {
storage::EdgeTypeId GetEdgeType(EdgeTypeIx edge_type) { return context_->db->NameToEdgeType(edge_type.name); }
std::vector<StorageLabelType> GetLabelIds(const std::vector<QueryLabelType> &labels) {
std::vector<StorageLabelType> label_ids;
label_ids.reserve(labels.size());
for (const auto &label : labels) {
if (const auto *label_atom = std::get_if<LabelIx>(&label)) {
label_ids.emplace_back(GetLabel(*label_atom));
} else {
label_ids.emplace_back(std::get<Expression *>(label));
}
}
return label_ids;
}
std::unique_ptr<LogicalOperator> HandleMatching(std::unique_ptr<LogicalOperator> last_op,
const SingleQueryPart &single_query_part, SymbolTable &symbol_table,
std::unordered_set<Symbol> &bound_symbols) {
@ -328,11 +341,6 @@ class RuleBasedPlanner {
std::unordered_set<Symbol> &bound_symbols) {
auto node_to_creation_info = [&](const NodeAtom &node) {
const auto &node_symbol = symbol_table.at(*node.identifier_);
std::vector<storage::LabelId> labels;
labels.reserve(node.labels_.size());
for (const auto &label : node.labels_) {
labels.push_back(GetLabel(label));
}
auto properties = std::invoke([&]() -> std::variant<PropertiesMapList, ParameterLookup *> {
if (const auto *node_properties =
@ -346,7 +354,7 @@ class RuleBasedPlanner {
}
return std::get<ParameterLookup *>(node.properties_);
});
return NodeCreationInfo{node_symbol, labels, properties};
return NodeCreationInfo{node_symbol, GetLabelIds(node.labels_), properties};
};
auto base = [&](NodeAtom *node) -> std::unique_ptr<LogicalOperator> {
@ -423,23 +431,13 @@ class RuleBasedPlanner {
return std::make_unique<plan::SetProperties>(std::move(input_op), input_symbol, set->expression_, op);
} else if (auto *set = utils::Downcast<query::SetLabels>(clause)) {
const auto &input_symbol = symbol_table.at(*set->identifier_);
std::vector<storage::LabelId> labels;
labels.reserve(set->labels_.size());
for (const auto &label : set->labels_) {
labels.push_back(GetLabel(label));
}
return std::make_unique<plan::SetLabels>(std::move(input_op), input_symbol, labels);
return std::make_unique<plan::SetLabels>(std::move(input_op), input_symbol, GetLabelIds(set->labels_));
} else if (auto *rem = utils::Downcast<query::RemoveProperty>(clause)) {
return std::make_unique<plan::RemoveProperty>(std::move(input_op), GetProperty(rem->property_lookup_->property_),
rem->property_lookup_);
} else if (auto *rem = utils::Downcast<query::RemoveLabels>(clause)) {
const auto &input_symbol = symbol_table.at(*rem->identifier_);
std::vector<storage::LabelId> labels;
labels.reserve(rem->labels_.size());
for (const auto &label : rem->labels_) {
labels.push_back(GetLabel(label));
}
return std::make_unique<plan::RemoveLabels>(std::move(input_op), input_symbol, labels);
return std::make_unique<plan::RemoveLabels>(std::move(input_op), input_symbol, GetLabelIds(rem->labels_));
}
return nullptr;
}

View File

@ -53,8 +53,45 @@ def test_given_one_row_in_db_when_load_csv_after_match_then_pass():
assert len(list(results)) == 4
def test_load_csv_with_parameters():
def test_creating_labels_with_load_csv_variable():
memgraph = Memgraph("localhost", 7687)
results = list(
memgraph.execute_and_fetch(
f"""LOAD CSV FROM '{get_file_path(SIMPLE_CSV_FILE)}' WITH HEADER AS row
CREATE (p:row.name)
RETURN p
"""
)
)
assert len(results) == 4
assert results[0]["p"]._labels == {"Joseph"}
assert results[1]["p"]._labels == {"Peter"}
assert results[2]["p"]._labels == {"Ella"}
assert results[3]["p"]._labels == {"Joe"}
def test_create_relationships_with_load_csv_variable2():
memgraph = Memgraph("localhost", 7687)
results = list(
memgraph.execute_and_fetch(
f"""LOAD CSV FROM '{get_file_path(SIMPLE_CSV_FILE)}' WITH HEADER AS row
CREATE (p:row.name:Person:row.id)
RETURN p
"""
)
)
assert len(results) == 4
assert results[0]["p"]._labels == {"Joseph", "Person", "1"}
assert results[1]["p"]._labels == {"Peter", "Person", "2"}
assert results[2]["p"]._labels == {"Ella", "Person", "3"}
assert results[3]["p"]._labels == {"Joe", "Person", "4"}
def test_load_csv_with_parameters():
URI = "bolt://localhost:7687"
AUTH = ("", "")

View File

@ -264,3 +264,113 @@ Feature: With
| id |
| 0 |
| 1 |
Scenario: With test 17:
Given an empty graph
And having executed:
"""
CREATE ({name: "node1"})
"""
When executing query:
"""
MATCH (n) WITH n AS node
CREATE (m:node.name)
"""
When executing query:
"""
MATCH (n:node1) RETURN n;
"""
Then the result should be:
| n |
| (:node1) |
Scenario: With test 18:
Given an empty graph
And having executed:
"""
CREATE ({name: "LabelToAdd"})
"""
When executing query:
"""
MATCH (n) WITH n AS node
SET node:node.name
"""
When executing query:
"""
MATCH (n) RETURN n;
"""
Then the result should be:
| n |
| (:LabelToAdd {name: 'LabelToAdd'}) |
Scenario: With test 19:
Given an empty graph
And having executed:
"""
CREATE (:labelToRemove {name: 'labelToRemove'})
"""
When executing query:
"""
MATCH (n) WITH n AS node
REMOVE node:node.name
"""
When executing query:
"""
MATCH (n) RETURN n;
"""
Then the result should be:
| n |
| ({name: 'labelToRemove'}) |
Scenario: With test 20:
Given an empty graph
And having executed:
"""
CREATE ({name: 'label1'})
"""
When executing query:
"""
MATCH (n) WITH n AS node
SET node:node.name:label2
"""
When executing query:
"""
MATCH (n) RETURN n;
"""
Then the result should be:
| n |
| (:label1:label2 {name: 'label1'}) |
Scenario: With test 21:
Given an empty graph
And having executed:
"""
CREATE ({name: 'label1'})
"""
When executing query:
"""
MATCH (n) WITH n AS node
SET node:label2:node.name
"""
When executing query:
"""
MATCH (n) RETURN n;
"""
Then the result should be:
| n |
| (:label2:label1 {name: 'label1'}) |
Scenario: With test 22:
Given an empty graph
And having executed:
"""
WITH {value: {label: "labelvalue"}} as label
CREATE (n:label.value.label);
"""
When executing query:
"""
MATCH (n) RETURN n;
"""
Then the result should be:
| n |
| (:labelvalue) |

View File

@ -12,6 +12,7 @@
#include <gtest/gtest.h>
#include "disk_test_utils.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "query/plan/pretty_print.hpp"
@ -515,7 +516,7 @@ TYPED_TEST(PrintToJsonTest, SetLabels) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::SetLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
this->Check(last_op.get(), R"(
{
@ -554,7 +555,7 @@ TYPED_TEST(PrintToJsonTest, RemoveLabels) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::RemoveLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
this->Check(last_op.get(), R"(
{

View File

@ -425,7 +425,7 @@ auto GetSet(AstStorage &storage, const std::string &name, Expression *expr, bool
/// Create a set labels clause for given identifier name and labels.
auto GetSet(AstStorage &storage, const std::string &name, std::vector<std::string> label_names) {
std::vector<LabelIx> labels;
std::vector<QueryLabelType> labels;
labels.reserve(label_names.size());
for (const auto &label : label_names) {
labels.push_back(storage.GetLabelIx(label));
@ -438,7 +438,7 @@ auto GetRemove(AstStorage &storage, PropertyLookup *prop_lookup) { return storag
/// Create a remove labels clause for given identifier name and labels.
auto GetRemove(AstStorage &storage, const std::string &name, std::vector<std::string> label_names) {
std::vector<LabelIx> labels;
std::vector<QueryLabelType> labels;
labels.reserve(label_names.size());
for (const auto &label : label_names) {
labels.push_back(storage.GetLabelIx(label));

View File

@ -497,7 +497,7 @@ class MatchCreateNodeWithAuthFixture : public QueryPlanTest<StorageType> {
NodeCreationInfo m{};
m.symbol = symbol_table.CreateSymbol("m", true);
std::vector<memgraph::storage::LabelId> labels{dba.NameToLabel("l2")};
std::vector<StorageLabelType> labels{dba.NameToLabel("l2")};
m.labels = labels;
// creation op
auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
@ -627,7 +627,7 @@ class MatchCreateExpandWithAuthFixture : public QueryPlanTest<StorageType> {
// 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")};
std::vector<StorageLabelType> labels{dba.NameToLabel("l2")};
m.labels = labels;
EdgeCreationInfo r;
@ -1231,12 +1231,14 @@ TYPED_TEST(QueryPlanTest, SetLabels) {
ASSERT_TRUE(dba.InsertVertex().AddLabel(label1).HasValue());
ASSERT_TRUE(dba.InsertVertex().AddLabel(label1).HasValue());
dba.AdvanceCommand();
std::vector<StorageLabelType> labels;
labels.emplace_back(label2);
labels.emplace_back(label3);
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 label_set = std::make_shared<plan::SetLabels>(n.op_, n.sym_, labels);
auto context = MakeContext(this->storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*label_set, &context));
@ -1255,12 +1257,14 @@ TYPED_TEST(QueryPlanTest, SetLabelsWithFineGrained) {
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
dba.AdvanceCommand();
std::vector<StorageLabelType> labels_variant;
labels_variant.emplace_back(labels[1]);
labels_variant.emplace_back(labels[2]);
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]});
auto label_set = std::make_shared<plan::SetLabels>(n.op_, n.sym_, labels_variant);
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
@ -1396,12 +1400,14 @@ TYPED_TEST(QueryPlanTest, RemoveLabels) {
ASSERT_TRUE(v2.AddLabel(label1).HasValue());
ASSERT_TRUE(v2.AddLabel(label3).HasValue());
dba.AdvanceCommand();
std::vector<StorageLabelType> labels;
labels.emplace_back(label1);
labels.emplace_back(label2);
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 label_remove = std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, labels);
auto context = MakeContext(this->storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*label_remove, &context));
@ -1425,12 +1431,14 @@ TYPED_TEST(QueryPlanTest, RemoveLabelsFineGrainedFiltering) {
ASSERT_TRUE(v2.AddLabel(labels[0]).HasValue());
ASSERT_TRUE(v2.AddLabel(labels[2]).HasValue());
dba.AdvanceCommand();
std::vector<StorageLabelType> labels_variant;
labels_variant.emplace_back(labels[0]);
labels_variant.emplace_back(labels[1]);
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]});
auto label_remove = std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, labels_variant);
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
@ -1569,15 +1577,16 @@ TYPED_TEST(QueryPlanTest, SetRemove) {
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
dba.AdvanceCommand();
std::vector<StorageLabelType> labels;
labels.emplace_back(label1);
labels.emplace_back(label2);
// 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 set = std::make_shared<plan::SetLabels>(scan_all.op_, scan_all.sym_, labels);
auto rem = std::make_shared<plan::RemoveLabels>(set, scan_all.sym_, labels);
auto context = MakeContext(this->storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*rem, &context));
dba.AdvanceCommand();
@ -1773,10 +1782,12 @@ TYPED_TEST(QueryPlanTest, SetLabelsOnNull) {
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
memgraph::query::DbAccessor dba(storage_dba.get());
auto label = dba.NameToLabel("label");
std::vector<StorageLabelType> labels;
labels.emplace_back(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});
auto set_op = std::make_shared<plan::SetLabels>(optional, n.sym_, labels);
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));
@ -1801,11 +1812,12 @@ TYPED_TEST(QueryPlanTest, RemoveLabelsOnNull) {
auto storage_dba = this->db->Access(ReplicationRole::MAIN);
memgraph::query::DbAccessor dba(storage_dba.get());
auto label = dba.NameToLabel("label");
std::vector<StorageLabelType> labels;
labels.emplace_back(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});
auto remove_op = std::make_shared<plan::RemoveLabels>(optional, n.sym_, labels);
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));
@ -1906,7 +1918,7 @@ TYPED_TEST(QueryPlanTest, DeleteRemoveLabels) {
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")};
std::vector<StorageLabelType> 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);

View File

@ -315,11 +315,12 @@ TYPED_TEST(QueryPlan, NodeFilterLabelsAndProperties) {
// make a scan all
auto n = MakeScanAll(this->storage, symbol_table, "n");
n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label)));
std::vector<memgraph::query::LabelIx> labels;
labels.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label)));
std::get<0>(n.node_->properties_)[this->storage.GetPropertyIx(property.first)] = LITERAL(42);
// node filtering
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, labels),
EQ(PROPERTY_LOOKUP(dba, n.node_->identifier_, property), LITERAL(42)));
auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
@ -366,11 +367,12 @@ TYPED_TEST(QueryPlan, NodeFilterMultipleLabels) {
// make a scan all
auto n = MakeScanAll(this->storage, symbol_table, "n");
n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label1)));
n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label2)));
std::vector<memgraph::query::LabelIx> labels;
labels.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label1)));
labels.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label2)));
// node filtering
auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, labels);
auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
// make a named expression and a produce
@ -2805,9 +2807,10 @@ TYPED_TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
// OPTIONAL MATCH (n :missing)
auto n = MakeScanAll(this->storage, symbol_table, "n");
auto label_missing = "missing";
n.node_->labels_.emplace_back(this->storage.GetLabelIx(label_missing));
std::vector<memgraph::query::LabelIx> labels;
labels.emplace_back(this->storage.GetLabelIx(label_missing));
auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, labels);
auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
auto optional = std::make_shared<plan::Optional>(nullptr, node_filter, std::vector<Symbol>{n.sym_});
// WITH n
@ -3619,7 +3622,8 @@ class ExistsFixture : public testing::Test {
exists_expression->MapTo(symbol_table.CreateAnonymousSymbol());
auto scan_all = MakeScanAll(storage, symbol_table, "n");
scan_all.node_->labels_.emplace_back(storage.GetLabelIx(match_label));
std::vector<memgraph::query::LabelIx> labels;
labels.emplace_back(storage.GetLabelIx(match_label));
std::shared_ptr<LogicalOperator> last_op = std::make_shared<Expand>(
nullptr, scan_all.sym_, dest_sym, edge_sym, direction, edge_types, false, memgraph::storage::View::OLD);
@ -3656,8 +3660,7 @@ class ExistsFixture : public testing::Test {
last_op = std::make_shared<Limit>(std::move(last_op), storage.Create<PrimitiveLiteral>(1));
last_op = std::make_shared<EvaluatePatternFilter>(std::move(last_op), symbol_table.at(*exists_expression));
auto *total_expression =
AND(storage.Create<LabelsTest>(scan_all.node_->identifier_, scan_all.node_->labels_), exists_expression);
auto *total_expression = AND(storage.Create<LabelsTest>(scan_all.node_->identifier_, labels), exists_expression);
auto filter = std::make_shared<Filter>(scan_all.op_, std::vector<std::shared_ptr<LogicalOperator>>{last_op},
total_expression);
@ -3709,7 +3712,8 @@ class ExistsFixture : public testing::Test {
exists_expression2->MapTo(symbol_table.CreateAnonymousSymbol());
auto scan_all = MakeScanAll(storage, symbol_table, "n");
scan_all.node_->labels_.emplace_back(storage.GetLabelIx(match_label));
std::vector<memgraph::query::LabelIx> labels;
labels.emplace_back(storage.GetLabelIx(match_label));
std::shared_ptr<LogicalOperator> last_op = std::make_shared<Expand>(
nullptr, scan_all.sym_, dest_sym, edge_sym, direction, first_edge_type, false, memgraph::storage::View::OLD);
@ -3721,7 +3725,7 @@ class ExistsFixture : public testing::Test {
last_op2 = std::make_shared<Limit>(std::move(last_op2), storage.Create<PrimitiveLiteral>(1));
last_op2 = std::make_shared<EvaluatePatternFilter>(std::move(last_op2), symbol_table.at(*exists_expression2));
Expression *total_expression = storage.Create<LabelsTest>(scan_all.node_->identifier_, scan_all.node_->labels_);
Expression *total_expression = storage.Create<LabelsTest>(scan_all.node_->identifier_, labels);
if (or_flag) {
total_expression = AND(total_expression, OR(exists_expression, exists_expression2));
@ -3841,7 +3845,11 @@ TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilter) {
// MATCH (n) WHERE n.prop = 2 CALL { MATCH (m) RETURN m } RETURN n, m
auto n = MakeScanAll(this->storage, this->symbol_table, "n");
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
std::vector<memgraph::query::LabelIx> labels;
for (const auto &label : n.node_->labels_) {
labels.emplace_back(std::get<memgraph::query::LabelIx>(label));
}
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, labels),
EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(2)));
auto filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
@ -3866,11 +3874,15 @@ TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilterInsideSubquery) {
// MATCH (n) CALL { MATCH (m) WHERE m.prop = 2 RETURN m } RETURN n, m
auto n = MakeScanAll(this->storage, this->symbol_table, "n");
std::vector<memgraph::query::LabelIx> labels;
for (const auto &label : n.node_->labels_) {
labels.emplace_back(std::get<memgraph::query::LabelIx>(label));
}
auto return_n =
NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
auto m = MakeScanAll(this->storage, this->symbol_table, "m");
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, labels),
EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(2)));
auto filter = std::make_shared<Filter>(m.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
@ -3891,7 +3903,11 @@ TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilterNoResults) {
// MATCH (n) WHERE n.prop = 3 CALL { MATCH (m) RETURN m } RETURN n, m
auto n = MakeScanAll(this->storage, this->symbol_table, "n");
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
std::vector<memgraph::query::LabelIx> labels;
for (const auto &label : n.node_->labels_) {
labels.emplace_back(std::get<memgraph::query::LabelIx>(label));
}
auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, labels),
EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(3)));
auto filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);

View File

@ -290,9 +290,10 @@ TYPED_TEST(OperatorToStringTest, SetProperties) {
TYPED_TEST(OperatorToStringTest, SetLabels) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::SetLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType> labels;
labels.emplace_back(this->dba.NameToLabel("label1"));
labels.emplace_back(this->dba.NameToLabel("label2"));
last_op = std::make_shared<plan::SetLabels>(last_op, node_sym, labels);
std::string expected_string{"SetLabels"};
EXPECT_EQ(last_op->ToString(), expected_string);
@ -311,9 +312,10 @@ TYPED_TEST(OperatorToStringTest, RemoveProperty) {
TYPED_TEST(OperatorToStringTest, RemoveLabels) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::RemoveLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType> labels;
labels.emplace_back(this->dba.NameToLabel("label1"));
labels.emplace_back(this->dba.NameToLabel("label2"));
last_op = std::make_shared<plan::RemoveLabels>(last_op, node_sym, labels);
std::string expected_string{"RemoveLabels"};
EXPECT_EQ(last_op->ToString(), expected_string);

View File

@ -183,10 +183,10 @@ TYPED_TEST(ReadWriteTypeCheckTest, SetRemovePropertiesLabels) {
plan::SetProperties::Op::REPLACE);
last_op = std::make_shared<plan::SetLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
last_op = std::make_shared<plan::RemoveLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::vector<StorageLabelType>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
this->CheckPlanType(last_op.get(), RWType::RW);
}