Plan Unwind operator
Summary: Support ListLiteral in test macros. Test planning Unwind. Support UNWIND in test macros. Test SymbolGenerator for UNWIND clause. Use namespace in QueryPlan Unwind test. Reviewers: mislav.bradac, buda, florijan Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D331
This commit is contained in:
parent
eef6fb1981
commit
cbd3998899
@ -114,6 +114,14 @@ void SymbolGenerator::PostVisit(Where &) { scope_.in_where = false; }
|
||||
void SymbolGenerator::Visit(Merge &) { scope_.in_merge = true; }
|
||||
void SymbolGenerator::PostVisit(Merge &) { scope_.in_merge = false; }
|
||||
|
||||
void SymbolGenerator::PostVisit(Unwind &unwind) {
|
||||
const auto &name = unwind.named_expression_->name_;
|
||||
if (HasSymbol(name)) {
|
||||
throw RedeclareVariableError(name);
|
||||
}
|
||||
symbol_table_[*unwind.named_expression_] = CreateSymbol(name);
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
void SymbolGenerator::Visit(Identifier &ident) {
|
||||
|
@ -33,6 +33,7 @@ class SymbolGenerator : public TreeVisitorBase {
|
||||
void PostVisit(Where &) override;
|
||||
void Visit(Merge &) override;
|
||||
void PostVisit(Merge &) override;
|
||||
void PostVisit(Unwind &) override;
|
||||
|
||||
// Expressions
|
||||
void Visit(Identifier &) override;
|
||||
|
@ -533,6 +533,10 @@ std::unique_ptr<LogicalOperator> MakeLogicalPlan(
|
||||
bound_symbols)) {
|
||||
is_write = true;
|
||||
input_op = op;
|
||||
} else if (auto *unwind = dynamic_cast<query::Unwind *>(clause)) {
|
||||
input_op = new plan::Unwind(std::shared_ptr<LogicalOperator>(input_op),
|
||||
unwind->named_expression_->expression_,
|
||||
symbol_table.at(*unwind->named_expression_));
|
||||
} else {
|
||||
throw utils::NotYetImplemented();
|
||||
}
|
||||
|
@ -252,6 +252,17 @@ auto GetWith(AstTreeStorage &storage, T... exprs) {
|
||||
return with;
|
||||
}
|
||||
|
||||
///
|
||||
/// Create the UNWIND clause with given named expression.
|
||||
///
|
||||
auto GetUnwind(AstTreeStorage &storage, NamedExpression *named_expr) {
|
||||
return storage.Create<query::Unwind>(named_expr);
|
||||
}
|
||||
auto GetUnwind(AstTreeStorage &storage, Expression *expr, NamedExpression *as) {
|
||||
as->expression_ = expr;
|
||||
return GetUnwind(storage, as);
|
||||
}
|
||||
|
||||
///
|
||||
/// Create the delete clause with given named expressions.
|
||||
///
|
||||
@ -357,6 +368,9 @@ auto GetMerge(AstTreeStorage &storage, Pattern *pattern, OnMatch on_match,
|
||||
{__VA_ARGS__})
|
||||
#define IDENT(name) storage.Create<query::Identifier>((name))
|
||||
#define LITERAL(val) storage.Create<query::PrimitiveLiteral>((val))
|
||||
#define LIST(...) \
|
||||
storage.Create<query::ListLiteral>( \
|
||||
std::vector<query::Expression *>{__VA_ARGS__})
|
||||
#define PROPERTY_LOOKUP(...) \
|
||||
query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
|
||||
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
|
||||
@ -366,6 +380,7 @@ auto GetMerge(AstTreeStorage &storage, Pattern *pattern, OnMatch on_match,
|
||||
#define AS(name) storage.Create<query::NamedExpression>((name))
|
||||
#define RETURN(...) query::test_common::GetReturn(storage, __VA_ARGS__)
|
||||
#define WITH(...) query::test_common::GetWith(storage, __VA_ARGS__)
|
||||
#define UNWIND(...) query::test_common::GetUnwind(storage, __VA_ARGS__)
|
||||
#define ORDER_BY(...) query::test_common::GetOrderBy(__VA_ARGS__)
|
||||
#define SKIP(expr) \
|
||||
query::test_common::Skip { (expr) }
|
||||
|
@ -72,6 +72,7 @@ class PlanChecker : public LogicalOperatorVisitor {
|
||||
op.input()->Accept(*this);
|
||||
return false;
|
||||
}
|
||||
void Visit(Unwind &op) override { CheckOp(op); }
|
||||
|
||||
std::list<BaseOpChecker *> checkers_;
|
||||
|
||||
@ -117,6 +118,7 @@ using ExpectExpandUniquenessFilter =
|
||||
using ExpectSkip = OpChecker<Skip>;
|
||||
using ExpectLimit = OpChecker<Limit>;
|
||||
using ExpectOrderBy = OpChecker<OrderBy>;
|
||||
using ExpectUnwind = OpChecker<Unwind>;
|
||||
|
||||
class ExpectAccumulate : public OpChecker<Accumulate> {
|
||||
public:
|
||||
@ -669,4 +671,13 @@ TEST(TestLogicalPlanner, MatchOptionalMatchWhereReturn) {
|
||||
CheckPlan(*query, ExpectScanAll(), ExpectOptional(optional), ExpectProduce());
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchUnwindReturn) {
|
||||
// Test MATCH (n) UNWIND [1,2,3] AS x RETURN n AS n, x AS x
|
||||
AstTreeStorage storage;
|
||||
auto query = QUERY(MATCH(PATTERN(NODE("n"))),
|
||||
UNWIND(LIST(LITERAL(1), LITERAL(2), LITERAL(3)), AS("x")),
|
||||
RETURN(IDENT("n"), AS("n"), IDENT("x"), AS("x")));
|
||||
CheckPlan(*query, ExpectScanAll(), ExpectUnwind(), ExpectProduce());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -724,4 +724,41 @@ TEST(TestSymbolGenerator, MergeOnMatchOnCreate) {
|
||||
EXPECT_EQ(m, symbol_table.at(*m_prop->expression_));
|
||||
}
|
||||
|
||||
TEST(TestSymbolGenerator, WithUnwindRedeclareReturn) {
|
||||
// Test WITH [1, 2] AS list UNWIND list AS list RETURN list
|
||||
AstTreeStorage storage;
|
||||
auto query = QUERY(WITH(LIST(LITERAL(1), LITERAL(2)), AS("list")),
|
||||
UNWIND(IDENT("list"), AS("list")),
|
||||
RETURN(IDENT("list"), AS("list")));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), RedeclareVariableError);
|
||||
}
|
||||
|
||||
TEST(TestSymbolGenerator, WithUnwindReturn) {
|
||||
// WITH [1, 2] AS list UNWIND list AS elem RETURN list AS list, elem AS elem
|
||||
AstTreeStorage storage;
|
||||
auto with_as_list = AS("list");
|
||||
auto unwind = UNWIND(IDENT("list"), AS("elem"));
|
||||
auto ret_list = IDENT("list");
|
||||
auto ret_as_list = AS("list");
|
||||
auto ret_elem = IDENT("elem");
|
||||
auto ret_as_elem = AS("elem");
|
||||
auto query = QUERY(WITH(LIST(LITERAL(1), LITERAL(2)), with_as_list), unwind,
|
||||
RETURN(ret_list, ret_as_list, ret_elem, ret_as_elem));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
// Symbols for: `list`, `elem`, `AS list`, `AS elem`
|
||||
EXPECT_EQ(symbol_table.max_position(), 4);
|
||||
const auto &list = symbol_table.at(*with_as_list);
|
||||
EXPECT_EQ(list, symbol_table.at(*unwind->named_expression_->expression_));
|
||||
const auto &elem = symbol_table.at(*unwind->named_expression_);
|
||||
EXPECT_NE(list, elem);
|
||||
EXPECT_EQ(list, symbol_table.at(*ret_list));
|
||||
EXPECT_NE(list, symbol_table.at(*ret_as_list));
|
||||
EXPECT_EQ(elem, symbol_table.at(*ret_elem));
|
||||
EXPECT_NE(elem, symbol_table.at(*ret_as_elem));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user