diff --git a/src/query/frontend/logical/planner.cpp b/src/query/frontend/logical/planner.cpp index bc37c2716..a166fbe50 100644 --- a/src/query/frontend/logical/planner.cpp +++ b/src/query/frontend/logical/planner.cpp @@ -186,6 +186,9 @@ std::unique_ptr MakeLogicalPlan( input_op = GenReturn(*ret, input_op); } else if (auto *create = dynamic_cast(clause_ptr)) { input_op = GenCreate(*create, input_op, symbol_table, bound_symbols); + } else if (auto *del = dynamic_cast(clause_ptr)) { + input_op = new plan::Delete(std::shared_ptr(input_op), + del->expressions_, del->detach_); } else { throw NotYetImplemented(); } diff --git a/tests/unit/query_common.hpp b/tests/unit/query_common.hpp index 54421ddb2..77668d05f 100644 --- a/tests/unit/query_common.hpp +++ b/tests/unit/query_common.hpp @@ -90,6 +90,18 @@ auto GetReturn(AstTreeStorage &storage, return ret; } +/// +/// Create the delete clause with given named expressions. +/// +auto GetDelete(AstTreeStorage &storage, std::vector exprs, + bool detach = false) { + auto del = storage.Create(); + del->expressions_.insert(del->expressions_.begin(), exprs.begin(), + exprs.end()); + del->detach_ = detach; + return del; +} + } // namespace test_common } // namespace query @@ -120,5 +132,8 @@ auto GetReturn(AstTreeStorage &storage, query::test_common::GetPropertyLookup(storage, __VA_ARGS__) #define NEXPR(name, expr) storage.Create((name), (expr)) #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 QUERY(...) query::test_common::GetQuery(storage, {__VA_ARGS__}) #define LESS(expr1, expr2) storage.Create((expr1), (expr2)) diff --git a/tests/unit/query_planner.cpp b/tests/unit/query_planner.cpp index 01e55b375..bceccd862 100644 --- a/tests/unit/query_planner.cpp +++ b/tests/unit/query_planner.cpp @@ -28,6 +28,7 @@ class PlanChecker : public LogicalOperatorVisitor { void Visit(CreateNode &op) override { AssertType(op); } void Visit(CreateExpand &op) override { AssertType(op); } + void Visit(Delete &op) override { AssertType(op); } void Visit(ScanAll &op) override { AssertType(op); } void Visit(Expand &op) override { AssertType(op); } void Visit(NodeFilter &op) override { AssertType(op); } @@ -145,4 +146,11 @@ TEST(TestLogicalPlanner, MatchWhereReturn) { typeid(Produce).hash_code()}); } +TEST(TestLogicalPlanner, MatchDelete) { + // Test MATCH (n) DELETE n + AstTreeStorage storage; + auto query = QUERY(MATCH(PATTERN(NODE("n"))), DELETE(IDENT("n"))); + CheckPlan(*query, {typeid(ScanAll).hash_code(), typeid(Delete).hash_code()}); +} + } diff --git a/tests/unit/query_semantic.cpp b/tests/unit/query_semantic.cpp index fdd79e665..91cde8765 100644 --- a/tests/unit/query_semantic.cpp +++ b/tests/unit/query_semantic.cpp @@ -237,4 +237,28 @@ TEST(TestSymbolGenerator, MatchWhereUnbound) { EXPECT_THROW(query->Accept(symbol_generator), UnboundVariableError); } +TEST(TestSymbolGenerator, CreateDelete) { + // Test CREATE (n) DELETE n + AstTreeStorage storage; + auto node = NODE("n"); + auto ident = IDENT("n"); + auto query = QUERY(CREATE(PATTERN(node)), DELETE(ident)); + SymbolTable symbol_table; + SymbolGenerator symbol_generator(symbol_table); + query->Accept(symbol_generator); + EXPECT_EQ(symbol_table.max_position(), 1); + auto node_symbol = symbol_table.at(*node->identifier_); + auto ident_symbol = symbol_table.at(*ident); + EXPECT_EQ(node_symbol, ident_symbol); +} + +TEST(TestSymbolGenerator, CreateDeleteUnbound) { + // Test CREATE (n) DELETE missing + AstTreeStorage storage; + auto query = QUERY(CREATE(PATTERN(NODE("n"))), DELETE(IDENT("missing"))); + SymbolTable symbol_table; + SymbolGenerator symbol_generator(symbol_table); + EXPECT_THROW(query->Accept(symbol_generator), UnboundVariableError); +} + }