Compare commits

...

2 Commits

Author SHA1 Message Date
Andreja Tonev
bddecadeab Update tests 2024-03-04 17:24:30 +01:00
Andreja Tonev
f1edc456ab Allow only one relation in EXISTS() 2024-03-04 15:26:58 +01:00
6 changed files with 39 additions and 56 deletions

View File

@ -2058,6 +2058,18 @@ antlrcpp::Any CypherMainVisitor::visitPatternPart(MemgraphCypher::PatternPartCon
return pattern;
}
antlrcpp::Any CypherMainVisitor::visitForcePatternPart(MemgraphCypher::ForcePatternPartContext *ctx) {
auto *pattern = std::any_cast<Pattern *>(ctx->relationshipsPattern()->accept(this));
if (ctx->variable()) {
auto variable = std::any_cast<std::string>(ctx->variable()->accept(this));
pattern->identifier_ = storage_->Create<Identifier>(variable);
users_identifiers.insert(variable);
} else {
anonymous_identifiers.push_back(&pattern->identifier_);
}
return pattern;
}
antlrcpp::Any CypherMainVisitor::visitPatternElement(MemgraphCypher::PatternElementContext *ctx) {
if (ctx->patternElement()) {
return ctx->patternElement()->accept(this);
@ -2510,6 +2522,8 @@ antlrcpp::Any CypherMainVisitor::visitAtom(MemgraphCypher::AtomContext *ctx) {
auto variable = std::any_cast<std::string>(ctx->variable()->accept(this));
users_identifiers.insert(variable);
return static_cast<Expression *>(storage_->Create<Identifier>(variable));
} else if (ctx->existsExpression()) {
return std::any_cast<Expression *>(ctx->existsExpression()->accept(this));
} else if (ctx->functionInvocation()) {
return std::any_cast<Expression *>(ctx->functionInvocation()->accept(this));
} else if (ctx->COALESCE()) {
@ -2521,7 +2535,7 @@ antlrcpp::Any CypherMainVisitor::visitAtom(MemgraphCypher::AtomContext *ctx) {
} else if (ctx->COUNT()) {
// Here we handle COUNT(*). COUNT(expression) is handled in
// visitFunctionInvocation with other aggregations. This is visible in
// functionInvocation and atom producions in opencypher grammar.
// functionInvocation and atom production in opencypher grammar.
return static_cast<Expression *>(storage_->Create<Aggregation>(nullptr, nullptr, Aggregation::Op::COUNT, false));
} else if (ctx->ALL()) {
auto *ident = storage_->Create<Identifier>(
@ -2576,8 +2590,6 @@ antlrcpp::Any CypherMainVisitor::visitAtom(MemgraphCypher::AtomContext *ctx) {
auto *list = std::any_cast<Expression *>(ctx->extractExpression()->idInColl()->expression()->accept(this));
auto *expr = std::any_cast<Expression *>(ctx->extractExpression()->expression()->accept(this));
return static_cast<Expression *>(storage_->Create<Extract>(ident, list, expr));
} else if (ctx->existsExpression()) {
return std::any_cast<Expression *>(ctx->existsExpression()->accept(this));
} else if (ctx->patternComprehension()) {
return std::any_cast<Expression *>(ctx->patternComprehension()->accept(this));
}
@ -2631,10 +2643,13 @@ antlrcpp::Any CypherMainVisitor::visitLiteral(MemgraphCypher::LiteralContext *ct
antlrcpp::Any CypherMainVisitor::visitExistsExpression(MemgraphCypher::ExistsExpressionContext *ctx) {
auto *exists = storage_->Create<Exists>();
exists->pattern_ = std::any_cast<Pattern *>(ctx->patternPart()->accept(this));
if (exists->pattern_->identifier_) {
throw SyntaxException("Identifiers are not supported in exists(...).");
if (ctx->forcePatternPart()) {
exists->pattern_ = std::any_cast<Pattern *>(ctx->forcePatternPart()->accept(this));
if (exists->pattern_->identifier_) {
throw SyntaxException("Identifiers are not supported in exists(...).");
}
} else {
throw SyntaxException("EXISTS supports only a single relation as its input.");
}
return static_cast<Expression *>(exists);

View File

@ -709,6 +709,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitPatternPart(MemgraphCypher::PatternPartContext *ctx) override;
/**
* @return Pattern*
*/
antlrcpp::Any visitForcePatternPart(MemgraphCypher::ForcePatternPartContext *ctx) override;
/**
* @return Pattern*
*/

View File

@ -286,7 +286,11 @@ reduceExpression : accumulator=variable '=' initial=expression ',' idInColl '|'
extractExpression : idInColl '|' expression ;
existsExpression : patternPart ;
existsExpression : forcePatternPart | .* ;
forcePatternPart : ( variable '=' relationshipsPattern )
| relationshipsPattern
;
idInColl : variable IN expression ;

View File

@ -357,22 +357,6 @@ Feature: WHERE exists
"""
Then the result should be empty
Scenario: Test node-only hop
Given an empty graph
And having executed:
"""
CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3})
"""
When executing query:
"""
MATCH (n) WHERE exists((n)) RETURN n.prop;
"""
Then the result should be:
| n.prop |
| 1 |
| 2 |
| 3 |
Scenario: Test exists with different edge type
Given an empty graph
And having executed:

View File

@ -357,22 +357,6 @@ Feature: WHERE exists
"""
Then the result should be empty
Scenario: Test node-only hop
Given an empty graph
And having executed:
"""
CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3})
"""
When executing query:
"""
MATCH (n) WHERE exists((n)) RETURN n.prop;
"""
Then the result should be:
| n.prop |
| 1 |
| 2 |
| 3 |
Scenario: Test exists with different edge type
Given an empty graph
And having executed:

View File

@ -4484,6 +4484,13 @@ TEST_P(CypherMainVisitorTest, ExistsThrow) {
TestInvalidQueryWithMessage<SyntaxException>("MATCH (n) WHERE exists(p=(n)-[]->()) RETURN n;", ast_generator,
"Identifiers are not supported in exists(...).");
TestInvalidQueryWithMessage<SyntaxException>("MATCH (n) WHERE exists() RETURN n;", ast_generator,
"EXISTS supports only a single relation as its input.");
TestInvalidQueryWithMessage<SyntaxException>("MATCH (n) WHERE exists((n)) RETURN n;", ast_generator,
"EXISTS supports only a single relation as its input.");
TestInvalidQueryWithMessage<SyntaxException>("MATCH (n) WHERE exists((n)-[]) RETURN n;", ast_generator,
"EXISTS supports only a single relation as its input.");
}
TEST_P(CypherMainVisitorTest, Exists) {
@ -4533,22 +4540,6 @@ TEST_P(CypherMainVisitorTest, Exists) {
ASSERT_TRUE(edge2);
ASSERT_TRUE(node3);
}
{
const auto *query = dynamic_cast<CypherQuery *>(ast_generator.ParseQuery("MATCH (n) WHERE exists((n)) RETURN n;"));
const auto *match = dynamic_cast<Match *>(query->single_query_->clauses_[0]);
const auto *exists = dynamic_cast<Exists *>(match->where_->expression_);
ASSERT_TRUE(exists);
const auto pattern = exists->pattern_;
ASSERT_TRUE(pattern->atoms_.size() == 1);
const auto *node = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
ASSERT_TRUE(node);
}
}
TEST_P(CypherMainVisitorTest, CallSubqueryThrow) {