From f56029365759fe2642b573304c4f1160aa3899f6 Mon Sep 17 00:00:00 2001 From: Jure Bajic Date: Thu, 9 Sep 2021 12:39:13 +0200 Subject: [PATCH] Add Property Map support in CREATE clause (#220) --- src/query/frontend/ast/ast.lcp | 48 +++++--- .../frontend/ast/cypher_main_visitor.cpp | 29 +++-- .../frontend/semantic/symbol_generator.cpp | 47 +++++--- src/query/plan/operator.cpp | 26 ++++- src/query/plan/operator.lcp | 71 ++++++++++-- src/query/plan/preprocess.cpp | 104 ++++++++++-------- src/query/plan/pretty_print.cpp | 7 +- src/query/plan/rule_based_planner.hpp | 41 +++++-- tests/benchmark/query/planner.cpp | 6 +- tests/unit/cypher_main_visitor.cpp | 13 ++- tests/unit/interpreter.cpp | 71 ++++++++++++ tests/unit/query_plan.cpp | 21 ++-- .../query_plan_create_set_remove_delete.cpp | 19 ++-- tests/unit/query_plan_match_filter_return.cpp | 5 +- ...query_plan_v2_create_set_remove_delete.cpp | 3 +- tests/unit/query_semantic.cpp | 20 ++-- tests/unit/query_variable_start_planner.cpp | 3 +- 17 files changed, 390 insertions(+), 144 deletions(-) diff --git a/src/query/frontend/ast/ast.lcp b/src/query/frontend/ast/ast.lcp index f8758df95..9bca16e37 100644 --- a/src/query/frontend/ast/ast.lcp +++ b/src/query/frontend/ast/ast.lcp @@ -3,6 +3,7 @@ #include #include +#include #include #include "query/frontend/ast/ast_visitor.hpp" @@ -1237,6 +1238,19 @@ cpp<# (:clone :ignore-other-base-classes t) (:type-info :ignore-other-base-classes t)) +(defun clone-variant-properties (source destination) + #>cpp + if (const auto *properties = std::get_if>(&${source})) { + auto &new_obj_properties = std::get>(${destination}); + for (const auto &[property, value_expression] : *properties) { + PropertyIx key = storage->GetPropertyIx(property.name); + new_obj_properties[key] = value_expression->Clone(storage); + } + } else { + ${destination} = std::get(${source})->Clone(storage); + } + cpp<#) + (lcp:define-class node-atom (pattern-atom) ((labels "std::vector" :scope :public :slk-load (lambda (member) @@ -1249,20 +1263,22 @@ cpp<# } cpp<#) :clone (clone-name-ix-vector "Label")) - (properties "std::unordered_map" - :slk-save #'slk-save-property-map - :slk-load #'slk-load-property-map - :clone #'clone-property-map - :scope :public)) + (properties "std::variant, ParameterLookup*>" + :clone #'clone-variant-properties + :scope :public)) (:public #>cpp bool Accept(HierarchicalTreeVisitor &visitor) override { if (visitor.PreVisit(*this)) { - bool cont = identifier_->Accept(visitor); - for (auto &property : properties_) { - if (cont) { - cont = property.second->Accept(visitor); + if (auto* properties = std::get_if>(&properties_)) { + bool cont = identifier_->Accept(visitor); + for (auto &property : *properties) { + if (cont) { + cont = property.second->Accept(visitor); + } } + } else { + std::get(properties_)->Accept(visitor); } } return visitor.PostVisit(*this); @@ -1293,11 +1309,11 @@ cpp<# } cpp<#) :clone (clone-name-ix-vector "EdgeType")) - (properties "std::unordered_map" + (properties "std::variant, ParameterLookup*>" :scope :public :slk-save #'slk-save-property-map :slk-load #'slk-load-property-map - :clone #'clone-property-map) + :clone #'clone-variant-properties) (lower-bound "Expression *" :initval "nullptr" :scope :public :slk-save #'slk-save-ast-pointer :slk-load (slk-load-ast-pointer "Expression") @@ -1349,10 +1365,14 @@ cpp<# bool Accept(HierarchicalTreeVisitor &visitor) override { if (visitor.PreVisit(*this)) { bool cont = identifier_->Accept(visitor); - for (auto &property : properties_) { - if (cont) { - cont = property.second->Accept(visitor); + if (auto *properties = std::get_if>(&properties_)) { + for (auto &property : *properties) { + if (cont) { + cont = property.second->Accept(visitor); + } } + } else { + std::get(properties_)->Accept(visitor); } if (cont && lower_bound_) { cont = lower_bound_->Accept(visitor); diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index 9365b12b9..572dba5bc 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -7,6 +7,7 @@ // of the same name, EOF. // This hides the definition of the macro which causes // the compilation to fail. +#include "query/frontend/ast/ast_visitor.hpp" #include "query/procedure/module.hpp" ////////////////////////////////////////////////////// #include "query/frontend/ast/cypher_main_visitor.hpp" @@ -21,6 +22,7 @@ #include #include #include +#include #include #include "query/exceptions.hpp" @@ -1111,7 +1113,12 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(MemgraphCypher::NodePatternCon node->labels_ = ctx->nodeLabels()->accept(this).as>(); } if (ctx->properties()) { - node->properties_ = ctx->properties()->accept(this).as>(); + // This can return either properties or parameters + if (ctx->properties()->mapLiteral()) { + node->properties_ = ctx->properties()->accept(this).as>(); + } else { + node->properties_ = ctx->properties()->accept(this).as(); + } } return node; } @@ -1125,15 +1132,12 @@ antlrcpp::Any CypherMainVisitor::visitNodeLabels(MemgraphCypher::NodeLabelsConte } antlrcpp::Any CypherMainVisitor::visitProperties(MemgraphCypher::PropertiesContext *ctx) { - if (!ctx->mapLiteral()) { - // If child is not mapLiteral that means child is params. At the moment - // we don't support properties to be a param because we can generate - // better logical plan if we have an information about properties at - // compile time. - // TODO: implement other clauses. - throw utils::NotYetImplemented("property parameters"); + if (ctx->mapLiteral()) { + return ctx->mapLiteral()->accept(this); } - return ctx->mapLiteral()->accept(this); + // If child is not mapLiteral that means child is params. + MG_ASSERT(ctx->parameter()); + return ctx->parameter()->accept(this); } antlrcpp::Any CypherMainVisitor::visitMapLiteral(MemgraphCypher::MapLiteralContext *ctx) { @@ -1332,7 +1336,12 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(MemgraphCypher::Relati case 0: break; case 1: { - edge->properties_ = properties[0]->accept(this).as>(); + if (properties[0]->mapLiteral()) { + edge->properties_ = properties[0]->accept(this).as>(); + break; + } + MG_ASSERT(properties[0]->parameter()); + edge->properties_ = properties[0]->accept(this).as(); break; } default: diff --git a/src/query/frontend/semantic/symbol_generator.cpp b/src/query/frontend/semantic/symbol_generator.cpp index 96aff3646..a2e9f8f91 100644 --- a/src/query/frontend/semantic/symbol_generator.cpp +++ b/src/query/frontend/semantic/symbol_generator.cpp @@ -6,7 +6,10 @@ #include #include +#include +#include "query/frontend/ast/ast.hpp" +#include "query/frontend/ast/ast_visitor.hpp" #include "utils/algorithm.hpp" #include "utils/logging.hpp" @@ -402,19 +405,33 @@ bool SymbolGenerator::PostVisit(Pattern &) { } bool SymbolGenerator::PreVisit(NodeAtom &node_atom) { + auto check_node_semantic = [&node_atom, this](const bool props_or_labels) { + const auto &node_name = node_atom.identifier_->name_; + if ((scope_.in_create || scope_.in_merge) && props_or_labels && HasSymbol(node_name)) { + throw SemanticException("Cannot create node '" + node_name + + "' with labels or properties, because it is already declared."); + } + scope_.in_pattern_atom_identifier = true; + node_atom.identifier_->Accept(*this); + scope_.in_pattern_atom_identifier = false; + }; + scope_.in_node_atom = true; - bool props_or_labels = !node_atom.properties_.empty() || !node_atom.labels_.empty(); - const auto &node_name = node_atom.identifier_->name_; - if ((scope_.in_create || scope_.in_merge) && props_or_labels && HasSymbol(node_name)) { - throw SemanticException("Cannot create node '" + node_name + - "' with labels or properties, because it is already declared."); + if (auto *properties = std::get_if>(&node_atom.properties_)) { + bool props_or_labels = !properties->empty() || !node_atom.labels_.empty(); + + check_node_semantic(props_or_labels); + for (auto kv : *properties) { + kv.second->Accept(*this); + } + + return false; } - for (auto kv : node_atom.properties_) { - kv.second->Accept(*this); - } - scope_.in_pattern_atom_identifier = true; - node_atom.identifier_->Accept(*this); - scope_.in_pattern_atom_identifier = false; + auto &properties_parameter = std::get(node_atom.properties_); + bool props_or_labels = !properties_parameter || !node_atom.labels_.empty(); + + check_node_semantic(props_or_labels); + properties_parameter->Accept(*this); return false; } @@ -444,8 +461,12 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) { "edge."); } } - for (auto kv : edge_atom.properties_) { - kv.second->Accept(*this); + if (auto *properties = std::get_if>(&edge_atom.properties_)) { + for (auto kv : *properties) { + kv.second->Accept(*this); + } + } else { + std::get(edge_atom.properties_)->Accept(*this); } if (edge_atom.IsVariable()) { scope_.in_edge_range = true; diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index b2c13c27b..492624a05 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -182,7 +182,18 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *fram storage::View::NEW); // TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory // when we update PropertyValue with custom allocator. - for (auto &kv : node_info.properties) PropsSetChecked(&new_node, kv.first, kv.second->Accept(evaluator)); + if (const auto *node_info_properties = std::get_if(&node_info.properties)) { + for (const auto &[key, value_expression] : *node_info_properties) { + PropsSetChecked(&new_node, key, value_expression->Accept(evaluator)); + } + } else { + auto property_map = evaluator.Visit(*std::get(node_info.properties)); + for (const auto &[key, value] : property_map.ValueMap()) { + auto property_id = dba.NameToProperty(key); + PropsSetChecked(&new_node, property_id, value); + } + } + (*frame)[node_info.symbol] = new_node; return (*frame)[node_info.symbol].ValueVertex(); } @@ -255,7 +266,18 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert auto maybe_edge = dba->InsertEdge(from, to, edge_info.edge_type); if (maybe_edge.HasValue()) { auto &edge = *maybe_edge; - for (auto kv : edge_info.properties) PropsSetChecked(&edge, kv.first, kv.second->Accept(*evaluator)); + if (const auto *properties = std::get_if(&edge_info.properties)) { + for (const auto &[key, value_expression] : *properties) { + PropsSetChecked(&edge, key, value_expression->Accept(*evaluator)); + } + } else { + auto property_map = evaluator->Visit(*std::get(edge_info.properties)); + for (const auto &[key, value] : property_map.ValueMap()) { + auto property_id = dba->NameToProperty(key); + PropsSetChecked(&edge, property_id, value); + } + } + (*frame)[edge_info.symbol] = edge; } else { switch (maybe_edge.GetError()) { diff --git a/src/query/plan/operator.lcp b/src/query/plan/operator.lcp index 34ea048e5..c605ee288 100644 --- a/src/query/plan/operator.lcp +++ b/src/query/plan/operator.lcp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "query/common.hpp" @@ -348,15 +349,54 @@ and false on every following Pull.") } cpp<#) +(defun clone-variant-properties (source destination) + #>cpp + if (const auto *props = std::get_if(&${source})) { + auto &destination_props = std::get(${destination}); + destination_props.resize(props->size()); + for (auto i0 = 0; i0 < props->size(); ++i0) { + { + storage::PropertyId first1 = (*props)[i0].first; + Expression *second2; + second2 = (*props)[i0].second ? (*props)[i0].second->Clone(storage) : nullptr; + destination_props[i0] = std::make_pair(std::move(first1), std::move(second2)); + } + } + } else { + ${destination} = std::get(${source})->Clone(storage); + } + cpp<#) + +#>cpp +using PropertiesMapList = std::vector>; +cpp<# + (lcp:define-struct node-creation-info () ((symbol "Symbol") (labels "std::vector") - (properties "std::vector>" + (properties "std::variant" :slk-save #'slk-save-properties - :slk-load #'slk-load-properties)) + :slk-load #'slk-load-properties + :clone #'clone-variant-properties)) (:serialize (:slk :save-args '((helper "query::plan::LogicalOperator::SaveHelper *")) :load-args '((helper "query::plan::LogicalOperator::SlkLoadHelper *")))) - (:clone :args '((storage "AstStorage *")))) + (:clone :args '((storage "AstStorage *"))) + (:public + #>cpp + NodeCreationInfo() = default; + + NodeCreationInfo( + Symbol symbol, std::vector labels, + std::variant properties) + : symbol{std::move(symbol)}, labels{std::move(labels)}, properties{std::move(properties)} {}; + + NodeCreationInfo(Symbol symbol, std::vector labels, + PropertiesMapList properties) + : symbol{std::move(symbol)}, labels{std::move(labels)}, properties{std::move(properties)} {}; + + NodeCreationInfo(Symbol symbol, std::vector labels, ParameterLookup* properties) + : symbol{std::move(symbol)}, labels{std::move(labels)}, properties{properties} {}; + cpp<#)) (lcp:define-class create-node (logical-operator) ((input "std::shared_ptr" :scope :public @@ -375,7 +415,7 @@ and false on every following Pull.") "Operator for creating a node. This op is used both for creating a single node (`CREATE` statement without -a preceeding `MATCH`), or multiple nodes (`MATCH ... CREATE` or +a preceding `MATCH`), or multiple nodes (`MATCH ... CREATE` or `CREATE (), () ...`). @sa CreateExpand") @@ -421,14 +461,31 @@ a preceeding `MATCH`), or multiple nodes (`MATCH ... CREATE` or (lcp:define-struct edge-creation-info () ((symbol "Symbol") - (properties "std::vector>" + (properties "std::variant" :slk-save #'slk-save-properties - :slk-load #'slk-load-properties) + :slk-load #'slk-load-properties + :clone #'clone-variant-properties) (edge-type "::storage::EdgeTypeId") (direction "::EdgeAtom::Direction" :initval "EdgeAtom::Direction::BOTH")) (:serialize (:slk :save-args '((helper "query::plan::LogicalOperator::SaveHelper *")) :load-args '((helper "query::plan::LogicalOperator::SlkLoadHelper *")))) - (:clone :args '((storage "AstStorage *")))) + (:clone :args '((storage "AstStorage *"))) + (:public + #>cpp + EdgeCreationInfo() = default; + + EdgeCreationInfo(Symbol symbol, std::variant properties, + storage::EdgeTypeId edge_type, EdgeAtom::Direction direction) + : symbol{std::move(symbol)}, properties{std::move(properties)}, edge_type{edge_type}, direction{direction} {}; + + EdgeCreationInfo(Symbol symbol, PropertiesMapList properties, + storage::EdgeTypeId edge_type, EdgeAtom::Direction direction) + : symbol{std::move(symbol)}, properties{std::move(properties)}, edge_type{edge_type}, direction{direction} {}; + + EdgeCreationInfo(Symbol symbol, ParameterLookup* properties, + storage::EdgeTypeId edge_type, EdgeAtom::Direction direction) + : symbol{std::move(symbol)}, properties{properties}, edge_type{edge_type}, direction{direction} {}; + cpp<#)) (lcp:define-class create-expand (logical-operator) ((node-info "NodeCreationInfo" :scope :public diff --git a/src/query/plan/preprocess.cpp b/src/query/plan/preprocess.cpp index b89071709..e3192dd79 100644 --- a/src/query/plan/preprocess.cpp +++ b/src/query/plan/preprocess.cpp @@ -1,8 +1,13 @@ -#include "query/plan/preprocess.hpp" - #include #include #include +#include +#include +#include + +#include "query/exceptions.hpp" +#include "query/frontend/ast/ast_visitor.hpp" +#include "query/plan/preprocess.hpp" namespace query::plan { @@ -218,55 +223,64 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table, UsedSymbolsCollector collector(symbol_table); auto add_properties_variable = [&](EdgeAtom *atom) { const auto &symbol = symbol_table.at(*atom->identifier_); - for (auto &prop_pair : atom->properties_) { - // We need to store two property-lookup filters in all_filters. One is - // used for inlining property filters into variable expansion, and - // utilizes the inner_edge symbol. The other is used for post-expansion - // filtering and does not use the inner_edge symbol, but the edge symbol - // (a list of edges). - { - collector.symbols_.clear(); - prop_pair.second->Accept(collector); - collector.symbols_.emplace(symbol_table.at(*atom->filter_lambda_.inner_node)); - collector.symbols_.emplace(symbol_table.at(*atom->filter_lambda_.inner_edge)); - // First handle the inline property filter. - auto *property_lookup = storage.Create(atom->filter_lambda_.inner_edge, prop_pair.first); - auto *prop_equal = storage.Create(property_lookup, prop_pair.second); - // Currently, variable expand has no gains if we set PropertyFilter. - all_filters_.emplace_back(FilterInfo{FilterInfo::Type::Generic, prop_equal, collector.symbols_}); - } - { - collector.symbols_.clear(); - prop_pair.second->Accept(collector); - collector.symbols_.insert(symbol); // PropertyLookup uses the symbol. - // Now handle the post-expansion filter. - // Create a new identifier and a symbol which will be filled in All. - auto *identifier = storage.Create(atom->identifier_->name_, atom->identifier_->user_declared_) - ->MapTo(symbol_table.CreateSymbol(atom->identifier_->name_, false)); - // Create an equality expression and store it in all_filters_. - auto *property_lookup = storage.Create(identifier, prop_pair.first); - auto *prop_equal = storage.Create(property_lookup, prop_pair.second); - // Currently, variable expand has no gains if we set PropertyFilter. - all_filters_.emplace_back(FilterInfo{ - FilterInfo::Type::Generic, - storage.Create(identifier, atom->identifier_, storage.Create(prop_equal)), collector.symbols_}); + if (auto *properties = std::get_if>(&atom->properties_)) { + for (auto &prop_pair : *properties) { + // We need to store two property-lookup filters in all_filters. One is + // used for inlining property filters into variable expansion, and + // utilizes the inner_edge symbol. The other is used for post-expansion + // filtering and does not use the inner_edge symbol, but the edge symbol + // (a list of edges). + { + collector.symbols_.clear(); + prop_pair.second->Accept(collector); + collector.symbols_.emplace(symbol_table.at(*atom->filter_lambda_.inner_node)); + collector.symbols_.emplace(symbol_table.at(*atom->filter_lambda_.inner_edge)); + // First handle the inline property filter. + auto *property_lookup = storage.Create(atom->filter_lambda_.inner_edge, prop_pair.first); + auto *prop_equal = storage.Create(property_lookup, prop_pair.second); + // Currently, variable expand has no gains if we set PropertyFilter. + all_filters_.emplace_back(FilterInfo{FilterInfo::Type::Generic, prop_equal, collector.symbols_}); + } + { + collector.symbols_.clear(); + prop_pair.second->Accept(collector); + collector.symbols_.insert(symbol); // PropertyLookup uses the symbol. + // Now handle the post-expansion filter. + // Create a new identifier and a symbol which will be filled in All. + auto *identifier = storage.Create(atom->identifier_->name_, atom->identifier_->user_declared_) + ->MapTo(symbol_table.CreateSymbol(atom->identifier_->name_, false)); + // Create an equality expression and store it in all_filters_. + auto *property_lookup = storage.Create(identifier, prop_pair.first); + auto *prop_equal = storage.Create(property_lookup, prop_pair.second); + // Currently, variable expand has no gains if we set PropertyFilter. + all_filters_.emplace_back( + FilterInfo{FilterInfo::Type::Generic, + storage.Create(identifier, atom->identifier_, storage.Create(prop_equal)), + collector.symbols_}); + } } + return; } + throw SemanticException("Property map matching not supported in MATCH/MERGE clause!"); }; auto add_properties = [&](auto *atom) { const auto &symbol = symbol_table.at(*atom->identifier_); - for (auto &prop_pair : atom->properties_) { - // Create an equality expression and store it in all_filters_. - auto *property_lookup = storage.Create(atom->identifier_, prop_pair.first); - auto *prop_equal = storage.Create(property_lookup, prop_pair.second); - collector.symbols_.clear(); - prop_equal->Accept(collector); - FilterInfo filter_info{FilterInfo::Type::Property, prop_equal, collector.symbols_}; - // Store a PropertyFilter on the value of the property. - filter_info.property_filter.emplace(symbol_table, symbol, prop_pair.first, prop_pair.second, - PropertyFilter::Type::EQUAL); - all_filters_.emplace_back(filter_info); + if (auto *properties = std::get_if>(&atom->properties_)) { + for (auto &prop_pair : *properties) { + // Create an equality expression and store it in all_filters_. + auto *property_lookup = storage.Create(atom->identifier_, prop_pair.first); + auto *prop_equal = storage.Create(property_lookup, prop_pair.second); + collector.symbols_.clear(); + prop_equal->Accept(collector); + FilterInfo filter_info{FilterInfo::Type::Property, prop_equal, collector.symbols_}; + // Store a PropertyFilter on the value of the property. + filter_info.property_filter.emplace(symbol_table, symbol, prop_pair.first, prop_pair.second, + PropertyFilter::Type::EQUAL); + all_filters_.emplace_back(filter_info); + } + return; } + throw SemanticException("Property map matching not supported in MATCH/MERGE clause!"); }; auto add_node_filter = [&](NodeAtom *node) { const auto &node_symbol = symbol_table.at(*node->identifier_); diff --git a/src/query/plan/pretty_print.cpp b/src/query/plan/pretty_print.cpp index cd3fe0069..65db68f51 100644 --- a/src/query/plan/pretty_print.cpp +++ b/src/query/plan/pretty_print.cpp @@ -1,4 +1,5 @@ #include "query/plan/pretty_print.hpp" +#include #include "query/db_accessor.hpp" #include "query/frontend/ast/pretty_print.hpp" @@ -353,14 +354,16 @@ json ToJson(const NodeCreationInfo &node_info, const DbAccessor &dba) { json self; self["symbol"] = ToJson(node_info.symbol); self["labels"] = ToJson(node_info.labels, dba); - self["properties"] = ToJson(node_info.properties, dba); + const auto *props = std::get_if(&node_info.properties); + self["properties"] = ToJson(props ? *props : PropertiesMapList{}, dba); return self; } json ToJson(const EdgeCreationInfo &edge_info, const DbAccessor &dba) { json self; self["symbol"] = ToJson(edge_info.symbol); - self["properties"] = ToJson(edge_info.properties, dba); + const auto *props = std::get_if(&edge_info.properties); + self["properties"] = ToJson(props ? *props : PropertiesMapList{}, dba); self["edge_type"] = ToJson(edge_info.edge_type, dba); self["direction"] = ToString(edge_info.direction); return self; diff --git a/src/query/plan/rule_based_planner.hpp b/src/query/plan/rule_based_planner.hpp index 30300962b..7e69db3eb 100644 --- a/src/query/plan/rule_based_planner.hpp +++ b/src/query/plan/rule_based_planner.hpp @@ -2,10 +2,12 @@ #pragma once #include +#include #include "gflags/gflags.h" #include "query/frontend/ast/ast.hpp" +#include "query/frontend/ast/ast_visitor.hpp" #include "query/plan/operator.hpp" #include "query/plan/preprocess.hpp" #include "utils/logging.hpp" @@ -247,11 +249,19 @@ class RuleBasedPlanner { for (const auto &label : node.labels_) { labels.push_back(GetLabel(label)); } - std::vector> properties; - properties.reserve(node.properties_.size()); - for (const auto &kv : node.properties_) { - properties.push_back({GetProperty(kv.first), kv.second}); - } + + auto properties = std::invoke([&]() -> std::variant { + if (const auto *node_properties = + std::get_if>(&node.properties_)) { + PropertiesMapList vector_props; + vector_props.reserve(node_properties->size()); + for (const auto &kv : *node_properties) { + vector_props.push_back({GetProperty(kv.first), kv.second}); + } + return std::move(vector_props); + } + return std::get(node.properties_); + }); return NodeCreationInfo{node_symbol, labels, properties}; }; @@ -260,9 +270,8 @@ class RuleBasedPlanner { if (bound_symbols.insert(node_symbol).second) { auto node_info = node_to_creation_info(*node); return std::make_unique(std::move(input_op), node_info); - } else { - return std::move(input_op); } + return std::move(input_op); }; auto collect = [&](std::unique_ptr last_op, NodeAtom *prev_node, EdgeAtom *edge, NodeAtom *node) { @@ -279,11 +288,19 @@ class RuleBasedPlanner { LOG_FATAL("Symbols used for created edges cannot be redeclared."); } auto node_info = node_to_creation_info(*node); - std::vector> properties; - properties.reserve(edge->properties_.size()); - for (const auto &kv : edge->properties_) { - properties.push_back({GetProperty(kv.first), kv.second}); - } + auto properties = std::invoke([&]() -> std::variant { + if (const auto *edge_properties = + std::get_if>(&edge->properties_)) { + PropertiesMapList vector_props; + vector_props.reserve(edge_properties->size()); + for (const auto &kv : *edge_properties) { + vector_props.push_back({GetProperty(kv.first), kv.second}); + } + return std::move(vector_props); + } + return std::get(edge->properties_); + }); + MG_ASSERT(edge->edge_types_.size() == 1, "Creating an edge with a single type should be required by syntax"); EdgeCreationInfo edge_info{edge_symbol, properties, GetEdgeType(edge->edge_types_[0]), edge->direction_}; return std::make_unique(node_info, edge_info, std::move(last_op), input_symbol, node_existing); diff --git a/tests/benchmark/query/planner.cpp b/tests/benchmark/query/planner.cpp index d9943b221..4c273acb1 100644 --- a/tests/benchmark/query/planner.cpp +++ b/tests/benchmark/query/planner.cpp @@ -1,6 +1,6 @@ -#include - #include +#include +#include #include "query/frontend/semantic/symbol_generator.hpp" #include "query/plan/cost_estimator.hpp" @@ -70,7 +70,7 @@ static query::CypherQuery *AddIndexedMatches(int num_matches, const std::string std::string node1_name = "node" + std::to_string(i - 1); auto *node = storage.Create(storage.Create(node1_name)); node->labels_.emplace_back(storage.GetLabelIx(label)); - node->properties_[storage.GetPropertyIx(property)] = storage.Create(i); + std::get<0>(node->properties_)[storage.GetPropertyIx(property)] = storage.Create(i); pattern->atoms_.emplace_back(node); single_query->clauses_.emplace_back(match); query->single_query_ = single_query; diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 7fe229568..a92c0daea 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include ////////////////////////////////////////////////////// @@ -914,7 +915,7 @@ TEST_P(CypherMainVisitorTest, NodePattern) { EXPECT_THAT(node->labels_, UnorderedElementsAre(ast_generator.Label("label1"), ast_generator.Label("label2"), ast_generator.Label("label3"))); std::unordered_map properties; - for (auto x : node->properties_) { + for (auto x : std::get<0>(node->properties_)) { TypedValue value = ast_generator.LiteralValue(x.second); ASSERT_TRUE(value.type() == TypedValue::Type::Int); properties[x.first] = value.ValueInt(); @@ -943,7 +944,7 @@ TEST_P(CypherMainVisitorTest, NodePatternIdentifier) { EXPECT_EQ(node->identifier_->name_, "var"); EXPECT_TRUE(node->identifier_->user_declared_); EXPECT_THAT(node->labels_, UnorderedElementsAre()); - EXPECT_THAT(node->properties_, UnorderedElementsAre()); + EXPECT_THAT(std::get<0>(node->properties_), UnorderedElementsAre()); } TEST_P(CypherMainVisitorTest, RelationshipPatternNoDetails) { @@ -1029,7 +1030,7 @@ TEST_P(CypherMainVisitorTest, RelationshipPatternDetails) { EXPECT_THAT(edge->edge_types_, UnorderedElementsAre(ast_generator.EdgeType("type1"), ast_generator.EdgeType("type2"))); std::unordered_map properties; - for (auto x : edge->properties_) { + for (auto x : std::get<0>(edge->properties_)) { TypedValue value = ast_generator.LiteralValue(x.second); ASSERT_TRUE(value.type() == TypedValue::Type::Int); properties[x.first] = value.ValueInt(); @@ -1169,7 +1170,7 @@ TEST_P(CypherMainVisitorTest, RelationshipPatternUnboundedWithProperty) { EXPECT_EQ(edge->type_, EdgeAtom::Type::DEPTH_FIRST); EXPECT_EQ(edge->lower_bound_, nullptr); EXPECT_EQ(edge->upper_bound_, nullptr); - ast_generator.CheckLiteral(edge->properties_[ast_generator.Prop("prop")], 42); + ast_generator.CheckLiteral(std::get<0>(edge->properties_)[ast_generator.Prop("prop")], 42); } TEST_P(CypherMainVisitorTest, RelationshipPatternDotsUnboundedWithEdgeTypeProperty) { @@ -1186,7 +1187,7 @@ TEST_P(CypherMainVisitorTest, RelationshipPatternDotsUnboundedWithEdgeTypeProper EXPECT_EQ(edge->type_, EdgeAtom::Type::DEPTH_FIRST); EXPECT_EQ(edge->lower_bound_, nullptr); EXPECT_EQ(edge->upper_bound_, nullptr); - ast_generator.CheckLiteral(edge->properties_[ast_generator.Prop("prop")], 42); + ast_generator.CheckLiteral(std::get<0>(edge->properties_)[ast_generator.Prop("prop")], 42); ASSERT_EQ(edge->edge_types_.size(), 1U); auto edge_type = ast_generator.EdgeType("edge_type"); EXPECT_EQ(edge->edge_types_[0], edge_type); @@ -1205,7 +1206,7 @@ TEST_P(CypherMainVisitorTest, RelationshipPatternUpperBoundedWithProperty) { EXPECT_EQ(edge->type_, EdgeAtom::Type::DEPTH_FIRST); EXPECT_EQ(edge->lower_bound_, nullptr); ast_generator.CheckLiteral(edge->upper_bound_, 2); - ast_generator.CheckLiteral(edge->properties_[ast_generator.Prop("prop")], 42); + ast_generator.CheckLiteral(std::get<0>(edge->properties_)[ast_generator.Prop("prop")], 42); } // TODO maybe uncomment diff --git a/tests/unit/interpreter.cpp b/tests/unit/interpreter.cpp index 5cc9df849..abd0d9417 100644 --- a/tests/unit/interpreter.cpp +++ b/tests/unit/interpreter.cpp @@ -224,6 +224,77 @@ TEST_F(InterpreterTest, Parameters) { } } +// Run CREATE/MATCH/MERGE queries with property map +TEST_F(InterpreterTest, ParametersAsPropertyMap) { + { + std::map property_map{}; + property_map["name"] = storage::PropertyValue("name1"); + property_map["age"] = storage::PropertyValue(25); + auto stream = Interpret("CREATE (n $prop) RETURN n", { + {"prop", storage::PropertyValue(property_map)}, + }); + ASSERT_EQ(stream.GetHeader().size(), 1U); + ASSERT_EQ(stream.GetHeader()[0], "n"); + ASSERT_EQ(stream.GetResults().size(), 1U); + ASSERT_EQ(stream.GetResults()[0].size(), 1U); + auto result = stream.GetResults()[0][0].ValueVertex(); + EXPECT_EQ(result.properties["name"].ValueString(), "name1"); + EXPECT_EQ(result.properties["age"].ValueInt(), 25); + } + { + std::map property_map{}; + property_map["name"] = storage::PropertyValue("name1"); + property_map["age"] = storage::PropertyValue(25); + Interpret("CREATE (:Person)"); + auto stream = + Interpret("MATCH (m: Person) CREATE (n $prop) RETURN n", { + {"prop", storage::PropertyValue(property_map)}, + }); + ASSERT_EQ(stream.GetHeader().size(), 1U); + ASSERT_EQ(stream.GetHeader()[0], "n"); + ASSERT_EQ(stream.GetResults().size(), 1U); + ASSERT_EQ(stream.GetResults()[0].size(), 1U); + auto result = stream.GetResults()[0][0].ValueVertex(); + EXPECT_EQ(result.properties["name"].ValueString(), "name1"); + EXPECT_EQ(result.properties["age"].ValueInt(), 25); + } + { + std::map property_map{}; + property_map["name"] = storage::PropertyValue("name1"); + property_map["weight"] = storage::PropertyValue(121); + auto stream = Interpret("CREATE ()-[r:TO $prop]->() RETURN r", { + {"prop", storage::PropertyValue(property_map)}, + }); + ASSERT_EQ(stream.GetHeader().size(), 1U); + ASSERT_EQ(stream.GetHeader()[0], "r"); + ASSERT_EQ(stream.GetResults().size(), 1U); + ASSERT_EQ(stream.GetResults()[0].size(), 1U); + auto result = stream.GetResults()[0][0].ValueEdge(); + EXPECT_EQ(result.properties["name"].ValueString(), "name1"); + EXPECT_EQ(result.properties["weight"].ValueInt(), 121); + } + { + std::map property_map{}; + property_map["name"] = storage::PropertyValue("name1"); + property_map["age"] = storage::PropertyValue(15); + ASSERT_THROW(Interpret("MATCH (n $prop) RETURN n", + { + {"prop", storage::PropertyValue(property_map)}, + }), + query::SemanticException); + } + { + std::map property_map{}; + property_map["name"] = storage::PropertyValue("name1"); + property_map["age"] = storage::PropertyValue(15); + ASSERT_THROW(Interpret("MERGE (n $prop) RETURN n", + { + {"prop", storage::PropertyValue(property_map)}, + }), + query::SemanticException); + } +} + // Test bfs end to end. TEST_F(InterpreterTest, Bfs) { srand(0); diff --git a/tests/unit/query_plan.cpp b/tests/unit/query_plan.cpp index c3679223d..826a54481 100644 --- a/tests/unit/query_plan.cpp +++ b/tests/unit/query_plan.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -617,10 +618,10 @@ TYPED_TEST(TestPlanner, MatchCrossReferenceVariable) { AstStorage storage; auto node_n = NODE("n"); auto m_prop = PROPERTY_LOOKUP("m", prop.second); - node_n->properties_[storage.GetPropertyIx(prop.first)] = m_prop; + std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = m_prop; auto node_m = NODE("m"); auto n_prop = PROPERTY_LOOKUP("n", prop.second); - node_m->properties_[storage.GetPropertyIx(prop.first)] = n_prop; + std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop; auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n), PATTERN(node_m)), RETURN("n"))); // We expect both ScanAll to come before filters (2 are joined into one), // because they need to populate the symbol values. @@ -765,7 +766,7 @@ TYPED_TEST(TestPlanner, UnwindMergeNodeProperty) { AstStorage storage; FakeDbAccessor dba; auto node_n = NODE("n"); - node_n->properties_[storage.GetPropertyIx("prop")] = IDENT("i"); + std::get<0>(node_n->properties_)[storage.GetPropertyIx("prop")] = IDENT("i"); auto *query = QUERY(SINGLE_QUERY(UNWIND(LIST(LITERAL(1)), AS("i")), MERGE(PATTERN(node_n)))); std::list on_match{new ExpectScanAll(), new ExpectFilter()}; std::list on_create{new ExpectCreateNode()}; @@ -783,7 +784,7 @@ TYPED_TEST(TestPlanner, UnwindMergeNodePropertyWithIndex) { const auto property = PROPERTY_PAIR("prop"); dba.SetIndexCount(label, property.second, 1); auto node_n = NODE("n", label_name); - node_n->properties_[storage.GetPropertyIx(property.first)] = IDENT("i"); + std::get<0>(node_n->properties_)[storage.GetPropertyIx(property.first)] = IDENT("i"); auto *query = QUERY(SINGLE_QUERY(UNWIND(LIST(LITERAL(1)), AS("i")), MERGE(PATTERN(node_n)))); std::list on_match{new ExpectScanAllByLabelPropertyValue(label, property, IDENT("i"))}; std::list on_create{new ExpectCreateNode()}; @@ -916,8 +917,8 @@ TYPED_TEST(TestPlanner, AtomIndexedLabelProperty) { dba.SetIndexCount(label, property.second, 1); auto node = NODE("n", "label"); auto lit_42 = LITERAL(42); - node->properties_[storage.GetPropertyIx(property.first)] = lit_42; - node->properties_[storage.GetPropertyIx(not_indexed.first)] = LITERAL(0); + std::get<0>(node->properties_)[storage.GetPropertyIx(property.first)] = lit_42; + std::get<0>(node->properties_)[storage.GetPropertyIx(not_indexed.first)] = LITERAL(0); auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), RETURN("n"))); auto symbol_table = query::MakeSymbolTable(query); auto planner = MakePlanner(&dba, storage, symbol_table, query); @@ -935,7 +936,7 @@ TYPED_TEST(TestPlanner, AtomPropertyWhereLabelIndexing) { dba.SetIndexCount(label, property.second, 0); auto node = NODE("n"); auto lit_42 = LITERAL(42); - node->properties_[storage.GetPropertyIx(property.first)] = lit_42; + std::get<0>(node->properties_)[storage.GetPropertyIx(property.first)] = lit_42; auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), WHERE(AND(PROPERTY_LOOKUP("n", not_indexed), @@ -1141,7 +1142,7 @@ TYPED_TEST(TestPlanner, MatchExpandVariableInlinedFilter) { auto prop = PROPERTY_PAIR("prop"); AstStorage storage; auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH, {type}); - edge->properties_[storage.GetPropertyIx(prop.first)] = LITERAL(42); + std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42); auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r"))); CheckPlan(query, storage, ExpectScanAll(), ExpectExpandVariable(), // Filter is both inlined and post-expand @@ -1155,7 +1156,7 @@ TYPED_TEST(TestPlanner, MatchExpandVariableNotInlinedFilter) { auto prop = PROPERTY_PAIR("prop"); AstStorage storage; auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH, {type}); - edge->properties_[storage.GetPropertyIx(prop.first)] = EQ(PROPERTY_LOOKUP("m", prop), LITERAL(42)); + std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = EQ(PROPERTY_LOOKUP("m", prop), LITERAL(42)); auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r"))); CheckPlan(query, storage, ExpectScanAll(), ExpectExpandVariable(), ExpectFilter(), ExpectProduce()); } @@ -1241,7 +1242,7 @@ TYPED_TEST(TestPlanner, MatchScanToExpand) { dba.SetIndexCount(label, FLAGS_query_vertex_count_to_expand_existing + 1); AstStorage storage; auto node_m = NODE("m", "label"); - node_m->properties_[storage.GetPropertyIx("property")] = LITERAL(1); + std::get<0>(node_m->properties_)[storage.GetPropertyIx("property")] = LITERAL(1); auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), node_m)), RETURN("r"))); auto symbol_table = query::MakeSymbolTable(query); auto planner = MakePlanner(&dba, storage, symbol_table, query); diff --git a/tests/unit/query_plan_create_set_remove_delete.cpp b/tests/unit/query_plan_create_set_remove_delete.cpp index c734df0f9..2cf19f1c9 100644 --- a/tests/unit/query_plan_create_set_remove_delete.cpp +++ b/tests/unit/query_plan_create_set_remove_delete.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "gmock/gmock.h" @@ -29,7 +30,8 @@ TEST(QueryPlan, CreateNodeWithAttributes) { NodeCreationInfo node; node.symbol = symbol_table.CreateSymbol("n", true); node.labels.emplace_back(label); - node.properties.emplace_back(property.second, LITERAL(42)); + std::get>>(node.properties) + .emplace_back(property.second, LITERAL(42)); auto create = std::make_shared(nullptr, node); auto context = MakeContext(storage, symbol_table, &dba); @@ -73,7 +75,8 @@ TEST(QueryPlan, CreateReturn) { NodeCreationInfo node; node.symbol = symbol_table.CreateSymbol("n", true); node.labels.emplace_back(label); - node.properties.emplace_back(property.second, LITERAL(42)); + std::get>>(node.properties) + .emplace_back(property.second, LITERAL(42)); auto create = std::make_shared(nullptr, node); auto named_expr_n = @@ -118,18 +121,20 @@ TEST(QueryPlan, CreateExpand) { NodeCreationInfo n; n.symbol = symbol_table.CreateSymbol("n", true); n.labels.emplace_back(label_node_1); - n.properties.emplace_back(property.second, LITERAL(1)); + std::get>>(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); - m.properties.emplace_back(property.second, LITERAL(2)); + std::get>>(m.properties) + .emplace_back(property.second, LITERAL(2)); EdgeCreationInfo r; r.symbol = symbol_table.CreateSymbol("r", true); r.edge_type = edge_type; - r.properties.emplace_back(property.second, LITERAL(3)); + std::get<0>(r.properties).emplace_back(property.second, LITERAL(3)); auto create_op = std::make_shared(nullptr, n); auto create_expand = std::make_shared(m, r, create_op, n.symbol, cycle); @@ -717,7 +722,7 @@ TEST(QueryPlan, NodeFilterSet) { SymbolTable symbol_table; // MATCH (n {prop: 42}) -[r]- (m) auto scan_all = MakeScanAll(storage, symbol_table, "n"); - scan_all.node_->properties_[storage.GetPropertyIx(prop.first)] = LITERAL(42); + std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42); auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false, storage::View::OLD); auto *filter_expr = @@ -755,7 +760,7 @@ TEST(QueryPlan, FilterRemove) { SymbolTable symbol_table; // MATCH (n) -[r]- (m) WHERE n.prop < 43 auto scan_all = MakeScanAll(storage, symbol_table, "n"); - scan_all.node_->properties_[storage.GetPropertyIx(prop.first)] = LITERAL(42); + std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42); auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false, storage::View::OLD); auto filter_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop); diff --git a/tests/unit/query_plan_match_filter_return.cpp b/tests/unit/query_plan_match_filter_return.cpp index e7d78a51a..79d15913a 100644 --- a/tests/unit/query_plan_match_filter_return.cpp +++ b/tests/unit/query_plan_match_filter_return.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -165,7 +166,7 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) { // make a scan all auto n = MakeScanAll(storage, symbol_table, "n"); n.node_->labels_.emplace_back(storage.GetLabelIx(dba.LabelToName(label))); - n.node_->properties_[storage.GetPropertyIx(property.first)] = LITERAL(42); + std::get<0>(n.node_->properties_)[storage.GetPropertyIx(property.first)] = LITERAL(42); // node filtering auto *filter_expr = AND(storage.Create(n.node_->identifier_, n.node_->labels_), @@ -1449,7 +1450,7 @@ TEST(QueryPlan, EdgeFilter) { auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {edge_type}, "m", false, storage::View::OLD); r_m.edge_->edge_types_.push_back(storage.GetEdgeTypeIx(dba.EdgeTypeToName(edge_type))); - r_m.edge_->properties_[storage.GetPropertyIx(prop.first)] = LITERAL(42); + std::get<0>(r_m.edge_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42); auto *filter_expr = EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42)); auto edge_filter = std::make_shared(r_m.op_, filter_expr); diff --git a/tests/unit/query_plan_v2_create_set_remove_delete.cpp b/tests/unit/query_plan_v2_create_set_remove_delete.cpp index 1abf73b26..363c53332 100644 --- a/tests/unit/query_plan_v2_create_set_remove_delete.cpp +++ b/tests/unit/query_plan_v2_create_set_remove_delete.cpp @@ -19,7 +19,8 @@ TEST(QueryPlan, CreateNodeWithAttributes) { query::plan::NodeCreationInfo node; node.symbol = symbol_table.CreateSymbol("n", true); node.labels.emplace_back(label); - node.properties.emplace_back(property, ast.Create(42)); + std::get>>(node.properties) + .emplace_back(property, ast.Create(42)); query::plan::CreateNode create_node(nullptr, node); DbAccessor execution_dba(&dba); diff --git a/tests/unit/query_semantic.cpp b/tests/unit/query_semantic.cpp index 1e19f6594..1811ace5b 100644 --- a/tests/unit/query_semantic.cpp +++ b/tests/unit/query_semantic.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "gtest/gtest.h" @@ -75,8 +76,8 @@ TEST_F(TestSymbolGenerator, MatchNodeUnboundReturn) { TEST_F(TestSymbolGenerator, CreatePropertyUnbound) { // AST with unbound variable in create: CREATE ({prop: x}) auto node = NODE("anon"); - node->properties_[storage.GetPropertyIx("prop")] = IDENT("x"); - auto query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(node)))); + std::get<0>(node->properties_)[storage.GetPropertyIx("prop")] = IDENT("x"); + auto *query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(node)))); EXPECT_THROW(query::MakeSymbolTable(query_ast), UnboundVariableError); } @@ -292,7 +293,7 @@ TEST_F(TestSymbolGenerator, CreateExpandProperty) { // Test CREATE (n) -[r :r]-> (n {prop: 42}) auto r_type = "r"; auto n_prop = NODE("n"); - n_prop->properties_[storage.GetPropertyIx("prop")] = LITERAL(42); + std::get<0>(n_prop->properties_)[storage.GetPropertyIx("prop")] = LITERAL(42); auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", EdgeAtom::Direction::OUT, {r_type}), n_prop)))); EXPECT_THROW(query::MakeSymbolTable(query), SemanticException); } @@ -337,7 +338,7 @@ TEST_F(TestSymbolGenerator, MatchPropCreateNodeProp) { auto node_n = NODE("n"); auto node_m = NODE("m"); auto n_prop = PROPERTY_LOOKUP("n", prop.second); - node_m->properties_[storage.GetPropertyIx(prop.first)] = n_prop; + std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop; auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n)), CREATE(PATTERN(node_m)))); auto symbol_table = query::MakeSymbolTable(query); // symbols: pattern * 2, `node_n`, `node_m` @@ -552,10 +553,10 @@ TEST_F(TestSymbolGenerator, MatchCrossReferenceVariable) { auto prop = PROPERTY_PAIR("prop"); auto node_n = NODE("n"); auto m_prop = PROPERTY_LOOKUP("m", prop.second); - node_n->properties_[storage.GetPropertyIx(prop.first)] = m_prop; + std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = m_prop; auto node_m = NODE("m"); auto n_prop = PROPERTY_LOOKUP("n", prop.second); - node_m->properties_[storage.GetPropertyIx(prop.first)] = n_prop; + std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop; auto ident_n = IDENT("n"); auto as_n = AS("n"); auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n), PATTERN(node_m)), RETURN(ident_n, as_n))); @@ -624,7 +625,7 @@ TEST_F(TestSymbolGenerator, MatchEdgeWithIdentifierInProperty) { auto prop = PROPERTY_PAIR("prop"); auto edge = EDGE("r"); auto n_prop = PROPERTY_LOOKUP("n", prop.second); - edge->properties_[storage.GetPropertyIx(prop.first)] = n_prop; + std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = n_prop; auto node_n = NODE("n"); auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge, NODE("m"))), RETURN("r"))); auto symbol_table = query::MakeSymbolTable(query); @@ -707,7 +708,7 @@ TEST_F(TestSymbolGenerator, MatchPropertySameIdentifier) { auto prop = PROPERTY_PAIR("prop"); auto node_n = NODE("n"); auto n_prop = PROPERTY_LOOKUP("n", prop.second); - node_n->properties_[storage.GetPropertyIx(prop.first)] = n_prop; + std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = n_prop; auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n)), RETURN("n"))); auto symbol_table = query::MakeSymbolTable(query); auto n = symbol_table.at(*node_n->identifier_); @@ -1140,7 +1141,8 @@ TEST_F(TestSymbolGenerator, PredefinedIdentifiers) { // UNWIND first_op as u CREATE(first_op {prop: u}) auto unwind = UNWIND(first_op, AS("u")); auto node = NODE("first_op"); - node->properties_[storage.GetPropertyIx("prop")] = dynamic_cast(unwind->named_expression_->expression_); + std::get<0>(node->properties_)[storage.GetPropertyIx("prop")] = + dynamic_cast(unwind->named_expression_->expression_); query = QUERY(SINGLE_QUERY(unwind, CREATE(PATTERN(node)))); ASSERT_THROW(query::MakeSymbolTable(query, {first_op}), SemanticException); } diff --git a/tests/unit/query_variable_start_planner.cpp b/tests/unit/query_variable_start_planner.cpp index d819bac8b..570d7806f 100644 --- a/tests/unit/query_variable_start_planner.cpp +++ b/tests/unit/query_variable_start_planner.cpp @@ -1,4 +1,5 @@ #include +#include #include "gtest/gtest.h" @@ -277,7 +278,7 @@ TEST(TestVariableStartPlanner, MatchVariableExpandBoth) { AstStorage storage; auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH); auto node_n = NODE("n"); - node_n->properties_[storage.GetPropertyIx("id")] = LITERAL(1); + std::get<0>(node_n->properties_)[storage.GetPropertyIx("id")] = LITERAL(1); auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge, NODE("m"))), RETURN("r"))); // We expect to get a single column with the following rows: TypedValue r1_list(std::vector{TypedValue(r1)}); // [r1]