Compare commits
2 Commits
master
...
better_exi
Author | SHA1 | Date | |
---|---|---|---|
|
bddecadeab | ||
|
f1edc456ab |
@ -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);
|
||||
|
@ -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*
|
||||
*/
|
||||
|
@ -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 ;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user