Skip to content

planner: backport partial order TopN optimization#68995

Open
winoros wants to merge 1 commit into
pingcap:release-8.5from
winoros:backport-partial-order-topn-release-8.5
Open

planner: backport partial order TopN optimization#68995
winoros wants to merge 1 commit into
pingcap:release-8.5from
winoros:backport-partial-order-topn-release-8.5

Conversation

@winoros
Copy link
Copy Markdown
Member

@winoros winoros commented Jun 5, 2026

What problem does this PR solve?

Issue Number: ref #63280, ref #65813

Problem Summary:

Backport the partial ordered index / TopN optimizer changes to release-8.5.

Source PRs:

This also updates github.com/pingcap/tipb to f9f651ef5fbc4dcf31c452ab701ca3f67c6c99a8, which contains the Limit executor field needed by the optimizer backport.

What changed and how does it work?

This is a manual reconstruction from the source PRs instead of a direct cherry-pick, because release-8.5 still uses the older planner physical operator layout and the merge commits conflict heavily with the current master layout.

  • Add partial-order physical property matching for prefix indexes.
  • Generate a CopMultiReadTaskType TopN alternative carrying partial-order requirements.
  • Carry the prefix-column match result through index access path selection and CopTask.
  • Push a partial-order Limit to the cop index plan when possible, and keep the root TopN.
  • Support ORDER_INDEX / NO_ORDER_INDEX behavior with partial order.
  • Fix the pruned-column regression from planner, partial_order: fix index out of range error when query has pruned column #66268.
  • Change tidb_opt_partial_ordered_index_for_topn to enum values DISABLE / COST.
  • Add planner integration coverage for the optimizer path and bugfix regressions.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Commands:

make failpoint-enable
go test -tags intest ./pkg/planner/core -run TestSetVarPartialOrderedIndexForTopN -count=1
go test -tags intest ./pkg/sessionctx/variable -run 'TestTiDBOptPartialOrderedIndexForTopN|TestTiDBOptPartialOrderedIndexForTopNSessionAndGlobal' -count=1
make failpoint-disable
make bazel_prepare
cd tests/integrationtest && ./run-tests.sh -r planner/core/partial_order_topn
cd tests/integrationtest && ./run-tests.sh -b n -t planner/core/partial_order_topn
make lint

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

Support partial ordered index optimization for TopN queries when tidb_opt_partial_ordered_index_for_topn is set to COST.

Summary by CodeRabbit

  • New Features

    • Partial-ordered index optimization for TOP N: faster ORDER BY … LIMIT when using compatible prefix indexes; EXPLAIN now can show prefix_col and prefix_len metadata.
  • Configuration Changes

    • System variable tidb_opt_partial_ordered_index_for_topn converted to enum with values DISABLE (default) and COST.
  • Tests

    • New integration and unit tests added to validate partial-order TopN behavior and sysvar semantics.

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 5, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@ti-chi-bot ti-chi-bot Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/cherry-pick-not-approved do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. labels Jun 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d6f10880-7b19-4f4d-ac96-306513e88c95

📥 Commits

Reviewing files that changed from the base of the PR and between 813b81e and c0b9059.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (22)
  • DEPS.bzl
  • go.mod
  • pkg/planner/core/casetest/rule/BUILD.bazel
  • pkg/planner/core/exhaust_physical_plans.go
  • pkg/planner/core/explain.go
  • pkg/planner/core/find_best_task.go
  • pkg/planner/core/hint_test.go
  • pkg/planner/core/operator/logicalop/logical_projection.go
  • pkg/planner/core/physical_plans.go
  • pkg/planner/core/plan_clone_generated.go
  • pkg/planner/core/plan_to_pb.go
  • pkg/planner/core/task.go
  • pkg/planner/core/task_base.go
  • pkg/planner/property/physical_property.go
  • pkg/planner/util/path.go
  • pkg/sessionctx/variable/session.go
  • pkg/sessionctx/variable/session_test.go
  • pkg/sessionctx/variable/sysvar.go
  • pkg/sessionctx/variable/sysvar_test.go
  • pkg/sessionctx/variable/tidb_vars.go
  • tests/integrationtest/r/planner/core/partial_order_topn.result
  • tests/integrationtest/t/planner/core/partial_order_topn.test
✅ Files skipped from review due to trivial changes (2)
  • pkg/planner/core/casetest/rule/BUILD.bazel
  • tests/integrationtest/r/planner/core/partial_order_topn.result
🚧 Files skipped from review as they are similar to previous changes (19)
  • pkg/planner/core/plan_clone_generated.go
  • go.mod
  • pkg/planner/core/task_base.go
  • pkg/planner/core/operator/logicalop/logical_projection.go
  • pkg/planner/core/explain.go
  • pkg/planner/core/hint_test.go
  • pkg/sessionctx/variable/sysvar.go
  • pkg/sessionctx/variable/session.go
  • pkg/planner/util/path.go
  • pkg/planner/core/plan_to_pb.go
  • pkg/planner/core/physical_plans.go
  • DEPS.bzl
  • pkg/sessionctx/variable/session_test.go
  • pkg/sessionctx/variable/tidb_vars.go
  • pkg/planner/core/exhaust_physical_plans.go
  • pkg/planner/property/physical_property.go
  • tests/integrationtest/t/planner/core/partial_order_topn.test
  • pkg/planner/core/find_best_task.go
  • pkg/sessionctx/variable/sysvar_test.go

