Update and refactor query unit tests
Summary: Test multiple create Add utility macros for easier creation of AST Use test query macros when testing semantic analysis Document the query test macros Use query test macros in interpreter tests Reviewers: florijan, mislav.bradac, buda Reviewed By: mislav.bradac Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D178
This commit is contained in:
parent
6a547839e1
commit
71c2813f39
@ -18,6 +18,8 @@
|
||||
#include "query/frontend/opencypher/parser.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
|
||||
#include "query_common.hpp"
|
||||
|
||||
using namespace query;
|
||||
|
||||
/**
|
||||
@ -83,28 +85,25 @@ auto MakeProduce(std::shared_ptr<LogicalOperator> input,
|
||||
*
|
||||
* Returns (node_atom, scan_all_logical_op, symbol).
|
||||
*/
|
||||
auto MakeScanAll(AstTreeStorage &ast_storage, SymbolTable &symbol_table,
|
||||
auto MakeScanAll(AstTreeStorage &storage, SymbolTable &symbol_table,
|
||||
const std::string &identifier) {
|
||||
auto node =
|
||||
ast_storage.Create<NodeAtom>(ast_storage.Create<Identifier>(identifier));
|
||||
auto node = NODE(identifier);
|
||||
auto logical_op = std::make_shared<ScanAll>(node);
|
||||
auto symbol = symbol_table.CreateSymbol(identifier);
|
||||
symbol_table[*node->identifier_] = symbol;
|
||||
return std::make_tuple(node, logical_op, symbol);
|
||||
}
|
||||
|
||||
auto MakeExpand(AstTreeStorage &ast_storage, SymbolTable &symbol_table,
|
||||
auto MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table,
|
||||
std::shared_ptr<LogicalOperator> input, Symbol input_symbol,
|
||||
const std::string &edge_identifier,
|
||||
EdgeAtom::Direction direction, bool edge_cycle,
|
||||
const std::string &node_identifier, bool node_cycle) {
|
||||
auto edge = ast_storage.Create<EdgeAtom>(
|
||||
ast_storage.Create<Identifier>(edge_identifier), direction);
|
||||
auto edge = EDGE(edge_identifier, direction);
|
||||
auto edge_sym = symbol_table.CreateSymbol(edge_identifier);
|
||||
symbol_table[*edge->identifier_] = edge_sym;
|
||||
|
||||
auto node = ast_storage.Create<NodeAtom>(
|
||||
ast_storage.Create<Identifier>(node_identifier));
|
||||
auto node = NODE(node_identifier);
|
||||
auto node_sym = symbol_table.CreateSymbol(node_identifier);
|
||||
symbol_table[*node->identifier_] = node_sym;
|
||||
|
||||
@ -136,8 +135,7 @@ TEST(Interpreter, MatchReturn) {
|
||||
SymbolTable symbol_table;
|
||||
|
||||
auto scan_all = MakeScanAll(storage, symbol_table, "n");
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("n", storage.Create<Identifier>("n"));
|
||||
auto output = NEXPR("n", IDENT("n"));
|
||||
auto produce = MakeProduce(std::get<1>(scan_all), output);
|
||||
symbol_table[*output->expression_] = std::get<2>(scan_all);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
@ -177,15 +175,14 @@ TEST(Interpreter, NodeFilterLabelsAndProperties) {
|
||||
// make a scan all
|
||||
auto n = MakeScanAll(storage, symbol_table, "n");
|
||||
std::get<0>(n)->labels_.emplace_back(label);
|
||||
std::get<0>(n)->properties_[property] = storage.Create<Literal>(42);
|
||||
std::get<0>(n)->properties_[property] = LITERAL(42);
|
||||
|
||||
// node filtering
|
||||
auto node_filter = std::make_shared<NodeFilter>(
|
||||
std::get<1>(n), std::get<2>(n), std::get<0>(n));
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("x", storage.Create<Identifier>("n"));
|
||||
auto output = NEXPR("x", IDENT("n"));
|
||||
symbol_table[*output->expression_] = std::get<2>(n);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto produce = MakeProduce(node_filter, output);
|
||||
@ -232,8 +229,7 @@ TEST(Interpreter, NodeFilterMultipleLabels) {
|
||||
std::get<1>(n), std::get<2>(n), std::get<0>(n));
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("n", storage.Create<Identifier>("n"));
|
||||
auto output = NEXPR("n", IDENT("n"));
|
||||
auto produce = MakeProduce(node_filter, output);
|
||||
|
||||
// fill up the symbol table
|
||||
@ -254,10 +250,10 @@ TEST(Interpreter, CreateNodeWithAttributes) {
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
|
||||
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>("n"));
|
||||
auto node = NODE("n");
|
||||
symbol_table[*node->identifier_] = symbol_table.CreateSymbol("n");
|
||||
node->labels_.emplace_back(label);
|
||||
node->properties_[property] = storage.Create<Literal>(42);
|
||||
node->properties_[property] = LITERAL(42);
|
||||
|
||||
auto create = std::make_shared<CreateNode>(node, nullptr);
|
||||
ExecuteCreate(create, *dba, symbol_table);
|
||||
@ -288,21 +284,19 @@ TEST(Interpreter, CreateReturn) {
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
|
||||
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>("n"));
|
||||
auto node = NODE("n");
|
||||
auto sym_n = symbol_table.CreateSymbol("n");
|
||||
symbol_table[*node->identifier_] = sym_n;
|
||||
node->labels_.emplace_back(label);
|
||||
node->properties_[property] = storage.Create<Literal>(42);
|
||||
node->properties_[property] = LITERAL(42);
|
||||
|
||||
auto create = std::make_shared<CreateNode>(node, nullptr);
|
||||
auto named_expr_n =
|
||||
storage.Create<NamedExpression>("n", storage.Create<Identifier>("n"));
|
||||
auto named_expr_n = NEXPR("n", IDENT("n"));
|
||||
symbol_table[*named_expr_n] = symbol_table.CreateSymbol("named_expr_n");
|
||||
symbol_table[*named_expr_n->expression_] = sym_n;
|
||||
auto prop_lookup =
|
||||
storage.Create<PropertyLookup>(storage.Create<Identifier>("n"), property);
|
||||
auto prop_lookup = PROPERTY_LOOKUP("n", property);
|
||||
symbol_table[*prop_lookup->expression_] = sym_n;
|
||||
auto named_expr_n_p = storage.Create<NamedExpression>("n", prop_lookup);
|
||||
auto named_expr_n_p = NEXPR("n", prop_lookup);
|
||||
symbol_table[*named_expr_n_p] = symbol_table.CreateSymbol("named_expr_n_p");
|
||||
symbol_table[*named_expr_n->expression_] = sym_n;
|
||||
|
||||
@ -340,25 +334,24 @@ TEST(Interpreter, CreateExpand) {
|
||||
int before_e = CountIterable(dba->edges());
|
||||
|
||||
// data for the first node
|
||||
auto n = storage.Create<NodeAtom>(storage.Create<Identifier>("n"));
|
||||
auto n = NODE("n");
|
||||
n->labels_.emplace_back(label_node_1);
|
||||
n->properties_[property] = storage.Create<Literal>(1);
|
||||
n->properties_[property] = LITERAL(1);
|
||||
auto n_sym = symbol_table.CreateSymbol("n");
|
||||
symbol_table[*n->identifier_] = n_sym;
|
||||
|
||||
// data for the second node
|
||||
auto m = storage.Create<NodeAtom>(storage.Create<Identifier>("m"));
|
||||
auto m = NODE("m");
|
||||
m->labels_.emplace_back(label_node_2);
|
||||
m->properties_[property] = storage.Create<Literal>(2);
|
||||
m->properties_[property] = LITERAL(2);
|
||||
if (cycle)
|
||||
symbol_table[*m->identifier_] = n_sym;
|
||||
else
|
||||
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
||||
|
||||
auto r = storage.Create<EdgeAtom>(storage.Create<Identifier>("r"),
|
||||
EdgeAtom::Direction::RIGHT);
|
||||
auto r = EDGE("r", EdgeAtom::Direction::RIGHT);
|
||||
r->edge_types_.emplace_back(edge_type);
|
||||
r->properties_[property] = storage.Create<Literal>(3);
|
||||
r->properties_[property] = LITERAL(3);
|
||||
|
||||
auto create_op = std::make_shared<CreateNode>(n, nullptr);
|
||||
auto create_expand =
|
||||
@ -413,7 +406,7 @@ TEST(Interpreter, MatchCreateNode) {
|
||||
auto n_sym = symbol_table.CreateSymbol("n");
|
||||
symbol_table[*std::get<0>(n_scan_all)->identifier_] = n_sym;
|
||||
// second node
|
||||
auto m = storage.Create<NodeAtom>(storage.Create<Identifier>("m"));
|
||||
auto m = NODE("m");
|
||||
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
||||
// creation op
|
||||
auto create_node = std::make_shared<CreateNode>(m, std::get<1>(n_scan_all));
|
||||
@ -453,14 +446,13 @@ TEST(Interpreter, MatchCreateExpand) {
|
||||
symbol_table[*std::get<0>(n_scan_all)->identifier_] = n_sym;
|
||||
|
||||
// data for the second node
|
||||
auto m = storage.Create<NodeAtom>(storage.Create<Identifier>("m"));
|
||||
auto m = NODE("m");
|
||||
if (cycle)
|
||||
symbol_table[*m->identifier_] = n_sym;
|
||||
else
|
||||
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
||||
|
||||
auto r = storage.Create<EdgeAtom>(storage.Create<Identifier>("r"),
|
||||
EdgeAtom::Direction::RIGHT);
|
||||
auto r = EDGE("r", EdgeAtom::Direction::RIGHT);
|
||||
r->edge_types_.emplace_back(edge_type);
|
||||
|
||||
auto create_expand = std::make_shared<CreateExpand>(
|
||||
@ -503,8 +495,7 @@ TEST(Interpreter, Expand) {
|
||||
"r", direction, false, "m", false);
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("m", storage.Create<Identifier>("m"));
|
||||
auto output = NEXPR("m", IDENT("m"));
|
||||
symbol_table[*output->expression_] = std::get<3>(r_m);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto produce = MakeProduce(std::get<4>(r_m), output);
|
||||
@ -544,8 +535,7 @@ TEST(Interpreter, ExpandNodeCycle) {
|
||||
symbol_table[*std::get<0>(n)->identifier_];
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("n", storage.Create<Identifier>("n"));
|
||||
auto output = NEXPR("n", IDENT("n"));
|
||||
symbol_table[*output->expression_] = std::get<2>(n);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto produce = MakeProduce(std::get<4>(r_n), output);
|
||||
@ -589,8 +579,7 @@ TEST(Interpreter, ExpandEdgeCycle) {
|
||||
symbol_table[*std::get<0>(r_j)->identifier_];
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("r", storage.Create<Identifier>("r"));
|
||||
auto output = NEXPR("r", IDENT("r"));
|
||||
symbol_table[*output->expression_] = std::get<1>(r_j);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto produce = MakeProduce(std::get<4>(r_k), output);
|
||||
@ -645,13 +634,12 @@ TEST(Interpreter, EdgeFilter) {
|
||||
auto r_m = MakeExpand(storage, symbol_table, std::get<1>(n), std::get<2>(n),
|
||||
"r", EdgeAtom::Direction::RIGHT, false, "m", false);
|
||||
std::get<0>(r_m)->edge_types_.push_back(edge_types[0]);
|
||||
std::get<0>(r_m)->properties_[prop] = storage.Create<Literal>(42);
|
||||
std::get<0>(r_m)->properties_[prop] = LITERAL(42);
|
||||
auto edge_filter = std::make_shared<EdgeFilter>(
|
||||
std::get<4>(r_m), std::get<1>(r_m), std::get<0>(r_m));
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("m", storage.Create<Identifier>("m"));
|
||||
auto output = NEXPR("m", IDENT("m"));
|
||||
symbol_table[*output->expression_] = std::get<3>(r_m);
|
||||
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto produce = MakeProduce(edge_filter, output);
|
||||
@ -688,8 +676,7 @@ TEST(Interpreter, EdgeFilterMultipleTypes) {
|
||||
std::get<0>(r_m)->edge_types_.push_back(type_2);
|
||||
|
||||
// make a named expression and a produce
|
||||
auto output =
|
||||
storage.Create<NamedExpression>("m", storage.Create<Identifier>("m"));
|
||||
auto output = NEXPR("m", IDENT("m"));
|
||||
auto produce = MakeProduce(edge_filter, output);
|
||||
|
||||
// fill up the symbol table
|
||||
|
122
tests/unit/query_common.hpp
Normal file
122
tests/unit/query_common.hpp
Normal file
@ -0,0 +1,122 @@
|
||||
namespace query {
|
||||
|
||||
namespace test_common {
|
||||
|
||||
///
|
||||
/// Create PropertyLookup with given name and property.
|
||||
///
|
||||
/// Name is used to create the Identifier which is used for property lookup.
|
||||
///
|
||||
auto GetPropertyLookup(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::Property property) {
|
||||
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
||||
property);
|
||||
}
|
||||
|
||||
///
|
||||
/// Create an EdgeAtom with given name, edge_type and direction.
|
||||
///
|
||||
/// Name is used to create the Identifier which is assigned to the edge.
|
||||
///
|
||||
auto GetEdge(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::EdgeType edge_type = nullptr,
|
||||
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH) {
|
||||
auto edge = storage.Create<EdgeAtom>(storage.Create<Identifier>(name), dir);
|
||||
if (edge_type) edge->edge_types_.emplace_back(edge_type);
|
||||
return edge;
|
||||
}
|
||||
|
||||
///
|
||||
/// Create an EdgeAtom with given name and direction.
|
||||
///
|
||||
auto GetEdge(AstTreeStorage &storage, const std::string &name,
|
||||
EdgeAtom::Direction dir) {
|
||||
return GetEdge(storage, name, nullptr, dir);
|
||||
}
|
||||
|
||||
///
|
||||
/// Create a NodeAtom with given name and label.
|
||||
///
|
||||
/// Name is used to create the Identifier which is assigned to the node.
|
||||
///
|
||||
auto GetNode(AstTreeStorage &storage, const std::string &name,
|
||||
GraphDb::Label label = nullptr) {
|
||||
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>(name));
|
||||
if (label) node->labels_.emplace_back(label);
|
||||
return node;
|
||||
}
|
||||
|
||||
///
|
||||
/// Create a Pattern with given atoms.
|
||||
///
|
||||
auto GetPattern(AstTreeStorage &storage, std::vector<PatternAtom *> atoms) {
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
pattern->atoms_.insert(pattern->atoms_.begin(), atoms.begin(), atoms.end());
|
||||
return pattern;
|
||||
}
|
||||
|
||||
///
|
||||
/// This function creates an AST node which can store patterns and fills them
|
||||
/// with given patterns.
|
||||
///
|
||||
/// The function is most commonly used to create Match and Create clauses.
|
||||
///
|
||||
template <class TWithPatterns>
|
||||
auto GetWithPatterns(AstTreeStorage &storage, std::vector<Pattern *> patterns) {
|
||||
auto with_patterns = storage.Create<TWithPatterns>();
|
||||
with_patterns->patterns_.insert(with_patterns->patterns_.begin(),
|
||||
patterns.begin(), patterns.end());
|
||||
return with_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;
|
||||
}
|
||||
|
||||
///
|
||||
/// 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());
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace test_common
|
||||
|
||||
} // namespace query
|
||||
|
||||
///
|
||||
/// All the following macros implicitly pass `storage` variable to functions.
|
||||
/// You
|
||||
/// need to have `AstTreeStorage storage;` somewhere in scope to use them.
|
||||
/// Refer to function documentation to see what the macro does.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// // Create MATCH (n) -[r]- (m) RETURN m AS new_name
|
||||
/// AstTreeStorage storage;
|
||||
/// auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
|
||||
/// RETURN(NEXPR("new_name"), IDENT("m")));
|
||||
///
|
||||
#define NODE(...) query::test_common::GetNode(storage, __VA_ARGS__)
|
||||
#define EDGE(...) query::test_common::GetEdge(storage, __VA_ARGS__)
|
||||
#define PATTERN(...) query::test_common::GetPattern(storage, {__VA_ARGS__})
|
||||
#define MATCH(...) \
|
||||
query::test_common::GetWithPatterns<Match>(storage, {__VA_ARGS__})
|
||||
#define CREATE(...) \
|
||||
query::test_common::GetWithPatterns<Create>(storage, {__VA_ARGS__})
|
||||
#define IDENT(name) storage.Create<Identifier>((name))
|
||||
#define LITERAL(val) storage.Create<Literal>((val))
|
||||
#define PROPERTY_LOOKUP(...) query::test_common::GetPropertyLookup(storage, __VA_ARGS__)
|
||||
#define NEXPR(name, expr) storage.Create<NamedExpression>((name), (expr))
|
||||
#define RETURN(...) query::test_common::GetReturn(storage, {__VA_ARGS__})
|
||||
#define QUERY(...) query::test_common::GetQuery(storage, {__VA_ARGS__})
|
@ -9,7 +9,10 @@
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
|
||||
#include "query_common.hpp"
|
||||
|
||||
using namespace query;
|
||||
using Direction = EdgeAtom::Direction;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -37,37 +40,10 @@ class PlanChecker : public LogicalOperatorVisitor {
|
||||
std::list<size_t> types_;
|
||||
};
|
||||
|
||||
// Returns a `(name1) -[name2]- (name3) ...` pattern.
|
||||
auto GetPattern(AstTreeStorage &storage, std::vector<std::string> names) {
|
||||
bool is_node{true};
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
for (auto &name : names) {
|
||||
PatternAtom *atom;
|
||||
auto identifier = storage.Create<Identifier>(name);
|
||||
if (is_node) {
|
||||
atom = storage.Create<NodeAtom>(identifier);
|
||||
} else {
|
||||
atom = storage.Create<EdgeAtom>(identifier);
|
||||
}
|
||||
pattern->atoms_.emplace_back(atom);
|
||||
is_node = !is_node;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchNodeReturn) {
|
||||
// Test MATCH (n) RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "n";
|
||||
named_expr->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
auto query = QUERY(MATCH(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
@ -82,16 +58,8 @@ TEST(TestLogicalPlanner, MatchNodeReturn) {
|
||||
TEST(TestLogicalPlanner, CreateNodeReturn) {
|
||||
// Test CREATE (n) RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(GetPattern(storage, {"n"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "n";
|
||||
named_expr->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
auto query =
|
||||
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query->Accept(symbol_generator);
|
||||
@ -106,15 +74,9 @@ TEST(TestLogicalPlanner, CreateNodeReturn) {
|
||||
TEST(TestLogicalPlanner, CreateExpand) {
|
||||
// Test CREATE (n) -[r :rel1]-> (m)
|
||||
AstTreeStorage storage;
|
||||
auto create = storage.Create<Create>();
|
||||
auto pattern = GetPattern(storage, {"n", "r", "m"});
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::RIGHT;
|
||||
std::string relationship("relationship");
|
||||
edge_atom->edge_types_.emplace_back(&relationship);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
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);
|
||||
@ -126,21 +88,48 @@ TEST(TestLogicalPlanner, CreateExpand) {
|
||||
plan->Accept(plan_checker);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, CreateNodeExpandNode) {
|
||||
// Test CREATE (n) -[r :rel]-> (m), (l)
|
||||
AstTreeStorage storage;
|
||||
std::string relationship("rel");
|
||||
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);
|
||||
}
|
||||
|
||||
TEST(TestLogicalPlanner, MatchCreateExpand) {
|
||||
// Test MATCH (n) CREATE (n) -[r :rel1]-> (m)
|
||||
AstTreeStorage storage;
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto create = storage.Create<Create>();
|
||||
auto pattern = GetPattern(storage, {"n", "r", "m"});
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::RIGHT;
|
||||
std::string relationship("relationship");
|
||||
edge_atom->edge_types_.emplace_back(&relationship);
|
||||
query->clauses_.emplace_back(create);
|
||||
auto query = QUERY(
|
||||
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);
|
||||
@ -155,21 +144,9 @@ TEST(TestLogicalPlanner, MatchCreateExpand) {
|
||||
TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
||||
// Test MATCH (n :label) RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
auto node_atom = storage.Create<NodeAtom>(storage.Create<Identifier>("n"));
|
||||
std::string label("label");
|
||||
node_atom->labels_.emplace_back(&label);
|
||||
pattern->atoms_.emplace_back(node_atom);
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(pattern);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "n";
|
||||
named_expr->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
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);
|
||||
@ -185,20 +162,10 @@ TEST(TestLogicalPlanner, MatchLabeledNodes) {
|
||||
TEST(TestLogicalPlanner, MatchPathReturn) {
|
||||
// Test MATCH (n) -[r :relationship]- (m) RETURN n AS n
|
||||
AstTreeStorage storage;
|
||||
auto match = storage.Create<Match>();
|
||||
auto pattern = GetPattern(storage, {"n", "r", "m"});
|
||||
match->patterns_.emplace_back(pattern);
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
std::string relationship("relationship");
|
||||
edge_atom->edge_types_.emplace_back(&relationship);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "n";
|
||||
named_expr->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
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);
|
||||
|
@ -7,203 +7,27 @@
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
|
||||
#include "query_common.hpp"
|
||||
|
||||
using namespace query;
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns a `(name1) -[name2]- (name3) ...` pattern.
|
||||
auto GetPattern(AstTreeStorage &storage, std::vector<std::string> names) {
|
||||
bool is_node{true};
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
for (auto &name : names) {
|
||||
PatternAtom *atom;
|
||||
auto identifier = storage.Create<Identifier>(name);
|
||||
if (is_node) {
|
||||
atom = storage.Create<NodeAtom>(identifier);
|
||||
} else {
|
||||
atom = storage.Create<EdgeAtom>(identifier);
|
||||
}
|
||||
pattern->atoms_.emplace_back(atom);
|
||||
is_node = !is_node;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
// Returns a `MATCH (node)` clause.
|
||||
auto GetMatchNode(AstTreeStorage &storage, const std::string &node_name) {
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {node_name}));
|
||||
return match;
|
||||
}
|
||||
|
||||
// Build a simple AST which describes:
|
||||
// MATCH (node_atom_1) RETURN node_atom_1 AS node_atom_1
|
||||
Query *MatchNodeReturn(AstTreeStorage &storage) {
|
||||
auto match = GetMatchNode(storage, "node_atom_1");
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "node_atom_1";
|
||||
named_expr->expression_ = storage.Create<Identifier>("node_atom_1");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST using variable in return bound by naming the previous return expression.
|
||||
// This is treated as an unbound variable.
|
||||
// MATCH (node_atom_1) RETURN node_atom_1 AS n, n AS n
|
||||
Query *MatchUnboundMultiReturn(AstTreeStorage &storage) {
|
||||
auto match = GetMatchNode(storage, "node_atom_1");
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto named_expr_1 = storage.Create<NamedExpression>();
|
||||
named_expr_1->name_ = "n";
|
||||
named_expr_1->expression_ = storage.Create<Identifier>("node_atom_1");
|
||||
auto named_expr_2 = storage.Create<NamedExpression>();
|
||||
named_expr_2->name_ = "n";
|
||||
named_expr_2->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr_1);
|
||||
ret->named_expressions_.emplace_back(named_expr_2);
|
||||
query->clauses_.emplace_back(ret);
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST with unbound variable in return: MATCH (n) RETURN x AS x
|
||||
Query *MatchNodeUnboundReturn(AstTreeStorage &storage) {
|
||||
auto match = GetMatchNode(storage, "n");
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "x";
|
||||
named_expr->expression_ = storage.Create<Identifier>("x");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST with match pattern referencing an edge multiple times:
|
||||
// MATCH (n) -[r]-> (n) -[r]-> (n) RETURN r AS r
|
||||
// This usually throws a redeclaration error, but we support it.
|
||||
Query *MatchSameEdge(AstTreeStorage &storage) {
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n", "r", "n", "r", "n"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "r";
|
||||
named_expr->expression_ = storage.Create<Identifier>("r");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
return query;
|
||||
}
|
||||
|
||||
std::string prop_name = "prop";
|
||||
|
||||
// AST with unbound variable in create: CREATE ({prop: x})
|
||||
Query *CreatePropertyUnbound(AstTreeStorage &storage) {
|
||||
auto prop_expr = storage.Create<Identifier>("x");
|
||||
auto node_atom = storage.Create<NodeAtom>();
|
||||
node_atom->identifier_ = storage.Create<Identifier>("anon");
|
||||
node_atom->properties_[&prop_name] = prop_expr;
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
pattern->atoms_.emplace_back(node_atom);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
return query;
|
||||
}
|
||||
|
||||
// Simple AST returning a created node: CREATE (n) RETURN n
|
||||
Query *CreateNodeReturn(AstTreeStorage &storage) {
|
||||
auto node_atom = storage.Create<NodeAtom>();
|
||||
node_atom->identifier_ = storage.Create<Identifier>("n");
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
pattern->atoms_.emplace_back(node_atom);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
|
||||
auto named_expr = storage.Create<NamedExpression>();
|
||||
named_expr->name_ = "n";
|
||||
named_expr->expression_ = storage.Create<Identifier>("n");
|
||||
auto ret = storage.Create<Return>();
|
||||
ret->named_expressions_.emplace_back(named_expr);
|
||||
query->clauses_.emplace_back(ret);
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST with redeclaring a variable when creating nodes: CREATE (n), (n)
|
||||
Query *CreateRedeclareNode(AstTreeStorage &storage) {
|
||||
auto create = storage.Create<Create>();
|
||||
for (int patterns = 0; patterns < 2; ++patterns) {
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
auto node_atom = storage.Create<NodeAtom>();
|
||||
node_atom->identifier_ = storage.Create<Identifier>("n");
|
||||
pattern->atoms_.emplace_back(node_atom);
|
||||
create->patterns_.emplace_back(pattern);
|
||||
}
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST with redeclaring a variable when creating nodes with multiple creates:
|
||||
// CREATE (n) CREATE (n)
|
||||
Query *MultiCreateRedeclareNode(AstTreeStorage &storage) {
|
||||
auto query = storage.query();
|
||||
|
||||
for (int creates = 0; creates < 2; ++creates) {
|
||||
auto pattern = storage.Create<Pattern>();
|
||||
auto node_atom = storage.Create<NodeAtom>();
|
||||
node_atom->identifier_ = storage.Create<Identifier>("n");
|
||||
pattern->atoms_.emplace_back(node_atom);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern);
|
||||
query->clauses_.emplace_back(create);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
// AST with redeclaring a match node variable in create: MATCH (n) CREATE (n)
|
||||
Query *MatchCreateRedeclareNode(AstTreeStorage &storage) {
|
||||
auto match = GetMatchNode(storage, "n");
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto node_atom_2 = storage.Create<NodeAtom>();
|
||||
node_atom_2->identifier_ = storage.Create<Identifier>("n");
|
||||
auto pattern_2 = storage.Create<Pattern>();
|
||||
pattern_2->atoms_.emplace_back(node_atom_2);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern_2);
|
||||
query->clauses_.emplace_back(create);
|
||||
return query;
|
||||
}
|
||||
|
||||
TEST(TestSymbolGenerator, MatchNodeReturn) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MatchNodeReturn(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"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query_ast->Accept(symbol_generator);
|
||||
EXPECT_EQ(symbol_table.max_position(), 2);
|
||||
auto match = dynamic_cast<Match*>(query_ast->clauses_[0]);
|
||||
auto match = dynamic_cast<Match *>(query_ast->clauses_[0]);
|
||||
auto pattern = match->patterns_[0];
|
||||
auto node_atom = dynamic_cast<NodeAtom*>(pattern->atoms_[0]);
|
||||
auto node_atom = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
|
||||
auto node_sym = symbol_table[*node_atom->identifier_];
|
||||
EXPECT_EQ(node_sym.name_, "node_atom_1");
|
||||
auto ret = dynamic_cast<Return*>(query_ast->clauses_[1]);
|
||||
auto ret = dynamic_cast<Return *>(query_ast->clauses_[1]);
|
||||
auto named_expr = ret->named_expressions_[0];
|
||||
auto column_sym = symbol_table[*named_expr];
|
||||
EXPECT_EQ(node_sym.name_, column_sym.name_);
|
||||
@ -215,7 +39,12 @@ TEST(TestSymbolGenerator, MatchNodeReturn) {
|
||||
TEST(TestSymbolGenerator, MatchUnboundMultiReturn) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MatchUnboundMultiReturn(storage);
|
||||
// AST using variable in return bound by naming the previous return
|
||||
// expression. This is treated as an unbound variable.
|
||||
// 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"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
|
||||
}
|
||||
@ -223,7 +52,9 @@ TEST(TestSymbolGenerator, MatchUnboundMultiReturn) {
|
||||
TEST(TestSymbolGenerator, MatchNodeUnboundReturn) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MatchNodeUnboundReturn(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"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
|
||||
}
|
||||
@ -231,11 +62,16 @@ TEST(TestSymbolGenerator, MatchNodeUnboundReturn) {
|
||||
TEST(TestSymbolGenerator, MatchSameEdge) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MatchSameEdge(storage);
|
||||
// AST with match pattern referencing an edge multiple times:
|
||||
// MATCH (n) -[r]- (n) -[r]- (n) RETURN r AS r
|
||||
// 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"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query_ast->Accept(symbol_generator);
|
||||
EXPECT_EQ(symbol_table.max_position(), 3);
|
||||
auto match = dynamic_cast<Match*>(query_ast->clauses_[0]);
|
||||
auto match = dynamic_cast<Match *>(query_ast->clauses_[0]);
|
||||
auto pattern = match->patterns_[0];
|
||||
std::vector<Symbol> node_symbols;
|
||||
std::vector<Symbol> edge_symbols;
|
||||
@ -257,7 +93,7 @@ TEST(TestSymbolGenerator, MatchSameEdge) {
|
||||
for (auto &symbol : edge_symbols) {
|
||||
EXPECT_EQ(edge_symbol, symbol);
|
||||
}
|
||||
auto ret = dynamic_cast<Return*>(query_ast->clauses_[1]);
|
||||
auto ret = dynamic_cast<Return *>(query_ast->clauses_[1]);
|
||||
auto named_expr = ret->named_expressions_[0];
|
||||
auto ret_symbol = symbol_table[*named_expr->expression_];
|
||||
EXPECT_EQ(edge_symbol, ret_symbol);
|
||||
@ -266,7 +102,11 @@ TEST(TestSymbolGenerator, MatchSameEdge) {
|
||||
TEST(TestSymbolGenerator, CreatePropertyUnbound) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = CreatePropertyUnbound(storage);
|
||||
// AST with unbound variable in create: CREATE ({prop: x})
|
||||
auto node = NODE("anon");
|
||||
std::string prop_name = "prop";
|
||||
node->properties_[&prop_name] = IDENT("x");
|
||||
auto query_ast = QUERY(CREATE(PATTERN(node)));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), UnboundVariableError);
|
||||
}
|
||||
@ -274,16 +114,18 @@ TEST(TestSymbolGenerator, CreatePropertyUnbound) {
|
||||
TEST(TestSymbolGenerator, CreateNodeReturn) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = CreateNodeReturn(storage);
|
||||
// Simple AST returning a created node: CREATE (n) RETURN n
|
||||
auto query_ast =
|
||||
QUERY(CREATE(PATTERN(NODE("n"))), RETURN(NEXPR("n", IDENT("n"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
query_ast->Accept(symbol_generator);
|
||||
EXPECT_EQ(symbol_table.max_position(), 2);
|
||||
auto create = dynamic_cast<Create*>(query_ast->clauses_[0]);
|
||||
auto create = dynamic_cast<Create *>(query_ast->clauses_[0]);
|
||||
auto pattern = create->patterns_[0];
|
||||
auto node_atom = dynamic_cast<NodeAtom*>(pattern->atoms_[0]);
|
||||
auto node_atom = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
|
||||
auto node_sym = symbol_table[*node_atom->identifier_];
|
||||
EXPECT_EQ(node_sym.name_, "n");
|
||||
auto ret = dynamic_cast<Return*>(query_ast->clauses_[1]);
|
||||
auto ret = dynamic_cast<Return *>(query_ast->clauses_[1]);
|
||||
auto named_expr = ret->named_expressions_[0];
|
||||
auto column_sym = symbol_table[*named_expr];
|
||||
EXPECT_EQ(node_sym.name_, column_sym.name_);
|
||||
@ -295,7 +137,8 @@ TEST(TestSymbolGenerator, CreateNodeReturn) {
|
||||
TEST(TestSymbolGenerator, CreateRedeclareNode) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = CreateRedeclareNode(storage);
|
||||
// AST with redeclaring a variable when creating nodes: CREATE (n), (n)
|
||||
auto query_ast = QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("n"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), RedeclareVariableError);
|
||||
}
|
||||
@ -303,7 +146,10 @@ TEST(TestSymbolGenerator, CreateRedeclareNode) {
|
||||
TEST(TestSymbolGenerator, MultiCreateRedeclareNode) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MultiCreateRedeclareNode(storage);
|
||||
// AST with redeclaring a variable when creating nodes with multiple creates:
|
||||
// CREATE (n) CREATE (n)
|
||||
auto query_ast =
|
||||
QUERY(CREATE(PATTERN(NODE("n"))), CREATE(PATTERN(NODE("n"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), RedeclareVariableError);
|
||||
}
|
||||
@ -311,7 +157,8 @@ TEST(TestSymbolGenerator, MultiCreateRedeclareNode) {
|
||||
TEST(TestSymbolGenerator, MatchCreateRedeclareNode) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
auto query_ast = MatchCreateRedeclareNode(storage);
|
||||
// AST with redeclaring a match node variable in create: MATCH (n) CREATE (n)
|
||||
auto query_ast = QUERY(MATCH(PATTERN(NODE("n"))), CREATE(PATTERN(NODE("n"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query_ast->Accept(symbol_generator), RedeclareVariableError);
|
||||
}
|
||||
@ -320,20 +167,12 @@ TEST(TestSymbolGenerator, MatchCreateRedeclareEdge) {
|
||||
SymbolTable symbol_table;
|
||||
AstTreeStorage storage;
|
||||
// AST with redeclaring a match edge variable in create:
|
||||
// MATCH (n) -[r]- (m) CREATE (n) -[r] -> (l)
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n", "r", "m"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
|
||||
auto create = storage.Create<Create>();
|
||||
auto pattern = GetPattern(storage, {"n", "r", "l"});
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::RIGHT;
|
||||
// MATCH (n) -[r]- (m) CREATE (n) -[r :relationship]-> (l)
|
||||
std::string relationship("relationship");
|
||||
edge_atom->edge_types_.emplace_back(&relationship);
|
||||
create->patterns_.emplace_back(pattern);
|
||||
query->clauses_.emplace_back(create);
|
||||
auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
|
||||
CREATE(PATTERN(NODE("n"), EDGE("r", &relationship,
|
||||
EdgeAtom::Direction::RIGHT),
|
||||
NODE("l"))));
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), RedeclareVariableError);
|
||||
}
|
||||
@ -342,10 +181,7 @@ TEST(TestSymbolGenerator, MatchTypeMismatch) {
|
||||
AstTreeStorage storage;
|
||||
// Using an edge variable as a node causes a type mismatch.
|
||||
// MATCH (n) -[r]-> (r)
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n", "r", "r"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("r"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), TypeMismatchError);
|
||||
@ -355,16 +191,10 @@ TEST(TestSymbolGenerator, MatchCreateTypeMismatch) {
|
||||
AstTreeStorage storage;
|
||||
// Using an edge variable as a node causes a type mismatch.
|
||||
// MATCH (n1) -[r1]- (n2) CREATE (r1) -[r2]-> (n2)
|
||||
auto match = storage.Create<Match>();
|
||||
match->patterns_.emplace_back(GetPattern(storage, {"n1", "r1", "n2"}));
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(match);
|
||||
auto create = storage.Create<Create>();
|
||||
auto pattern = GetPattern(storage, {"r1", "r2", "n2"});
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::RIGHT;
|
||||
query->clauses_.emplace_back(create);
|
||||
auto query =
|
||||
QUERY(MATCH(PATTERN(NODE("n1"), EDGE("r1"), NODE("n2"))),
|
||||
CREATE(PATTERN(NODE("r1"), EDGE("r2", EdgeAtom::Direction::RIGHT),
|
||||
NODE("n2"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), TypeMismatchError);
|
||||
@ -374,17 +204,11 @@ TEST(TestSymbolGenerator, CreateMultipleEdgeType) {
|
||||
AstTreeStorage storage;
|
||||
// Multiple edge relationship are not allowed when creating edges.
|
||||
// CREATE (n) -[r :rel1 | :rel2]-> (m)
|
||||
auto pattern = GetPattern(storage, {"n", "r", "m"});
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::RIGHT;
|
||||
std::string rel1("rel1");
|
||||
edge_atom->edge_types_.emplace_back(&rel1);
|
||||
std::string rel2("rel2");
|
||||
edge_atom->edge_types_.emplace_back(&rel2);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
auto edge = EDGE("r", &rel1, EdgeAtom::Direction::RIGHT);
|
||||
edge->edge_types_.emplace_back(&rel2);
|
||||
auto query = QUERY(CREATE(PATTERN(NODE("n"), edge, NODE("m"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), SemanticException);
|
||||
@ -394,17 +218,8 @@ TEST(TestSymbolGenerator, CreateBidirectionalEdge) {
|
||||
AstTreeStorage storage;
|
||||
// Bidirectional relationships are not allowed when creating edges.
|
||||
// CREATE (n) -[r :rel1]- (m)
|
||||
auto pattern = GetPattern(storage, {"n", "r", "m"});
|
||||
auto edge_atom = dynamic_cast<EdgeAtom*>(pattern->atoms_[1]);
|
||||
edge_atom->direction_ = EdgeAtom::Direction::BOTH;
|
||||
std::string rel1("rel1");
|
||||
edge_atom->edge_types_.emplace_back(&rel1);
|
||||
std::string rel2("rel2");
|
||||
edge_atom->edge_types_.emplace_back(&rel2);
|
||||
auto create = storage.Create<Create>();
|
||||
create->patterns_.emplace_back(pattern);
|
||||
auto query = storage.query();
|
||||
query->clauses_.emplace_back(create);
|
||||
auto query = QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", &rel1), NODE("m"))));
|
||||
SymbolTable symbol_table;
|
||||
SymbolGenerator symbol_generator(symbol_table);
|
||||
EXPECT_THROW(query->Accept(symbol_generator), SemanticException);
|
||||
|
Loading…
Reference in New Issue
Block a user