diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index 48464d097..6a51c5498 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -1,5 +1,6 @@ #pragma once +#include <map> #include <memory> #include <vector> @@ -13,6 +14,7 @@ class AstTreeStorage; class Tree : public ::utils::Visitable<TreeVisitorBase> { friend class AstTreeStorage; + public: int uid() const { return uid_; } @@ -30,6 +32,7 @@ class Expression : public Tree { class Identifier : public Expression { friend class AstTreeStorage; + public: DEFVISITABLE(TreeVisitorBase); std::string name_; @@ -40,6 +43,7 @@ class Identifier : public Expression { class PropertyLookup : public Expression { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -57,13 +61,13 @@ class PropertyLookup : public Expression { // between the two depending on Expression result protected: - PropertyLookup(int uid, Expression* expression, - GraphDb::Property property) + PropertyLookup(int uid, Expression *expression, GraphDb::Property property) : Expression(uid), expression_(expression), property_(property) {} }; class NamedExpression : public Tree { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -72,18 +76,20 @@ class NamedExpression : public Tree { } std::string name_; - Expression* expression_ = nullptr; + Expression *expression_ = nullptr; protected: NamedExpression(int uid) : Tree(uid) {} - NamedExpression(int uid, std::string name, Expression *expression) : - Tree(uid), name_(name), expression_(expression) {} + NamedExpression(int uid, std::string name, Expression *expression) + : Tree(uid), name_(name), expression_(expression) {} }; class PatternAtom : public Tree { friend class AstTreeStorage; + public: - Identifier* identifier_ = nullptr; + Identifier *identifier_ = nullptr; + protected: PatternAtom(int uid) : Tree(uid) {} PatternAtom(int uid, Identifier *identifier) @@ -92,6 +98,7 @@ class PatternAtom : public Tree { class NodeAtom : public PatternAtom { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -100,7 +107,8 @@ class NodeAtom : public PatternAtom { } std::vector<GraphDb::Label> labels_; - std::map<GraphDb::Property, Expression*> properties_; + // TODO: change to unordered_map + std::map<GraphDb::Property, Expression *> properties_; protected: using PatternAtom::PatternAtom; @@ -108,6 +116,7 @@ class NodeAtom : public PatternAtom { class EdgeAtom : public PatternAtom { friend class AstTreeStorage; + public: enum class Direction { LEFT, RIGHT, BOTH }; @@ -119,6 +128,8 @@ class EdgeAtom : public PatternAtom { Direction direction_ = Direction::BOTH; std::vector<GraphDb::EdgeType> types_; + // TODO: change to unordered_map + std::map<GraphDb::Property, Expression *> properties_; protected: using PatternAtom::PatternAtom; @@ -126,12 +137,14 @@ class EdgeAtom : public PatternAtom { class Clause : public Tree { friend class AstTreeStorage; + public: Clause(int uid) : Tree(uid) {} }; class Pattern : public Tree { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -140,8 +153,8 @@ class Pattern : public Tree { } visitor.PostVisit(*this); } - Identifier* identifier_ = nullptr; - std::vector<PatternAtom*> atoms_; + Identifier *identifier_ = nullptr; + std::vector<PatternAtom *> atoms_; protected: Pattern(int uid) : Tree(uid) {} @@ -149,6 +162,7 @@ class Pattern : public Tree { class Query : public Tree { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -157,16 +171,18 @@ class Query : public Tree { } visitor.PostVisit(*this); } - std::vector<Clause*> clauses_; + std::vector<Clause *> clauses_; protected: Query(int uid) : Tree(uid) {} }; class Create : public Clause { + friend class AstTreeStorage; + public: Create(int uid) : Clause(uid) {} - std::vector<Pattern*> patterns_; + std::vector<Pattern *> patterns_; void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); for (auto &pattern : patterns_) { @@ -178,8 +194,9 @@ class Create : public Clause { class Match : public Clause { friend class AstTreeStorage; + public: - std::vector<Pattern*> patterns_; + std::vector<Pattern *> patterns_; void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); for (auto &pattern : patterns_) { @@ -194,6 +211,7 @@ class Match : public Clause { class Return : public Clause { friend class AstTreeStorage; + public: void Accept(TreeVisitorBase &visitor) override { visitor.Visit(*this); @@ -202,7 +220,7 @@ class Return : public Clause { } visitor.PostVisit(*this); } - std::vector<NamedExpression*> named_expressions_; + std::vector<NamedExpression *> named_expressions_; protected: Return(int uid) : Clause(uid) {} @@ -215,14 +233,12 @@ class AstTreeStorage { friend class AstTreeStorage; public: - AstTreeStorage() { - storage_.emplace_back(new Query(next_uid_++)); - } + AstTreeStorage() { storage_.emplace_back(new Query(next_uid_++)); } AstTreeStorage(const AstTreeStorage &) = delete; AstTreeStorage &operator=(const AstTreeStorage &) = delete; - template<typename T, typename... Args> - T *Create(Args&&... args) { + template <typename T, typename... Args> + T *Create(Args &&... args) { // Never call create for a Query. Call query() instead. static_assert(!std::is_same<T, Query>::value, "Call query() instead"); // TODO: use std::forward here @@ -231,7 +247,7 @@ class AstTreeStorage { return p; } - Query *query() { return dynamic_cast<Query*>(storage_[0].get()); } + Query *query() { return dynamic_cast<Query *>(storage_[0].get()); } private: int next_uid_ = 0; diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index 1df03b15f..5fa80a7f3 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -41,8 +41,8 @@ namespace { const std::string CypherMainVisitor::kAnonPrefix = "anon"; -antlrcpp::Any -CypherMainVisitor::visitSingleQuery(CypherParser::SingleQueryContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitSingleQuery( + CypherParser::SingleQueryContext *ctx) { query_ = storage_.query(); for (auto *child : ctx->clause()) { query_->clauses_.push_back(child->accept(this)); @@ -68,12 +68,15 @@ antlrcpp::Any CypherMainVisitor::visitClause(CypherParser::ClauseContext *ctx) { if (ctx->cypherMatch()) { return (Clause *)ctx->cypherMatch()->accept(this).as<Match *>(); } + if (ctx->create()) { + return (Clause *)ctx->create()->accept(this).as<Create *>(); + } throw std::exception(); return visitChildren(ctx); } -antlrcpp::Any -CypherMainVisitor::visitCypherMatch(CypherParser::CypherMatchContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitCypherMatch( + CypherParser::CypherMatchContext *ctx) { auto *match = storage_.Create<Match>(); if (ctx->OPTIONAL() || ctx->where()) { throw std::exception(); @@ -82,24 +85,31 @@ CypherMainVisitor::visitCypherMatch(CypherParser::CypherMatchContext *ctx) { return match; } -antlrcpp::Any -CypherMainVisitor::visitCypherReturn(CypherParser::CypherReturnContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitCreate(CypherParser::CreateContext *ctx) { + auto *create = storage_.Create<Create>(); + create->patterns_ = ctx->pattern()->accept(this).as<std::vector<Pattern *>>(); + return create; + ; +} + +antlrcpp::Any CypherMainVisitor::visitCypherReturn( + CypherParser::CypherReturnContext *ctx) { if (ctx->DISTINCT()) { throw std::exception(); } return visitChildren(ctx); } -antlrcpp::Any -CypherMainVisitor::visitReturnBody(CypherParser::ReturnBodyContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitReturnBody( + CypherParser::ReturnBodyContext *ctx) { if (ctx->order() || ctx->skip() || ctx->limit()) { throw std::exception(); } return ctx->returnItems()->accept(this); } -antlrcpp::Any -CypherMainVisitor::visitReturnItems(CypherParser::ReturnItemsContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitReturnItems( + CypherParser::ReturnItemsContext *ctx) { auto *return_clause = storage_.Create<Return>(); if (ctx->getTokens(kReturnAllTokenId).size()) { throw std::exception(); @@ -110,8 +120,8 @@ CypherMainVisitor::visitReturnItems(CypherParser::ReturnItemsContext *ctx) { return return_clause; } -antlrcpp::Any -CypherMainVisitor::visitReturnItem(CypherParser::ReturnItemContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitReturnItem( + CypherParser::ReturnItemContext *ctx) { auto *named_expr = storage_.Create<NamedExpression>(); if (ctx->variable()) { named_expr->name_ = @@ -124,8 +134,8 @@ CypherMainVisitor::visitReturnItem(CypherParser::ReturnItemContext *ctx) { return named_expr; } -antlrcpp::Any -CypherMainVisitor::visitNodePattern(CypherParser::NodePatternContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitNodePattern( + CypherParser::NodePatternContext *ctx) { auto *node = storage_.Create<NodeAtom>(); if (ctx->variable()) { std::string variable = ctx->variable()->accept(this); @@ -139,17 +149,15 @@ CypherMainVisitor::visitNodePattern(CypherParser::NodePatternContext *ctx) { ctx->nodeLabels()->accept(this).as<std::vector<GraphDb::Label>>(); } if (ctx->properties()) { - throw std::exception(); - // node.properties = ctx->properties() - // ->accept(this) - // .as<std::unordered_map<std::string, - // std::string>>(); + node->properties_ = ctx->properties() + ->accept(this) + .as<std::map<GraphDb::Property, Expression *>>(); } return node; } -antlrcpp::Any -CypherMainVisitor::visitNodeLabels(CypherParser::NodeLabelsContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitNodeLabels( + CypherParser::NodeLabelsContext *ctx) { std::vector<GraphDb::Label> labels; for (auto *node_label : ctx->nodeLabel()) { labels.push_back(ctx_.db_accessor_.label(node_label->accept(this))); @@ -157,31 +165,35 @@ CypherMainVisitor::visitNodeLabels(CypherParser::NodeLabelsContext *ctx) { return labels; } -antlrcpp::Any -CypherMainVisitor::visitProperties(CypherParser::PropertiesContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitProperties( + CypherParser::PropertiesContext *ctx) { if (!ctx->mapLiteral()) { // If child is not mapLiteral that means child is params. At the moment - // memgraph doesn't support params. + // we don't support properties to be a param because we can generate + // better logical plan if we have an information about properties at + // compile time. throw std::exception(); } return ctx->mapLiteral()->accept(this); } -antlrcpp::Any -CypherMainVisitor::visitMapLiteral(CypherParser::MapLiteralContext *ctx) { - throw std::exception(); - (void)ctx; - return 0; - // std::unordered_map<std::string, std::string> map; - // for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) { - // map[ctx->propertyKeyName()[i]->accept(this).as<std::string>()] = - // ctx->expression()[i]->accept(this).as<std::string>(); - // } - // return map; +antlrcpp::Any CypherMainVisitor::visitMapLiteral( + CypherParser::MapLiteralContext *ctx) { + std::map<GraphDb::Property, Expression *> map; + for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) { + map[ctx->propertyKeyName()[i]->accept(this)] = + ctx->expression()[i]->accept(this); + } + return map; } -antlrcpp::Any -CypherMainVisitor::visitSymbolicName(CypherParser::SymbolicNameContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitPropertyKeyName( + CypherParser::PropertyKeyNameContext *ctx) { + return ctx_.db_accessor_.property(visitChildren(ctx)); +} + +antlrcpp::Any CypherMainVisitor::visitSymbolicName( + CypherParser::SymbolicNameContext *ctx) { if (ctx->EscapedSymbolicName()) { // We don't allow at this point for variable to be EscapedSymbolicName // because we would have t ofigure out how escaping works since same @@ -192,8 +204,8 @@ CypherMainVisitor::visitSymbolicName(CypherParser::SymbolicNameContext *ctx) { return std::string(ctx->getText()); } -antlrcpp::Any -CypherMainVisitor::visitPattern(CypherParser::PatternContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitPattern( + CypherParser::PatternContext *ctx) { std::vector<Pattern *> patterns; for (auto *pattern_part : ctx->patternPart()) { patterns.push_back(pattern_part->accept(this)); @@ -201,8 +213,8 @@ CypherMainVisitor::visitPattern(CypherParser::PatternContext *ctx) { return patterns; } -antlrcpp::Any -CypherMainVisitor::visitPatternPart(CypherParser::PatternPartContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitPatternPart( + CypherParser::PatternPartContext *ctx) { Pattern *pattern = ctx->anonymousPatternPart()->accept(this); if (ctx->variable()) { std::string variable = ctx->variable()->accept(this); @@ -298,8 +310,8 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipTypes( return types; } -antlrcpp::Any -CypherMainVisitor::visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitRangeLiteral( + CypherParser::RangeLiteralContext *ctx) { if (ctx->integerLiteral().size() == 0U) { // -[*]- return std::pair<int64_t, int64_t>(1LL, LLONG_MAX); @@ -326,8 +338,8 @@ CypherMainVisitor::visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) { } } -antlrcpp::Any -CypherMainVisitor::visitExpression(CypherParser::ExpressionContext *ctx) { +antlrcpp::Any CypherMainVisitor::visitExpression( + CypherParser::ExpressionContext *ctx) { return visitChildren(ctx); } @@ -504,23 +516,22 @@ CypherMainVisitor::visitExpression(CypherParser::ExpressionContext *ctx) { // } // return visitChildren(ctx); //} -// -// antlrcpp::Any -// CypherMainVisitor::visitExpression2(CypherParser::Expression2Context *ctx) { -// if (ctx->nodeLabels().size()) { -// // TODO: Implement this. We don't currently support label checking in -// // expresssion. -// throw SemanticException(); -// } -// auto operand = ctx->atom()->accept(this).as<std::string>(); -// for (int i = 0; i < (int)ctx->propertyLookup().size(); ++i) { -// auto lhs_id = new_id(); -// symbol_table_[lhs_id] = -// SimpleExpression{Function::PROPERTY_GETTER, {operand}}; -// operand = lhs_id; -// } -// return operand; -//} + +antlrcpp::Any CypherMainVisitor::visitExpression2( + CypherParser::Expression2Context *ctx) { + if (ctx->nodeLabels().size()) { + // TODO: Implement this. We don't currently support label checking in + // expresssion. + throw std::exception(); + } + Expression *expression = ctx->atom()->accept(this); + for (auto *lookup : ctx->propertyLookup()) { + auto property_lookup = + storage_.Create<PropertyLookup>(expression, lookup->accept(this)); + expression = property_lookup; + } + return expression; +} antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { if (ctx->literal()) { @@ -555,7 +566,7 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { } else if (ctx->variable()) { std::string variable = ctx->variable()->accept(this); users_identifiers.insert(variable); - return storage_.Create<Identifier>(variable); + return (Expression *)storage_.Create<Identifier>(variable); } // TODO: Implement this. We don't support comprehensions, functions, // filtering... at the moment. diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp index 29515a990..f38dfe181 100644 --- a/src/query/frontend/ast/cypher_main_visitor.hpp +++ b/src/query/frontend/ast/cypher_main_visitor.hpp @@ -15,10 +15,10 @@ using query::Context; using antlropencypher::CypherParser; class CypherMainVisitor : public antlropencypher::CypherBaseVisitor { -public: + public: CypherMainVisitor(Context &ctx) : ctx_(ctx) {} -private: + private: // template <typename TExpression> // antlrcpp::Any // LeftAssociativeOperatorExpression(std::vector<TExpression *> children, @@ -49,90 +49,109 @@ private: // children, std::vector<Function>((int)children.size() - 1, op)); // } - antlrcpp::Any - visitSingleQuery(CypherParser::SingleQueryContext *ctx) override; + /** + * @return Query* + */ + antlrcpp::Any visitSingleQuery( + CypherParser::SingleQueryContext *ctx) override; + /** + * @return Clause* + */ antlrcpp::Any visitClause(CypherParser::ClauseContext *ctx) override; - antlrcpp::Any - visitCypherMatch(CypherParser::CypherMatchContext *ctx) override; + /** + * @return Match* + */ + antlrcpp::Any visitCypherMatch( + CypherParser::CypherMatchContext *ctx) override; - antlrcpp::Any - visitCypherReturn(CypherParser::CypherReturnContext *ctx) override; + /** + * @return Create* + */ + antlrcpp::Any visitCreate(CypherParser::CreateContext *ctx) override; + /** + * @return Return* + */ + antlrcpp::Any visitCypherReturn( + CypherParser::CypherReturnContext *ctx) override; + + /** + * @return Return* + */ antlrcpp::Any visitReturnBody(CypherParser::ReturnBodyContext *ctx) override; - antlrcpp::Any - visitReturnItems(CypherParser::ReturnItemsContext *ctx) override; + /** + * @return Return* + */ + antlrcpp::Any visitReturnItems( + CypherParser::ReturnItemsContext *ctx) override; + /** + * @return NamedExpression* + */ antlrcpp::Any visitReturnItem(CypherParser::ReturnItemContext *ctx) override; /** - * Creates Node and stores it in symbol_table_. If variable is defined it is - * stored in ids_map_. - * - * @return string - node id. - */ - antlrcpp::Any - visitNodePattern(CypherParser::NodePatternContext *ctx) override; + * @return NodeAtom* + */ + antlrcpp::Any visitNodePattern( + CypherParser::NodePatternContext *ctx) override; /** - * @return vector<string> labels. - */ + * @return vector<GraphDb::Label> + */ antlrcpp::Any visitNodeLabels(CypherParser::NodeLabelsContext *ctx) override; /** - * @return unordered_map<string, string> properties - property key to - * expression id. - */ + * @return unordered_map<GraphDb::Property, Expression*> + */ antlrcpp::Any visitProperties(CypherParser::PropertiesContext *ctx) override; /** - * @return unordered_map<string, string> map - key to expression id. - */ + * @return unordered_map<GraphDb::Property, Expression*> + */ antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override; /** - * @return string. - */ - antlrcpp::Any - visitSymbolicName(CypherParser::SymbolicNameContext *ctx) override; + * @return GraphDb::Property + */ + antlrcpp::Any visitPropertyKeyName( + CypherParser::PropertyKeyNameContext *ctx) override; /** - * @return vector<PatternPart> pattern. - */ + * @return string + */ + antlrcpp::Any visitSymbolicName( + CypherParser::SymbolicNameContext *ctx) override; + + /** + * @return vector<Pattern*> + */ antlrcpp::Any visitPattern(CypherParser::PatternContext *ctx) override; /** - * Stores PatternPart in symbol_table_. If variable is defined it is stored - *in - * ids_map_. - * - * @return string - pattern part id. - */ - antlrcpp::Any - visitPatternPart(CypherParser::PatternPartContext *ctx) override; + * @return Pattern* + */ + antlrcpp::Any visitPatternPart( + CypherParser::PatternPartContext *ctx) override; /** - * Creates PatternPart. - * - * @return PatternPart. - */ - antlrcpp::Any - visitPatternElement(CypherParser::PatternElementContext *ctx) override; + * @return Pattern* + */ + antlrcpp::Any visitPatternElement( + CypherParser::PatternElementContext *ctx) override; /** - * @return pair<string, string> - node and relationship ids. - */ + * @return vector<pair<EdgeAtom*, NodeAtom*>> + */ antlrcpp::Any visitPatternElementChain( CypherParser::PatternElementChainContext *ctx) override; /** - * Creates Relationship and stores it in symbol_table_. If variable is defined - * it is stored in symbol_table_. - * - * @return string - relationship id. - */ + *@return EdgeAtom* + */ antlrcpp::Any visitRelationshipPattern( CypherParser::RelationshipPatternContext *ctx) override; @@ -142,23 +161,24 @@ private: */ antlrcpp::Any visitRelationshipDetail( CypherParser::RelationshipDetailContext *ctx) override; + /** - * @return vector<string>. - */ - antlrcpp::Any - visitRelationshipTypes(CypherParser::RelationshipTypesContext *ctx) override; + * @return vector<GraphDb::EdgeType> + */ + antlrcpp::Any visitRelationshipTypes( + CypherParser::RelationshipTypesContext *ctx) override; /** * @return pair<int64_t, int64_t>. */ - antlrcpp::Any - visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) override; + antlrcpp::Any visitRangeLiteral( + CypherParser::RangeLiteralContext *ctx) override; /** - * Top level expression. - * - * @return string - expression id. - */ + * Top level expression, does nothing. + * + * @return Expression* + */ antlrcpp::Any visitExpression(CypherParser::ExpressionContext *ctx) override; ///** @@ -248,18 +268,18 @@ private: // antlrcpp::Any // visitExpression3(CypherParser::Expression3Context *ctx) override; - ///** - //* Property lookup, test for node labels existence... - //* - //* @return string - expression id. - //*/ - // antlrcpp::Any - // visitExpression2(CypherParser::Expression2Context *ctx) override; + /** + * Property lookup, test for node labels existence... + * + * @return Expression* + */ + antlrcpp::Any visitExpression2( + CypherParser::Expression2Context *ctx) override; /** * Literals, params, list comprehension... * - * @return string - expression id. + * @return Expression* */ antlrcpp::Any visitAtom(CypherParser::AtomContext *ctx) override; @@ -285,14 +305,14 @@ private: /** * @return int64_t. */ - antlrcpp::Any - visitIntegerLiteral(CypherParser::IntegerLiteralContext *ctx) override; + antlrcpp::Any visitIntegerLiteral( + CypherParser::IntegerLiteralContext *ctx) override; -public: + public: Query *query() { return query_; } const static std::string kAnonPrefix; -private: + private: Context &ctx_; // Set of identifiers from queries. std::unordered_set<std::string> users_identifiers; diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 65ae3463e..d12f7a1d7 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -7,10 +7,10 @@ #include "antlr4-runtime.h" #include "dbms/dbms.hpp" #include "gmock/gmock.h" +#include "gtest/gtest.h" #include "query/context.hpp" #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" -#include "gtest/gtest.h" namespace { @@ -19,11 +19,15 @@ using namespace query::frontend; using testing::UnorderedElementsAre; class AstGenerator { -public: + public: AstGenerator(const std::string &query) - : dbms_(), db_accessor_(dbms_.active()), - context_(Config{}, *db_accessor_), query_string_(query), parser_(query), - visitor_(context_), query_([&]() { + : dbms_(), + db_accessor_(dbms_.active()), + context_(Config{}, *db_accessor_), + query_string_(query), + parser_(query), + visitor_(context_), + query_([&]() { visitor_.visit(parser_.tree()); return visitor_.query(); }()) {} @@ -37,11 +41,11 @@ public: Query *query_; }; -TEST(CompilerStructuresTest, SyntaxException) { +TEST(CypherMainVisitorTest, SyntaxException) { ASSERT_THROW(AstGenerator("CREATE ()-[*1...2]-()"), std::exception); } -TEST(CompilerStructuresTest, NodePattern) { +TEST(CypherMainVisitorTest, NodePattern) { AstGenerator ast_generator("MATCH (:label1:label2:label3)"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); @@ -62,7 +66,7 @@ TEST(CompilerStructuresTest, NodePattern) { // TODO: add test for properties. } -TEST(CompilerStructuresTest, NodePatternIdentifier) { +TEST(CypherMainVisitorTest, NodePatternIdentifier) { AstGenerator ast_generator("MATCH (var)"); auto *query = ast_generator.query_; auto *match = dynamic_cast<Match *>(query->clauses_[0]); @@ -73,7 +77,7 @@ TEST(CompilerStructuresTest, NodePatternIdentifier) { // TODO: add test for properties. } -TEST(CompilerStructuresTest, RelationshipPatternNoDetails) { +TEST(CypherMainVisitorTest, RelationshipPatternNoDetails) { AstGenerator ast_generator("MATCH ()--()"); auto *query = ast_generator.query_; auto *match = dynamic_cast<Match *>(query->clauses_[0]); @@ -92,7 +96,7 @@ TEST(CompilerStructuresTest, RelationshipPatternNoDetails) { CypherMainVisitor::kAnonPrefix + std::to_string(2)); } -TEST(CompilerStructuresTest, RelationshipPatternDetails) { +TEST(CypherMainVisitorTest, RelationshipPatternDetails) { AstGenerator ast_generator("MATCH ()<-[:type1|type2]-()"); auto *query = ast_generator.query_; auto *match = dynamic_cast<Match *>(query->clauses_[0]); @@ -105,7 +109,7 @@ TEST(CompilerStructuresTest, RelationshipPatternDetails) { // TODO: test properties } -TEST(CompilerStructuresTest, RelationshipPatternVariable) { +TEST(CypherMainVisitorTest, RelationshipPatternVariable) { AstGenerator ast_generator("MATCH ()-[var]->()"); auto *query = ast_generator.query_; auto *match = dynamic_cast<Match *>(query->clauses_[0]); @@ -116,7 +120,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { } // // Relationship with unbounded variable range. -// TEST(CompilerStructuresTest, RelationshipPatternUnbounded) { +// TEST(CypherMainVisitorTest, RelationshipPatternUnbounded) { // ParserTables parser("CREATE ()-[*]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); @@ -126,7 +130,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Relationship with lower bounded variable range. -// TEST(CompilerStructuresTest, RelationshipPatternLowerBounded) { +// TEST(CypherMainVisitorTest, RelationshipPatternLowerBounded) { // ParserTables parser("CREATE ()-[*5..]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); @@ -136,7 +140,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Relationship with upper bounded variable range. -// TEST(CompilerStructuresTest, RelationshipPatternUpperBounded) { +// TEST(CypherMainVisitorTest, RelationshipPatternUpperBounded) { // ParserTables parser("CREATE ()-[*..10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); @@ -145,7 +149,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Relationship with lower and upper bounded variable range. -// TEST(CompilerStructuresTest, RelationshipPatternLowerUpperBounded) { +// TEST(CypherMainVisitorTest, RelationshipPatternLowerUpperBounded) { // ParserTables parser("CREATE ()-[*5..10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); @@ -154,7 +158,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Relationship with fixed number of edges. -// TEST(CompilerStructuresTest, RelationshipPatternFixedRange) { +// TEST(CypherMainVisitorTest, RelationshipPatternFixedRange) { // ParserTables parser("CREATE ()-[*10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); @@ -163,15 +167,14 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Relationship with invalid bound (larger than long long). -// TEST(CompilerStructuresTest, RelationshipPatternInvalidBound) { +// TEST(CypherMainVisitorTest, RelationshipPatternInvalidBound) { // ASSERT_THROW( // ParserTables parser("CREATE ()-[*100000000000000000000000000]-()"), // SemanticException); // } // -// // // PatternPart. -// TEST(CompilerStructuresTest, PatternPart) { +// TEST(CypherMainVisitorTest, PatternPart) { // ParserTables parser("CREATE ()--()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.pattern_parts_.size(), 1U); @@ -182,7 +185,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // PatternPart in braces. -// TEST(CompilerStructuresTest, PatternPartBraces) { +// TEST(CypherMainVisitorTest, PatternPartBraces) { // ParserTables parser("CREATE ((()--()))"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.pattern_parts_.size(), 1U); @@ -193,7 +196,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // PatternPart with variable. -// TEST(CompilerStructuresTest, PatternPartVariable) { +// TEST(CypherMainVisitorTest, PatternPartVariable) { // ParserTables parser("CREATE var=()--()"); // ASSERT_EQ(parser.identifiers_map_.size(), 1U); // ASSERT_EQ(parser.pattern_parts_.size(), 1U); @@ -209,8 +212,49 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) { // } // // // Multiple nodes with same variable and properties. -// TEST(CompilerStructuresTest, MultipleNodesWithVariableAndProperties) { +// TEST(CypherMainVisitorTest, MultipleNodesWithVariableAndProperties) { // ASSERT_THROW(ParserTables parser("CREATE (a {b: 5})-[]-(a {c: 5})"), // SemanticException); // } + +TEST(CypherMainVisitorTest, ReturnUnanemdIdentifier) { + AstGenerator ast_generator("RETURN var"); + auto *query = ast_generator.query_; + ASSERT_EQ(query->clauses_.size(), 1U); + auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]); + ASSERT_TRUE(return_clause); + ASSERT_EQ(return_clause->named_expressions_.size(), 1U); + auto *named_expr = return_clause->named_expressions_[0]; + ASSERT_TRUE(named_expr); + ASSERT_EQ(named_expr->name_, "var"); + auto *identifier = dynamic_cast<Identifier *>(named_expr->expression_); + ASSERT_TRUE(identifier); + ASSERT_EQ(identifier->name_, "var"); +} + +TEST(CypherMainVisitorTest, ReturnNamedIdentifier) { + AstGenerator ast_generator("RETURN var AS var5"); + auto *query = ast_generator.query_; + auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]); + auto *named_expr = return_clause->named_expressions_[0]; + ASSERT_EQ(named_expr->name_, "var5"); + auto *identifier = dynamic_cast<Identifier *>(named_expr->expression_); + ASSERT_EQ(identifier->name_, "var"); +} + +TEST(CypherMainVisitorTest, Create) { + AstGenerator ast_generator("CREATE (n)"); + auto *query = ast_generator.query_; + ASSERT_EQ(query->clauses_.size(), 1U); + auto *create = dynamic_cast<Create *>(query->clauses_[0]); + ASSERT_TRUE(create); + ASSERT_EQ(create->patterns_.size(), 1U); + ASSERT_TRUE(create->patterns_[0]); + ASSERT_EQ(create->patterns_[0]->atoms_.size(), 1U); + auto node = dynamic_cast<NodeAtom *>(create->patterns_[0]->atoms_[0]); + ASSERT_TRUE(node); + ASSERT_TRUE(node->identifier_); + ASSERT_EQ(node->identifier_->name_, "n"); + // TODO: add test for properties. +} }