Make Where and NamedExpression macros easier to use

Summary:
`Where` can now be constructed in a `QUERY`, instead of requiring manual
addition to `Match`. For example:

    auto query = QUERY(MATCH(pattern), WHERE(expr), ...);

compared to:

    auto match = MATCH(pattern);
    match->where_ = WHERE(expr);
    auto query = QUERY(match, ...);

Similarly, `AS` can be used instead of `NEXPR` to create
`NamedExpressions` only with a name. This is meant to be used with
`RETURN` which will look at the previous `Expression` and store it
inside `NamedExpression`. For example:

    auto ret = RETURN(IDENT("n"), AS("n"),
                      PROPERTY_LOOKUP("n", prop), AS("prop_val"));

compared to:

    auto ret = RETURN(NEXPR("n", IDENT("n")),
                      NEXPR("prop_val", PROPERTY_LOOKUP("n", prop)));

Reviewers: florijan, mislav.bradac

Reviewed By: florijan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D195
This commit is contained in:
Teon Banek 2017-03-28 15:39:18 +02:00
parent af8df9e282
commit 9d3b4aab4b
4 changed files with 64 additions and 29 deletions

View File

@ -375,7 +375,8 @@ class NamedExpression : public Tree {
protected:
NamedExpression(int uid) : Tree(uid) {}
NamedExpression(int uid, std::string name, Expression *expression)
NamedExpression(int uid, const std::string &name) : Tree(uid), name_(name) {}
NamedExpression(int uid, const std::string &name, Expression *expression)
: Tree(uid), name_(name), expression_(expression) {}
};

View File

@ -72,23 +72,54 @@ auto GetWithPatterns(AstTreeStorage &storage, std::vector<Pattern *> patterns) {
///
/// Create a query with given clauses.
///
auto GetQuery(AstTreeStorage &storage, std::vector<Clause *> clauses) {
auto query = storage.query();
query->clauses_.insert(query->clauses_.begin(), clauses.begin(),
clauses.end());
return query;
auto GetQuery(AstTreeStorage &storage, Clause *clause) {
storage.query()->clauses_.emplace_back(clause);
return storage.query();
}
template <class... T>
auto GetQuery(AstTreeStorage &storage, Clause *clause, T *... clauses) {
storage.query()->clauses_.emplace_back(clause);
return GetQuery(storage, clauses...);
}
template <class... T>
auto GetQuery(AstTreeStorage &storage, Match *match, Where *where,
T *... clauses) {
match->where_ = where;
storage.query()->clauses_.emplace_back(match);
return GetQuery(storage, clauses...);
}
///
/// Create the return clause with given named expressions.
///
auto GetReturn(AstTreeStorage &storage,
std::vector<NamedExpression *> named_exprs) {
auto ret = storage.Create<Return>();
ret->named_expressions_.insert(ret->named_expressions_.begin(),
named_exprs.begin(), named_exprs.end());
auto GetReturn(Return *ret, NamedExpression *named_expr) {
ret->named_expressions_.emplace_back(named_expr);
return ret;
}
auto GetReturn(Return *ret, Expression *expr, NamedExpression *named_expr) {
// This overload supports `RETURN(expr, AS(name))` construct, since
// NamedExpression does not inherit Expression.
named_expr->expression_ = expr;
ret->named_expressions_.emplace_back(named_expr);
return ret;
}
template <class... T>
auto GetReturn(Return *ret, Expression *expr, NamedExpression *named_expr,
T *... rest) {
named_expr->expression_ = expr;
ret->named_expressions_.emplace_back(named_expr);
return GetReturn(ret, rest...);
}
template <class... T>
auto GetReturn(Return *ret, NamedExpression *named_expr, T *... rest) {
ret->named_expressions_.emplace_back(named_expr);
return GetReturn(ret, rest...);
}
template <class... T>
auto GetReturn(AstTreeStorage &storage, T *... exprs) {
auto ret = storage.Create<Return>();
return GetReturn(ret, exprs...);
}
///
/// Create the delete clause with given named expressions.
@ -158,10 +189,14 @@ auto GetSet(AstTreeStorage &storage, const std::string &name,
#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__})
// AS is alternative to NEXPR which does not initialize NamedExpression with
// Expression. It should be used with RETURN. For example:
// RETURN(IDENT("n"), AS("n")) vs. RETURN(NEXPR("n", IDENT("n"))).
#define AS(name) storage.Create<query::NamedExpression>((name))
#define RETURN(...) query::test_common::GetReturn(storage, __VA_ARGS__)
#define DELETE(...) query::test_common::GetDelete(storage, {__VA_ARGS__})
#define DETACH_DELETE(...) \
query::test_common::GetDelete(storage, {__VA_ARGS__}, true)
#define SET(...) query::test_common::GetSet(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))

View File

@ -60,15 +60,14 @@ auto CheckPlan(query::Query &query, std::list<size_t> expected_types) {
TEST(TestLogicalPlanner, MatchNodeReturn) {
// Test MATCH (n) RETURN n AS n
AstTreeStorage storage;
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(IDENT("n"), AS("n")));
CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Produce).hash_code()});
}
TEST(TestLogicalPlanner, CreateNodeReturn) {
// Test CREATE (n) RETURN n AS n
AstTreeStorage storage;
auto query =
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
auto query = QUERY(CREATE(PATTERN(NODE("n"))), RETURN(IDENT("n"), AS("n")));
CheckPlan(*query,
{typeid(CreateNode).hash_code(), typeid(Produce).hash_code()});
}
@ -120,7 +119,7 @@ TEST(TestLogicalPlanner, MatchLabeledNodes) {
AstTreeStorage storage;
std::string label("label");
auto query =
QUERY(MATCH(PATTERN(NODE("n", &label))), RETURN(NEXPR("n", IDENT("n"))));
QUERY(MATCH(PATTERN(NODE("n", &label))), RETURN(IDENT("n"), AS("n")));
CheckPlan(*query,
{typeid(ScanAll).hash_code(), typeid(NodeFilter).hash_code(),
typeid(Produce).hash_code()});
@ -132,7 +131,7 @@ TEST(TestLogicalPlanner, MatchPathReturn) {
std::string relationship("relationship");
auto query =
QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", &relationship), NODE("m"))),
RETURN(NEXPR("n", IDENT("n"))));
RETURN(IDENT("n"), AS("n")));
CheckPlan(*query,
{typeid(ScanAll).hash_code(), typeid(Expand).hash_code(),
typeid(EdgeFilter).hash_code(), typeid(Produce).hash_code()});
@ -142,9 +141,9 @@ 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"))));
auto query = QUERY(MATCH(PATTERN(NODE("n"))),
WHERE(LESS(PROPERTY_LOOKUP("n", &property), LITERAL(42))),
RETURN(IDENT("n"), AS("n")));
CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Filter).hash_code(),
typeid(Produce).hash_code()});
}