📝 Walkthrough

Walkthrough

Adds partial-ordered-index TopN support: converts the session/sysvar to DISABLE/COST, extends PhysicalProperty with partial-order metadata and matching, integrates prefix-index matching into candidate selection and keep-order logic, pushes limits into matched index plans, updates plan cloning/explain/serialization, and adds integration tests.

Changes

Partial Ordered Index TopN Optimization

Layer / File(s) Summary
Session variable configuration and enum conversion
pkg/sessionctx/variable/session.go, pkg/sessionctx/variable/sysvar.go, pkg/sessionctx/variable/tidb_vars.go, pkg/sessionctx/variable/session_test.go, pkg/sessionctx/variable/sysvar_test.go, pkg/planner/core/hint_test.go
OptPartialOrderedIndexForTopN changed from boolean to string enum with values DISABLE/COST. Added IsPartialOrderedIndexForTopNEnabled() and updated sysvar validation, storage, hints, and tests.
Physical property types and order-keeping helpers
pkg/planner/property/physical_property.go
Add PartialOrderInfo and PartialOrderMatchResult; implement NeedKeepOrder, GetSortDescForKeepOrder, GetSortItemsForKeepOrder; include partial-order sort items in HashCode and clone essential fields.
TopN/Limit prefix fields and access path tracking
pkg/planner/core/physical_plans.go, pkg/planner/util/path.go, pkg/planner/core/task_base.go
Add PrefixCol and PrefixLen to PhysicalTopN/PhysicalLimit, ForcePartialOrder to AccessPath, and partialOrderMatchResult to CopTask; update memory usage accounting and cloning.
Partial order plan candidate generation
pkg/planner/core/exhaust_physical_plans.go
getPhysTopN can emit additional partial-order PhysicalTopN candidates when the child tree matches the restricted DataSource/Selection/Projection pattern; new helpers validate pattern and build candidate with PartialOrderInfo.
Partial order sort item transformation
pkg/planner/core/operator/logicalop/logical_projection.go
TryToGetChildProp now rewrites both SortItems and PartialOrderInfo.SortItems through projection; new helpers fail on scalar-function projections.
Partial order matching and candidate selection
pkg/planner/core/find_best_task.go
Cache partialOrderMatchResult per candidate, implement matchPartialOrderProperty to detect prefix-index matches, integrate matching into skyline pruning, and precompute match results for index candidates.
Index scan generation with keep-order refactoring
pkg/planner/core/find_best_task.go
Refactor keep-order logic to use prop.NeedKeepOrder() and related helpers; reject forced-partial-order candidates without PartialOrderInfo; store matched result on CopTask; derive ByItems and scan direction via keep-order helpers.
TopN partial-order task execution
pkg/planner/core/task.go
PhysicalTopN.Attach2Task routes matched partial-order CopTasks to handlePartialOrderTopN, which sets PrefixCol/PrefixLen, optionally pushes a PhysicalLimit into the index plan, converts to root task, and attaches the result.
Plan output: cloning, explain, and protobuf
pkg/planner/core/plan_clone_generated.go, pkg/planner/core/explain.go, pkg/planner/core/plan_to_pb.go
Deep-clone PrefixCol in plan cache clones; include prefix_col/prefix_len in ExplainInfo when set; serialize PrefixCol into Limit protobuf as TruncateKeyExpr when present.
Integration test suite
tests/integrationtest/t/planner/core/partial_order_topn.test, tests/integrationtest/r/planner/core/partial_order_topn.result
Add comprehensive SQL and golden tests covering prefix/non-prefix indexes, hints, unsupported cases, transactions, edge cases, and session-variable semantics under DISABLE/COST.

Dependency Updates and Test Infrastructure

Layer / File(s) Summary
tipb dependency version bump
DEPS.bzl, go.mod
Update pinned github.com/pingcap/tipb pseudo-version, Bazel SHA/strip_prefix and artifact URLs.
Test shard count increase
pkg/planner/core/casetest/rule/BUILD.bazel
Increase rule_test shard_count from 12 to 13.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pingcap/tidb#68755: Related changes to tidb_opt_partial_ordered_index_for_topn session/sysvar handling and hint behavior.

Suggested labels

ok-to-test, needs-cherry-pick-release-8.5, type/cherry-pick-for-release-8.5

Suggested reviewers

  • qw4990
  • terry1purcell
  • AilinKid
  • yudongusa

Poem

🐇 In burrows of code I softly hop,

Prefix columns lead the TopN crop.
DISABLE or COST the session sings,
Indexes trim their ordering strings.
Hooray — the planner finds its hop!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change—backporting a partial order TopN optimization feature to release-8.5—and is directly related to the changeset.
Description check ✅ Passed The description includes issue references, a clear problem statement about backporting optimizer changes, detailed explanation of what changed and how, completed test checklist with specific test commands, documentation checkboxes marked for variable changes and experimental features, and a properly formatted release note.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ti-chi-bot ti-chi-bot Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jun 5, 2026
@tiprow
Copy link
Copy Markdown

