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 {
|
class Match : public Clause {
|
||||||
friend class AstTreeStorage;
|
friend class AstTreeStorage;
|
||||||
|
|
||||||
@ -501,6 +517,9 @@ class Match : public Clause {
|
|||||||
for (auto &pattern : patterns_) {
|
for (auto &pattern : patterns_) {
|
||||||
pattern->Accept(visitor);
|
pattern->Accept(visitor);
|
||||||
}
|
}
|
||||||
|
if (where_) {
|
||||||
|
where_->Accept(visitor);
|
||||||
|
}
|
||||||
visitor.PostVisit(*this);
|
visitor.PostVisit(*this);
|
||||||
}
|
}
|
||||||
std::vector<Pattern *> patterns_;
|
std::vector<Pattern *> patterns_;
|
||||||
@ -545,21 +564,6 @@ class Delete : public Clause {
|
|||||||
Delete(int uid) : Clause(uid) {}
|
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 {
|
class SetProperty : public Clause {
|
||||||
friend class AstTreeStorage;
|
friend class AstTreeStorage;
|
||||||
|
|
||||||
|
@ -152,7 +152,13 @@ auto GenMatch(Match &match, LogicalOperator *input_op,
|
|||||||
// TODO: Support matching multiple patterns.
|
// TODO: Support matching multiple patterns.
|
||||||
throw NotYetImplemented();
|
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) {
|
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.
|
/// All the following macros implicitly pass `storage` variable to functions.
|
||||||
/// You
|
/// You need to have `AstTreeStorage storage;` somewhere in scope to use them.
|
||||||
/// need to have `AstTreeStorage storage;` somewhere in scope to use them.
|
|
||||||
/// Refer to function documentation to see what the macro does.
|
/// Refer to function documentation to see what the macro does.
|
||||||
///
|
///
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
@ -112,11 +111,14 @@ auto GetReturn(AstTreeStorage &storage,
|
|||||||
#define PATTERN(...) query::test_common::GetPattern(storage, {__VA_ARGS__})
|
#define PATTERN(...) query::test_common::GetPattern(storage, {__VA_ARGS__})
|
||||||
#define MATCH(...) \
|
#define MATCH(...) \
|
||||||
query::test_common::GetWithPatterns<query::Match>(storage, {__VA_ARGS__})
|
query::test_common::GetWithPatterns<query::Match>(storage, {__VA_ARGS__})
|
||||||
|
#define WHERE(expr) storage.Create<query::Where>((expr))
|
||||||
#define CREATE(...) \
|
#define CREATE(...) \
|
||||||
query::test_common::GetWithPatterns<query::Create>(storage, {__VA_ARGS__})
|
query::test_common::GetWithPatterns<query::Create>(storage, {__VA_ARGS__})
|
||||||
#define IDENT(name) storage.Create<query::Identifier>((name))
|
#define IDENT(name) storage.Create<query::Identifier>((name))
|
||||||
#define LITERAL(val) storage.Create<query::Literal>((val))
|
#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 NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
|
||||||
#define RETURN(...) query::test_common::GetReturn(storage, {__VA_ARGS__})
|
#define RETURN(...) query::test_common::GetReturn(storage, {__VA_ARGS__})
|
||||||
#define QUERY(...) query::test_common::GetQuery(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(Expand &op) override { AssertType(op); }
|
||||||
void Visit(NodeFilter &op) override { AssertType(op); }
|
void Visit(NodeFilter &op) override { AssertType(op); }
|
||||||
void Visit(EdgeFilter &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); }
|
void Visit(Produce &op) override { AssertType(op); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -43,19 +44,20 @@ class PlanChecker : public LogicalOperatorVisitor {
|
|||||||
std::list<size_t> types_;
|
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(TestLogicalPlanner, MatchNodeReturn) {
|
||||||
// Test MATCH (n) RETURN n AS n
|
// Test MATCH (n) RETURN n AS n
|
||||||
AstTreeStorage storage;
|
AstTreeStorage storage;
|
||||||
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Produce).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, CreateNodeReturn) {
|
TEST(TestLogicalPlanner, CreateNodeReturn) {
|
||||||
@ -63,15 +65,8 @@ TEST(TestLogicalPlanner, CreateNodeReturn) {
|
|||||||
AstTreeStorage storage;
|
AstTreeStorage storage;
|
||||||
auto query =
|
auto query =
|
||||||
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(CreateNode).hash_code(), typeid(Produce).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, CreateExpand) {
|
TEST(TestLogicalPlanner, CreateExpand) {
|
||||||
@ -80,30 +75,16 @@ TEST(TestLogicalPlanner, CreateExpand) {
|
|||||||
std::string relationship("relationship");
|
std::string relationship("relationship");
|
||||||
auto query = QUERY(CREATE(PATTERN(
|
auto query = QUERY(CREATE(PATTERN(
|
||||||
NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m"))));
|
NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(CreateNode).hash_code(), typeid(CreateExpand).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, CreateMultipleNode) {
|
TEST(TestLogicalPlanner, CreateMultipleNode) {
|
||||||
// Test CREATE (n), (m)
|
// Test CREATE (n), (m)
|
||||||
AstTreeStorage storage;
|
AstTreeStorage storage;
|
||||||
auto query = QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("m"))));
|
auto query = QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("m"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(CreateNode).hash_code(), typeid(CreateNode).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
||||||
@ -113,16 +94,9 @@ TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
|||||||
auto query = QUERY(CREATE(
|
auto query = QUERY(CREATE(
|
||||||
PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m")),
|
PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT), NODE("m")),
|
||||||
PATTERN(NODE("l"))));
|
PATTERN(NODE("l"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(CreateNode).hash_code(), typeid(CreateExpand).hash_code(),
|
||||||
query->Accept(symbol_generator);
|
typeid(CreateNode).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, MatchCreateExpand) {
|
TEST(TestLogicalPlanner, MatchCreateExpand) {
|
||||||
@ -133,15 +107,8 @@ TEST(TestLogicalPlanner, MatchCreateExpand) {
|
|||||||
MATCH(PATTERN(NODE("n"))),
|
MATCH(PATTERN(NODE("n"))),
|
||||||
CREATE(PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT),
|
CREATE(PATTERN(NODE("n"), EDGE("r", &relationship, Direction::RIGHT),
|
||||||
NODE("m"))));
|
NODE("m"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(ScanAll).hash_code(), typeid(CreateExpand).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
||||||
@ -150,16 +117,9 @@ TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
|||||||
std::string label("label");
|
std::string label("label");
|
||||||
auto query =
|
auto query =
|
||||||
QUERY(MATCH(PATTERN(NODE("n", &label))), RETURN(NEXPR("n", IDENT("n"))));
|
QUERY(MATCH(PATTERN(NODE("n", &label))), RETURN(NEXPR("n", IDENT("n"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(ScanAll).hash_code(), typeid(NodeFilter).hash_code(),
|
||||||
query->Accept(symbol_generator);
|
typeid(Produce).hash_code()});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TestLogicalPlanner, MatchPathReturn) {
|
TEST(TestLogicalPlanner, MatchPathReturn) {
|
||||||
@ -169,17 +129,20 @@ TEST(TestLogicalPlanner, MatchPathReturn) {
|
|||||||
auto query =
|
auto query =
|
||||||
QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", &relationship), NODE("m"))),
|
QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", &relationship), NODE("m"))),
|
||||||
RETURN(NEXPR("n", IDENT("n"))));
|
RETURN(NEXPR("n", IDENT("n"))));
|
||||||
SymbolTable symbol_table;
|
CheckPlan(*query,
|
||||||
SymbolGenerator symbol_generator(symbol_table);
|
{typeid(ScanAll).hash_code(), typeid(Expand).hash_code(),
|
||||||
query->Accept(symbol_generator);
|
typeid(EdgeFilter).hash_code(), typeid(Produce).hash_code()});
|
||||||
auto plan = MakeLogicalPlan(*query, symbol_table);
|
}
|
||||||
std::list<size_t> expected_types;
|
|
||||||
expected_types.emplace_back(typeid(ScanAll).hash_code());
|
TEST(TestLogicalPlanner, MatchWhereReturn) {
|
||||||
expected_types.emplace_back(typeid(Expand).hash_code());
|
// Test MATCH (n) WHERE n.property < 42 RETURN n AS n
|
||||||
expected_types.emplace_back(typeid(EdgeFilter).hash_code());
|
AstTreeStorage storage;
|
||||||
expected_types.emplace_back(typeid(Produce).hash_code());
|
std::string property("property");
|
||||||
PlanChecker plan_checker(expected_types);
|
auto match = MATCH(PATTERN(NODE("n")));
|
||||||
plan->Accept(plan_checker);
|
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);
|
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