Query::Plan::Operator - split into hpp and cpp
Reviewers: teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D204
This commit is contained in:
parent
cac422f6a8
commit
d3692d439c
@ -362,6 +362,7 @@ set(memgraph_src_files
|
|||||||
${src_dir}/query/console.cpp
|
${src_dir}/query/console.cpp
|
||||||
${src_dir}/query/frontend/ast/cypher_main_visitor.cpp
|
${src_dir}/query/frontend/ast/cypher_main_visitor.cpp
|
||||||
${src_dir}/query/backend/cpp/typed_value.cpp
|
${src_dir}/query/backend/cpp/typed_value.cpp
|
||||||
|
${src_dir}/query/frontend/logical/operator.cpp
|
||||||
${src_dir}/query/frontend/logical/planner.cpp
|
${src_dir}/query/frontend/logical/planner.cpp
|
||||||
${src_dir}/query/frontend/semantic/symbol_generator.cpp
|
${src_dir}/query/frontend/semantic/symbol_generator.cpp
|
||||||
)
|
)
|
||||||
|
713
src/query/frontend/logical/operator.cpp
Normal file
713
src/query/frontend/logical/operator.cpp
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
#include "operator.hpp"
|
||||||
|
#include "query/exceptions.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace query {
|
||||||
|
namespace plan {
|
||||||
|
|
||||||
|
CreateNode::CreateNode(NodeAtom *node_atom,
|
||||||
|
std::shared_ptr<LogicalOperator> input)
|
||||||
|
: node_atom_(node_atom), input_(input) {}
|
||||||
|
|
||||||
|
void CreateNode::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
if (input_) input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> CreateNode::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<CreateNodeCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateNode::CreateNodeCursor::CreateNodeCursor(CreateNode &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self),
|
||||||
|
db_(db),
|
||||||
|
input_cursor_(self.input_ ? self.input_->MakeCursor(db) : nullptr) {}
|
||||||
|
|
||||||
|
bool CreateNode::CreateNodeCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (input_cursor_) {
|
||||||
|
if (input_cursor_->Pull(frame, symbol_table)) {
|
||||||
|
Create(frame, symbol_table);
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
} else if (!did_create_) {
|
||||||
|
Create(frame, symbol_table);
|
||||||
|
did_create_ = true;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateNode::CreateNodeCursor::Create(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
auto new_node = db_.insert_vertex();
|
||||||
|
for (auto label : self_.node_atom_->labels_) new_node.add_label(label);
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
for (auto &kv : self_.node_atom_->properties_) {
|
||||||
|
kv.second->Accept(evaluator);
|
||||||
|
new_node.PropsSet(kv.first, evaluator.PopBack());
|
||||||
|
}
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]] = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateExpand::CreateExpand(NodeAtom *node_atom, EdgeAtom *edge_atom,
|
||||||
|
const std::shared_ptr<LogicalOperator> &input,
|
||||||
|
const Symbol &input_symbol, bool node_existing)
|
||||||
|
: node_atom_(node_atom),
|
||||||
|
edge_atom_(edge_atom),
|
||||||
|
input_(input),
|
||||||
|
input_symbol_(input_symbol),
|
||||||
|
node_existing_(node_existing) {}
|
||||||
|
|
||||||
|
void CreateExpand::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> CreateExpand::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<CreateExpandCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateExpand::CreateExpandCursor::CreateExpandCursor(CreateExpand &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), db_(db), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool CreateExpand::CreateExpandCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
// get the origin vertex
|
||||||
|
TypedValue vertex_value = frame[self_.input_symbol_];
|
||||||
|
auto v1 = vertex_value.Value<VertexAccessor>();
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
|
||||||
|
// get the destination vertex (possibly an existing node)
|
||||||
|
VertexAccessor v2 = OtherVertex(frame, symbol_table, evaluator);
|
||||||
|
|
||||||
|
// create an edge between the two nodes
|
||||||
|
switch (self_.edge_atom_->direction_) {
|
||||||
|
case EdgeAtom::Direction::LEFT:
|
||||||
|
CreateEdge(v2, v1, frame, symbol_table, evaluator);
|
||||||
|
break;
|
||||||
|
case EdgeAtom::Direction::RIGHT:
|
||||||
|
CreateEdge(v1, v2, frame, symbol_table, evaluator);
|
||||||
|
break;
|
||||||
|
case EdgeAtom::Direction::BOTH:
|
||||||
|
permanent_fail("Undefined direction not allowed in create");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexAccessor CreateExpand::CreateExpandCursor::OtherVertex(
|
||||||
|
Frame &frame, SymbolTable &symbol_table, ExpressionEvaluator &evaluator) {
|
||||||
|
if (self_.node_existing_) {
|
||||||
|
TypedValue &dest_node_value =
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]];
|
||||||
|
return dest_node_value.Value<VertexAccessor>();
|
||||||
|
} else {
|
||||||
|
// the node does not exist, it needs to be created
|
||||||
|
auto node = db_.insert_vertex();
|
||||||
|
for (auto label : self_.node_atom_->labels_) node.add_label(label);
|
||||||
|
for (auto kv : self_.node_atom_->properties_) {
|
||||||
|
kv.second->Accept(evaluator);
|
||||||
|
node.PropsSet(kv.first, evaluator.PopBack());
|
||||||
|
}
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]] = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateExpand::CreateExpandCursor::CreateEdge(
|
||||||
|
VertexAccessor &from, VertexAccessor &to, Frame &frame,
|
||||||
|
SymbolTable &symbol_table, ExpressionEvaluator &evaluator) {
|
||||||
|
EdgeAccessor edge =
|
||||||
|
db_.insert_edge(from, to, self_.edge_atom_->edge_types_[0]);
|
||||||
|
for (auto kv : self_.edge_atom_->properties_) {
|
||||||
|
kv.second->Accept(evaluator);
|
||||||
|
edge.PropsSet(kv.first, evaluator.PopBack());
|
||||||
|
}
|
||||||
|
frame[symbol_table[*self_.edge_atom_->identifier_]] = edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanAll::ScanAll(NodeAtom *node_atom) : node_atom_(node_atom) {}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> ScanAll::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<ScanAllCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanAll::ScanAllCursor::ScanAllCursor(ScanAll &self, GraphDbAccessor &db)
|
||||||
|
: self_(self), vertices_(db.vertices()), vertices_it_(vertices_.begin()) {}
|
||||||
|
|
||||||
|
bool ScanAll::ScanAllCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
if (vertices_it_ == vertices_.end()) return false;
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]] = *vertices_it_++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expand::Expand(NodeAtom *node_atom, EdgeAtom *edge_atom,
|
||||||
|
const std::shared_ptr<LogicalOperator> &input,
|
||||||
|
const Symbol &input_symbol, bool node_cycle, bool edge_cycle)
|
||||||
|
: node_atom_(node_atom),
|
||||||
|
edge_atom_(edge_atom),
|
||||||
|
input_(input),
|
||||||
|
input_symbol_(input_symbol),
|
||||||
|
node_cycle_(node_cycle),
|
||||||
|
edge_cycle_(edge_cycle) {}
|
||||||
|
|
||||||
|
void Expand::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> Expand::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<ExpandCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expand::ExpandCursor::ExpandCursor(Expand &self, GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool Expand::ExpandCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
while (true) {
|
||||||
|
// attempt to get a value from the incoming edges
|
||||||
|
if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
|
||||||
|
EdgeAccessor edge = *(*in_edges_it_)++;
|
||||||
|
if (HandleEdgeCycle(edge, frame, symbol_table) &&
|
||||||
|
PullNode(edge, EdgeAtom::Direction::LEFT, frame, symbol_table))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to get a value from the outgoing edges
|
||||||
|
if (out_edges_ && *out_edges_it_ != out_edges_->end()) {
|
||||||
|
EdgeAccessor edge = *(*out_edges_it_)++;
|
||||||
|
if (HandleEdgeCycle(edge, frame, symbol_table) &&
|
||||||
|
PullNode(edge, EdgeAtom::Direction::RIGHT, frame, symbol_table))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are here, either the edges have not been initialized,
|
||||||
|
// or they have been exhausted. attempt to initialize the edges,
|
||||||
|
// if the input is exhausted
|
||||||
|
if (!InitEdges(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
// we have re-initialized the edges, continue with the loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expand::ExpandCursor::InitEdges(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
TypedValue vertex_value = frame[self_.input_symbol_];
|
||||||
|
auto vertex = vertex_value.Value<VertexAccessor>();
|
||||||
|
|
||||||
|
auto direction = self_.edge_atom_->direction_;
|
||||||
|
if (direction == EdgeAtom::Direction::LEFT ||
|
||||||
|
direction == EdgeAtom::Direction::BOTH) {
|
||||||
|
in_edges_ = std::make_unique<InEdgeT>(vertex.in());
|
||||||
|
in_edges_it_ = std::make_unique<InEdgeIteratorT>(in_edges_->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == EdgeAtom::Direction::RIGHT ||
|
||||||
|
direction == EdgeAtom::Direction::BOTH) {
|
||||||
|
out_edges_ = std::make_unique<InEdgeT>(vertex.out());
|
||||||
|
out_edges_it_ = std::make_unique<InEdgeIteratorT>(out_edges_->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add support for Front and Back expansion (when QueryPlanner
|
||||||
|
// will need it). For now only Back expansion (left to right) is
|
||||||
|
// supported
|
||||||
|
// TODO add support for named paths
|
||||||
|
// TODO add support for uniqueness (edge, vertex)
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expand::ExpandCursor::HandleEdgeCycle(EdgeAccessor &new_edge, Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (self_.edge_cycle_) {
|
||||||
|
TypedValue &old_edge_value =
|
||||||
|
frame[symbol_table[*self_.edge_atom_->identifier_]];
|
||||||
|
return old_edge_value.Value<EdgeAccessor>() == new_edge;
|
||||||
|
} else {
|
||||||
|
// not doing a cycle, so put the new_edge into the frame and return true
|
||||||
|
frame[symbol_table[*self_.edge_atom_->identifier_]] = new_edge;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expand::ExpandCursor::PullNode(EdgeAccessor &new_edge,
|
||||||
|
EdgeAtom::Direction direction, Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
switch (direction) {
|
||||||
|
case EdgeAtom::Direction::LEFT:
|
||||||
|
return HandleNodeCycle(new_edge.from(), frame, symbol_table);
|
||||||
|
case EdgeAtom::Direction::RIGHT:
|
||||||
|
return HandleNodeCycle(new_edge.to(), frame, symbol_table);
|
||||||
|
case EdgeAtom::Direction::BOTH:
|
||||||
|
permanent_fail("Must indicate exact expansion direction here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expand::ExpandCursor::HandleNodeCycle(VertexAccessor new_node,
|
||||||
|
Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (self_.node_cycle_) {
|
||||||
|
TypedValue &old_node_value =
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]];
|
||||||
|
return old_node_value.Value<VertexAccessor>() == new_node;
|
||||||
|
} else {
|
||||||
|
// not doing a cycle, so put the new_edge into the frame and return true
|
||||||
|
frame[symbol_table[*self_.node_atom_->identifier_]] = new_node;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeFilter::NodeFilter(std::shared_ptr<LogicalOperator> input,
|
||||||
|
Symbol input_symbol, NodeAtom *node_atom)
|
||||||
|
: input_(input), input_symbol_(input_symbol), node_atom_(node_atom) {}
|
||||||
|
|
||||||
|
void NodeFilter::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> NodeFilter::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<NodeFilterCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeFilter::NodeFilterCursor::NodeFilterCursor(NodeFilter &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self_.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool NodeFilter::NodeFilterCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
while (input_cursor_->Pull(frame, symbol_table)) {
|
||||||
|
const auto &vertex = frame[self_.input_symbol_].Value<VertexAccessor>();
|
||||||
|
if (VertexPasses(vertex, frame, symbol_table)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeFilter::NodeFilterCursor::VertexPasses(const VertexAccessor &vertex,
|
||||||
|
Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
for (auto label : self_.node_atom_->labels_)
|
||||||
|
if (!vertex.has_label(label)) return false;
|
||||||
|
|
||||||
|
ExpressionEvaluator expression_evaluator(frame, symbol_table);
|
||||||
|
for (auto prop_pair : self_.node_atom_->properties_) {
|
||||||
|
prop_pair.second->Accept(expression_evaluator);
|
||||||
|
TypedValue comparison_result =
|
||||||
|
vertex.PropsAt(prop_pair.first) == expression_evaluator.PopBack();
|
||||||
|
if (comparison_result.type() == TypedValue::Type::Null ||
|
||||||
|
!comparison_result.Value<bool>())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeFilter::EdgeFilter(std::shared_ptr<LogicalOperator> input,
|
||||||
|
Symbol input_symbol, EdgeAtom *edge_atom)
|
||||||
|
: input_(input), input_symbol_(input_symbol), edge_atom_(edge_atom) {}
|
||||||
|
|
||||||
|
void EdgeFilter::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> EdgeFilter::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<EdgeFilterCursor>(*this, db);
|
||||||
|
}
|
||||||
|
EdgeFilter::EdgeFilterCursor::EdgeFilterCursor(EdgeFilter &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self_.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool EdgeFilter::EdgeFilterCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
while (input_cursor_->Pull(frame, symbol_table)) {
|
||||||
|
const auto &edge = frame[self_.input_symbol_].Value<EdgeAccessor>();
|
||||||
|
if (EdgePasses(edge, frame, symbol_table)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeFilter::EdgeFilterCursor::EdgePasses(const EdgeAccessor &edge,
|
||||||
|
Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
// edge type filtering - logical OR
|
||||||
|
const auto &types = self_.edge_atom_->edge_types_;
|
||||||
|
GraphDbTypes::EdgeType type = edge.edge_type();
|
||||||
|
if (!std::any_of(types.begin(), types.end(),
|
||||||
|
[type](auto t) { return t == type; }))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ExpressionEvaluator expression_evaluator(frame, symbol_table);
|
||||||
|
for (auto prop_pair : self_.edge_atom_->properties_) {
|
||||||
|
prop_pair.second->Accept(expression_evaluator);
|
||||||
|
TypedValue comparison_result =
|
||||||
|
edge.PropsAt(prop_pair.first) == expression_evaluator.PopBack();
|
||||||
|
if (comparison_result.type() == TypedValue::Type::Null ||
|
||||||
|
!comparison_result.Value<bool>())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter::Filter(const std::shared_ptr<LogicalOperator> &input_,
|
||||||
|
Expression *expression_)
|
||||||
|
: input_(input_), expression_(expression_) {}
|
||||||
|
|
||||||
|
void Filter::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> Filter::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<FilterCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter::FilterCursor::FilterCursor(Filter &self, GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self_.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool Filter::FilterCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
while (input_cursor_->Pull(frame, symbol_table)) {
|
||||||
|
self_.expression_->Accept(evaluator);
|
||||||
|
TypedValue result = evaluator.PopBack();
|
||||||
|
if (result.type() == TypedValue::Type::Null || !result.Value<bool>())
|
||||||
|
continue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Produce::Produce(std::shared_ptr<LogicalOperator> input,
|
||||||
|
std::vector<NamedExpression *> named_expressions)
|
||||||
|
: input_(input), named_expressions_(named_expressions) {}
|
||||||
|
|
||||||
|
void Produce::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
if (input_) input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> Produce::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<ProduceCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<NamedExpression *> &Produce::named_expressions() {
|
||||||
|
return named_expressions_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Produce::ProduceCursor::ProduceCursor(Produce &self, GraphDbAccessor &db)
|
||||||
|
: self_(self),
|
||||||
|
input_cursor_(self.input_ ? self_.input_->MakeCursor(db) : nullptr) {}
|
||||||
|
bool Produce::ProduceCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
if (input_cursor_) {
|
||||||
|
if (input_cursor_->Pull(frame, symbol_table)) {
|
||||||
|
for (auto named_expr : self_.named_expressions_)
|
||||||
|
named_expr->Accept(evaluator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (!did_produce_) {
|
||||||
|
for (auto named_expr : self_.named_expressions_)
|
||||||
|
named_expr->Accept(evaluator);
|
||||||
|
did_produce_ = true;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Delete::Delete(const std::shared_ptr<LogicalOperator> &input_,
|
||||||
|
const std::vector<Expression *> &expressions, bool detach_)
|
||||||
|
: input_(input_), expressions_(expressions), detach_(detach_) {}
|
||||||
|
|
||||||
|
void Delete::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> Delete::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<DeleteCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
Delete::DeleteCursor::DeleteCursor(Delete &self, GraphDbAccessor &db)
|
||||||
|
: self_(self), db_(db), input_cursor_(self_.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool Delete::DeleteCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
for (Expression *expression : self_.expressions_) {
|
||||||
|
expression->Accept(evaluator);
|
||||||
|
TypedValue value = evaluator.PopBack();
|
||||||
|
switch (value.type()) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
// if we got a Null, that's OK, probably it's an OPTIONAL MATCH
|
||||||
|
return true;
|
||||||
|
case TypedValue::Type::Vertex:
|
||||||
|
if (self_.detach_)
|
||||||
|
db_.detach_remove_vertex(value.Value<VertexAccessor>());
|
||||||
|
else if (!db_.remove_vertex(value.Value<VertexAccessor>()))
|
||||||
|
throw query::QueryRuntimeException(
|
||||||
|
"Failed to remove vertex because of it's existing "
|
||||||
|
"connections. Consider using DETACH DELETE.");
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Edge:
|
||||||
|
db_.remove_edge(value.Value<EdgeAccessor>());
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Path:
|
||||||
|
// TODO consider path deletion
|
||||||
|
default:
|
||||||
|
throw TypedValueException("Can only delete edges and vertices");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProperty::SetProperty(const std::shared_ptr<LogicalOperator> input,
|
||||||
|
PropertyLookup *lhs, Expression *rhs)
|
||||||
|
: input_(input), lhs_(lhs), rhs_(rhs) {}
|
||||||
|
|
||||||
|
void SetProperty::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> SetProperty::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<SetPropertyCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProperty::SetPropertyCursor::SetPropertyCursor(SetProperty &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool SetProperty::SetPropertyCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
self_.lhs_->expression_->Accept(evaluator);
|
||||||
|
TypedValue lhs = evaluator.PopBack();
|
||||||
|
self_.rhs_->Accept(evaluator);
|
||||||
|
TypedValue rhs = evaluator.PopBack();
|
||||||
|
|
||||||
|
// TODO the following code uses implicit TypedValue to PropertyValue
|
||||||
|
// conversion which throws a TypedValueException if impossible
|
||||||
|
// this is correct, but the end user will get a not-very-informative
|
||||||
|
// error message, so address this when improving error feedback
|
||||||
|
|
||||||
|
switch (lhs.type()) {
|
||||||
|
case TypedValue::Type::Vertex:
|
||||||
|
lhs.Value<VertexAccessor>().PropsSet(self_.lhs_->property_, rhs);
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Edge:
|
||||||
|
lhs.Value<EdgeAccessor>().PropsSet(self_.lhs_->property_, rhs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Properties can only be set on Vertices and Edges");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProperties::SetProperties(const std::shared_ptr<LogicalOperator> input,
|
||||||
|
const Symbol input_symbol, Expression *rhs, Op op)
|
||||||
|
: input_(input), input_symbol_(input_symbol), rhs_(rhs), op_(op) {}
|
||||||
|
|
||||||
|
void SetProperties::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> SetProperties::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<SetPropertiesCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProperties::SetPropertiesCursor::SetPropertiesCursor(SetProperties &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), db_(db), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool SetProperties::SetPropertiesCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
TypedValue lhs = frame[self_.input_symbol_];
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
self_.rhs_->Accept(evaluator);
|
||||||
|
TypedValue rhs = evaluator.PopBack();
|
||||||
|
|
||||||
|
switch (lhs.type()) {
|
||||||
|
case TypedValue::Type::Vertex:
|
||||||
|
Set(lhs.Value<VertexAccessor>(), rhs);
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Edge:
|
||||||
|
Set(lhs.Value<EdgeAccessor>(), rhs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Properties can only be set on Vertices and Edges");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TRecordAccessor>
|
||||||
|
void SetProperties::SetPropertiesCursor::Set(TRecordAccessor &record,
|
||||||
|
const TypedValue &rhs) {
|
||||||
|
if (self_.op_ == Op::REPLACE) record.PropsClear();
|
||||||
|
|
||||||
|
auto set_props = [&record](const auto &properties) {
|
||||||
|
for (const auto &kv : properties) record.PropsSet(kv.first, kv.second);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (rhs.type()) {
|
||||||
|
case TypedValue::Type::Edge:
|
||||||
|
set_props(rhs.Value<EdgeAccessor>().Properties());
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Vertex:
|
||||||
|
set_props(rhs.Value<VertexAccessor>().Properties());
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Map: {
|
||||||
|
// TODO the following code uses implicit TypedValue to PropertyValue
|
||||||
|
// conversion which throws a TypedValueException if impossible
|
||||||
|
// this is correct, but the end user will get a not-very-informative
|
||||||
|
// error message, so address this when improving error feedback
|
||||||
|
for (const auto &kv : rhs.Value<std::map<std::string, TypedValue>>())
|
||||||
|
record.PropsSet(db_.property(kv.first), kv.second);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Can only set Vertices, Edges and maps as properties");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiate the SetProperties function with concrete TRecordAccessor types
|
||||||
|
template void SetProperties::SetPropertiesCursor::Set(
|
||||||
|
RecordAccessor<Vertex> &record, const TypedValue &rhs);
|
||||||
|
template void SetProperties::SetPropertiesCursor::Set(
|
||||||
|
RecordAccessor<Edge> &record, const TypedValue &rhs);
|
||||||
|
|
||||||
|
SetLabels::SetLabels(const std::shared_ptr<LogicalOperator> input,
|
||||||
|
const Symbol input_symbol,
|
||||||
|
const std::vector<GraphDbTypes::Label> &labels)
|
||||||
|
: input_(input), input_symbol_(input_symbol), labels_(labels) {}
|
||||||
|
|
||||||
|
void SetLabels::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> SetLabels::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<SetLabelsCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLabels::SetLabelsCursor::SetLabelsCursor(SetLabels &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool SetLabels::SetLabelsCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
TypedValue vertex_value = frame[self_.input_symbol_];
|
||||||
|
VertexAccessor vertex = vertex_value.Value<VertexAccessor>();
|
||||||
|
for (auto label : self_.labels_) vertex.add_label(label);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveProperty::RemoveProperty(const std::shared_ptr<LogicalOperator> input,
|
||||||
|
PropertyLookup *lhs)
|
||||||
|
: input_(input), lhs_(lhs) {}
|
||||||
|
|
||||||
|
void RemoveProperty::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> RemoveProperty::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<RemovePropertyCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveProperty::RemovePropertyCursor::RemovePropertyCursor(RemoveProperty &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame,
|
||||||
|
SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
ExpressionEvaluator evaluator(frame, symbol_table);
|
||||||
|
self_.lhs_->expression_->Accept(evaluator);
|
||||||
|
TypedValue lhs = evaluator.PopBack();
|
||||||
|
|
||||||
|
switch (lhs.type()) {
|
||||||
|
case TypedValue::Type::Vertex:
|
||||||
|
lhs.Value<VertexAccessor>().PropsErase(self_.lhs_->property_);
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Edge:
|
||||||
|
lhs.Value<EdgeAccessor>().PropsErase(self_.lhs_->property_);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// TODO consider throwing a TypedValueException here
|
||||||
|
// deal with this when we'll be overhauling error-feedback
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Properties can only be removed on Vertices and Edges");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveLabels::RemoveLabels(const std::shared_ptr<LogicalOperator> input,
|
||||||
|
const Symbol input_symbol,
|
||||||
|
const std::vector<GraphDbTypes::Label> &labels)
|
||||||
|
: input_(input), input_symbol_(input_symbol), labels_(labels) {}
|
||||||
|
|
||||||
|
void RemoveLabels::Accept(LogicalOperatorVisitor &visitor) {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
input_->Accept(visitor);
|
||||||
|
visitor.PostVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Cursor> RemoveLabels::MakeCursor(GraphDbAccessor &db) {
|
||||||
|
return std::make_unique<RemoveLabelsCursor>(*this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveLabels::RemoveLabelsCursor::RemoveLabelsCursor(RemoveLabels &self,
|
||||||
|
GraphDbAccessor &db)
|
||||||
|
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||||
|
|
||||||
|
bool RemoveLabels::RemoveLabelsCursor::Pull(
|
||||||
|
Frame &frame, SymbolTable &symbol_table) {
|
||||||
|
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||||
|
|
||||||
|
TypedValue vertex_value = frame[self_.input_symbol_];
|
||||||
|
VertexAccessor vertex = vertex_value.Value<VertexAccessor>();
|
||||||
|
for (auto label : self_.labels_) vertex.remove_label(label);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace plan
|
||||||
|
} // namespace query
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@ class Visitable {
|
|||||||
void Accept(TVisitor &visitor) override \
|
void Accept(TVisitor &visitor) override \
|
||||||
{ visitor.Visit(*this); visitor.PostVisit(*this); }
|
{ visitor.Visit(*this); visitor.PostVisit(*this); }
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "query/context.hpp"
|
#include "query/context.hpp"
|
||||||
#include "query/frontend/interpret/interpret.hpp"
|
#include "query/frontend/interpret/interpret.hpp"
|
||||||
#include "query/frontend/logical/planner.hpp"
|
#include "query/frontend/logical/planner.hpp"
|
||||||
|
#include "query/exceptions.hpp"
|
||||||
|
|
||||||
#include "query_common.hpp"
|
#include "query_common.hpp"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user