Add Property Map support in CREATE clause (#220)
This commit is contained in:
parent
2afc1b99f6
commit
f560293657
@ -3,6 +3,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#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<std::unordered_map<PropertyIx, Expression *>>(&${source})) {
|
||||
auto &new_obj_properties = std::get<std::unordered_map<PropertyIx, Expression *>>(${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<ParameterLookup *>(${source})->Clone(storage);
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(lcp:define-class node-atom (pattern-atom)
|
||||
((labels "std::vector<LabelIx>" :scope :public
|
||||
:slk-load (lambda (member)
|
||||
@ -1249,21 +1263,23 @@ cpp<#
|
||||
}
|
||||
cpp<#)
|
||||
:clone (clone-name-ix-vector "Label"))
|
||||
(properties "std::unordered_map<PropertyIx, Expression *>"
|
||||
:slk-save #'slk-save-property-map
|
||||
:slk-load #'slk-load-property-map
|
||||
:clone #'clone-property-map
|
||||
(properties "std::variant<std::unordered_map<PropertyIx, Expression *>, ParameterLookup*>"
|
||||
:clone #'clone-variant-properties
|
||||
:scope :public))
|
||||
(:public
|
||||
#>cpp
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
if (auto* properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&properties_)) {
|
||||
bool cont = identifier_->Accept(visitor);
|
||||
for (auto &property : properties_) {
|
||||
for (auto &property : *properties) {
|
||||
if (cont) {
|
||||
cont = property.second->Accept(visitor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::get<ParameterLookup*>(properties_)->Accept(visitor);
|
||||
}
|
||||
}
|
||||
return visitor.PostVisit(*this);
|
||||
}
|
||||
@ -1293,11 +1309,11 @@ cpp<#
|
||||
}
|
||||
cpp<#)
|
||||
:clone (clone-name-ix-vector "EdgeType"))
|
||||
(properties "std::unordered_map<PropertyIx, Expression *>"
|
||||
(properties "std::variant<std::unordered_map<PropertyIx, Expression *>, 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,11 +1365,15 @@ cpp<#
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
bool cont = identifier_->Accept(visitor);
|
||||
for (auto &property : properties_) {
|
||||
if (auto *properties = std::get_if<std::unordered_map<query::PropertyIx, query::Expression *>>(&properties_)) {
|
||||
for (auto &property : *properties) {
|
||||
if (cont) {
|
||||
cont = property.second->Accept(visitor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::get<ParameterLookup *>(properties_)->Accept(visitor);
|
||||
}
|
||||
if (cont && lower_bound_) {
|
||||
cont = lower_bound_->Accept(visitor);
|
||||
}
|
||||
|
@ -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 <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "query/exceptions.hpp"
|
||||
@ -1111,7 +1113,12 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(MemgraphCypher::NodePatternCon
|
||||
node->labels_ = ctx->nodeLabels()->accept(this).as<std::vector<LabelIx>>();
|
||||
}
|
||||
if (ctx->properties()) {
|
||||
// This can return either properties or parameters
|
||||
if (ctx->properties()->mapLiteral()) {
|
||||
node->properties_ = ctx->properties()->accept(this).as<std::unordered_map<PropertyIx, Expression *>>();
|
||||
} else {
|
||||
node->properties_ = ctx->properties()->accept(this).as<ParameterLookup *>();
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@ -1125,16 +1132,13 @@ 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);
|
||||
}
|
||||
// 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) {
|
||||
std::unordered_map<PropertyIx, Expression *> map;
|
||||
@ -1332,9 +1336,14 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(MemgraphCypher::Relati
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
if (properties[0]->mapLiteral()) {
|
||||
edge->properties_ = properties[0]->accept(this).as<std::unordered_map<PropertyIx, Expression *>>();
|
||||
break;
|
||||
}
|
||||
MG_ASSERT(properties[0]->parameter());
|
||||
edge->properties_ = properties[0]->accept(this).as<ParameterLookup *>();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw SemanticException("Only one property map can be supplied for edge.");
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
#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) {
|
||||
scope_.in_node_atom = true;
|
||||
bool props_or_labels = !node_atom.properties_.empty() || !node_atom.labels_.empty();
|
||||
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.");
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
scope_.in_node_atom = true;
|
||||
if (auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&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;
|
||||
}
|
||||
auto &properties_parameter = std::get<ParameterLookup *>(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,9 +461,13 @@ bool SymbolGenerator::PreVisit(EdgeAtom &edge_atom) {
|
||||
"edge.");
|
||||
}
|
||||
}
|
||||
for (auto kv : edge_atom.properties_) {
|
||||
if (auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&edge_atom.properties_)) {
|
||||
for (auto kv : *properties) {
|
||||
kv.second->Accept(*this);
|
||||
}
|
||||
} else {
|
||||
std::get<ParameterLookup *>(edge_atom.properties_)->Accept(*this);
|
||||
}
|
||||
if (edge_atom.IsVariable()) {
|
||||
scope_.in_edge_range = true;
|
||||
if (edge_atom.lower_bound_) {
|
||||
|
@ -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<PropertiesMapList>(&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<ParameterLookup *>(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<PropertiesMapList>(&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<ParameterLookup *>(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()) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#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<PropertiesMapList>(&${source})) {
|
||||
auto &destination_props = std::get<PropertiesMapList>(${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<ParameterLookup *>(${source})->Clone(storage);
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
#>cpp
|
||||
using PropertiesMapList = std::vector<std::pair<storage::PropertyId, Expression *>>;
|
||||
cpp<#
|
||||
|
||||
(lcp:define-struct node-creation-info ()
|
||||
((symbol "Symbol")
|
||||
(labels "std::vector<storage::LabelId>")
|
||||
(properties "std::vector<std::pair<storage::PropertyId, Expression *>>"
|
||||
(properties "std::variant<PropertiesMapList, ParameterLookup *>"
|
||||
: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<storage::LabelId> 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)
|
||||
: symbol{std::move(symbol)}, labels{std::move(labels)}, properties{std::move(properties)} {};
|
||||
|
||||
NodeCreationInfo(Symbol symbol, std::vector<storage::LabelId> 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<LogicalOperator>" :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<std::pair<storage::PropertyId, Expression *>>"
|
||||
(properties "std::variant<PropertiesMapList, ParameterLookup *>"
|
||||
: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<PropertiesMapList, ParameterLookup *> 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
|
||||
|
@ -1,8 +1,13 @@
|
||||
#include "query/plan/preprocess.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/plan/preprocess.hpp"
|
||||
|
||||
namespace query::plan {
|
||||
|
||||
@ -218,7 +223,8 @@ 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_) {
|
||||
if (auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&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
|
||||
@ -247,15 +253,20 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
|
||||
auto *property_lookup = storage.Create<PropertyLookup>(identifier, prop_pair.first);
|
||||
auto *prop_equal = storage.Create<EqualOperator>(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<All>(identifier, atom->identifier_, storage.Create<Where>(prop_equal)), collector.symbols_});
|
||||
all_filters_.emplace_back(
|
||||
FilterInfo{FilterInfo::Type::Generic,
|
||||
storage.Create<All>(identifier, atom->identifier_, storage.Create<Where>(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_) {
|
||||
if (auto *properties = std::get_if<std::unordered_map<PropertyIx, Expression *>>(&atom->properties_)) {
|
||||
for (auto &prop_pair : *properties) {
|
||||
// Create an equality expression and store it in all_filters_.
|
||||
auto *property_lookup = storage.Create<PropertyLookup>(atom->identifier_, prop_pair.first);
|
||||
auto *prop_equal = storage.Create<EqualOperator>(property_lookup, prop_pair.second);
|
||||
@ -267,6 +278,9 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
|
||||
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_);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "query/plan/pretty_print.hpp"
|
||||
#include <variant>
|
||||
|
||||
#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<PropertiesMapList>(&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<PropertiesMapList>(&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;
|
||||
|
@ -2,10 +2,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#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<std::pair<storage::PropertyId, Expression *>> 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<PropertiesMapList, ParameterLookup *> {
|
||||
if (const auto *node_properties =
|
||||
std::get_if<std::unordered_map<PropertyIx, Expression *>>(&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<ParameterLookup *>(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<CreateNode>(std::move(input_op), node_info);
|
||||
} else {
|
||||
return std::move(input_op);
|
||||
}
|
||||
return std::move(input_op);
|
||||
};
|
||||
|
||||
auto collect = [&](std::unique_ptr<LogicalOperator> 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<std::pair<storage::PropertyId, Expression *>> 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<PropertiesMapList, ParameterLookup *> {
|
||||
if (const auto *edge_properties =
|
||||
std::get_if<std::unordered_map<PropertyIx, Expression *>>(&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<ParameterLookup *>(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<CreateExpand>(node_info, edge_info, std::move(last_op), input_symbol, node_existing);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <benchmark/benchmark_api.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#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<query::NodeAtom>(storage.Create<query::Identifier>(node1_name));
|
||||
node->labels_.emplace_back(storage.GetLabelIx(label));
|
||||
node->properties_[storage.GetPropertyIx(property)] = storage.Create<query::PrimitiveLiteral>(i);
|
||||
std::get<0>(node->properties_)[storage.GetPropertyIx(property)] = storage.Create<query::PrimitiveLiteral>(i);
|
||||
pattern->atoms_.emplace_back(node);
|
||||
single_query->clauses_.emplace_back(match);
|
||||
query->single_query_ = single_query;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
@ -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<PropertyIx, int64_t> 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<PropertyIx, int64_t> 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
|
||||
|
@ -224,6 +224,77 @@ TEST_F(InterpreterTest, Parameters) {
|
||||
}
|
||||
}
|
||||
|
||||
// Run CREATE/MATCH/MERGE queries with property map
|
||||
TEST_F(InterpreterTest, ParametersAsPropertyMap) {
|
||||
{
|
||||
std::map<std::string, storage::PropertyValue> 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<std::string, storage::PropertyValue> 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<std::string, storage::PropertyValue> 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<std::string, storage::PropertyValue> 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<std::string, storage::PropertyValue> 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);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <tuple>
|
||||
#include <typeinfo>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
@ -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<BaseOpChecker *> on_match{new ExpectScanAll(), new ExpectFilter()};
|
||||
std::list<BaseOpChecker *> 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<BaseOpChecker *> on_match{new ExpectScanAllByLabelPropertyValue(label, property, IDENT("i"))};
|
||||
std::list<BaseOpChecker *> 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<TypeParam>(&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<TypeParam>(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<TypeParam>(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<TypeParam>(&dba, storage, symbol_table, query);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#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<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
|
||||
.emplace_back(property.second, LITERAL(42));
|
||||
|
||||
auto create = std::make_shared<CreateNode>(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<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
|
||||
.emplace_back(property.second, LITERAL(42));
|
||||
|
||||
auto create = std::make_shared<CreateNode>(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<std::vector<std::pair<storage::PropertyId, Expression *>>>(n.properties)
|
||||
.emplace_back(property.second, LITERAL(1));
|
||||
|
||||
// data for the second node
|
||||
NodeCreationInfo m;
|
||||
m.symbol = cycle ? n.symbol : symbol_table.CreateSymbol("m", true);
|
||||
m.labels.emplace_back(label_node_2);
|
||||
m.properties.emplace_back(property.second, LITERAL(2));
|
||||
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(m.properties)
|
||||
.emplace_back(property.second, LITERAL(2));
|
||||
|
||||
EdgeCreationInfo r;
|
||||
r.symbol = symbol_table.CreateSymbol("r", true);
|
||||
r.edge_type = edge_type;
|
||||
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<CreateNode>(nullptr, n);
|
||||
auto create_expand = std::make_shared<CreateExpand>(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);
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
@ -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<LabelsTest>(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<Filter>(r_m.op_, filter_expr);
|
||||
|
||||
|
@ -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<PrimitiveLiteral>(42));
|
||||
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
|
||||
.emplace_back(property, ast.Create<PrimitiveLiteral>(42));
|
||||
|
||||
query::plan::CreateNode create_node(nullptr, node);
|
||||
DbAccessor execution_dba(&dba);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <variant>
|
||||
|
||||
#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<Identifier *>(unwind->named_expression_->expression_);
|
||||
std::get<0>(node->properties_)[storage.GetPropertyIx("prop")] =
|
||||
dynamic_cast<Identifier *>(unwind->named_expression_->expression_);
|
||||
query = QUERY(SINGLE_QUERY(unwind, CREATE(PATTERN(node))));
|
||||
ASSERT_THROW(query::MakeSymbolTable(query, {first_op}), SemanticException);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
#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>{TypedValue(r1)}); // [r1]
|
||||
|
Loading…
Reference in New Issue
Block a user