diff --git a/src/planner/match/WhereClausePlanner.cpp b/src/planner/match/WhereClausePlanner.cpp index 71f98bf90..cb09ec1f2 100644 --- a/src/planner/match/WhereClausePlanner.cpp +++ b/src/planner/match/WhereClausePlanner.cpp @@ -21,7 +21,7 @@ StatusOr WhereClausePlanner::transform(CypherClauseContextBase* ctx) { if (wctx->filter) { SubPlan wherePlan; auto* newFilter = MatchSolver::doRewrite(wctx->qctx, *wctx->aliasesUsed, wctx->filter); - wherePlan.root = Filter::make(wctx->qctx, nullptr, newFilter, true); + wherePlan.root = Filter::make(wctx->qctx, nullptr, newFilter, needStableFilter_); wherePlan.tail = wherePlan.root; return wherePlan; diff --git a/src/planner/match/WhereClausePlanner.h b/src/planner/match/WhereClausePlanner.h index e4d5913ca..82a14fafb 100644 --- a/src/planner/match/WhereClausePlanner.h +++ b/src/planner/match/WhereClausePlanner.h @@ -16,9 +16,14 @@ namespace graph { */ class WhereClausePlanner final : public CypherClausePlanner { public: - WhereClausePlanner() = default; + explicit WhereClausePlanner(bool needStableFilter = false) + : needStableFilter_(needStableFilter) {} StatusOr transform(CypherClauseContextBase* clauseCtx) override; + +private: + // `needStableFilter_=true` only if there is orderBy in withClause(to avoid unstableErase) + bool needStableFilter_{false}; }; } // namespace graph } // namespace nebula diff --git a/src/planner/match/WithClausePlanner.cpp b/src/planner/match/WithClausePlanner.cpp index 0d796519d..42d65eb29 100644 --- a/src/planner/match/WithClausePlanner.cpp +++ b/src/planner/match/WithClausePlanner.cpp @@ -53,7 +53,9 @@ Status WithClausePlanner::buildWith(WithClauseContext* wctx, SubPlan& subPlan) { } if (wctx->where != nullptr) { - auto wherePlan = std::make_unique()->transform(wctx->where.get()); + bool needStableFilter = wctx->order ? true : false; + auto wherePlan = + std::make_unique(needStableFilter)->transform(wctx->where.get()); NG_RETURN_IF_ERROR(wherePlan); auto plan = std::move(wherePlan).value(); SegmentsConnector::addInput(plan.tail, subPlan.root, true); diff --git a/src/planner/plan/Query.cpp b/src/planner/plan/Query.cpp index 4004eafb7..00e59bdf9 100644 --- a/src/planner/plan/Query.cpp +++ b/src/planner/plan/Query.cpp @@ -203,6 +203,7 @@ Filter::Filter(QueryContext* qctx, PlanNode* input, Expression* condition, bool std::unique_ptr Filter::explain() const { auto desc = SingleInputNode::explain(); addDescription("condition", condition_ ? condition_->toString() : "", desc.get()); + addDescription("isStable", needStableFilter_ ? "true" : "false", desc.get()); return desc; } diff --git a/tests/tck/features/match/With.feature b/tests/tck/features/match/With.feature index 1d90ee180..2cbed4a5d 100644 --- a/tests/tck/features/match/With.feature +++ b/tests/tck/features/match/With.feature @@ -121,9 +121,7 @@ Feature: With clause | "hello" | 1 | 1 | | "hello" | 2 | 2 | | "hello" | 3 | 3 | - - Scenario: match with return - When executing query: + When profiling query: """ MATCH (v:player) WITH v.age AS age, v AS v, v.name AS name @@ -154,6 +152,19 @@ Feature: With clause | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | 36 | | ("Carmelo Anthony" :player{age: 34, name: "Carmelo Anthony"}) | 34 | | ("LeBron James" :player{age: 34, name: "LeBron James"}) | 34 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 13 | Project | 12 | | + | 12 | Filter | 17 | {"isStable": "true"} | + | 17 | TopN | 9 | | + | 9 | Project | 8 | | + | 8 | Filter | 7 | {"isStable": "false"} | + | 7 | Project | 6 | | + | 6 | Project | 5 | | + | 5 | Filter | 16 | {"isStable": "false"} | + | 16 | GetVertices | 1 | | + | 1 | IndexScan | 0 | | + | 0 | Start | | | Scenario: with exists When executing query: