From aa49a0997e3f2e0e0eacb19e277904fa8a51256c Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Mon, 25 Jan 2016 21:22:12 +0100 Subject: [PATCH] MATCH ... SET ... RETURN clause --- .gitignore | 1 + cypher/ast/ast.hpp | 1 + cypher/ast/ast_visitor.hpp | 27 +++--- cypher/ast/queries.hpp | 26 ++++-- cypher/ast/set.hpp | 58 ++++++++++++ cypher/ast/start.hpp | 25 ++--- cypher/cypher.y | 91 +++++++++++++++---- cypher/cypher_lexer.hpp | 5 +- cypher/debug/tree_print.hpp | 30 ++++++ .../query/read-write/match-set-return.cypher | 1 + cypher/query/read-write/match-set.cypher | 1 + cypher/query/write/create.cypher | 2 +- cypher/visitor/traverser.hpp | 41 ++++++++- 13 files changed, 251 insertions(+), 58 deletions(-) create mode 100644 cypher/ast/set.hpp create mode 100644 cypher/query/read-write/match-set-return.cypher create mode 100644 cypher/query/read-write/match-set.cypher diff --git a/.gitignore b/.gitignore index b3a41542e..789ad1fc2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ memgraph *~ *.pyc +*.breakpoint diff --git a/cypher/ast/ast.hpp b/cypher/ast/ast.hpp index 6e51a8388..a9c2368d4 100644 --- a/cypher/ast/ast.hpp +++ b/cypher/ast/ast.hpp @@ -16,3 +16,4 @@ #include "match.hpp" #include "queries.hpp" #include "start.hpp" +#include "set.hpp" diff --git a/cypher/ast/ast_visitor.hpp b/cypher/ast/ast_visitor.hpp index 8dc0aece3..d897f5164 100644 --- a/cypher/ast/ast_visitor.hpp +++ b/cypher/ast/ast_visitor.hpp @@ -40,29 +40,34 @@ struct Relationship; struct Node; struct LabelList; - struct Pattern; struct Return; struct ReturnList; struct Distinct; -struct Delete; - -struct Match; -struct Where; - -struct Start; -struct ReadQuery; -struct WriteQuery; -struct DeleteQuery; struct Create; +struct Match; +struct Where; +struct Set; +struct Delete; + +struct Start; +struct WriteQuery; +struct ReadQuery; +struct UpdateQuery; +struct DeleteQuery; + +struct SetKey; +struct SetValue; +struct SetElement; +struct SetList; struct AstVisitor : public Visitor {}; + DeleteQuery, UpdateQuery, Set, SetKey, SetValue, SetElement, SetList> {}; } diff --git a/cypher/ast/queries.hpp b/cypher/ast/queries.hpp index 3de9863d6..4c004fbf7 100644 --- a/cypher/ast/queries.hpp +++ b/cypher/ast/queries.hpp @@ -1,14 +1,24 @@ #pragma once #include "ast_node.hpp" -#include "match.hpp" -#include "return.hpp" #include "create.hpp" +#include "match.hpp" +#include "set.hpp" #include "delete.hpp" +#include "return.hpp" namespace ast { +struct WriteQuery : public AstNode +{ + WriteQuery(Create* create, Return* return_clause) + : create(create), return_clause(return_clause) {} + + Create* create; + Return* return_clause; +}; + struct ReadQuery : public AstNode { ReadQuery(Match* match, Return* return_clause) @@ -18,12 +28,16 @@ struct ReadQuery : public AstNode Return* return_clause; }; -struct WriteQuery : public AstNode +struct UpdateQuery : public AstNode { - WriteQuery(Create* create, Return* return_clause) - : create(create), return_clause(return_clause) {} + UpdateQuery(Match* match_clause, Set* set_clause, Return* return_clause) + : match_clause(match_clause), set_clause(set_clause), + return_clause(return_clause) + { + } - Create* create; + Match* match_clause; + Set* set_clause; Return* return_clause; }; diff --git a/cypher/ast/set.hpp b/cypher/ast/set.hpp new file mode 100644 index 000000000..ab08f4912 --- /dev/null +++ b/cypher/ast/set.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "accessor.hpp" +#include "list.hpp" +#include "expr.hpp" + +namespace ast +{ +// +// SetList +// ^ +// | +// |------------------------------------| +// SetElement +// ^ +// | +// |-------------------| +// Accessor SetValue +// ^ ^ +// | | +// |-------| |-------| +// SET n.name = "Paul", n.surname = "Scholes" +// ^ ^ +// | | +// element entity element property +// + +struct SetValue : public AstNode +{ + SetValue(Expr* value) + : value(value) {} + + Expr* value; +}; + +struct SetElement : public AstNode +{ + SetElement(Accessor* accessor, SetValue* set_value) + : accessor(accessor), set_value(set_value) {} + + Accessor* accessor; + SetValue* set_value; +}; + +struct SetList : public List +{ + using List::List; +}; + +struct Set : public AstNode +{ + Set(SetList* set_list) + : set_list(set_list) {} + + SetList* set_list; +}; + +} diff --git a/cypher/ast/start.hpp b/cypher/ast/start.hpp index 784fd200a..649e2a1e7 100644 --- a/cypher/ast/start.hpp +++ b/cypher/ast/start.hpp @@ -8,26 +8,17 @@ namespace ast struct Start : public AstNode { - Start(ReadQuery* read_query) - : read_query(read_query) - { - } - - Start(WriteQuery* write_query) - : write_query(write_query) - { - } - - Start(DeleteQuery* delete_query) - : delete_query(delete_query) - { - } + Start(WriteQuery* write_query) : write_query(write_query) {} + Start(ReadQuery* read_query) : read_query(read_query) {} + Start(UpdateQuery* update_query) : update_query(update_query) {} + Start(DeleteQuery* delete_query) : delete_query(delete_query) {} // TODO: the start structure must have a different implementation // is this class necessary? - ReadQuery* read_query{nullptr}; - WriteQuery* write_query{nullptr}; - DeleteQuery* delete_query{nullptr}; + WriteQuery* write_query {nullptr}; + ReadQuery* read_query {nullptr}; + UpdateQuery* update_query {nullptr}; + DeleteQuery* delete_query {nullptr}; }; }; diff --git a/cypher/cypher.y b/cypher/cypher.y index cc6f756ca..739944fd3 100644 --- a/cypher/cypher.y +++ b/cypher/cypher.y @@ -55,24 +55,20 @@ // start structure -start ::= read_query(RQ). { - ast->root = ast->create(RQ); -} - start ::= write_query(WQ). { ast->root = ast->create(WQ); } -start ::= delete_query(DQ). { - ast->root = ast->create(DQ); +start ::= read_query(RQ). { + ast->root = ast->create(RQ); } -// read query structure +start ::= update_query(UQ). { + ast->root = ast->create(UQ); +} -%type read_query {ast::ReadQuery*} - -read_query(RQ) ::= match_clause(M) return_clause(R). { - RQ = ast->create(M, R); +start ::= delete_query(DQ). { + ast->root = ast->create(DQ); } // write query structure @@ -87,6 +83,34 @@ write_query(WQ) ::= create_clause(C). { WQ = ast->create(C, nullptr); } +// read query structure + +%type read_query {ast::ReadQuery*} + +read_query(RQ) ::= match_clause(M) return_clause(R). { + RQ = ast->create(M, R); +} + +// update query structure + +%type update_query {ast::UpdateQuery*} + +update_query(UQ) ::= match_clause(M) set_clause(S) return_clause(R). { + UQ = ast->create(M, S, R); +} + +update_query(UQ) ::= match_clause(M) set_clause(S). { + UQ = ast->create(M, S, nullptr); +} + +// set clause + +%type set_clause {ast::Set*} + +set_clause(S) ::= SET set_list(L). { + S = ast->create(L); +} + // delete query structure %type delete_query {ast::DeleteQuery*} @@ -250,6 +274,9 @@ distinct(R) ::= DISTINCT idn(I). { R = ast->create(I); } +// list of properties +// e.g. { name: "wayne", surname: "rooney"} + %type properties {ast::PropertyList*} // '{' '}' @@ -343,12 +370,6 @@ idn(I) ::= IDN(X). { I = ast->create(X->value); } -//%type alias {ast::Alias*} -// -//alias(A) ::= IDN(X) AS IDN(Y). { -// A = ast->create(X->value, Y->value); -//} - expr(E) ::= INT(V). { auto value = std::stoi(V->value); E = ast->create(value); @@ -369,3 +390,39 @@ expr(E) ::= BOOL(V). { E = ast->create(value); } +//%type alias {ast::Alias*} +// +//alias(A) ::= IDN(X) AS IDN(Y). { +// A = ast->create(X->value, Y->value); +//} + +// set list +// e.g. MATCH (n) SET n.name = "Ryan", n.surname = "Giggs" RETURN n + +%type set_list {ast::SetList*} + +set_list(L) ::= set_element(E) COMMA set_list(N). { + L = ast->create(E, N); +} + +set_list(L) ::= set_element(E). { + L = ast->create(E, nullptr); +} + +%type set_element {ast::SetElement*} + +set_element(E) ::= accessor(A) EQ set_value(V). { + E = ast->create(A, V); +} + +%type accessor {ast::Accessor*} + +accessor(A) ::= idn(E) DOT idn(P). { + A = ast->create(E, P); +} + +%type set_value {ast::SetValue*} + +set_value(V) ::= expr(E). { + V = ast->create(E); +} diff --git a/cypher/cypher_lexer.hpp b/cypher/cypher_lexer.hpp index 65b191278..4e92b8ef1 100644 --- a/cypher/cypher_lexer.hpp +++ b/cypher/cypher_lexer.hpp @@ -44,12 +44,13 @@ public: rule("(?i:FALSE)", TK_BOOL); // keywords + rule("(?i:CREATE)", TK_CREATE); rule("(?i:MATCH)", TK_MATCH); rule("(?i:WHERE)", TK_WHERE); + rule("(?i:SET)", TK_SET); rule("(?i:RETURN)", TK_RETURN); - rule("(?i:DELETE)", TK_DELETE); - rule("(?i:CREATE)", TK_CREATE); rule("(?i:DISTINCT)", TK_DISTINCT); + rule("(?i:DELETE)", TK_DELETE); rule("(?i:AND)", TK_AND); rule("(?i:OR)", TK_OR); diff --git a/cypher/debug/tree_print.hpp b/cypher/debug/tree_print.hpp index 769b66c8e..135ff8529 100644 --- a/cypher/debug/tree_print.hpp +++ b/cypher/debug/tree_print.hpp @@ -317,6 +317,36 @@ public: Traverser::visit(create); } + void visit(ast::UpdateQuery& update_query) override + { + auto entry = printer.advance("Update Query"); + Traverser::visit(update_query); + } + + void visit(ast::Set& set_clause) override + { + auto entry = printer.advance("Set"); + Traverser::visit(set_clause); + } + + void visit(ast::SetValue& set_value) override + { + auto entry = printer.advance("Set Value"); + Traverser::visit(set_value); + } + + void visit(ast::SetElement& set_element) override + { + auto entry = printer.advance("Set Element"); + Traverser::visit(set_element); + } + + void visit(ast::SetList& set_list) override + { + auto entry = printer.advance("Set List"); + Traverser::visit(set_list); + } + private: Printer printer; }; diff --git a/cypher/query/read-write/match-set-return.cypher b/cypher/query/read-write/match-set-return.cypher new file mode 100644 index 000000000..f5cb83248 --- /dev/null +++ b/cypher/query/read-write/match-set-return.cypher @@ -0,0 +1 @@ +MATCH (n {surname:"Neville"}) SET n.surname="Neville", n.age=30 RETURN n diff --git a/cypher/query/read-write/match-set.cypher b/cypher/query/read-write/match-set.cypher new file mode 100644 index 000000000..0806e0cfd --- /dev/null +++ b/cypher/query/read-write/match-set.cypher @@ -0,0 +1 @@ +MATCH (n {age: 30, role: "player"}) SET n.age=31 diff --git a/cypher/query/write/create.cypher b/cypher/query/write/create.cypher index de91f83c4..39cf4da9f 100644 --- a/cypher/query/write/create.cypher +++ b/cypher/query/write/create.cypher @@ -1 +1 @@ -CREATE (n {name: {value}}) +CREATE (n {name: "value"}) diff --git a/cypher/visitor/traverser.hpp b/cypher/visitor/traverser.hpp index 4730ae0a1..252e486e6 100644 --- a/cypher/visitor/traverser.hpp +++ b/cypher/visitor/traverser.hpp @@ -1,7 +1,7 @@ #pragma once -#include "cypher/ast/ast_visitor.hpp" #include "cypher/ast/ast.hpp" +#include "cypher/ast/ast_visitor.hpp" class Traverser : public ast::AstVisitor { @@ -12,10 +12,12 @@ public: void visit(ast::Start& start) override { + if (start.write_query != nullptr) + accept(start.write_query); if (start.read_query != nullptr) accept(start.read_query); - if (start.read_query != nullptr) - accept(start.write_query); + if (start.update_query != nullptr) + accept(start.update_query); if (start.delete_query != nullptr) accept(start.delete_query); } @@ -25,7 +27,7 @@ public: accept(delete_query.match); accept(delete_query.delete_clause); } - + void visit(ast::ReadQuery& read_query) override { accept(read_query.match); @@ -196,6 +198,37 @@ public: accept(write_query.return_clause); } + void visit(ast::UpdateQuery& update_query) override + { + accept(update_query.match_clause); + accept(update_query.set_clause); + if (update_query.return_clause != nullptr) + accept(update_query.return_clause); + } + + void visit(ast::Set& set_clause) override + { + accept(set_clause.set_list); + } + + void visit(ast::SetValue& set_value) override + { + accept(set_value.value); + } + + void visit(ast::SetElement& set_element) override + { + accept(set_element.accessor); + accept(set_element.set_value); + } + + void visit(ast::SetList& set_list) override + { + accept(set_list.value); + if (set_list.next != nullptr) + accept(set_list.next); + } + void visit(ast::Create& create) override { accept(create.pattern);