diff --git a/src/graph/planner/SequentialPlanner.cpp b/src/graph/planner/SequentialPlanner.cpp index 9e0690f1e3f..f7095d5fec6 100644 --- a/src/graph/planner/SequentialPlanner.cpp +++ b/src/graph/planner/SequentialPlanner.cpp @@ -24,6 +24,10 @@ StatusOr SequentialPlanner::transform(AstContext* astCtx) { subPlan.root = validators.back()->root(); ifBuildDataCollect(subPlan, qctx); for (auto iter = validators.begin(); iter < validators.end() - 1; ++iter) { + // Remove left tail kStart plannode before append plan. + // It allows that kUse sentence to append kMatch Sentence. + // For example: Use ...; Match ... + rmLeftTailStartNode((iter + 1)->get(), iter->get()->sentence()->kind()); NG_RETURN_IF_ERROR((iter + 1)->get()->appendPlan(iter->get()->root())); } if (validators.front()->tail()->isSingleInput()) { @@ -59,5 +63,27 @@ void SequentialPlanner::ifBuildDataCollect(SubPlan& subPlan, QueryContext* qctx) break; } } + +// When appending plans, it need to remove left tail plannode. +// Because the left tail plannode is StartNode which needs to be removed, +// and remain one size for add dependency +// TODO: It's a temporary solution, remove it after Execute multiple sequences one by one. +void SequentialPlanner::rmLeftTailStartNode(Validator* validator, Sentence::Kind appendPlanKind) { + if (appendPlanKind != Sentence::Kind::kUse || + validator->tail()->kind() != PlanNode::Kind::kStart || + validator->root()->dependencies().size() == 0UL) { + return; + } + + PlanNode* node = validator->root(); + while (node->dependencies()[0]->dependencies().size() > 0UL) { + node = const_cast(node->dependencies()[0]); + } + if (node->dependencies().size() == 1UL) { + // Remain one size for add dependency + node->dependencies()[0] = nullptr; + validator->setTail(node); + } +} } // namespace graph } // namespace nebula diff --git a/src/graph/planner/SequentialPlanner.h b/src/graph/planner/SequentialPlanner.h index 656bede413e..a37efdf7e97 100644 --- a/src/graph/planner/SequentialPlanner.h +++ b/src/graph/planner/SequentialPlanner.h @@ -8,6 +8,7 @@ #include "graph/context/QueryContext.h" #include "graph/planner/Planner.h" +#include "graph/validator/Validator.h" namespace nebula { namespace graph { @@ -27,6 +28,8 @@ class SequentialPlanner final : public Planner { void ifBuildDataCollect(SubPlan& subPlan, QueryContext* qctx); + void rmLeftTailStartNode(Validator* validator, Sentence::Kind appendPlanKind); + private: SequentialPlanner() = default; }; diff --git a/src/graph/planner/plan/PlanNode.h b/src/graph/planner/plan/PlanNode.h index 4db19bdadf0..4948c473a3c 100644 --- a/src/graph/planner/plan/PlanNode.h +++ b/src/graph/planner/plan/PlanNode.h @@ -238,6 +238,10 @@ class PlanNode { return dependencies_; } + auto& dependencies() { + return dependencies_; + } + const PlanNode* dep(size_t index = 0) const { DCHECK_LT(index, dependencies_.size()); return dependencies_.at(index); diff --git a/src/graph/validator/SequentialValidator.cpp b/src/graph/validator/SequentialValidator.cpp index 6ec59785523..f7ffe9d440c 100644 --- a/src/graph/validator/SequentialValidator.cpp +++ b/src/graph/validator/SequentialValidator.cpp @@ -48,5 +48,6 @@ const Sentence* SequentialValidator::getFirstSentence(const Sentence* sentence) auto pipe = static_cast(sentence); return getFirstSentence(pipe->left()); } + } // namespace graph } // namespace nebula diff --git a/src/graph/validator/Validator.cpp b/src/graph/validator/Validator.cpp index 5aa074d892c..03f8d48867d 100644 --- a/src/graph/validator/Validator.cpp +++ b/src/graph/validator/Validator.cpp @@ -8,6 +8,7 @@ #include #include "common/function/FunctionManager.h" +#include "graph/planner/plan/PlanNode.h" #include "graph/planner/plan/Query.h" #include "graph/util/ExpressionUtils.h" #include "graph/util/SchemaUtil.h" @@ -299,8 +300,9 @@ std::vector Validator::getOutColNames() const { Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { DCHECK(node != nullptr); DCHECK(appended != nullptr); + if (!node->isSingleInput()) { - return Status::SemanticError("%s not support to append an input.", + return Status::SemanticError("PlanNode(%s) not support to append an input.", PlanNode::toString(node->kind())); } static_cast(node)->dependsOn(appended); diff --git a/src/graph/validator/Validator.h b/src/graph/validator/Validator.h index b1206769fc8..d7bbcb516de 100644 --- a/src/graph/validator/Validator.h +++ b/src/graph/validator/Validator.h @@ -87,6 +87,10 @@ class Validator { exprProps_ = exprProps; } + void setTail(PlanNode* tail) { + tail_ = tail; + } + const std::set& userDefinedVarNameList() const { return userDefinedVarNameList_; } diff --git a/tests/tck/features/match/Base.feature b/tests/tck/features/match/Base.feature index 13573b18c47..c5b99b14ad5 100644 --- a/tests/tck/features/match/Base.feature +++ b/tests/tck/features/match/Base.feature @@ -699,3 +699,13 @@ Feature: Basic match Then the result should be, in any order: | v | | ("Tim Duncan":bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + + Scenario: Sequential sentence + When executing query: + """ + USE nba; + MATCH (n:player) RETURN n LIMIT 1; + """ + Then the result should be, in any order: + | n | + | ("Boris Diaw" :player{age: 36, name: "Boris Diaw"}) | diff --git a/tests/tck/features/match/MultiLineMultiQueryParts.feature b/tests/tck/features/match/MultiLineMultiQueryParts.feature new file mode 100644 index 00000000000..63aaf7e4899 --- /dev/null +++ b/tests/tck/features/match/MultiLineMultiQueryParts.feature @@ -0,0 +1,225 @@ +# Copyright (c) 2021 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License. +Feature: Multi Line Multi Query Parts + + Background: + Given a graph with space named "nba" + + Scenario: Multi Line Multi Path Patterns + When executing query: + """ + USE nba; + MATCH (m)-[]-(n), (n)-[]-(l) WHERE id(m)=="Tim Duncan" + RETURN m.player.name AS n1, n.player.name AS n2, + CASE WHEN l.team.name is not null THEN l.team.name + WHEN l.player.name is not null THEN l.player.name ELSE "null" END AS n3 ORDER BY n1, n2, n3 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | + | "Tim Duncan" | "Aron Baynes" | "Tim Duncan" | + | "Tim Duncan" | "Boris Diaw" | "Hawks" | + | "Tim Duncan" | "Boris Diaw" | "Hornets" | + | "Tim Duncan" | "Boris Diaw" | "Jazz" | + | "Tim Duncan" | "Boris Diaw" | "Spurs" | + | "Tim Duncan" | "Boris Diaw" | "Suns" | + | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | + When executing query: + """ + USE nba; + MATCH (m)-[]-(n), (n)-[]-(l) WHERE id(n)=="Tim Duncan" + RETURN m.player.name AS n1, n.player.name AS n2, l.player.name AS n3 ORDER BY n1, n2, n3 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | + | "Aron Baynes" | "Tim Duncan" | "Aron Baynes" | + | "Aron Baynes" | "Tim Duncan" | "Boris Diaw" | + | "Aron Baynes" | "Tim Duncan" | "Danny Green" | + | "Aron Baynes" | "Tim Duncan" | "Danny Green" | + | "Aron Baynes" | "Tim Duncan" | "Dejounte Murray" | + | "Aron Baynes" | "Tim Duncan" | "LaMarcus Aldridge" | + | "Aron Baynes" | "Tim Duncan" | "LaMarcus Aldridge" | + | "Aron Baynes" | "Tim Duncan" | "Manu Ginobili" | + | "Aron Baynes" | "Tim Duncan" | "Manu Ginobili" | + | "Aron Baynes" | "Tim Duncan" | "Manu Ginobili" | + When executing query: + """ + USE nba; + MATCH (m)-[]-(n), (n)-[]-(l), (l)-[]-(h) WHERE id(m)=="Tim Duncan" + RETURN m.player.name AS n1, n.player.name AS n2, l.team.name AS n3, h.player.name AS n4 + ORDER BY n1, n2, n3, n4 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | n4 | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Kyrie Irving" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Rajon Rondo" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Ray Allen" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Shaquile O'Neal" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Blake Griffin" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | + + Scenario: Multi Line Multi Match + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + MATCH (n)-[]-(l) + RETURN m.player.name AS n1, n.player.name AS n2, + CASE WHEN l.player.name is not null THEN l.player.name + WHEN l.team.name is not null THEN l.team.name ELSE "null" END AS n3 ORDER BY n1, n2, n3 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | + | "Tim Duncan" | "Aron Baynes" | "Tim Duncan" | + | "Tim Duncan" | "Boris Diaw" | "Hawks" | + | "Tim Duncan" | "Boris Diaw" | "Hornets" | + | "Tim Duncan" | "Boris Diaw" | "Jazz" | + | "Tim Duncan" | "Boris Diaw" | "Spurs" | + | "Tim Duncan" | "Boris Diaw" | "Suns" | + | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + MATCH (n)-[]-(l), (l)-[]-(h) + RETURN m.player.name AS n1, n.player.name AS n2, l.team.name AS n3, h.player.name AS n4 + ORDER BY n1, n2, n3, n4 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | n4 | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Kyrie Irving" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Rajon Rondo" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Ray Allen" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Shaquile O'Neal" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Blake Griffin" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + MATCH (n)-[]-(l) + MATCH (l)-[]-(h) + RETURN m.player.name AS n1, n.player.name AS n2, l.team.name AS n3, h.player.name AS n4 + ORDER BY n1, n2, n3, n4 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | n4 | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Kyrie Irving" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Rajon Rondo" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Ray Allen" | + | "Tim Duncan" | "Aron Baynes" | "Celtics" | "Shaquile O'Neal" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Blake Griffin" | + | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | + | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | + When executing query: + """ + USE nba; + MATCH (v:player{name:"Tony Parker"}) + WITH v AS a + MATCH p=(o:player{name:"Tim Duncan"})-[]->(a) + RETURN o.player.name + """ + Then the result should be, in order: + | o.player.name | + | "Tim Duncan" | + | "Tim Duncan" | + + Scenario: Multi Line Optional Match + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + OPTIONAL MATCH (n)<-[:serve]-(l) + RETURN m.player.name AS n1, n.player.name AS n2, l AS n3 ORDER BY n1, n2, n3 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | n3 | + | "Tim Duncan" | "Aron Baynes" | NULL | + | "Tim Duncan" | "Boris Diaw" | NULL | + | "Tim Duncan" | "Danny Green" | NULL | + | "Tim Duncan" | "Danny Green" | NULL | + | "Tim Duncan" | "Dejounte Murray" | NULL | + | "Tim Duncan" | "LaMarcus Aldridge" | NULL | + | "Tim Duncan" | "LaMarcus Aldridge" | NULL | + | "Tim Duncan" | "Manu Ginobili" | NULL | + | "Tim Duncan" | "Manu Ginobili" | NULL | + | "Tim Duncan" | "Manu Ginobili" | NULL | + + Scenario: Multi Line Multi Query Parts + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + WITH n, n.player.name AS n1 ORDER BY n1 LIMIT 10 + MATCH (n)-[]-(l) + RETURN n.player.name AS n1, + CASE WHEN l.player.name is not null THEN l.player.name + WHEN l.team.name is not null THEN l.team.name ELSE "null" END AS n2 ORDER BY n1, n2 LIMIT 10 + """ + Then the result should be, in order: + | n1 | n2 | + | "Aron Baynes" | "Celtics" | + | "Aron Baynes" | "Pistons" | + | "Aron Baynes" | "Spurs" | + | "Aron Baynes" | "Tim Duncan" | + | "Boris Diaw" | "Hawks" | + | "Boris Diaw" | "Hornets" | + | "Boris Diaw" | "Jazz" | + | "Boris Diaw" | "Spurs" | + | "Boris Diaw" | "Suns" | + | "Boris Diaw" | "Tim Duncan" | + When executing query: + """ + USE nba; + MATCH (m:player{name:"Tim Duncan"})-[:like]-(n)--() + WITH m,count(*) AS lcount + MATCH (m)--(n) + RETURN count(*) AS scount, lcount + """ + Then the result should be, in order: + | scount | lcount | + | 19 | 110 | + When executing query: + """ + USE nba; + MATCH (m:player{name:"Tim Duncan"})-[:like]-(n)--() + WITH m,n + MATCH (m)--(n) + RETURN count(*) AS scount + """ + Then the result should be, in order: + | scount | + | 270 | + + Scenario: Multi Line Some Erros + When executing query: + """ + USE nba; + MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" + WITH n, n.player.name AS n1 ORDER BY n1 LIMIT 10 + RETURN m + """ + Then a SemanticError should be raised at runtime: Alias used but not defined: `m' + When executing query: + """ + USE nba; + MATCH (v:player)-[e]-(v:team) RETURN v, e + """ + Then a SemanticError should be raised at runtime: `v': Redefined alias in a single path pattern. diff --git a/tests/tck/features/match/MultiQueryParts.feature b/tests/tck/features/match/MultiQueryParts.feature index 07cdc177de6..2a5cda28864 100644 --- a/tests/tck/features/match/MultiQueryParts.feature +++ b/tests/tck/features/match/MultiQueryParts.feature @@ -26,25 +26,6 @@ Feature: Multi Query Parts | "Tim Duncan" | "Boris Diaw" | "Spurs" | | "Tim Duncan" | "Boris Diaw" | "Suns" | | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | - When executing query: - """ - MATCH (m)-[]-(n), (l)-[]-(n) WHERE id(m)=="Tim Duncan" - RETURN m.player.name AS n1, n.player.name AS n2, - CASE WHEN l.team.name is not null THEN l.team.name - WHEN l.player.name is not null THEN l.player.name ELSE "null" END AS n3 ORDER BY n1, n2, n3 LIMIT 10 - """ - Then the result should be, in order: - | n1 | n2 | n3 | - | "Tim Duncan" | "Aron Baynes" | "Celtics" | - | "Tim Duncan" | "Aron Baynes" | "Pistons" | - | "Tim Duncan" | "Aron Baynes" | "Spurs" | - | "Tim Duncan" | "Aron Baynes" | "Tim Duncan" | - | "Tim Duncan" | "Boris Diaw" | "Hawks" | - | "Tim Duncan" | "Boris Diaw" | "Hornets" | - | "Tim Duncan" | "Boris Diaw" | "Jazz" | - | "Tim Duncan" | "Boris Diaw" | "Spurs" | - | "Tim Duncan" | "Boris Diaw" | "Suns" | - | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | When executing query: """ MATCH (m)-[]-(n), (n)-[]-(l) WHERE id(n)=="Tim Duncan" @@ -80,7 +61,7 @@ Feature: Multi Query Parts | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | - # Below scenario is not suppoted for the execution plan has a scan. + # Below scenario is not supported for the execution plan has a scan. When executing query: """ MATCH (m)-[]-(n), (a)-[]-(c) WHERE id(m)=="Tim Duncan" @@ -179,7 +160,7 @@ Feature: Multi Query Parts | "Tim Duncan" | "Manu Ginobili" | NULL | | "Tim Duncan" | "Manu Ginobili" | NULL | | "Tim Duncan" | "Manu Ginobili" | NULL | - # Below scenario is not suppoted for the execution plan has a scan. + # Below scenario is not supported for the execution plan has a scan. When executing query: """ MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" @@ -230,7 +211,7 @@ Feature: Multi Query Parts Then the result should be, in order: | scount | | 270 | - # Below scenario is not suppoted for the execution plan has a scan. + # Below scenario is not supported for the execution plan has a scan. When executing query: """ MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan"