diff --git a/src/graph/context/ast/CypherAstContext.h b/src/graph/context/ast/CypherAstContext.h index f859b0e98f1..1b7ca85c235 100644 --- a/src/graph/context/ast/CypherAstContext.h +++ b/src/graph/context/ast/CypherAstContext.h @@ -106,6 +106,8 @@ struct Path final { // Flag for pattern predicate bool isPred{false}; bool isAntiPred{false}; + // if false do not generate path struct + bool genPath{true}; enum PathType : int8_t { kDefault, kAllShortest, kSingleShortest }; PathType pathType{PathType::kDefault}; diff --git a/src/graph/planner/match/MatchSolver.cpp b/src/graph/planner/match/MatchSolver.cpp index af09284e934..8f91271ddb0 100644 --- a/src/graph/planner/match/MatchSolver.cpp +++ b/src/graph/planner/match/MatchSolver.cpp @@ -278,7 +278,7 @@ void MatchSolver::buildProjectColumns(QueryContext* qctx, const Path& path, SubP DCHECK(!nodeInfos.empty()); addNode(nodeInfos.back()); - if (!path.anonymous) { + if (path.genPath) { DCHECK(!path.alias.empty()); columns->addColumn(new YieldColumn(DCHECK_NOTNULL(path.pathBuild), path.alias)); colNames.emplace_back(path.alias); diff --git a/src/graph/validator/MatchValidator.cpp b/src/graph/validator/MatchValidator.cpp index fd0a5149836..5df8609d134 100644 --- a/src/graph/validator/MatchValidator.cpp +++ b/src/graph/validator/MatchValidator.cpp @@ -132,6 +132,10 @@ Status MatchValidator::validatePath(const MatchPath *path, MatchClauseContext &m buildEdgeInfo(path, matchClauseCtx.paths.back().edgeInfos, matchClauseCtx.aliasesGenerated)); NG_RETURN_IF_ERROR( buildPathExpr(path, matchClauseCtx.paths.back(), matchClauseCtx.aliasesGenerated)); + if (path->alias() == nullptr) { + auto &genPath = matchClauseCtx.paths.back().genPath; + genPath = false; + } return Status::OK(); } @@ -170,10 +174,7 @@ Status MatchValidator::buildPathExpr(const MatchPath *path, } auto *pathAlias = path->alias(); - if (pathAlias == nullptr) { - return Status::OK(); - } - if (!aliasesGenerated.emplace(*pathAlias, AliasType::kPath).second) { + if (pathAlias != nullptr && !aliasesGenerated.emplace(*pathAlias, AliasType::kPath).second) { return Status::SemanticError("`%s': Redefined alias", pathAlias->c_str()); } auto *pool = qctx_->objPool(); @@ -184,8 +185,8 @@ Status MatchValidator::buildPathExpr(const MatchPath *path, } pathBuild->add(InputPropertyExpression::make(pool, nodeInfos.back().alias)); pathInfo.pathBuild = std::move(pathBuild); - pathInfo.anonymous = false; - pathInfo.alias = *pathAlias; + pathInfo.anonymous = pathAlias == nullptr; + pathInfo.alias = pathAlias == nullptr ? path->toString() : *pathAlias; return Status::OK(); } @@ -1105,7 +1106,6 @@ Status MatchValidator::validateMatchPathExpr( // Build path alias auto matchPathPtr = matchPathExprImpl->matchPathPtr(); auto pathAlias = matchPathPtr->toString(); - matchPathPtr->setAlias(new std::string(pathAlias)); if (matchPathExprImpl->genList() == nullptr) { // Don't done in expression visitor Expression *genList = InputPropertyExpression::make(pool, pathAlias); @@ -1192,7 +1192,6 @@ Status MatchValidator::validatePathInWhere( NG_RETURN_IF_ERROR(checkMatchPathExpr(pred, availableAliases)); // Build path alias auto pathAlias = pred->toString(); - pred->setAlias(new std::string(pathAlias)); paths.emplace_back(); NG_RETURN_IF_ERROR(validatePath(pred, paths.back())); NG_RETURN_IF_ERROR(buildRollUpPathInfo(pred, paths.back())); @@ -1216,7 +1215,6 @@ Status MatchValidator::validatePathInWhere( // Build path alias auto &matchPath = matchPathExprImpl->matchPath(); auto pathAlias = matchPath.toString(); - matchPath.setAlias(new std::string(pathAlias)); if (matchPathExprImpl->genList() == nullptr) { // Don't done in expression visitor Expression *genList = InputPropertyExpression::make(pool, pathAlias); @@ -1289,19 +1287,26 @@ Status MatchValidator::validatePathInWhere( } /*static*/ Status MatchValidator::buildRollUpPathInfo(const MatchPath *path, Path &pathInfo) { - DCHECK(!DCHECK_NOTNULL(path->alias())->empty()); for (const auto &node : path->nodes()) { // The inner variable of expression will be replaced by anno variable - if (!node->alias().empty() && node->alias()[0] != '_') { - pathInfo.compareVariables.emplace_back(node->alias()); + const auto &nodeAlias = node->alias(); + if (!nodeAlias.empty() && !AnonVarGenerator::isAnnoVar(nodeAlias)) { + pathInfo.compareVariables.emplace_back(nodeAlias); } } for (const auto &edge : path->edges()) { - if (edge->alias()[0] != '_') { - pathInfo.compareVariables.emplace_back(edge->alias()); + const auto &edgeAlias = edge->alias(); + if (!edgeAlias.empty() && !AnonVarGenerator::isAnnoVar(edgeAlias)) { + if (edge->range()) { + return Status::SemanticError( + "Variable '%s` 's type is edge list. not support used in multiple patterns " + "simultaneously.", + edgeAlias.c_str()); + } + pathInfo.compareVariables.emplace_back(edgeAlias); } } - pathInfo.collectVariable = *path->alias(); + pathInfo.collectVariable = path->toString(); pathInfo.rollUpApply = true; return Status::OK(); } diff --git a/tests/tck/features/match/PathExpr.feature b/tests/tck/features/match/PathExpr.feature index 369db9b86b4..c419d377f79 100644 --- a/tests/tck/features/match/PathExpr.feature +++ b/tests/tck/features/match/PathExpr.feature @@ -326,7 +326,7 @@ Feature: Basic match MATCH (v:player{name:"Tim Duncan"})-[e]->(t) WHERE (v)-[e]->(t:team) RETURN (v)-->() """ Then the result should be, in any order: - | (v)-->() = (v)-->() | + | (v)-->() | | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2015}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2001}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2002}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | # Fix issue https://github.com/vesoft-inc/nebula/issues/4792 When executing query: @@ -334,8 +334,8 @@ Feature: Basic match MATCH (v:player{name:"Tim Duncan"})-[e]->(t) WHERE (v)-[e]->(t:team) RETURN v, size((v)-->()), (v)-->() """ Then the result should be, in any order: - | v | size((v)-->() = (v)-->()) | (v)-->() = (v)-->() | - | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | 7 | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2015}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2001}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2002}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | + | v | size((v)-->()) | (v)-->() | + | ("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"}) | 7 | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2015}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2001}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:teammate@0 {end_year: 2016, start_year: 2002}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>, <("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | Scenario: In Unwind When executing query: @@ -363,7 +363,7 @@ Feature: Basic match | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})> | | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})> | - Scenario: pattern in where + Scenario: pattern in where or return When executing query: """ MATCH (v:player)-[e]->(b) @@ -413,6 +413,42 @@ Feature: Basic match | [[:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}], [:like "Manu Ginobili"->"Tim Duncan" @0 {likeness: 90}]] | | [[:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}], [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}]] | | [[:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}], [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}]] | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like]-(n) + RETURN ()-[e:like]-(n) + """ + Then the result should be, in any order: + | ()-[e:like]-(n) | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 80}]-("Aron Baynes" :player{age: 32, name: "Aron Baynes"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 80}]-("Boris Diaw" :player{age: 36, name: "Boris Diaw"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 70}]-("Danny Green" :player{age: 31, name: "Danny Green"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 99}]-("Dejounte Murray" :player{age: 29, name: "Dejounte Murray"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 75}]-("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 90}]-("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 55}]-("Marco Belinelli" :player{age: 32, name: "Marco Belinelli"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 80}]-("Shaquille O'Neal" :player{age: 47, name: "Shaquille O'Neal"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 80}]-("Tiago Splitter" :player{age: 34, name: "Tiago Splitter"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>] | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like]->(n) + RETURN ()-[e:like]->(n) + """ + Then the result should be, in any order: + | ()-[e:like]->(n) | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})>] | + | [<("Tim Duncan" :player{age: 42, name: "Tim Duncan"} :bachelor{name: "Tim Duncan", speciality: "psychology"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})>] | + + Scenario: list join not support + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[e:like*2]->(n) + RETURN ()-[e:like*2]->(n) + """ + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. When executing query: """ MATCH (v:player{name: 'Tim Duncan'})-[e:like*3]->(n), (t:team {name: "Spurs"}) @@ -420,30 +456,69 @@ Feature: Basic match UNWIND [n in ns | ()-[e*3]->(n:player)] AS p RETURN p """ - Then the result should be, in any order: - | p | - | [] | - | [] | - | [] | - | [] | - | [] | + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. When executing query: """ MATCH (v:player)-[e:like*3]->(n) WHERE (n)-[e*3]->(:player) RETURN v """ - Then the result should be, in any order: - | v | + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. When executing query: """ MATCH (v:player)-[e:like*1..3]->(n) WHERE (n)-[e*1..4]->(:player) return v """ - Then the result should be, in any order: - | v | + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. When executing query: """ MATCH (v:player)-[e:like*3]->(n) WHERE id(v)=="Tim Duncan" and (n)-[e*3]->(:player) return v """ - Then the result should be, in any order: - | v | + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. + When executing query: + """ + MATCH (v:player)-[e:like]->(n) WHERE (n)-[e*1..4]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: `e' is defined with type Edge, but referenced with type EdgeList + When executing query: + """ + MATCH (v:player)-[e:like*3]->(n) WHERE (n)-[e*1]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously. + When executing query: + """ + MATCH (v:player)-[e:like*1..3]->(n) WHERE (n)-[e]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: `e' is defined with type EdgeList, but referenced with type Edge + When executing query: + """ + MATCH (v:player)-[e:like]->(n), (n)-[e*1..4]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: `e': Redefined alias + When executing query: + """ + MATCH (v:player)-[e:like*3]->(n) MATCH (n)-[e*1]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: e binding to different type: Edge vs EdgeList + When executing query: + """ + MATCH (v:player)-[e:like*1..3]->(n) MATCH (n)-[e]->(:player) RETURN v + """ + Then a SemanticError should be raised at runtime: e binding to different type: Edge vs EdgeList + When executing query: + """ + MATCH (v:player)-[e:like]->(n) WITH (n)-[e*1..4]->(:player) AS v RETURN v + """ + Then a SemanticError should be raised at runtime: `e' is defined with type Edge, but referenced with type EdgeList + When executing query: + """ + MATCH (v:player)-[e:like*3]->(n) UNWIND (n)-[e*1]->(:player) as v RETURN v + """ + Then a SemanticError should be raised at runtime: Variable `v` already declared + When executing query: + """ + MATCH (v:player)-[e:like*1..3]->(n) + WITH collect(n) as ns, e as e + WITH ns[0] AS n,e AS e + MATCH (n)-[e]->(v:player) RETURN v + """ + Then a SemanticError should be raised at runtime: e binding to different type: Edge vs EdgeList diff --git a/tests/tck/features/match/PathExprRefLocalVariable.feature b/tests/tck/features/match/PathExprRefLocalVariable.feature index 5790242cdd7..8483198249d 100644 --- a/tests/tck/features/match/PathExprRefLocalVariable.feature +++ b/tests/tck/features/match/PathExprRefLocalVariable.feature @@ -238,6 +238,4 @@ Feature: Path expression reference local defined variables """ MATCH (v:player{name: 'Tim Duncan'})-[e:like*1..3]->(n), (t:team {name: "Spurs"}) WITH v, e, collect(distinct n) AS ns UNWIND [n in ns | ()-[e*2..4]->(n:player)] AS p RETURN count(p) AS count """ - Then the result should be, in any order: - | count | - | 11 | + Then a SemanticError should be raised at runtime: Variable 'e` 's type is edge list. not support used in multiple patterns simultaneously.