[master < T1204] Add detailed operator info to PROFILE and EXPLAIN (#1204)

This commit is contained in:
Ante Pušić 2023-09-11 14:34:27 +02:00 committed by GitHub
parent 29a505cb38
commit 060b9d1c16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 765 additions and 139 deletions

View File

@ -172,7 +172,10 @@ inline void AbortCheck(ExecutionContext const &context) {
} // namespace
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define SCOPED_PROFILE_OP(name) ScopedProfile profile{ComputeProfilingKey(this), name, &context};
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define SCOPED_PROFILE_OP_BY_REF(ref) ScopedProfile profile{ComputeProfilingKey(this), ref, &context};
bool Once::OnceCursor::Pull(Frame &, ExecutionContext &context) {
SCOPED_PROFILE_OP("Once");
@ -352,7 +355,7 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert
} // namespace
bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("CreateExpand");
SCOPED_PROFILE_OP_BY_REF(self_);
if (!input_cursor_->Pull(frame, context)) return false;
@ -431,16 +434,17 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, Exec
template <class TVerticesFun>
class ScanAllCursor : public Cursor {
public:
explicit ScanAllCursor(Symbol output_symbol, UniqueCursorPtr input_cursor, storage::View view,
explicit ScanAllCursor(const ScanAll &self, Symbol output_symbol, UniqueCursorPtr input_cursor, storage::View view,
TVerticesFun get_vertices, const char *op_name)
: output_symbol_(output_symbol),
: self_(self),
output_symbol_(output_symbol),
input_cursor_(std::move(input_cursor)),
view_(view),
get_vertices_(std::move(get_vertices)),
op_name_(op_name) {}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP(op_name_);
SCOPED_PROFILE_OP_BY_REF(self_);
AbortCheck(context);
@ -491,6 +495,7 @@ class ScanAllCursor : public Cursor {
}
private:
const ScanAll &self_;
const Symbol output_symbol_;
const UniqueCursorPtr input_cursor_;
storage::View view_;
@ -513,8 +518,8 @@ UniqueCursorPtr ScanAll::MakeCursor(utils::MemoryResource *mem) const {
auto *db = context.db_accessor;
return std::make_optional(db->Vertices(view_));
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAll");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, *this, output_symbol_, input_->MakeCursor(mem),
view_, std::move(vertices), "ScanAll");
}
std::vector<Symbol> ScanAll::ModifiedSymbols(const SymbolTable &table) const {
@ -536,8 +541,8 @@ UniqueCursorPtr ScanAllByLabel::MakeCursor(utils::MemoryResource *mem) const {
auto *db = context.db_accessor;
return std::make_optional(db->Vertices(view_, label_));
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAllByLabel");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, *this, output_symbol_, input_->MakeCursor(mem),
view_, std::move(vertices), "ScanAllByLabel");
}
// TODO(buda): Implement ScanAllByLabelProperty operator to iterate over
@ -601,8 +606,8 @@ UniqueCursorPtr ScanAllByLabelPropertyRange::MakeCursor(utils::MemoryResource *m
if (maybe_upper && maybe_upper->value().IsNull()) return std::nullopt;
return std::make_optional(db->Vertices(view_, label_, property_, maybe_lower, maybe_upper));
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAllByLabelPropertyRange");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
mem, *this, output_symbol_, input_->MakeCursor(mem), view_, std::move(vertices), "ScanAllByLabelPropertyRange");
}
ScanAllByLabelPropertyValue::ScanAllByLabelPropertyValue(const std::shared_ptr<LogicalOperator> &input,
@ -633,8 +638,8 @@ UniqueCursorPtr ScanAllByLabelPropertyValue::MakeCursor(utils::MemoryResource *m
}
return std::make_optional(db->Vertices(view_, label_, property_, storage::PropertyValue(value)));
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAllByLabelPropertyValue");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(
mem, *this, output_symbol_, input_->MakeCursor(mem), view_, std::move(vertices), "ScanAllByLabelPropertyValue");
}
ScanAllByLabelProperty::ScanAllByLabelProperty(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol,
@ -651,8 +656,8 @@ UniqueCursorPtr ScanAllByLabelProperty::MakeCursor(utils::MemoryResource *mem) c
auto *db = context.db_accessor;
return std::make_optional(db->Vertices(view_, label_, property_));
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAllByLabelProperty");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, *this, output_symbol_, input_->MakeCursor(mem),
view_, std::move(vertices), "ScanAllByLabelProperty");
}
ScanAllById::ScanAllById(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol, Expression *expression,
@ -677,8 +682,8 @@ UniqueCursorPtr ScanAllById::MakeCursor(utils::MemoryResource *mem) const {
if (!maybe_vertex) return std::nullopt;
return std::vector<VertexAccessor>{*maybe_vertex};
};
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, output_symbol_, input_->MakeCursor(mem), view_,
std::move(vertices), "ScanAllById");
return MakeUniqueCursorPtr<ScanAllCursor<decltype(vertices)>>(mem, *this, output_symbol_, input_->MakeCursor(mem),
view_, std::move(vertices), "ScanAllById");
}
namespace {
@ -742,7 +747,7 @@ Expand::ExpandCursor::ExpandCursor(const Expand &self, int64_t input_degree, int
prev_existing_degree_(existing_node_degree) {}
bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("Expand");
SCOPED_PROFILE_OP_BY_REF(self_);
// A helper function for expanding a node from an edge.
auto pull_node = [this, &frame](const EdgeAccessor &new_edge, EdgeAtom::Direction direction) {
@ -1031,7 +1036,7 @@ class ExpandVariableCursor : public Cursor {
: self_(self), input_cursor_(self.input_->MakeCursor(mem)), edges_(mem), edges_it_(mem) {}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("ExpandVariable");
SCOPED_PROFILE_OP_BY_REF(self_);
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::OLD);
@ -2522,7 +2527,7 @@ Produce::ProduceCursor::ProduceCursor(const Produce &self, utils::MemoryResource
: self_(self), input_cursor_(self_.input_->MakeCursor(mem)) {}
bool Produce::ProduceCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("Produce");
SCOPED_PROFILE_OP_BY_REF(self_);
if (input_cursor_->Pull(frame, context)) {
// Produce should always yield the latest results.
@ -3403,7 +3408,7 @@ class AggregateCursor : public Cursor {
: self_(self), input_cursor_(self_.input_->MakeCursor(mem)), aggregation_(mem) {}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("Aggregate");
SCOPED_PROFILE_OP_BY_REF(self_);
if (!pulled_all_input_) {
ProcessAll(&frame, &context);
@ -3867,7 +3872,7 @@ class OrderByCursor : public Cursor {
: self_(self), input_cursor_(self_.input_->MakeCursor(mem)), cache_(mem) {}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("OrderBy");
SCOPED_PROFILE_OP_BY_REF(self_);
if (!did_pull_all_) {
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
@ -4269,7 +4274,7 @@ Union::UnionCursor::UnionCursor(const Union &self, utils::MemoryResource *mem)
: self_(self), left_cursor_(self.left_op_->MakeCursor(mem)), right_cursor_(self.right_op_->MakeCursor(mem)) {}
bool Union::UnionCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("Union");
SCOPED_PROFILE_OP_BY_REF(self_);
utils::pmr::unordered_map<std::string, TypedValue> results(context.evaluation_context.memory);
if (left_cursor_->Pull(frame, context)) {
@ -4343,7 +4348,7 @@ class CartesianCursor : public Cursor {
}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("Cartesian");
SCOPED_PROFILE_OP_BY_REF(self_);
if (!cartesian_pull_initialized_) {
// Pull all left_op frames.
@ -4632,7 +4637,7 @@ class CallProcedureCursor : public Cursor {
}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("CallProcedure");
SCOPED_PROFILE_OP_BY_REF(*self_);
AbortCheck(context);
@ -4911,7 +4916,7 @@ class LoadCsvCursor : public Cursor {
: self_(self), input_cursor_(self_->input_->MakeCursor(mem)), did_pull_{false} {}
bool Pull(Frame &frame, ExecutionContext &context) override {
SCOPED_PROFILE_OP("LoadCsv");
SCOPED_PROFILE_OP_BY_REF(*self_);
AbortCheck(context);

View File

@ -152,12 +152,19 @@ class HierarchicalLogicalOperatorVisitor : public LogicalOperatorCompositeVisito
using typename LogicalOperatorLeafVisitor::ReturnType;
};
class NamedLogicalOperator {
public:
mutable const DbAccessor *dba_{nullptr};
virtual std::string ToString() const = 0;
};
/// Base class for logical operators.
///
/// Each operator describes an operation, which is to be performed on the
/// database. Operators are iterated over using a @c Cursor. Various operators
/// can serve as inputs to others and thus a sequence of operations is formed.
class LogicalOperator : public utils::Visitable<HierarchicalLogicalOperatorVisitor> {
class LogicalOperator : public utils::Visitable<HierarchicalLogicalOperatorVisitor>,
public memgraph::query::plan::NamedLogicalOperator {
public:
static const utils::TypeInfo kType;
virtual const utils::TypeInfo &GetTypeInfo() const { return kType; }
@ -232,6 +239,8 @@ class LogicalOperator : public utils::Visitable<HierarchicalLogicalOperatorVisit
std::vector<std::shared_ptr<LogicalOperator>> loaded_ops;
};
std::string ToString() const override { return GetTypeInfo().name; }
virtual std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const = 0;
};
@ -464,6 +473,13 @@ class CreateExpand : public memgraph::query::plan::LogicalOperator {
/// if the given node atom refers to an existing node (either matched or created)
bool existing_node_;
std::string ToString() const override {
return fmt::format("CreateExpand ({}){}[{}:{}]{}({})", input_symbol_.name(),
edge_info_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-", edge_info_.symbol.name(),
dba_->EdgeTypeToName(edge_info_.edge_type),
edge_info_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-", node_info_.symbol.name());
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<CreateExpand>();
object->node_info_ = node_info_.Clone(storage);
@ -530,6 +546,8 @@ class ScanAll : public memgraph::query::plan::LogicalOperator {
/// transaction sees along with their modifications.
storage::View view_;
std::string ToString() const override { return fmt::format("ScanAll ({})", output_symbol_.name()); }
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAll>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -558,6 +576,10 @@ class ScanAllByLabel : public memgraph::query::plan::ScanAll {
storage::LabelId label_;
std::string ToString() const override {
return fmt::format("ScanAllByLabel ({} :{})", output_symbol_.name(), dba_->LabelToName(label_));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAllByLabel>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -610,6 +632,11 @@ class ScanAllByLabelPropertyRange : public memgraph::query::plan::ScanAll {
std::optional<Bound> lower_bound_;
std::optional<Bound> upper_bound_;
std::string ToString() const override {
return fmt::format("ScanAllByLabelPropertyRange ({0} :{1} {{{2}}})", output_symbol_.name(),
dba_->LabelToName(label_), dba_->PropertyToName(property_));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAllByLabelPropertyRange>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -668,6 +695,11 @@ class ScanAllByLabelPropertyValue : public memgraph::query::plan::ScanAll {
std::string property_name_;
Expression *expression_;
std::string ToString() const override {
return fmt::format("ScanAllByLabelPropertyValue ({0} :{1} {{{2}}})", output_symbol_.name(),
dba_->LabelToName(label_), dba_->PropertyToName(property_));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAllByLabelPropertyValue>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -704,6 +736,11 @@ class ScanAllByLabelProperty : public memgraph::query::plan::ScanAll {
std::string property_name_;
Expression *expression_;
std::string ToString() const override {
return fmt::format("ScanAllByLabelProperty ({0} :{1} {{{2}}})", output_symbol_.name(), dba_->LabelToName(label_),
dba_->PropertyToName(property_));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAllByLabelProperty>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -732,6 +769,8 @@ class ScanAllById : public memgraph::query::plan::ScanAll {
Expression *expression_;
std::string ToString() const override { return fmt::format("ScanAllById ({})", output_symbol_.name()); }
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ScanAllById>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -848,6 +887,15 @@ class Expand : public memgraph::query::plan::LogicalOperator {
/// State from which the input node should get expanded.
storage::View view_;
std::string ToString() const override {
return fmt::format(
"Expand ({}){}[{}{}]{}({})", input_symbol_.name(),
common_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-", common_.edge_symbol.name(),
utils::IterableToString(common_.edge_types, "|",
[this](const auto &edge_type) { return ":" + dba_->EdgeTypeToName(edge_type); }),
common_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-", common_.node_symbol.name());
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<Expand>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -955,6 +1003,37 @@ class ExpandVariable : public memgraph::query::plan::LogicalOperator {
std::optional<memgraph::query::plan::ExpansionLambda> weight_lambda_;
std::optional<Symbol> total_weight_;
std::string OperatorName() const {
using Type = query::EdgeAtom::Type;
switch (type_) {
case Type::DEPTH_FIRST:
return "ExpandVariable";
break;
case Type::BREADTH_FIRST:
return (common_.existing_node ? "STShortestPath" : "BFSExpand");
break;
case Type::WEIGHTED_SHORTEST_PATH:
return "WeightedShortestPath";
break;
case Type::ALL_SHORTEST_PATHS:
return "AllShortestPaths";
break;
case Type::SINGLE:
LOG_FATAL("Unexpected ExpandVariable::type_");
default:
LOG_FATAL("Unexpected ExpandVariable::type_");
}
}
std::string ToString() const override {
return fmt::format(
"{} ({}){}[{}{}]{}({})", OperatorName(), input_symbol_.name(),
common_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-", common_.edge_symbol.name(),
utils::IterableToString(common_.edge_types, "|",
[this](const auto &edge_type) { return ":" + dba_->EdgeTypeToName(edge_type); }),
common_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-", common_.node_symbol.name());
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<ExpandVariable>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -1097,6 +1176,11 @@ class Produce : public memgraph::query::plan::LogicalOperator {
std::shared_ptr<memgraph::query::plan::LogicalOperator> input_;
std::vector<NamedExpression *> named_expressions_;
std::string ToString() const override {
return fmt::format("Produce {{{}}}", utils::IterableToString(named_expressions_, ", ",
[](const auto &nexpr) { return nexpr->name_; }));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<Produce>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -1628,6 +1712,13 @@ class Aggregate : public memgraph::query::plan::LogicalOperator {
std::vector<Expression *> group_by_;
std::vector<Symbol> remember_;
std::string ToString() const override {
return fmt::format(
"Aggregate {{{0}}} {{{1}}}",
utils::IterableToString(aggregations_, ", ", [](const auto &aggr) { return aggr.output_sym.name(); }),
utils::IterableToString(remember_, ", ", [](const auto &sym) { return sym.name(); }));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<Aggregate>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -1833,6 +1924,11 @@ class OrderBy : public memgraph::query::plan::LogicalOperator {
std::vector<Expression *> order_by_;
std::vector<Symbol> output_symbols_;
std::string ToString() const override {
return fmt::format("OrderBy {{{}}}",
utils::IterableToString(output_symbols_, ", ", [](const auto &sym) { return sym.name(); }));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<OrderBy>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -2064,6 +2160,12 @@ class Union : public memgraph::query::plan::LogicalOperator {
std::vector<Symbol> left_symbols_;
std::vector<Symbol> right_symbols_;
std::string ToString() const override {
return fmt::format("Union {{{0} : {1}}}",
utils::IterableToString(left_symbols_, ", ", [](const auto &sym) { return sym.name(); }),
utils::IterableToString(right_symbols_, ", ", [](const auto &sym) { return sym.name(); }));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<Union>();
object->left_op_ = left_op_ ? left_op_->Clone(storage) : nullptr;
@ -2226,6 +2328,11 @@ class CallProcedure : public memgraph::query::plan::LogicalOperator {
mutable utils::MonotonicBufferResource monotonic_memory{1024UL * 1024UL};
utils::MemoryResource *memory_resource = &monotonic_memory;
std::string ToString() const override {
return fmt::format("CallProcedure<{0}> {{{1}}}", procedure_name_,
utils::IterableToString(result_symbols_, ", ", [](const auto &sym) { return sym.name(); }));
}
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<CallProcedure>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;
@ -2273,6 +2380,8 @@ class LoadCsv : public memgraph::query::plan::LogicalOperator {
Expression *nullif_{nullptr};
Symbol row_var_;
std::string ToString() const override { return fmt::format("LoadCsv {{{}}}", row_var_.name()); }
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
auto object = std::make_unique<LoadCsv>();
object->input_ = input_ ? input_->Clone(storage) : nullptr;

View File

@ -30,121 +30,68 @@ PlanPrinter::PlanPrinter(const DbAccessor *dba, std::ostream *out) : dba_(dba),
PRE_VISIT(CreateNode);
bool PlanPrinter::PreVisit(CreateExpand &op) {
WithPrintLn([&](auto &out) {
out << "* CreateExpand (" << op.input_symbol_.name() << ")"
<< (op.edge_info_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-") << "["
<< op.edge_info_.symbol.name() << ":" << dba_->EdgeTypeToName(op.edge_info_.edge_type) << "]"
<< (op.edge_info_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-") << "("
<< op.node_info_.symbol.name() << ")";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
PRE_VISIT(Delete);
bool PlanPrinter::PreVisit(query::plan::ScanAll &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAll"
<< " (" << op.output_symbol_.name() << ")";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
bool PlanPrinter::PreVisit(query::plan::ScanAllByLabel &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabel"
<< " (" << op.output_symbol_.name() << " :" << dba_->LabelToName(op.label_) << ")";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(query::plan::ScanAllByLabelPropertyValue &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabelPropertyValue"
<< " (" << op.output_symbol_.name() << " :" << dba_->LabelToName(op.label_) << " {"
<< dba_->PropertyToName(op.property_) << "})";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(query::plan::ScanAllByLabelPropertyRange &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabelPropertyRange"
<< " (" << op.output_symbol_.name() << " :" << dba_->LabelToName(op.label_) << " {"
<< dba_->PropertyToName(op.property_) << "})";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(query::plan::ScanAllByLabelProperty &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabelProperty"
<< " (" << op.output_symbol_.name() << " :" << dba_->LabelToName(op.label_) << " {"
<< dba_->PropertyToName(op.property_) << "})";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(ScanAllById &op) {
WithPrintLn([&](auto &out) {
out << "* ScanAllById"
<< " (" << op.output_symbol_.name() << ")";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
bool PlanPrinter::PreVisit(query::plan::Expand &op) {
WithPrintLn([&](auto &out) {
*out_ << "* Expand (" << op.input_symbol_.name() << ")"
<< (op.common_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-") << "["
<< op.common_.edge_symbol.name();
utils::PrintIterable(*out_, op.common_.edge_types, "|", [this](auto &stream, const auto &edge_type) {
stream << ":" << dba_->EdgeTypeToName(edge_type);
});
*out_ << "]" << (op.common_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-") << "("
<< op.common_.node_symbol.name() << ")";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(query::plan::ExpandVariable &op) {
using Type = query::EdgeAtom::Type;
WithPrintLn([&](auto &out) {
*out_ << "* ";
switch (op.type_) {
case Type::DEPTH_FIRST:
*out_ << "ExpandVariable";
break;
case Type::BREADTH_FIRST:
*out_ << (op.common_.existing_node ? "STShortestPath" : "BFSExpand");
break;
case Type::WEIGHTED_SHORTEST_PATH:
*out_ << "WeightedShortestPath";
break;
case Type::ALL_SHORTEST_PATHS:
*out_ << "AllShortestPaths";
break;
case Type::SINGLE:
LOG_FATAL("Unexpected ExpandVariable::type_");
}
*out_ << " (" << op.input_symbol_.name() << ")"
<< (op.common_.direction == query::EdgeAtom::Direction::IN ? "<-" : "-") << "["
<< op.common_.edge_symbol.name();
utils::PrintIterable(*out_, op.common_.edge_types, "|", [this](auto &stream, const auto &edge_type) {
stream << ":" << dba_->EdgeTypeToName(edge_type);
});
*out_ << "]" << (op.common_.direction == query::EdgeAtom::Direction::OUT ? "->" : "-") << "("
<< op.common_.node_symbol.name() << ")";
});
op.dba_ = dba_;
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
op.dba_ = nullptr;
return true;
}
bool PlanPrinter::PreVisit(query::plan::Produce &op) {
WithPrintLn([&](auto &out) {
out << "* Produce {";
utils::PrintIterable(out, op.named_expressions_, ", ", [](auto &out, const auto &nexpr) { out << nexpr->name_; });
out << "}";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
@ -160,14 +107,7 @@ PRE_VISIT(EmptyResult);
PRE_VISIT(EvaluatePatternFilter);
bool PlanPrinter::PreVisit(query::plan::Aggregate &op) {
WithPrintLn([&](auto &out) {
out << "* Aggregate {";
utils::PrintIterable(out, op.aggregations_, ", ",
[](auto &out, const auto &aggr) { out << aggr.output_sym.name(); });
out << "} {";
utils::PrintIterable(out, op.remember_, ", ", [](auto &out, const auto &sym) { out << sym.name(); });
out << "}";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
@ -175,11 +115,7 @@ PRE_VISIT(Skip);
PRE_VISIT(Limit);
bool PlanPrinter::PreVisit(query::plan::OrderBy &op) {
WithPrintLn([&op](auto &out) {
out << "* OrderBy {";
utils::PrintIterable(out, op.output_symbols_, ", ", [](auto &out, const auto &sym) { out << sym.name(); });
out << "}";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
@ -202,29 +138,19 @@ PRE_VISIT(Unwind);
PRE_VISIT(Distinct);
bool PlanPrinter::PreVisit(query::plan::Union &op) {
WithPrintLn([&op](auto &out) {
out << "* Union {";
utils::PrintIterable(out, op.left_symbols_, ", ", [](auto &out, const auto &sym) { out << sym.name(); });
out << " : ";
utils::PrintIterable(out, op.right_symbols_, ", ", [](auto &out, const auto &sym) { out << sym.name(); });
out << "}";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
Branch(*op.right_op_);
op.left_op_->Accept(*this);
return false;
}
bool PlanPrinter::PreVisit(query::plan::CallProcedure &op) {
WithPrintLn([&op](auto &out) {
out << "* CallProcedure<" << op.procedure_name_ << "> {";
utils::PrintIterable(out, op.result_symbols_, ", ", [](auto &out, const auto &sym) { out << sym.name(); });
out << "}";
});
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}
bool PlanPrinter::PreVisit(query::plan::LoadCsv &op) {
WithPrintLn([&op](auto &out) { out << "* LoadCsv {" << op.row_var_.name() << "}"; });
WithPrintLn([&](auto &out) { out << "* " << op.ToString(); });
return true;
}

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -57,7 +57,7 @@ class ProfilingStatsToTableHelper {
auto cycles = IndividualCycles(cumulative_stats);
rows_.emplace_back(std::vector<TypedValue>{
TypedValue(FormatOperator(cumulative_stats.name)), TypedValue(cumulative_stats.actual_hits),
TypedValue(FormatOperator(cumulative_stats.name.c_str())), TypedValue(cumulative_stats.actual_hits),
TypedValue(FormatRelativeTime(cycles)), TypedValue(FormatAbsoluteTime(cycles))});
for (size_t i = 1; i < cumulative_stats.children.size(); ++i) {
@ -137,7 +137,7 @@ class ProfilingStatsToJsonHelper {
void Output(const ProfilingStats &cumulative_stats, json *obj) {
auto cycles = IndividualCycles(cumulative_stats);
obj->emplace("name", cumulative_stats.name);
obj->emplace("name", cumulative_stats.name.c_str());
obj->emplace("actual_hits", cumulative_stats.actual_hits);
obj->emplace("relative_time", RelativeTime(cycles, total_cycles_));
obj->emplace("absolute_time", AbsoluteTime(cycles, total_cycles_, total_time_));

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -29,7 +29,7 @@ struct ProfilingStats {
int64_t actual_hits{0};
unsigned long long num_cycles{0};
uint64_t key{0};
const char *name{nullptr};
std::string name;
// TODO: This should use the allocator for query execution
std::vector<ProfilingStats> children;
};

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -28,6 +28,43 @@ namespace memgraph::query::plan {
*/
class ScopedProfile {
public:
ScopedProfile(uint64_t key, const query::plan::NamedLogicalOperator &op, query::ExecutionContext *context) noexcept
: context_(context) {
if (UNLIKELY(context_->is_profile_query)) {
root_ = context_->stats_root;
// Are we the root logical operator?
if (!root_) {
stats_ = &context_->stats;
stats_->key = key;
op.dba_ = context->db_accessor;
stats_->name = op.ToString();
op.dba_ = nullptr;
} else {
stats_ = nullptr;
// Was this logical operator already hit on one of the previous pulls?
auto it = std::find_if(root_->children.begin(), root_->children.end(),
[key](auto &stats) { return stats.key == key; });
if (it == root_->children.end()) {
root_->children.emplace_back();
stats_ = &root_->children.back();
stats_->key = key;
op.dba_ = context->db_accessor;
stats_->name = op.ToString();
op.dba_ = nullptr;
} else {
stats_ = &(*it);
}
}
context_->stats_root = stats_;
stats_->actual_hits++;
start_time_ = utils::ReadTSC();
}
}
ScopedProfile(uint64_t key, const char *name, query::ExecutionContext *context) noexcept : context_(context) {
if (UNLIKELY(context_->is_profile_query)) {
root_ = context_->stats_root;

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -22,6 +22,55 @@
namespace memgraph::utils {
/**
* Outputs a collection of items as a string, separating them with the given delimiter.
*
* @param first Starting iterator of collection which items are going to be
* printed.
* @param last Ending iterator of the collection.
* @param delim Delimiter that is put between items.
* @param transformation Function which accepts an item and returns a derived value.
*/
template <typename TIterator, typename TTransformation>
inline std::string IterableToString(TIterator first, TIterator last, const std::string_view delim = ", ",
TTransformation transformation = {}) {
std::string representation;
if (first != last) {
representation.append(transformation(*first));
++first;
}
for (; first != last; ++first) {
representation.append(delim);
representation.append(transformation(*first));
}
return representation;
}
/**
* Outputs a collection of items as a string, separating them with the given delimiter.
*
* @param iterable An iterable collection of items.
* @param delim Delimiter that is put between items.
* @param transformation Function which accepts an item and returns a derived value.
*/
template <typename TIterable, typename TTransformation>
inline std::string IterableToString(const TIterable &iterable, const std::string_view delim = ", ",
TTransformation transformation = {}) {
return IterableToString(iterable.begin(), iterable.end(), delim, transformation);
}
/**
* Outputs a collection of items as a string, separating them with the given delimiter.
*
* @param iterable An iterable collection of items.
* @param delim Delimiter that is put between items.
*/
template <typename TIterable>
inline std::string IterableToString(const TIterable &iterable, const std::string_view delim = ", ") {
return IterableToString(iterable, delim, [](const auto &item) { return item; });
}
/**
* Outputs a collection of items to the given stream, separating them with the
* given delimiter.

View File

@ -111,6 +111,9 @@ target_link_libraries(${test_prefix}query_plan_edge_cases mg-communication mg-qu
add_unit_test(query_plan_match_filter_return.cpp)
target_link_libraries(${test_prefix}query_plan_match_filter_return mg-query mg-query mg-glue)
add_unit_test(query_plan_operator_to_string.cpp)
target_link_libraries(${test_prefix}query_plan_operator_to_string mg-query)
add_unit_test(query_plan_read_write_typecheck.cpp
${CMAKE_SOURCE_DIR}/src/query/plan/read_write_type_checker.cpp)
target_link_libraries(${test_prefix}query_plan_read_write_typecheck mg-query)

View File

@ -740,7 +740,7 @@ TYPED_TEST(InterpreterTest, ProfileQuery) {
auto stream = this->Interpret("PROFILE MATCH (n) RETURN *;");
std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
EXPECT_EQ(stream.GetHeader(), expected_header);
std::vector<std::string> expected_rows{"* Produce", "* ScanAll", "* Once"};
std::vector<std::string> expected_rows{"* Produce {n}", "* ScanAll (n)", "* Once"};
ASSERT_EQ(stream.GetResults().size(), expected_rows.size());
auto expected_it = expected_rows.begin();
for (const auto &row : stream.GetResults()) {
@ -764,7 +764,7 @@ TYPED_TEST(InterpreterTest, ProfileQueryMultiplePulls) {
std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
EXPECT_EQ(stream.GetHeader(), expected_header);
std::vector<std::string> expected_rows{"* Produce", "* ScanAll", "* Once"};
std::vector<std::string> expected_rows{"* Produce {n}", "* ScanAll (n)", "* Once"};
auto expected_it = expected_rows.begin();
this->Pull(&stream, 1);
@ -806,7 +806,7 @@ TYPED_TEST(InterpreterTest, ProfileQueryWithParams) {
this->Interpret("PROFILE MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
EXPECT_EQ(stream.GetHeader(), expected_header);
std::vector<std::string> expected_rows{"* Produce", "* Filter", "* ScanAll", "* Once"};
std::vector<std::string> expected_rows{"* Produce {n}", "* Filter", "* ScanAll (n)", "* Once"};
ASSERT_EQ(stream.GetResults().size(), expected_rows.size());
auto expected_it = expected_rows.begin();
for (const auto &row : stream.GetResults()) {

View File

@ -0,0 +1,497 @@
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <gtest/gtest.h>
#include "disk_test_utils.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "query/plan/pretty_print.hpp"
#include "query_common.hpp"
#include "storage/v2/disk/storage.hpp"
#include "storage/v2/inmemory/storage.hpp"
using namespace memgraph::query;
using namespace memgraph::query::plan;
// The JSON formatted plan is consumed (or will be) by Memgraph Lab, and
// therefore should not be changed before synchronizing with whoever is
// maintaining Memgraph Lab. Hopefully, one day integration tests will exist and
// there will be no need to be super careful.
template <typename StorageType>
class OperatorToStringTest : public ::testing::Test {
protected:
const std::string testSuite = "plan_operator_to_string";
OperatorToStringTest()
: config(disk_test_utils::GenerateOnDiskConfig(testSuite)),
db(new StorageType(config)),
dba_storage(db->Access()),
dba(dba_storage.get()) {}
~OperatorToStringTest() {
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
disk_test_utils::RemoveRocksDbDirs(testSuite);
}
}
AstStorage storage;
SymbolTable symbol_table;
memgraph::storage::Config config;
std::unique_ptr<memgraph::storage::Storage> db;
std::unique_ptr<memgraph::storage::Storage::Accessor> dba_storage;
memgraph::query::DbAccessor dba;
Symbol GetSymbol(std::string name) { return symbol_table.CreateSymbol(name, true); }
};
using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
TYPED_TEST_CASE(OperatorToStringTest, StorageTypes);
TYPED_TEST(OperatorToStringTest, Once) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<Once>();
std::string expected_string{"Once"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, CreateNode) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<CreateNode>(
nullptr, NodeCreationInfo{this->GetSymbol("node"),
{this->dba.NameToLabel("Label1"), this->dba.NameToLabel("Label2")},
{{this->dba.NameToProperty("prop1"), LITERAL(5)},
{this->dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}});
std::string expected_string{"CreateNode"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, CreateExpand) {
Symbol node1_sym = this->GetSymbol("node1");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node1"));
last_op = std::make_shared<CreateExpand>(
NodeCreationInfo{this->GetSymbol("node2"),
{this->dba.NameToLabel("Label1"), this->dba.NameToLabel("Label2")},
{{this->dba.NameToProperty("prop1"), LITERAL(5)},
{this->dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}},
EdgeCreationInfo{this->GetSymbol("edge"),
{{this->dba.NameToProperty("weight"), LITERAL(5.32)}},
this->dba.NameToEdgeType("edge_type"),
EdgeAtom::Direction::OUT},
last_op, node1_sym, false);
last_op->dba_ = &this->dba;
std::string expected_string{"CreateExpand (node1)-[edge:edge_type]->(node2)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAll) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
std::string expected_string{"ScanAll (node)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAllByLabel) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAllByLabel>(nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"));
last_op->dba_ = &this->dba;
std::string expected_string{"ScanAllByLabel (node :Label)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAllByLabelPropertyRange) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAllByLabelPropertyRange>(
nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
memgraph::utils::MakeBoundInclusive<Expression *>(LITERAL(1)),
memgraph::utils::MakeBoundExclusive<Expression *>(LITERAL(20)));
last_op->dba_ = &this->dba;
std::string expected_string{"ScanAllByLabelPropertyRange (node :Label {prop})"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAllByLabelPropertyValue) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAllByLabelPropertyValue>(
nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
ADD(LITERAL(21), LITERAL(21)));
last_op->dba_ = &this->dba;
std::string expected_string{"ScanAllByLabelPropertyValue (node :Label {prop})"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAllByLabelProperty) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAllByLabelProperty>(nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"),
this->dba.NameToProperty("prop"), "prop");
last_op->dba_ = &this->dba;
std::string expected_string{"ScanAllByLabelProperty (node :Label {prop})"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ScanAllById) {
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<ScanAllById>(nullptr, this->GetSymbol("node"), ADD(LITERAL(21), LITERAL(21)));
last_op->dba_ = &this->dba;
std::string expected_string{"ScanAllById (node)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Expand) {
auto node1_sym = this->GetSymbol("node1");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
last_op = std::make_shared<Expand>(last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"),
EdgeAtom::Direction::BOTH,
std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
this->dba.NameToEdgeType("EdgeType2")},
false, memgraph::storage::View::OLD);
last_op->dba_ = &this->dba;
std::string expected_string{"Expand (node1)-[edge:EdgeType1|:EdgeType2]-(node2)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ExpandVariable) {
auto node1_sym = this->GetSymbol("node1");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
last_op = std::make_shared<ExpandVariable>(
last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"), EdgeAtom::Type::BREADTH_FIRST,
EdgeAtom::Direction::OUT,
std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
this->dba.NameToEdgeType("EdgeType2")},
false, LITERAL(2), LITERAL(5), false,
ExpansionLambda{this->GetSymbol("inner_node"), this->GetSymbol("inner_edge"),
PROPERTY_LOOKUP(this->dba, "inner_node", this->dba.NameToProperty("unblocked"))},
std::nullopt, std::nullopt);
last_op->dba_ = &this->dba;
std::string expected_string{"BFSExpand (node1)-[edge:EdgeType1|:EdgeType2]->(node2)"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, ConstructNamedPath) {
auto node1_sym = this->GetSymbol("node1");
auto edge1_sym = this->GetSymbol("edge1");
auto node2_sym = this->GetSymbol("node2");
auto edge2_sym = this->GetSymbol("edge2");
auto node3_sym = this->GetSymbol("node3");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::OUT,
std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
last_op = std::make_shared<Expand>(last_op, node2_sym, node3_sym, edge2_sym, EdgeAtom::Direction::OUT,
std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
last_op = std::make_shared<ConstructNamedPath>(
last_op, this->GetSymbol("path"), std::vector<Symbol>{node1_sym, edge1_sym, node2_sym, edge2_sym, node3_sym});
std::string expected_string{"ConstructNamedPath"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Filter) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node1"));
last_op =
std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
EQ(PROPERTY_LOOKUP(this->dba, "node1", this->dba.NameToProperty("prop")), LITERAL(5)));
std::string expected_string{"Filter"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Produce) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<Produce>(
nullptr, std::vector<NamedExpression *>{NEXPR("pet", LITERAL(5)), NEXPR("string", LITERAL("string"))});
std::string expected_string{"Produce {pet, string}"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Delete) {
auto node_sym = this->GetSymbol("node1");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<Expand>(last_op, node_sym, this->GetSymbol("node2"), this->GetSymbol("edge"),
EdgeAtom::Direction::BOTH, std::vector<memgraph::storage::EdgeTypeId>{}, false,
memgraph::storage::View::OLD);
last_op = std::make_shared<plan::Delete>(last_op, std::vector<Expression *>{IDENT("node2")}, true);
std::string expected_string{"Delete"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, SetProperty) {
memgraph::storage::PropertyId prop = this->dba.NameToProperty("prop");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP(this->dba, "node", prop),
ADD(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(1)));
std::string expected_string{"SetProperty"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, SetProperties) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::SetProperties>(last_op, node_sym,
MAP({{this->storage.GetPropertyIx("prop1"), LITERAL(1)},
{this->storage.GetPropertyIx("prop2"), LITERAL("propko")}}),
plan::SetProperties::Op::REPLACE);
std::string expected_string{"SetProperties"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, SetLabels) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::SetLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::string expected_string{"SetLabels"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, RemoveProperty) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::RemoveProperty>(
last_op, this->dba.NameToProperty("prop"), PROPERTY_LOOKUP(this->dba, "node", this->dba.NameToProperty("prop")));
std::string expected_string{"RemoveProperty"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, RemoveLabels) {
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::RemoveLabels>(
last_op, node_sym,
std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
std::string expected_string{"RemoveLabels"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, EdgeUniquenessFilter) {
auto node1_sym = this->GetSymbol("node1");
auto node2_sym = this->GetSymbol("node2");
auto node3_sym = this->GetSymbol("node3");
auto node4_sym = this->GetSymbol("node4");
auto edge1_sym = this->GetSymbol("edge1");
auto edge2_sym = this->GetSymbol("edge2");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::IN,
std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
last_op = std::make_shared<ScanAll>(last_op, node3_sym);
last_op = std::make_shared<Expand>(last_op, node3_sym, node4_sym, edge2_sym, EdgeAtom::Direction::OUT,
std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
last_op = std::make_shared<EdgeUniquenessFilter>(last_op, edge2_sym, std::vector<Symbol>{edge1_sym});
std::string expected_string{"EdgeUniquenessFilter"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Accumulate) {
memgraph::storage::PropertyId prop = this->dba.NameToProperty("prop");
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP(this->dba, "node", prop),
ADD(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(1)));
last_op = std::make_shared<plan::Accumulate>(last_op, std::vector<Symbol>{node_sym}, true);
std::string expected_string{"Accumulate"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Aggregate) {
memgraph::storage::PropertyId value = this->dba.NameToProperty("value");
memgraph::storage::PropertyId color = this->dba.NameToProperty("color");
memgraph::storage::PropertyId type = this->dba.NameToProperty("type");
auto node_sym = this->GetSymbol("node");
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<plan::Aggregate>(
nullptr,
std::vector<Aggregate::Element>{
{PROPERTY_LOOKUP(this->dba, "node", value), nullptr, Aggregation::Op::SUM, this->GetSymbol("sum")},
{PROPERTY_LOOKUP(this->dba, "node", value), PROPERTY_LOOKUP(this->dba, "node", color),
Aggregation::Op::COLLECT_MAP, this->GetSymbol("map")},
{nullptr, nullptr, Aggregation::Op::COUNT, this->GetSymbol("count")}},
std::vector<Expression *>{PROPERTY_LOOKUP(this->dba, "node", type)}, std::vector<Symbol>{node_sym});
std::string expected_string{"Aggregate {sum, map, count} {node}"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Skip) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
last_op = std::make_shared<Skip>(last_op, LITERAL(42));
std::string expected_string{"Skip"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Limit) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
last_op = std::make_shared<Limit>(last_op, LITERAL(42));
std::string expected_string{"Limit"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, OrderBy) {
Symbol person_sym = this->GetSymbol("person");
Symbol pet_sym = this->GetSymbol("pet");
memgraph::storage::PropertyId name = this->dba.NameToProperty("name");
memgraph::storage::PropertyId age = this->dba.NameToProperty("age");
std::shared_ptr<LogicalOperator> last_op;
last_op = std::make_shared<OrderBy>(nullptr,
std::vector<SortItem>{{Ordering::ASC, PROPERTY_LOOKUP(this->dba, "person", name)},
{Ordering::DESC, PROPERTY_LOOKUP(this->dba, "pet", age)}},
std::vector<Symbol>{person_sym, pet_sym});
std::string expected_string{"OrderBy {person, pet}"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Merge) {
Symbol node_sym = this->GetSymbol("node");
memgraph::storage::LabelId label = this->dba.NameToLabel("label");
std::shared_ptr<LogicalOperator> match = std::make_shared<ScanAllByLabel>(nullptr, node_sym, label);
std::shared_ptr<LogicalOperator> create =
std::make_shared<CreateNode>(nullptr, NodeCreationInfo{node_sym, {label}, {}});
std::shared_ptr<LogicalOperator> last_op = std::make_shared<plan::Merge>(nullptr, match, create);
std::string expected_string{"Merge"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Optional) {
Symbol node1_sym = this->GetSymbol("node1");
Symbol node2_sym = this->GetSymbol("node2");
Symbol edge_sym = this->GetSymbol("edge");
std::shared_ptr<LogicalOperator> input = std::make_shared<ScanAll>(nullptr, node1_sym);
std::shared_ptr<LogicalOperator> expand =
std::make_shared<Expand>(nullptr, node1_sym, node2_sym, edge_sym, EdgeAtom::Direction::OUT,
std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
std::shared_ptr<LogicalOperator> last_op =
std::make_shared<Optional>(input, expand, std::vector<Symbol>{node2_sym, edge_sym});
std::string expected_string{"Optional"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Unwind) {
std::shared_ptr<LogicalOperator> last_op =
std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(1), LITERAL(2), LITERAL(3)), this->GetSymbol("x"));
std::string expected_string{"Unwind"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Distinct) {
Symbol x = this->GetSymbol("x");
std::shared_ptr<LogicalOperator> last_op =
std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
last_op = std::make_shared<Distinct>(last_op, std::vector<Symbol>{x});
std::string expected_string{"Distinct"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Union) {
Symbol x = this->GetSymbol("x");
std::shared_ptr<LogicalOperator> lhs =
std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
Symbol node = this->GetSymbol("x");
std::shared_ptr<LogicalOperator> rhs = std::make_shared<ScanAll>(nullptr, node);
std::shared_ptr<LogicalOperator> last_op = std::make_shared<Union>(
lhs, rhs, std::vector<Symbol>{this->GetSymbol("x")}, std::vector<Symbol>{x}, std::vector<Symbol>{node});
std::string expected_string{"Union {x : x}"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, CallProcedure) {
memgraph::query::plan::CallProcedure call_op;
call_op.input_ = std::make_shared<Once>();
call_op.procedure_name_ = "mg.procedures";
call_op.arguments_ = {};
call_op.result_fields_ = {"is_editable", "is_write", "name", "path", "signature"};
call_op.result_symbols_ = {this->GetSymbol("is_editable"), this->GetSymbol("is_write"), this->GetSymbol("name"),
this->GetSymbol("path"), this->GetSymbol("signature")};
std::string expected_string{"CallProcedure<mg.procedures> {is_editable, is_write, name, path, signature}"};
EXPECT_EQ(call_op.ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, LoadCsv) {
memgraph::query::plan::LoadCsv last_op;
last_op.input_ = std::make_shared<Once>();
last_op.row_var_ = this->GetSymbol("transaction");
std::string expected_string{"LoadCsv {transaction}"};
EXPECT_EQ(last_op.ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Foreach) {
Symbol x = this->GetSymbol("x");
std::shared_ptr<LogicalOperator> create = std::make_shared<CreateNode>(
nullptr, NodeCreationInfo{this->GetSymbol("node"), {this->dba.NameToLabel("Label1")}, {}});
std::shared_ptr<LogicalOperator> foreach =
std::make_shared<plan::Foreach>(nullptr, std::move(create), LIST(LITERAL(1)), x);
std::string expected_string{"Foreach"};
EXPECT_EQ(foreach->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, EmptyResult) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<EmptyResult>(nullptr);
std::string expected_string{"EmptyResult"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, EvaluatePatternFilter) {
std::shared_ptr<LogicalOperator> last_op = std::make_shared<EvaluatePatternFilter>(nullptr, this->GetSymbol("node"));
std::string expected_string{"EvaluatePatternFilter"};
EXPECT_EQ(last_op->ToString(), expected_string);
}
TYPED_TEST(OperatorToStringTest, Apply) {
memgraph::query::plan::Apply last_op(nullptr, nullptr, false);
std::string expected_string{"Apply"};
EXPECT_EQ(last_op.ToString(), expected_string);
}