View File

@ -18,7 +18,7 @@ TEST(TestSymbolGenerator, MatchNodeReturn) {
AstTreeStorage storage;
// MATCH (node_atom_1) RETURN node_atom_1 AS node_atom_1
auto query_ast = QUERY(MATCH(PATTERN(NODE("node_atom_1"))),
RETURN(NEXPR("node_atom_1", IDENT("node_atom_1"))));
RETURN(IDENT("node_atom_1"), AS("node_atom_1")));
SymbolGenerator symbol_generator(symbol_table);
query_ast->Accept(symbol_generator);
EXPECT_EQ(symbol_table.max_position(), 2);
@ -45,7 +45,7 @@ TEST(TestSymbolGenerator, MatchUnboundMultiReturn) {
// MATCH (node_atom_1) RETURN node_atom_1 AS n, n AS n
auto query_ast =
QUERY(MATCH(PATTERN(NODE("node_atom_1"))),
RETURN(NEXPR("n", IDENT("node_atom_1")), NEXPR("n", IDENT("n"))));
RETURN(IDENT("node_atom_1"), AS("n"), IDENT("n"), AS("n")));
SymbolGenerator symbol_generator(symbol_table);
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
}
@ -55,7 +55,7 @@ TEST(TestSymbolGenerator, MatchNodeUnboundReturn) {
AstTreeStorage storage;
// AST with unbound variable in return: MATCH (n) RETURN x AS x
auto query_ast =
QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("x", IDENT("x"))));
QUERY(MATCH(PATTERN(NODE("n"))), RETURN(IDENT("x"), AS("x")));
SymbolGenerator symbol_generator(symbol_table);
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
}
@ -68,7 +68,7 @@ TEST(TestSymbolGenerator, MatchSameEdge) {
// This usually throws a redeclaration error, but we support it.
auto query_ast = QUERY(
MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("n"), EDGE("r"), NODE("n"))),
RETURN(NEXPR("r", IDENT("r"))));
RETURN(IDENT("r"), AS("r")));
SymbolGenerator symbol_generator(symbol_table);
query_ast->Accept(symbol_generator);
EXPECT_EQ(symbol_table.max_position(), 3);
@ -119,7 +119,7 @@ TEST(TestSymbolGenerator, CreateNodeReturn) {
AstTreeStorage storage;
// Simple AST returning a created node: CREATE (n) RETURN n
auto query_ast =
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(IDENT("n"), AS("n")));
SymbolGenerator symbol_generator(symbol_table);
query_ast->Accept(symbol_generator);
EXPECT_EQ(symbol_table.max_position(), 2);
@ -233,9 +233,9 @@ 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"))));
auto query = QUERY(MATCH(PATTERN(NODE("n"))),
WHERE(LESS(IDENT("missing"), LITERAL(42))),
RETURN(IDENT("n"), AS("n")));
SymbolTable symbol_table;
SymbolGenerator symbol_generator(symbol_table);
EXPECT_THROW(query->Accept(symbol_generator), UnboundVariableError);