Add basic planning of WHERE
Summary: Add testing unbound variable in WHERE Remove some duplication in planner tests Reviewers: florijan, mislav.bradac Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D186
This commit is contained in:
parent
d387651205
commit
4bfae46150
@ -492,6 +492,22 @@ class Create : public Clause {
|
||||
}
|
||||
};
|
||||
|
||||
class Where : public Tree {
|
||||
friend class AstTreeStorage;
|
||||
|
||||
public:
|
||||
void Accept(TreeVisitorBase &visitor) override {
|
||||
visitor.Visit(*this);
|
||||
expression_->Accept(visitor);
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
Expression *expression_ = nullptr;
|
||||
|
||||
protected:
|
||||
Where(int uid) : Tree(uid) {}
|
||||
Where(int uid, Expression *expression) : Tree(uid), expression_(expression) {}
|
||||
};
|
||||
|
||||
class Match : public Clause {
|
||||
friend class AstTreeStorage;
|
||||
|
||||
@ -501,6 +517,9 @@ class Match : public Clause {
|
||||
for (auto &pattern : patterns_) {
|
||||
pattern->Accept(visitor);
|
||||
}
|
||||
if (where_) {
|
||||
where_->Accept(visitor);
|
||||
}
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
std::vector<Pattern *> patterns_;
|
||||
@ -545,21 +564,6 @@ class Delete : public Clause {
|
||||
Delete(int uid) : Clause(uid) {}
|
||||
};
|
||||
|
||||
class Where : public Tree {
|
||||
friend class AstTreeStorage;
|
||||
|
||||
public:
|
||||
void Accept(TreeVisitorBase &visitor) override {
|
||||
visitor.Visit(*this);
|
||||
expression_->Accept(visitor);
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
Expression *expression_ = nullptr;
|
||||
|
||||
protected:
|
||||
Where(int uid) : Tree(uid) {}
|
||||
};
|
||||
|
||||
class SetProperty : public Clause {
|
||||
friend class AstTreeStorage;
|
||||
|
||||
|
@ -152,7 +152,13 @@ auto GenMatch(Match &match, LogicalOperator *input_op,
|
||||
// TODO: Support matching multiple patterns.
|
||||
throw NotYetImplemented();
|
||||
}
|
||||
return ReducePattern<LogicalOperator *>(*match.patterns_[0], base, collect);
|
||||
auto last_op =
|
||||
ReducePattern<LogicalOperator *>(*match.patterns_[0], base, collect);
|
||||
if (match.where_) {
|
||||
last_op = new Filter(std::shared_ptr<LogicalOperator>(last_op),
|
||||
match.where_->expression_);
|
||||
}
|
||||
return last_op;
|
||||
}
|
||||
|
||||
auto GenReturn(Return &ret, LogicalOperator *input_op) {
|
||||
|
@ -96,8 +96,7 @@ auto GetReturn(AstTreeStorage &storage,
|
||||
|
||||
///
|
||||
/// All the following macros implicitly pass `storage` variable to functions.
|
||||
/// You
|
||||
/// need to have `AstTreeStorage storage;` somewhere in scope to use them.
|
||||
/// You need to have `AstTreeStorage storage;` somewhere in scope to use them.
|
||||
/// Refer to function documentation to see what the macro does.
|
||||
///
|
||||
/// Example usage:
|
||||
@ -112,11 +111,14 @@ auto GetReturn(AstTreeStorage &storage,
|
||||
#define PATTERN(...) query::test_common::GetPattern(storage, {__VA_ARGS__})
|
||||
#define MATCH(...) \
|
||||
query::test_common::GetWithPatterns<query::Match>(storage, {__VA_ARGS__})
|
||||
#define WHERE(expr) storage.Create<query::Where>((expr))
|
||||
#define CREATE(...) \
|
||||
query::test_common::GetWithPatterns<query::Create>(storage, {__VA_ARGS__})
|
||||
#define IDENT(name) storage.Create<query::Identifier>((name))
|
||||
#define LITERAL(val) storage.Create<query::Literal>((val))
|
||||
#define PROPERTY_LOOKUP(...) query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
|
||||
#define PROPERTY_LOOKUP(...) \
|
||||
query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
|
||||
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
|
||||
#define RETURN(...) query::test_common::GetReturn(storage, {__VA_ARGS__})
|
||||
#define QUERY(...) query::test_common::GetQuery(storage, {__VA_ARGS__})
|
||||
#define LESS(expr1, expr2) storage.Create<query::LessOperator>((expr1), (expr2))
|
||||
|
@ -32,6 +32,7 @@ class PlanChecker : public LogicalOperatorVisitor {
|
||||
void Visit(Expand &op) override { AssertType(op); }
|
||||
void Visit(NodeFilter &op) override { AssertType(op); }
|
||||
void Visit(EdgeFilter &op) override { AssertType(op); }
|
||||
void Visit(Filter &op) override { AssertType(op); }
|
||||
void Visit(Produce &op) override { AssertType(op); }
|
||||
|
||||
private:
|
||||
@ -43,19 +44,20 @@ class PlanChecker : public LogicalOperatorVisitor {
|
||||
std::list<size_t> types_;
|
||||
};
|
||||
|
||||
auto CheckPlan(query::Query &query, std::list<size_t> expected_types) {
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query.Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(query, symbol_table);
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchNodeReturn) {
|
||||
// Test MATCH (n) RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(ScanAll).hash_code());
|
||||
expected_types.emplace_back(typeid(Produce).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Produce).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, CreateNodeReturn) {
|
||||
@ -63,15 +65,8 @@ TEST(TestLogicalPlanner, CreateNodeReturn) {
|
||||
AstTreeStorage storage;
|
||||
auto query =
|
||||
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
expected_types.emplace_back(typeid(Produce).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(CreateNode).hash_code(), typeid(Produce).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, CreateExpand) {
|
||||
@ -80,30 +75,16 @@ TEST(TestLogicalPlanner, CreateExpand) {
|
||||
std::string relationship("relationship");
|
||||
auto query = QUERY(CREATE(PATTERN(
|
||||
NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
expected_types.emplace_back(typeid(CreateExpand).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(CreateNode).hash_code(), typeid(CreateExpand).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, CreateMultipleNode) {
|
||||
// Test CREATE (n), (m)
|
||||
AstTreeStorage storage;
|
||||
auto query = QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("m"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(CreateNode).hash_code(), typeid(CreateNode).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
||||
@ -113,16 +94,9 @@ TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
||||
auto query = QUERY(CREATE(
|
||||
PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m")),
|
||||
PATTERN(NODE("l"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
expected_types.emplace_back(typeid(CreateExpand).hash_code());
|
||||
expected_types.emplace_back(typeid(CreateNode).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(CreateNode).hash_code(), typeid(CreateExpand).hash_code(),
|
||||
typeid(CreateNode).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchCreateExpand) {
|
||||
@ -133,15 +107,8 @@ TEST(TestLogicalPlanner, MatchCreateExpand) {
|
||||
MATCH(PATTERN(NODE("n"))),
|
||||
CREATE(PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT),
|
||||
NODE("m"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(ScanAll).hash_code());
|
||||
expected_types.emplace_back(typeid(CreateExpand).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(ScanAll).hash_code(), typeid(CreateExpand).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
||||
@ -150,16 +117,9 @@ TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
||||
std::string label("label");
|
||||
auto query =
|
||||
QUERY(MATCH(PATTERN(NODE("n", &label))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(ScanAll).hash_code());
|
||||
expected_types.emplace_back(typeid(NodeFilter).hash_code());
|
||||
expected_types.emplace_back(typeid(Produce).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(ScanAll).hash_code(), typeid(NodeFilter).hash_code(),
|
||||
typeid(Produce).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchPathReturn) {
|
||||
@ -169,17 +129,20 @@ TEST(TestLogicalPlanner, MatchPathReturn) {
|
||||
auto query =
|
||||
QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", &relationship), NODE("m"))),
|
||||
RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
||||
std::list<size_t> expected_types;
|
||||
expected_types.emplace_back(typeid(ScanAll).hash_code());
|
||||
expected_types.emplace_back(typeid(Expand).hash_code());
|
||||
expected_types.emplace_back(typeid(EdgeFilter).hash_code());
|
||||
expected_types.emplace_back(typeid(Produce).hash_code());
|
||||
PlanChecker plan_checker(expected_types);
|
||||
plan->Accept(plan_checker);
|
||||
CheckPlan(*query,
|
||||
{typeid(ScanAll).hash_code(), typeid(Expand).hash_code(),
|
||||
typeid(EdgeFilter).hash_code(), typeid(Produce).hash_code()});
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchWhereReturn) {
|
||||
// Test MATCH (n) WHERE n.property < 42 RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
std::string property("property");
|
||||
auto match = MATCH(PATTERN(NODE("n")));
|
||||
match->where_ = WHERE(LESS(PROPERTY_LOOKUP("n", &property), LITERAL(42)));
|
||||
auto query = QUERY(match, RETURN(NEXPR("n", IDENT("n"))));
|
||||
CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Filter).hash_code(),
|
||||
typeid(Produce).hash_code()});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -225,4 +225,16 @@ TEST(TestSymbolGenerator, CreateBidirectionalEdge) {
|
||||
EXPECT_THROW(query->Accept(symbol_generator), SemanticException);
|
||||
}
|
||||
|
||||
TEST(TestSymbolGenerator, MatchWhereUnbound) {
|
||||
// Test MATCH (n) WHERE missing < 42 RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
std::string property("property");
|
||||
auto match = MATCH(PATTERN(NODE("n")));
|
||||
match->where_ = WHERE(LESS(IDENT("missing"), LITERAL(42)));
|
||||
auto query = QUERY(match, RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), UnboundVariableError);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user