Add query parameters support for labels (#1602)
This commit is contained in:
parent
d4bcdb77ad
commit
b3d0c2ccc2
@ -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());
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -193,7 +193,7 @@ nodeLabels : nodeLabel ( nodeLabel )* ;
|
||||
|
||||
nodeLabel : ':' labelName ;
|
||||
|
||||
labelName : symbolicName ;
|
||||
labelName : symbolicName | parameter;
|
||||
|
||||
relTypeName : symbolicName ;
|
||||
|
||||
|
@ -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());
|
||||
};
|
||||
|
@ -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}) |
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user