[master < T1204] Add detailed operator info to PROFILE and EXPLAIN (#1204)
This commit is contained in:
parent
29a505cb38
commit
060b9d1c16
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_));
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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()) {
|
||||
|
497
tests/unit/query_plan_operator_to_string.cpp
Normal file
497
tests/unit/query_plan_operator_to_string.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user