diff --git a/src/query/cypher_query_interpreter.cpp b/src/query/cypher_query_interpreter.cpp index d3ddc22c4..30966119b 100644 --- a/src/query/cypher_query_interpreter.cpp +++ b/src/query/cypher_query_interpreter.cpp @@ -1,4 +1,4 @@ -// Copyright 2023 Memgraph Ltd. +// Copyright 2024 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source @@ -12,7 +12,6 @@ #include "query/cypher_query_interpreter.hpp" #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" -#include "utils/synchronized.hpp" // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) DEFINE_bool(query_cost_planner, true, "Use the cost-estimating query planner."); @@ -80,7 +79,7 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri // Convert the ANTLR4 parse tree into an AST. AstStorage ast_storage; frontend::ParsingContext context{.is_query_cached = true}; - frontend::CypherMainVisitor visitor(context, &ast_storage); + frontend::CypherMainVisitor visitor(context, &ast_storage, ¶meters); visitor.visit(parser->tree()); diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index 7002ee4b9..b62c9e301 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -1825,7 +1825,15 @@ antlrcpp::Any CypherMainVisitor::visitNodePattern(MemgraphCypher::NodePatternCon antlrcpp::Any CypherMainVisitor::visitNodeLabels(MemgraphCypher::NodeLabelsContext *ctx) { std::vector<LabelIx> labels; for (auto *node_label : ctx->nodeLabel()) { - labels.push_back(AddLabel(std::any_cast<std::string>(node_label->accept(this)))); + if (node_label->labelName()->symbolicName()) { + labels.emplace_back(AddLabel(std::any_cast<std::string>(node_label->accept(this)))); + } else { + // If we have a parameter, we have to resolve it. + const auto *param_lookup = std::any_cast<ParameterLookup *>(node_label->accept(this)); + const auto label_name = parameters_->AtTokenPosition(param_lookup->token_position_).ValueString(); + labels.emplace_back(storage_->GetLabelIx(label_name)); + query_info_.is_cacheable = false; // We can't cache queries with label parameters. + } } return labels; } diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp index 9e828674f..7f75b0050 100644 --- a/src/query/frontend/ast/cypher_main_visitor.hpp +++ b/src/query/frontend/ast/cypher_main_visitor.hpp @@ -17,6 +17,7 @@ #include "query/frontend/ast/ast.hpp" #include "query/frontend/opencypher/generated/MemgraphCypherBaseVisitor.h" +#include "query/parameters.hpp" #include "utils/exceptions.hpp" #include "utils/logging.hpp" @@ -30,7 +31,8 @@ struct ParsingContext { class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor { public: - explicit CypherMainVisitor(ParsingContext context, AstStorage *storage) : context_(context), storage_(storage) {} + explicit CypherMainVisitor(ParsingContext context, AstStorage *storage, Parameters *parameters) + : context_(context), storage_(storage), parameters_(parameters) {} private: Expression *CreateBinaryOperatorByToken(size_t token, Expression *e1, Expression *e2) { @@ -1022,6 +1024,8 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor { // return. bool in_with_ = false; + Parameters *parameters_; + QueryInfo query_info_; }; } // namespace memgraph::query::frontend diff --git a/src/query/frontend/opencypher/grammar/Cypher.g4 b/src/query/frontend/opencypher/grammar/Cypher.g4 index d387002d8..bb435b85d 100644 --- a/src/query/frontend/opencypher/grammar/Cypher.g4 +++ b/src/query/frontend/opencypher/grammar/Cypher.g4 @@ -193,7 +193,7 @@ nodeLabels : nodeLabel ( nodeLabel )* ; nodeLabel : ':' labelName ; -labelName : symbolicName ; +labelName : symbolicName | parameter; relTypeName : symbolicName ; diff --git a/tests/benchmark/query/execution.cpp b/tests/benchmark/query/execution.cpp index aa72e21d7..750dd5564 100644 --- a/tests/benchmark/query/execution.cpp +++ b/tests/benchmark/query/execution.cpp @@ -23,6 +23,7 @@ // variable of the same name, EOF. // This hides the definition of the macro which causes // the compilation to fail. +#include "query/parameters.hpp" #include "query/plan/planner.hpp" ////////////////////////////////////////////////////// #include "communication/result_stream_faker.hpp" @@ -119,10 +120,11 @@ static void AddTree(memgraph::storage::Storage *db, int vertex_count) { static memgraph::query::CypherQuery *ParseCypherQuery(const std::string &query_string, memgraph::query::AstStorage *ast) { memgraph::query::frontend::ParsingContext parsing_context; + memgraph::query::Parameters parameters; parsing_context.is_query_cached = false; memgraph::query::frontend::opencypher::Parser parser(query_string); // Convert antlr4 AST into Memgraph AST. - memgraph::query::frontend::CypherMainVisitor cypher_visitor(parsing_context, ast); + memgraph::query::frontend::CypherMainVisitor cypher_visitor(parsing_context, ast, ¶meters); cypher_visitor.visit(parser.tree()); return memgraph::utils::Downcast<memgraph::query::CypherQuery>(cypher_visitor.query()); }; diff --git a/tests/gql_behave/tests/memgraph_V1/features/parameters.feature b/tests/gql_behave/tests/memgraph_V1/features/parameters.feature index 908507e43..288f93206 100644 --- a/tests/gql_behave/tests/memgraph_V1/features/parameters.feature +++ b/tests/gql_behave/tests/memgraph_V1/features/parameters.feature @@ -52,7 +52,7 @@ Feature: Parameters | [1, 2, 3] | Scenario: Parameters in match: - Given an empty graph + Given an empty graph And having executed: """ CREATE (a {x : 10}) @@ -66,3 +66,90 @@ Feature: Parameters Then the result should be: | a.x | | 10 | + + Scenario: Label parameters in match: + Given an empty graph + And having executed: + """ + CREATE (a:Label1 {x : 10}) + """ + And parameters are: + | a | 10 | + | label | Label1 | + When executing query: + """ + MATCH (a:$label {x : $a}) RETURN a + """ + Then the result should be: + | a | + | (:Label1{x: 10}) | + + Scenario: Label parameters in create and match + Given an empty graph + And parameters are: + | a | 10 | + | label | Label1 | + When executing query: + """ + CREATE (a:$label {x: $a}) + """ + When executing query: + """ + MATCH (a:$label {x: $a}) RETURN a + """ + Then the result should be: + | a | + | (:Label1{x: 10}) | + + Scenario: Label parameters in merge + Given an empty graph + And parameters are: + | a | 10 | + | label | Label1 | + When executing query: + """ + MERGE (a:$label {x: $a}) RETURN a + """ + Then the result should be: + | a | + | (:Label1{x: 10}) | + + Scenario: Label parameters in set label + Given an empty graph + And having executed: + """ + CREATE (a:Label1 {x : 10}) + """ + And parameters are: + | new_label | Label2 | + When executing query: + """ + MATCH (a:Label1 {x: 10}) SET a:$new_label + """ + When executing query: + """ + MATCH (a:Label1:Label1 {x: 10}) RETURN a + """ + Then the result should be: + | a | + | (:Label1:Label2 {x: 10}) | + + Scenario: Label parameters in remove label + Given an empty graph + And having executed: + """ + CREATE (a:Label1:LabelToRemove {x : 10}) + """ + And parameters are: + | label_to_remove | LabelToRemove | + When executing query: + """ + MATCH (a {x: 10}) REMOVE a:$label_to_remove + """ + When executing query: + """ + MATCH (a {x: 10}) RETURN a + """ + Then the result should be: + | a | + | (:Label1 {x: 10}) | diff --git a/tests/manual/expression_pretty_printer.cpp b/tests/manual/expression_pretty_printer.cpp index e2d58b350..20757b195 100644 --- a/tests/manual/expression_pretty_printer.cpp +++ b/tests/manual/expression_pretty_printer.cpp @@ -1,4 +1,4 @@ -// Copyright 2022 Memgraph Ltd. +// Copyright 2023 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source @@ -17,6 +17,7 @@ #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/ast/pretty_print.hpp" #include "query/frontend/opencypher/parser.hpp" +#include "query/parameters.hpp" std::string AssembleQueryString(const std::string &expression_string) { return "return " + expression_string + " as expr"; @@ -24,8 +25,9 @@ std::string AssembleQueryString(const std::string &expression_string) { memgraph::query::Query *ParseQuery(const std::string &query_string, memgraph::query::AstStorage *ast_storage) { memgraph::query::frontend::ParsingContext context; + memgraph::query::Parameters parameters; memgraph::query::frontend::opencypher::Parser parser(query_string); - memgraph::query::frontend::CypherMainVisitor visitor(context, ast_storage); + memgraph::query::frontend::CypherMainVisitor visitor(context, ast_storage, ¶meters); visitor.visit(parser.tree()); return visitor.query(); diff --git a/tests/manual/interactive_planning.cpp b/tests/manual/interactive_planning.cpp index d5da0ba2b..76ae4cc1a 100644 --- a/tests/manual/interactive_planning.cpp +++ b/tests/manual/interactive_planning.cpp @@ -434,11 +434,12 @@ void ExaminePlans(memgraph::query::DbAccessor *dba, const memgraph::query::Symbo memgraph::query::Query *MakeAst(const std::string &query, memgraph::query::AstStorage *storage) { memgraph::query::frontend::ParsingContext parsing_context; + memgraph::query::Parameters parameters; parsing_context.is_query_cached = false; // query -> AST auto parser = std::make_unique<memgraph::query::frontend::opencypher::Parser>(query); // AST -> high level tree - memgraph::query::frontend::CypherMainVisitor visitor(parsing_context, storage); + memgraph::query::frontend::CypherMainVisitor visitor(parsing_context, storage, ¶meters); visitor.visit(parser->tree()); return visitor.query(); } diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 31fd95c6c..9cb33589c 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -37,6 +37,7 @@ #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" #include "query/frontend/stripped.hpp" +#include "query/parameters.hpp" #include "query/procedure/cypher_types.hpp" #include "query/procedure/mg_procedure_impl.hpp" #include "query/procedure/module.hpp" @@ -118,7 +119,8 @@ class AstGenerator : public Base { public: Query *ParseQuery(const std::string &query_string) override { ::frontend::opencypher::Parser parser(query_string); - CypherMainVisitor visitor(context_, &ast_storage_); + Parameters parameters; + CypherMainVisitor visitor(context_, &ast_storage_, ¶meters); visitor.visit(parser.tree()); return visitor.query(); } @@ -151,6 +153,7 @@ class ClonedAstGenerator : public Base { public: Query *ParseQuery(const std::string &query_string) override { ::frontend::opencypher::Parser parser(query_string); + Parameters parameters; AstStorage tmp_storage; { // Add a label, property and edge type into temporary storage so @@ -159,7 +162,7 @@ class ClonedAstGenerator : public Base { tmp_storage.GetPropertyIx("fdjakfjdklfjdaslk"); tmp_storage.GetEdgeTypeIx("fdjkalfjdlkajfdkla"); } - CypherMainVisitor visitor(context_, &tmp_storage); + CypherMainVisitor visitor(context_, &tmp_storage, ¶meters); visitor.visit(parser.tree()); return visitor.query()->Clone(&ast_storage_); } @@ -182,8 +185,9 @@ class CachedAstGenerator : public Base { StrippedQuery stripped(query_string); parameters_ = stripped.literals(); ::frontend::opencypher::Parser parser(stripped.query()); + Parameters parameters; AstStorage tmp_storage; - CypherMainVisitor visitor(context_, &tmp_storage); + CypherMainVisitor visitor(context_, &tmp_storage, ¶meters); visitor.visit(parser.tree()); return visitor.query()->Clone(&ast_storage_); }