tiprow Bot commented Jun 5, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@ti-chi-bot ti-chi-bot Bot added the sig/planner SIG: Planner label Jun 5, 2026
@winoros winoros marked this pull request as ready for review June 5, 2026 09:44
@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 5, 2026
@winoros winoros force-pushed the backport-partial-order-topn-release-8.5 branch from 813b81e to c0b9059 Compare June 5, 2026 09:49
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
DEPS.bzl (1)

5974-5980: Update tipb dependency: go.mod matches and Bazel metadata updates are present

  • DEPS.bzl updates github.com/pingcap/tipb to v0.0.0-20260605083900-f9f651ef5fbc, and go.mod contains the same version.
  • The same change set includes BUILD.bazel updates alongside DEPS.bzl, consistent with generated Bazel metadata (i.e., make bazel_prepare output).
  • Ensure compilation/tests cover any tipb API changes from commit f9f651ef5fbc (e.g., ExchangeType usage and the referenced “Limit executor field”).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DEPS.bzl` around lines 5974 - 5980, The tipb version bump may change
generated protobuf types (e.g., enum/field renames like ExchangeType or addition
of a Limit executor field); compile and run unit/integration tests (and run the
Bazel prep step used by this repo) to surface errors, then search the codebase
for references to tipb.ExchangeType and any places constructing/examining
executor types or limit fields and update call sites to the new enum names/field
accessors or message layout produced by the updated proto; ensure any BUILD/DEPS
metadata is consistent with the new generated code and re-run tests to verify
all API changes (ExchangeType usages and any Limit executor fields) are handled.
pkg/planner/core/exhaust_physical_plans.go (2)

2269-2272: 💤 Low value

Consider validating ByItems earlier to avoid unnecessary pattern checks.

When getPhysTopNWithPartialOrderProperty returns nil (because ByItems contain non-column expressions at lines 2351-2356), the earlier pattern validation in canUsePartialOrder4TopN becomes wasted work. Consider adding a ByItems column check to canUsePartialOrder4TopN:

♻️ Suggested refactor
 func canUsePartialOrder4TopN(lt *logicalop.LogicalTopN) bool {
 	if !lt.SCtx().GetSessionVars().IsPartialOrderedIndexForTopNEnabled() {
 		return false
 	}
 	if len(lt.ByItems) == 0 {
 		return false
 	}
+	// Partial order only supports column-based ordering
+	for _, item := range lt.ByItems {
+		if _, ok := item.Expr.(*expression.Column); !ok {
+			return false
+		}
+	}
 	return checkPartialOrderPattern(lt.Children()[0])
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/exhaust_physical_plans.go` around lines 2269 - 2272,
canUsePartialOrder4TopN currently runs expensive pattern checks even when
ByItems contain non-column expressions that will make
getPhysTopNWithPartialOrderProperty return nil; update canUsePartialOrder4TopN
to first validate lt.ByItems (or the ByItems slice used) contains only column
expressions (e.g., ColumnExpr) and return false early if any non-column
expression is found, so the later call to
getPhysTopNWithPartialOrderProperty(lt, prop) is avoided when it would
inevitably return nil.

2335-2346: 💤 Low value

Add documentation explaining the supported pattern.

The supported pattern (DataSource → Selection → Projection chain with single children) is not obvious from the code. Per coding guidelines, comments should explain non-obvious intent and constraints.

