Skip to content

Commit

Permalink
Allow usage of aliases defined in previous matches. (vesoft-inc#1870)
Browse files Browse the repository at this point in the history
<!--
Thanks for your contribution!
In order to review PR more efficiently, please add information according to the template.
-->

## What type of PR is this?
- [X] bug
- [ ] feature
- [ ] enhancement

## What problem(s) does this PR solve?
#### Issue(s) number: 
Close vesoft-inc#5020 

#### Description:
The `with *` in a match clause like `match ...(v999)... match ... match ... match ...(v999)... with *` from a multi-match query, where v999 is defined in a previous match, rebuilds named aliases from all query parts, causing v999 to duplicate and trigger a false error.

This is actually ok, since they are in fact the same ones. There is actually no redefinition. This compiles with openCypher and neo4j.

## How do you solve it?

1. Allow the usage of aliases defined in previous matches. Only report redefinition of aliases if an alias is repeateed within the current query part.
2. Fixed an unstable tck case.


## Special notes for your reviewer, ex. impact of this fix, design document, etc:



## Checklist:
Tests:
- [ ] Unit test(positive and negative cases)
- [ ] Function test
- [ ] Performance test
- [X] TCK

Affects:
- [ ] Documentation affected (Please add the label if documentation needs to be modified.)
- [ ] Incompatibility (If it breaks the compatibility, please describe it and add the label.)
- [ ] If it's needed to cherry-pick (If cherry-pick to some branches is required, please label the destination version(s).)
- [ ] Performance impacted: Consumes more CPU/Memory


## Release notes:

Please confirm whether to be reflected in release notes and how to describe:
> ex. Fixed the bug .....


Migrated from vesoft-inc#5021

Co-authored-by: Cheng Xuntao <[email protected]>
  • Loading branch information
nebula-bots and xtcyclist authored Dec 8, 2022
1 parent d368d01 commit 5f8b567
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
9 changes: 6 additions & 3 deletions src/graph/validator/MatchValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ Status MatchValidator::buildColumnsForAllNamedAliases(const std::vector<QueryPar
switch (boundary->kind) {
case CypherClauseKind::kUnwind: {
auto unwindCtx = static_cast<const UnwindClauseContext *>(boundary.get());
columns->addColumn(makeColumn(unwindCtx->alias));
columns->addColumn(makeColumn(unwindCtx->alias), true);
break;
}
case CypherClauseKind::kWith: {
Expand All @@ -410,7 +410,7 @@ Status MatchValidator::buildColumnsForAllNamedAliases(const std::vector<QueryPar
}
for (auto &col : yieldColumns->columns()) {
if (!col->alias().empty()) {
columns->addColumn(makeColumn(col->alias()));
columns->addColumn(makeColumn(col->alias()), true);
}
}
break;
Expand Down Expand Up @@ -621,7 +621,10 @@ Status MatchValidator::validateWith(const WithClause *with,
auto found = withClauseCtx.yield->aliasesAvailable.find(label);
DCHECK(found != withClauseCtx.yield->aliasesAvailable.end());
if (!withClauseCtx.aliasesGenerated.emplace(col->alias(), found->second).second) {
return Status::SemanticError("`%s': Redefined alias", col->alias().c_str());
auto columnFound = withClauseCtx.yield->yieldColumns->find(col->alias());
if (!(columnFound != nullptr && columnFound->isMatched())) {
return Status::SemanticError("`%s': Redefined alias", col->alias().c_str());
}
}
} else {
if (!withClauseCtx.aliasesGenerated.emplace(col->alias(), aliasType).second) {
Expand Down
22 changes: 21 additions & 1 deletion src/parser/Clauses.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class YieldColumn final {
explicit YieldColumn(Expression *expr, const std::string &alias = "") {
expr_ = expr;
alias_ = alias;
isMatched_ = false;
}

std::unique_ptr<YieldColumn> clone() const {
Expand Down Expand Up @@ -285,9 +286,18 @@ class YieldColumn final {

std::string toString() const;

void setMatched(bool isMatched) {
isMatched_ = isMatched;
}

bool isMatched() const {
return isMatched_;
}

private:
Expression *expr_{nullptr};
std::string alias_;
bool isMatched_;
};

bool operator==(const YieldColumn &l, const YieldColumn &r);
Expand All @@ -297,8 +307,9 @@ inline bool operator!=(const YieldColumn &l, const YieldColumn &r) {

class YieldColumns final {
public:
void addColumn(YieldColumn *field) {
void addColumn(YieldColumn *field, bool isMatched = false) {
columns_.emplace_back(field);
field->setMatched(isMatched);
}

std::vector<YieldColumn *> columns() const {
Expand Down Expand Up @@ -346,6 +357,15 @@ class YieldColumns final {

bool hasAgg() const;

YieldColumn *find(const std::string &name) const {
for (auto &col : columns_) {
if (name.compare(col->name()) == 0) {
return col.get();
}
}
return nullptr;
}

private:
std::vector<std::unique_ptr<YieldColumn>> columns_;
};
Expand Down
24 changes: 24 additions & 0 deletions tests/tck/features/match/With.feature
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,27 @@ Feature: With clause
| [:serve "Tim Duncan"->"Spurs" @0 {end_year: 2016, start_year: 1997}] |
| [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}] |
| [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}] |

Scenario: with wildcard after multiple matches
When executing query:
"""
match (v0:player)--(v1:team) where v1.team.name == "Spurs" and v0.player.name == "Tim Duncan"
match (v:player) where v.player.name != "Tim Duncan" with v0 where v0.player.age > 0
match (v0:player)
with *
return count(v0)
"""
Then the result should be, in order:
| count(v0) |
| 51 |
When executing query:
"""
match (v:player)
with v AS p
match (p)
with p AS v
match (v)
with *
return count (p)
"""
Then a SemanticError should be raised at runtime: Alias used but not defined: `p'

0 comments on commit 5f8b567

Please sign in to comment.