📝 Suggested documentation
+// checkPartialOrderPattern validates that the logical plan tree matches the pattern
+// supported by partial-order TopN optimization: a chain of DataSource, Selection,
+// and/or Projection operators, where each operator has at most one child.
 func checkPartialOrderPattern(plan base.LogicalPlan) bool {
 	switch p := plan.(type) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/exhaust_physical_plans.go` around lines 2335 - 2346, Add a
short documentation comment above the checkPartialOrderPattern function that
describes the exact supported pattern: a chain starting from
logicalop.DataSource optionally wrapped by a single-child
logicalop.LogicalSelection and/or a single-child logicalop.LogicalProjection
(i.e., DataSource → Selection → Projection where each wrapper must have exactly
one child). Mention the constraint that each LogicalSelection/LogicalProjection
must have len(Children()) == 1 and that any other node type returns false;
reference the function name checkPartialOrderPattern and the node types
logicalop.DataSource, logicalop.LogicalSelection, and
logicalop.LogicalProjection to make intent and limits clear.
pkg/planner/core/operator/logicalop/logical_projection.go (2)

601-613: ⚡ Quick win

Add documentation and defensive bounds checking.

Same issues as tryTransformSortItems: silently skips non-column/non-scalar-function expressions and lacks bounds checking.

🛡️ Suggested improvements
+// tryTransformSortItemPtrs transforms sort item pointers by mapping their columns
+// through the projection's expression list. Returns failure if any expression is a
+// ScalarFunction (cannot push down). Constants and other non-column expressions
+// are silently removed (sorting by constants is meaningless).
 func (p *LogicalProjection) tryTransformSortItemPtrs(items []*property.SortItem) ([]*property.SortItem, bool) {
 	newItems := make([]*property.SortItem, 0, len(items))
 	for _, item := range items {
 		idx := p.Schema().ColumnIndex(item.Col)
+		if idx < 0 {
+			// Column not found in schema; should not happen with valid properties
+			return nil, false
+		}
 		switch expr := p.Exprs[idx].(type) {
 		case *expression.Column:
 			newItems = append(newItems, &property.SortItem{Col: expr, Desc: item.Desc})
 		case *expression.ScalarFunction:
 			return nil, false
+		// Other types (Constant, CorrelatedColumn, etc.) are silently skipped
 		}
 	}
 	return newItems, true
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/operator/logicalop/logical_projection.go` around lines 601 -
613, In tryTransformSortItemPtrs, add defensive bounds checking on idx from
p.Schema().ColumnIndex(item.Col) (ensure 0 <= idx < len(p.Exprs)) and treat any
expression types other than *expression.Column and *expression.ScalarFunction as
a failure instead of silently skipping them; if the index is out of range or an
unexpected expr type is encountered, return nil, false—otherwise, for a Column
create the new SortItem as now and for a ScalarFunction return nil, false to
match tryTransformSortItems behavior.

587-599: ⚡ Quick win

Add documentation and defensive bounds checking.

The function silently skips non-column, non-scalar-function expressions (e.g., constants), which is non-obvious behavior. Additionally, ColumnIndex can return -1 if the column is not found, causing a panic.

🛡️ Suggested improvements
+// tryTransformSortItems transforms sort items by mapping their columns through
+// the projection's expression list. Returns failure if any expression is a
+// ScalarFunction (cannot push down). Constants and other non-column expressions
+// are silently removed (sorting by constants is meaningless).
 func (p *LogicalProjection) tryTransformSortItems(items []property.SortItem) ([]property.SortItem, bool) {
 	newItems := make([]property.SortItem, 0, len(items))
 	for _, item := range items {
 		idx := p.Schema().ColumnIndex(item.Col)
+		if idx < 0 {
+			// Column not found in schema; should not happen with valid properties
+			return nil, false
+		}
 		switch expr := p.Exprs[idx].(type) {
 		case *expression.Column:
 			newItems = append(newItems, property.SortItem{Col: expr, Desc: item.Desc})
 		case *expression.ScalarFunction:
 			return nil, false
+		// Other types (Constant, CorrelatedColumn, etc.) are silently skipped
 		}
 	}
 	return newItems, true
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/operator/logicalop/logical_projection.go` around lines 587 -
599, The tryTransformSortItems function currently can panic when
p.Schema().ColumnIndex returns -1 and silently ignores non-column/non-scalar
expressions; update tryTransformSortItems to first check idx :=
p.Schema().ColumnIndex(item.Col) and if idx < 0 return nil, false to avoid
panic, and treat any expression that is not *expression.Column as a
non-transformable case (i.e., return nil, false) instead of silently skipping
(handle the default case explicitly), and add a brief comment above
tryTransformSortItems describing that it only succeeds when every sort item maps
to a plain Column in p.Exprs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/planner/core/find_best_task.go`:
- Around line 1401-1415: The review points out that skylinePruning()/the loop is
mutating the shared AccessPath by setting path.ForcePartialOrder = true which
later causes convertToIndexScan() to reject paths when prop.PartialOrderInfo ==
nil; to fix, avoid persisting ForcePartialOrder on the shared
ds.PossibleAccessPaths: either operate on a shallow copy of the AccessPath or
use a candidate-local boolean (e.g., localForcePartialOrder) when
evaluating/constructing currentCandidate in getTableCandidate/skylinePruning
rather than writing to path.ForcePartialOrder, and if you must modify path,
ensure you clear/reset path.ForcePartialOrder before finishing each pruning pass
so convertToIndexScan() sees the original state.

In `@pkg/planner/core/physical_plans.go`:
- Around line 1331-1332: PhysicalTopN.MemoryUsage() and
PhysicalLimit.MemoryUsage() compute scalar sizes for new prefix metadata but
omit the memory cost of the PrefixCol object itself; update both methods to,
when PrefixCol is non-nil, add the result of PrefixCol.MemoryUsage() to the sum
(e.g., after computing sum use "if lt.PrefixCol != nil { sum +=
lt.PrefixCol.MemoryUsage() }" or the equivalent using the local
receiver/variable name) so the PrefixCol object's memory is included in total
accounting.

In `@pkg/planner/core/task_base.go`:
- Around line 264-265: CopTask.MemoryUsage currently omits the memory held by
the new partialOrderMatchResult field and still uses the old pointer count;
update the CopTask.MemoryUsage method to include the size of
partialOrderMatchResult (handle nil check) and account for its nested payload by
adding the estimated memory for property.PartialOrderMatchResult (including any
slices/maps/inner structs it owns) to the total, and increment the
pointer/object count accordingly so the reported memory reflects the nested
match-result payload.

In `@pkg/planner/core/task.go`:
- Around line 1112-1115: estimateMaxXForPartialOrder currently returns 0 causing
COST mode to under-estimate partial-order cop limits; update the block that
computes maxX/estimatedRows (where estimateMaxXForPartialOrder, maxX,
partialOrderedLimit, estimatedRows, copTask and childProfile are used) to use a
conservative fallback instead of zero: if estimateMaxXForPartialOrder yields a
non-positive or placeholder value, set maxX to the child plan's row count from
childProfile (e.g., the stats row count) or another conservative upper bound,
then compute estimatedRows = float64(partialOrderedLimit) + float64(maxX);
ensure the same change is applied at the other occurrence around lines 1130-1132
so DeriveLimitStats gets a conservative estimate.

In `@pkg/planner/property/physical_property.go`:
- Around line 291-295: Update PhysicalProperty.MemoryUsage to account for the
newly retained PartialOrderInfo: inside the MemoryUsage implementation
(function/method PhysicalProperty.MemoryUsage) check if p.PartialOrderInfo is
non-nil and include its memory footprint and the footprints of each entry in
p.PartialOrderInfo.SortItems (iterate SortItems and add per-item memory cost
similar to how other slice/field entries are counted). Ensure you include the
pointer/struct overhead for PartialOrderInfo and any per-sort-item allocations
so the total memory reflects the partial-order property.

In `@pkg/sessionctx/variable/sysvar.go`:
- Around line 2904-2913: Add an upgrade migration that rewrites legacy
boolean-stored values ("ON"/"OFF"/"1"/"0") for the
TiDBOptPartialOrderedIndexForTopN sysvar into the new enum values
("COST"/"DISABLE") before global variables are loaded; implement it alongside
other migrations (e.g., in the upgradeToVer228-style bootstrap step) so
mysql.global_variables rows are materialized/updated prior to sysvar validation,
update the migration to target the TiDBOptPartialOrderedIndexForTopN name, and
add an upgrade test that simulates an upgraded cluster with legacy persisted
values to assert the value is rewritten and accepted by the
Validation/SetSession logic.

In `@tests/integrationtest/t/planner/core/partial_order_topn.test`:
- Line 406: The test uses FORCE_INDEX(t_prefix_len, long_prefix) but
"long_prefix" is a column, not an index; update the hint to reference the real
index name (e.g., FORCE_INDEX(t_prefix_len, <actual_index_name>)) or
create/rename the index on t_prefix_len to match the hint before the query, and
then re-run and record the regenerated deterministic results; locate the
FORCE_INDEX usage in the test and replace the second argument with the actual
index identifier used by the table (or add an index creation step for that
identifier) so the planner path is reliably forced.

---

Nitpick comments:
In `@DEPS.bzl`:
- Around line 5974-5980: The tipb version bump may change generated protobuf
types (e.g., enum/field renames like ExchangeType or addition of a Limit
executor field); compile and run unit/integration tests (and run the Bazel prep
step used by this repo) to surface errors, then search the codebase for
references to tipb.ExchangeType and any places constructing/examining executor
types or limit fields and update call sites to the new enum names/field
accessors or message layout produced by the updated proto; ensure any BUILD/DEPS
metadata is consistent with the new generated code and re-run tests to verify
all API changes (ExchangeType usages and any Limit executor fields) are handled.

In `@pkg/planner/core/exhaust_physical_plans.go`:
- Around line 2269-2272: canUsePartialOrder4TopN currently runs expensive
pattern checks even when ByItems contain non-column expressions that will make
getPhysTopNWithPartialOrderProperty return nil; update canUsePartialOrder4TopN
to first validate lt.ByItems (or the ByItems slice used) contains only column
expressions (e.g., ColumnExpr) and return false early if any non-column
expression is found, so the later call to
getPhysTopNWithPartialOrderProperty(lt, prop) is avoided when it would
inevitably return nil.
- Around line 2335-2346: Add a short documentation comment above the
checkPartialOrderPattern function that describes the exact supported pattern: a
chain starting from logicalop.DataSource optionally wrapped by a single-child
logicalop.LogicalSelection and/or a single-child logicalop.LogicalProjection
(i.e., DataSource → Selection → Projection where each wrapper must have exactly
one child). Mention the constraint that each LogicalSelection/LogicalProjection
must have len(Children()) == 1 and that any other node type returns false;
reference the function name checkPartialOrderPattern and the node types
logicalop.DataSource, logicalop.LogicalSelection, and
logicalop.LogicalProjection to make intent and limits clear.

In `@pkg/planner/core/operator/logicalop/logical_projection.go`:
- Around line 601-613: In tryTransformSortItemPtrs, add defensive bounds
checking on idx from p.Schema().ColumnIndex(item.Col) (ensure 0 <= idx <
len(p.Exprs)) and treat any expression types other than *expression.Column and
*expression.ScalarFunction as a failure instead of silently skipping them; if
the index is out of range or an unexpected expr type is encountered, return nil,
false—otherwise, for a Column create the new SortItem as now and for a
ScalarFunction return nil, false to match tryTransformSortItems behavior.
- Around line 587-599: The tryTransformSortItems function currently can panic
when p.Schema().ColumnIndex returns -1 and silently ignores
non-column/non-scalar expressions; update tryTransformSortItems to first check
idx := p.Schema().ColumnIndex(item.Col) and if idx < 0 return nil, false to
avoid panic, and treat any expression that is not *expression.Column as a
non-transformable case (i.e., return nil, false) instead of silently skipping
(handle the default case explicitly), and add a brief comment above
tryTransformSortItems describing that it only succeeds when every sort item maps
to a plain Column in p.Exprs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2989e3e2-3f0d-416f-88f2-9ce3b26f05ac

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3636d and 813b81e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (22)
  • DEPS.bzl
  • go.mod
  • pkg/planner/core/casetest/rule/BUILD.bazel
  • pkg/planner/core/exhaust_physical_plans.go
  • pkg/planner/core/explain.go
  • pkg/planner/core/find_best_task.go
  • pkg/planner/core/hint_test.go
  • pkg/planner/core/operator/logicalop/logical_projection.go
  • pkg/planner/core/physical_plans.go
  • pkg/planner/core/plan_clone_generated.go
  • pkg/planner/core/plan_to_pb.go
  • pkg/planner/core/task.go
  • pkg/planner/core/task_base.go
  • pkg/planner/property/physical_property.go
  • pkg/planner/util/path.go
  • pkg/sessionctx/variable/session.go
  • pkg/sessionctx/variable/session_test.go
  • pkg/sessionctx/variable/sysvar.go
  • pkg/sessionctx/variable/sysvar_test.go
  • pkg/sessionctx/variable/tidb_vars.go
  • tests/integrationtest/r/planner/core/partial_order_topn.result
  • tests/integrationtest/t/planner/core/partial_order_topn.test

Comment on lines +1401 to +1415
if prop.PartialOrderInfo != nil {
continue
}
currentCandidate = getTableCandidate(ds, path, prop)
} else {
if !(len(path.AccessConds) > 0 || !prop.IsSortItemEmpty() || path.Forced || path.IsSingleScan) {
var matchPartialOrderIndex bool
if ds.SCtx().GetSessionVars().IsPartialOrderedIndexForTopNEnabled() &&
prop.PartialOrderInfo != nil {
if !matchPartialOrderProperty(path, prop.PartialOrderInfo).Matched {
continue
}
matchPartialOrderIndex = true
if path.Forced && !path.ForceNoKeepOrder {
path.ForcePartialOrder = true
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't persist ForcePartialOrder on the shared AccessPath.

skylinePruning() sets path.ForcePartialOrder = true in-place, but ds.PossibleAccessPaths are reused across later property explorations. After one partial-order pass marks a forced path here, a later normal pass can hit the prop.PartialOrderInfo == nil rejection in convertToIndexScan() and incorrectly discard that hinted path. Keep this flag candidate-local, or at least clear it before each pruning pass.

Minimal safe reset
for _, path := range ds.PossibleAccessPaths {
+	path.ForcePartialOrder = false
 	// We should check whether the possible access path is valid first.
 	if path.StoreType != kv.TiFlash && prop.IsFlashProp() {
 		continue
 	}

Also applies to: 2401-2404

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/find_best_task.go` around lines 1401 - 1415, The review
points out that skylinePruning()/the loop is mutating the shared AccessPath by
setting path.ForcePartialOrder = true which later causes convertToIndexScan() to
reject paths when prop.PartialOrderInfo == nil; to fix, avoid persisting
ForcePartialOrder on the shared ds.PossibleAccessPaths: either operate on a
shallow copy of the AccessPath or use a candidate-local boolean (e.g.,
localForcePartialOrder) when evaluating/constructing currentCandidate in
getTableCandidate/skylinePruning rather than writing to path.ForcePartialOrder,
and if you must modify path, ensure you clear/reset path.ForcePartialOrder
before finishing each pruning pass so convertToIndexScan() sees the original
state.

Comment on lines +1331 to +1332
sum = lt.BasePhysicalPlan.MemoryUsage() + size.SizeOfSlice + int64(cap(lt.ByItems))*size.SizeOfPointer +
size.SizeOfUint64*2 + size.SizeOfInt64 + size.SizeOfInt
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include PrefixCol object cost in memory accounting.

Both PhysicalTopN.MemoryUsage() and PhysicalLimit.MemoryUsage() include scalar field sizes for the new prefix metadata, but they do not include PrefixCol.MemoryUsage() when set.

Suggested patch
 func (lt *PhysicalTopN) MemoryUsage() (sum int64) {
@@
-	sum = lt.BasePhysicalPlan.MemoryUsage() + size.SizeOfSlice + int64(cap(lt.ByItems))*size.SizeOfPointer +
-		size.SizeOfUint64*2 + size.SizeOfInt64 + size.SizeOfInt
+	sum = lt.BasePhysicalPlan.MemoryUsage() + size.SizeOfSlice + int64(cap(lt.ByItems))*size.SizeOfPointer +
+		size.SizeOfUint64*2 + size.SizeOfPointer + size.SizeOfInt
@@
 	for _, item := range lt.PartitionBy {
 		sum += item.MemoryUsage()
 	}
+	if lt.PrefixCol != nil {
+		sum += lt.PrefixCol.MemoryUsage()
+	}
 	return
 }
@@
 func (p *PhysicalLimit) MemoryUsage() (sum int64) {
@@
-	sum = p.physicalSchemaProducer.MemoryUsage() + size.SizeOfUint64*2 + size.SizeOfInt64 + size.SizeOfInt
+	sum = p.physicalSchemaProducer.MemoryUsage() + size.SizeOfUint64*2 + size.SizeOfPointer + size.SizeOfInt
+	if p.PrefixCol != nil {
+		sum += p.PrefixCol.MemoryUsage()
+	}
 	return
 }

Also applies to: 2118-2118

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/physical_plans.go` around lines 1331 - 1332,
PhysicalTopN.MemoryUsage() and PhysicalLimit.MemoryUsage() compute scalar sizes
for new prefix metadata but omit the memory cost of the PrefixCol object itself;
update both methods to, when PrefixCol is non-nil, add the result of
PrefixCol.MemoryUsage() to the sum (e.g., after computing sum use "if
lt.PrefixCol != nil { sum += lt.PrefixCol.MemoryUsage() }" or the equivalent
using the local receiver/variable name) so the PrefixCol object's memory is
included in total accounting.

Comment on lines +264 to +265
// partialOrderMatchResult stores the match result for partial order optimization.
partialOrderMatchResult *property.PartialOrderMatchResult
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update CopTask.MemoryUsage for partialOrderMatchResult.

CopTask now retains partialOrderMatchResult, but MemoryUsage() still uses the old pointer count and does not include the nested match-result payload.

Suggested patch
 func (t *CopTask) MemoryUsage() (sum int64) {
@@
-	sum = size.SizeOfInterface*(2+int64(cap(t.idxMergePartPlans)+cap(t.rootTaskConds))) + size.SizeOfBool*3 + size.SizeOfUint64 +
-		size.SizeOfPointer*(3+int64(cap(t.commonHandleCols)+cap(t.tblCols))) + size.SizeOfSlice*4 + t.physPlanPartInfo.MemoryUsage()
+	sum = size.SizeOfInterface*(2+int64(cap(t.idxMergePartPlans)+cap(t.rootTaskConds))) + size.SizeOfBool*3 + size.SizeOfUint64 +
+		size.SizeOfPointer*(4+int64(cap(t.commonHandleCols)+cap(t.tblCols))) + size.SizeOfSlice*4 + t.physPlanPartInfo.MemoryUsage()
@@
 	for _, expr := range t.rootTaskConds {
 		sum += expr.MemoryUsage()
 	}
+	if t.partialOrderMatchResult != nil {
+		sum += size.SizeOfBool + size.SizeOfPointer + size.SizeOfInt
+		if t.partialOrderMatchResult.PrefixCol != nil {
+			sum += t.partialOrderMatchResult.PrefixCol.MemoryUsage()
+		}
+	}
 	return
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// partialOrderMatchResult stores the match result for partial order optimization.
partialOrderMatchResult *property.PartialOrderMatchResult
func (t *CopTask) MemoryUsage() (sum int64) {
sum = size.SizeOfInterface*(2+int64(cap(t.idxMergePartPlans)+cap(t.rootTaskConds))) + size.SizeOfBool*3 + size.SizeOfUint64 +
size.SizeOfPointer*(4+int64(cap(t.commonHandleCols)+cap(t.tblCols))) + size.SizeOfSlice*4 + t.physPlanPartInfo.MemoryUsage()
for _, expr := range t.rootTaskConds {
sum += expr.MemoryUsage()
}
if t.partialOrderMatchResult != nil {
sum += size.SizeOfBool + size.SizeOfPointer + size.SizeOfInt
if t.partialOrderMatchResult.PrefixCol != nil {
sum += t.partialOrderMatchResult.PrefixCol.MemoryUsage()
}
}
return
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/task_base.go` around lines 264 - 265, CopTask.MemoryUsage
currently omits the memory held by the new partialOrderMatchResult field and
still uses the old pointer count; update the CopTask.MemoryUsage method to
include the size of partialOrderMatchResult (handle nil check) and account for
its nested payload by adding the estimated memory for
property.PartialOrderMatchResult (including any slices/maps/inner structs it
owns) to the total, and increment the pointer/object count accordingly so the
reported memory reflects the nested match-result payload.

Comment thread pkg/planner/core/task.go
Comment on lines +1112 to +1115
maxX := estimateMaxXForPartialOrder(p.SCtx(), copTask)
estimatedRows := float64(partialOrderedLimit) + float64(maxX)
childProfile := copTask.indexPlan.StatsInfo()
limitStats := util.DeriveLimitStats(childProfile, estimatedRows)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

estimateMaxXForPartialOrder() is still a stub, so COST mode underprices this plan.

The partial-order cop limit can read more than Count+Offset rows from the last prefix bucket, but estimateMaxXForPartialOrder() always returns 0. That makes limitStats and the downstream lookup/network work look cheaper than they are, so tidb_opt_partial_ordered_index_for_topn=COST can pick this path too aggressively. Please compute a conservative extra-row bound here, or fall back to the child row count until the estimator exists.

Temporary conservative fallback
 func estimateMaxXForPartialOrder(_ base.PlanContext, _ *CopTask) uint64 {
-	return 0
+	if copTask == nil || copTask.indexPlan == nil || copTask.indexPlan.StatsInfo() == nil {
+		return 0
+	}
+	return uint64(math.Ceil(copTask.indexPlan.StatsInfo().RowCount))
 }

Also applies to: 1130-1132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/task.go` around lines 1112 - 1115,
estimateMaxXForPartialOrder currently returns 0 causing COST mode to
under-estimate partial-order cop limits; update the block that computes
maxX/estimatedRows (where estimateMaxXForPartialOrder, maxX,
partialOrderedLimit, estimatedRows, copTask and childProfile are used) to use a
conservative fallback instead of zero: if estimateMaxXForPartialOrder yields a
non-positive or placeholder value, set maxX to the child plan's row count from
childProfile (e.g., the stats row count) or another conservative upper bound,
then compute estimatedRows = float64(partialOrderedLimit) + float64(maxX);
ensure the same change is applied at the other occurrence around lines 1130-1132
so DeriveLimitStats gets a conservative estimate.

Comment on lines +291 to +295
// PartialOrderInfo is used for TopN's partial order optimization.
// When this field is not nil, it indicates that prefix index can be used
// to provide partial order for TopN.
PartialOrderInfo *PartialOrderInfo
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Account for PartialOrderInfo in PhysicalProperty.MemoryUsage.

PartialOrderInfo is newly retained on PhysicalProperty, but MemoryUsage() does not include it (or its SortItems), so memory tracking undercounts partial-order properties.

Suggested patch
 func (p *PhysicalProperty) MemoryUsage() (sum int64) {
@@
 	for _, mppCol := range p.MPPPartitionCols {
 		sum += mppCol.MemoryUsage()
 	}
+	if p.PartialOrderInfo != nil {
+		sum += size.SizeOfPointer + size.SizeOfSlice + int64(cap(p.PartialOrderInfo.SortItems))*size.SizeOfPointer
+		for _, item := range p.PartialOrderInfo.SortItems {
+			if item != nil {
+				sum += item.MemoryUsage()
+			}
+		}
+	}
 	return
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/property/physical_property.go` around lines 291 - 295, Update
PhysicalProperty.MemoryUsage to account for the newly retained PartialOrderInfo:
inside the MemoryUsage implementation (function/method
PhysicalProperty.MemoryUsage) check if p.PartialOrderInfo is non-nil and include
its memory footprint and the footprints of each entry in
p.PartialOrderInfo.SortItems (iterate SortItems and add per-item memory cost
similar to how other slice/field entries are counted). Ensure you include the
pointer/struct overhead for PartialOrderInfo and any per-sort-item allocations
so the total memory reflects the partial-order property.

Comment thread pkg/sessionctx/variable/sysvar.go
explain format='brief' select /*+ force_index(t_prefix_len, idx_short) */ * from t_prefix_len order by short_prefix limit 3;

# Long prefix (50 bytes)
explain format='brief' select /*+ force_index(t_prefix_len, long_prefix) */ * from t_prefix_len order by long_prefix limit 3;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the FORCE_INDEX target to an actual index.

Line 406 uses force_index(t_prefix_len, long_prefix), but long_prefix is a column name, not an index name. This means the test is not actually forcing the intended index path and can become non-deterministic.

🔧 Proposed fix
--- a/tests/integrationtest/t/planner/core/partial_order_topn.test
+++ b/tests/integrationtest/t/planner/core/partial_order_topn.test
@@
-explain format='brief' select  /*+ force_index(t_prefix_len, long_prefix) */  * from t_prefix_len order by long_prefix limit 3;
+explain format='brief' select  /*+ force_index(t_prefix_len, idx_long) */  * from t_prefix_len order by long_prefix limit 3;
--- a/tests/integrationtest/r/planner/core/partial_order_topn.result
+++ b/tests/integrationtest/r/planner/core/partial_order_topn.result
@@
-explain format='brief' select  /*+ force_index(t_prefix_len, long_prefix) */  * from t_prefix_len order by long_prefix limit 3;
+explain format='brief' select  /*+ force_index(t_prefix_len, idx_long) */  * from t_prefix_len order by long_prefix limit 3;

As per coding guidelines, test files should keep changes deterministic, and integration tests should record verified regenerated results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integrationtest/t/planner/core/partial_order_topn.test` at line 406,
The test uses FORCE_INDEX(t_prefix_len, long_prefix) but "long_prefix" is a
column, not an index; update the hint to reference the real index name (e.g.,
FORCE_INDEX(t_prefix_len, <actual_index_name>)) or create/rename the index on
t_prefix_len to match the hint before the query, and then re-run and record the
regenerated deterministic results; locate the FORCE_INDEX usage in the test and
replace the second argument with the actual index identifier used by the table
(or add an index creation step for that identifier) so the planner path is
reliably forced.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 5, 2026

Codecov Report

❌ Patch coverage is 75.46468% with 66 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (release-8.5@3d3636d). Learn more about missing BASE report.

Additional details and impacted files
@@               Coverage Diff                @@
##             release-8.5     #68995   +/-   ##
================================================
  Coverage               ?   55.1381%           
================================================
  Files                  ?       1826           
  Lines                  ?     659095           
  Branches               ?          0           
================================================
  Hits                   ?     363413           
  Misses                 ?     268611           
  Partials               ?      27071           
Flag Coverage Δ
integration 38.3143% <75.4646%> (?)
unit 65.0683% <62.4535%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
dumpling 55.3108% <0.0000%> (?)
parser ∅ <0.0000%> (?)
br 54.5986% <0.0000%> (?)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@winoros
Copy link
Copy Markdown
Member Author

winoros commented Jun 5, 2026

/retest

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 5, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: qw4990
Once this PR has been reviewed and has the lgtm label, please assign terry1purcell for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jun 5, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 5, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-06-05 12:04:37.082743835 +0000 UTC m=+529578.153061225: ☑️ agreed by qw4990.

@ti-chi-bot ti-chi-bot Bot added cherry-pick-approved Cherry pick PR approved by release team. and removed do-not-merge/cherry-pick-not-approved labels Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cherry-pick-approved Cherry pick PR approved by release team. needs-1-more-lgtm Indicates a PR needs 1 more LGTM. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/planner SIG: Planner size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants