Skip to content

pkg/planner: build multiple logical plans from shared AST in optimize#66743

Open
AilinKid wants to merge 12 commits intopingcap:masterfrom
AilinKid:ast-multi-logical-plan-step2
Open

pkg/planner: build multiple logical plans from shared AST in optimize#66743
AilinKid wants to merge 12 commits intopingcap:masterfrom
AilinKid:ast-multi-logical-plan-step2

Conversation

@AilinKid
Copy link
Contributor

@AilinKid AilinKid commented Mar 6, 2026

What problem does this PR solve?

Issue Number: ref #66651

Problem Summary:

This PR implements step 2 of #66651.
Step 1 has already been merged; this step adds the actual multi-build flow in Optimize for rebuilding logical plans from a shared AST.

What changed and how does it work?

  • Add multi-round logical-plan build flow in pkg/planner/optimize.go.
  • Keep privilege/table-lock checks fail-fast after the first successful build round.
  • Keep hint-processor AST metadata shared/read-only, and move per-build mutable hint state into PlanBuilder.
  • Isolate per-round mutable session/statement planner state with baseline save/restore only when restore is needed.
  • Keep non-logical-plan behavior compatible with existing flow.

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.

Unit test command:

  • go test -run TestOptimizeHintOnPartitionTable -tags=intest,deadlock ./pkg/planner/core/casetest/hint

Additional validation:

  • make bazel_lint_changed

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

None

Summary by CodeRabbit

  • Performance Improvements

    • Optimizer now runs multi-round plan selection, choosing the best plan across rounds for more consistent, efficient query execution.
    • Added timing instrumentation to report optimizer durations and help surface slow planning steps.
  • Reliability

    • Restores per-round planner state when selecting the best plan to avoid regressions between rounds.
    • Adds error handling and cleanup when no valid plan can be produced.

@ti-chi-bot ti-chi-bot bot added the release-note-none Denotes a PR that doesn't merit a release note. label Mar 6, 2026
@pantheon-ai
Copy link

pantheon-ai bot commented Mar 6, 2026

Review Complete

Findings: 2 issues
Posted: 2
Duplicates/Skipped: 0

ℹ️ Learn more details on Pantheon AI.

@ti-chi-bot ti-chi-bot bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. sig/planner SIG: Planner labels Mar 6, 2026
@tiprow
Copy link

tiprow bot commented Mar 6, 2026

Hi @AilinKid. Thanks for your PR.

PRs from untrusted users cannot be marked as trusted with /ok-to-test in this repo meaning untrusted PR authors can never trigger tests themselves. Collaborators can still trigger tests on the PR using /test all.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors optimizer to support per-round logical-plan build/optimization: adds snapshot/restore of per-build planner state, a build-and-optimize round helper, best-plan tracking, multi-round scaffolding (currently one round), timing instrumentation, and improved error handling for failed logical-plan builds.

Changes

Cohort / File(s) Summary
Optimizer core
pkg/planner/optimize.go
Adds logicalPlanBuildCtx and snapshot/restore helpers; introduces saveLogicalPlanBuildCtx / restoreLogicalPlanBuildCtx, buildAndOptimizeLogicalPlanRound, and multi-round scaffolding in optimize (currently one round). Tracks best plan/name/cost, supports per-round context save/restore, preserves non-logical-plan behavior, adds beginOpt timing instrumentation, and improves error handling when no logical plan can be built.

Sequence Diagram

sequenceDiagram
    participant AST as AST
    participant Pool as PlanBuilderPool
    participant Builder as PlanBuilder
    participant Validator as Priv/Lock Check
    participant Optimizer as core.DoOptimize
    participant Tracker as BestPlanTracker
    participant Restore as BaselineRestore

    AST->>Pool: request PlanBuilder
    Pool->>Builder: provide reusable builder
    Builder->>Builder: build logical plan
    Builder->>Builder: saveLogicalPlanBuildCtx
    Builder->>Validator: run fast privilege/lock/mode checks
    Builder->>Optimizer: submit logical plan for optimization
    Optimizer->>Tracker: return optimized plan + cost
    Tracker->>Tracker: compare & select best plan
    Tracker->>Restore: if best, request restore of per-round context
    Restore->>Builder: restoreLogicalPlanBuildCtx
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

size/XL, ok-to-test

Suggested reviewers

  • qw4990
  • guo-shaoge

Poem

🐰 I hopped through builds with careful paws,
I tucked the planner's crumbs in neat laws,
One round to test, one round to keep,
I saved the seeds before they sleep,
A tiny hop — the optimizer applauds 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing multi-round logical plan building from a shared AST in the optimize function, which matches the core objective of the PR.
Description check ✅ Passed The description follows the template with all required sections completed: problem statement with issue reference, detailed explanation of changes, checkbox items addressed, and release notes included.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.5.0)

Command failed


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.

Copy link

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/planner/optimize.go`:
- Around line 477-480: The DurationOptimizer.Total currently starts after the
AST hint walk and misses early returns, so change timing to cover the entire
optimize path by starting the timer before invoking
node.Node.Accept(hintProcessor) (the hintProcessor/hint.NewQBHintHandler call)
and ensure DurationOptimizer.Total is recorded on all exits (including early
returns) by using a deferred function that sets Total from the timer; apply this
to the block around PlanBuilder/optimizer flow so the timer starts before hint
handling and the defer guarantees Total is stored regardless of return path.
- Around line 441-456: The baseline currently captures only StmtCtx and
PlannerSelectBlockAsName in
captureLogicalPlanBuildBaseline/restoreLogicalPlanBuildBaseline but
buildLogicalPlan also resets per-build session state (SessionVars.MapScalarSubQ,
SessionVars.MapHashCode2UniqueID4ExtendedCol, and SessionVars.RewritePhaseInfo),
which will be left stale when needBaseline triggers multi-round selection;
update logicalPlanBuildBaseline to include those fields (or move them into a
round-local struct) and modify captureLogicalPlanBuildBaseline and
restoreLogicalPlanBuildBaseline to snapshot and restore
SessionVars.MapScalarSubQ, SessionVars.MapHashCode2UniqueID4ExtendedCol, and
SessionVars.RewritePhaseInfo so buildRound/needBaseline behavior returns a plan
with matching session-side artifacts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 37ab5b81-edfb-4b37-a54e-9c1aac3f832c

📥 Commits

Reviewing files that changed from the base of the PR and between d59e531 and 36d9021.

📒 Files selected for processing (1)
  • pkg/planner/optimize.go

@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 80.99174% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.5464%. Comparing base (d59e531) to head (f89fdd9).
⚠️ Report is 25 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #66743        +/-   ##
================================================
- Coverage   77.6885%   77.5464%   -0.1422%     
================================================
  Files          2008       1930        -78     
  Lines        549664     540676      -8988     
================================================
- Hits         427026     419275      -7751     
- Misses       120978     121383       +405     
+ Partials       1660         18      -1642     
Flag Coverage Δ
unit 76.5665% <80.8333%> (+0.2023%) ⬆️

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

Components Coverage Δ
dumpling 56.7974% <ø> (ø)
parser ∅ <ø> (∅)
br 48.7905% <ø> (-12.0903%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

visitInfo := core.VisitInfo4PrivCheck(ctx, is, node.Node, builder.GetVisitInfo())
if err := core.CheckPrivilege(activeRoles, pm, visitInfo); err != nil {
return nil, nil, 0, err
beginOpt := time.Now()
Copy link

Choose a reason for hiding this comment

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

[P2] Optimize_time now includes rewrite/build time

Why: DurationOptimizer.Total is now started before buildLogicalPlan(), so slow-log Optimize_time necessarily overlaps Rewrite_time, misleading diagnostics and slow-log rule matching that treats the two as disjoint phases.

Evidence: beginOpt := time.Now() at pkg/planner/optimize.go:497 is set before buildLogicalPlan(...) is called; sessVars.DurationOptimizer.Total = time.Since(beginOpt) at :583 captures the full window including rewrite. Slow log prints both Rewrite_time (slow_log.go:398) and Optimize_time (slow_log.go:408) independently — and slow-log rule matching keys on DurationOptimizer.Total (slow_log.go:756). Previously beginOpt was set after the build step, keeping the two intervals disjoint.

Suggestion: Move beginOpt := time.Now() to after the final buildLogicalPlan(...) call (i.e. just before core.DoOptimize(...)), restoring the original semantics where Optimize_time covers only the optimization phase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in f89fdd9. Optimize_time now starts right before the first core.DoOptimize(...) call (not before logical build/rewrite), and is still recorded through defer only when optimization actually starts.

Copy link

@pantheon-ai pantheon-ai bot Mar 10, 2026

Choose a reason for hiding this comment

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

@AilinKid I've received your follow-up on this review comment and will continue working on it. I'll update this comment when I have something to share.

ℹ️ Learn more details on Pantheon AI.

names := p.OutputNames()
var err error
// todo: you can customize each round's special builder (like semi join rewrite or not by signal)
p, err = buildLogicalPlan(ctx, sctx, node, builder)
Copy link

Choose a reason for hiding this comment

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

[P2] Unused view-hint (qb_name) warnings silently dropped on error paths

Why: The refactor removed the unconditional/deferred emission of unused qb_name view-hint warnings. Any early return before HandleUnusedViewHints() is reached silently loses user-visible diagnostics for failing statements — a regression from the previous behavior where emission ran unconditionally via defer.

Evidence: buildLogicalPlan(...) error at :516 returns immediately; subsequent error returns at :527, :532, :536, :554 also exit before the only builder.HandleUnusedViewHints() calls at :545/:557. PlanBuilder.HandleUnusedViewHints() (planbuilder.go:521) is the sole emission point that appends warnings into StmtCtx; if never invoked, the warning is lost entirely.

Suggestion: Add defer builder.HandleUnusedViewHints() immediately after builder is initialized inside the closure, restoring "warn even on error" semantics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed earlier in bc1b396 by adding defer builder.HandleUnusedViewHints() inside per-round build/optimize handling, so unused qb_name warnings are emitted even on error paths.

Copy link

@pantheon-ai pantheon-ai bot Mar 10, 2026

Choose a reason for hiding this comment

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

Thanks for the follow-up. Adding defer builder.HandleUnusedViewHints() inside the per-round closure is the right fix — it guarantees warning emission regardless of whether buildLogicalPlan, privilege checks, or DoOptimize returns an error. ✅

ℹ️ Learn more details on Pantheon AI.

@AilinKid
Copy link
Contributor Author

AilinKid commented Mar 9, 2026

/retest-required

@tiprow
Copy link

tiprow bot commented Mar 9, 2026

@AilinKid: PRs from untrusted users cannot be marked as trusted with /ok-to-test in this repo meaning untrusted PR authors can never trigger tests themselves. Collaborators can still trigger tests on the PR using /test.

Details

In response to this:

/retest-required

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

if needBaseline {
restoreLogicalPlanBuildBaseline(sessVars, bestState)
}
sessVars.DurationOptimizer.Total = time.Since(beginOpt)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Old impl will update DurationOptimizer even got error, but new impl will not. So better put this in defer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in bc1b396. DurationOptimizer.Total is now assigned in a deferred function, so it is recorded even when optimize returns with error.

if err != nil {
return nil, nil, false, err
}
builder.HandleUnusedViewHints()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Guess we should have more fine-grained handling of HandleUnusedViewHints() , because multiple round of calling this function may cause many warnings.(But I think we can leave a TODO here and refine it later)
And also we should put it in derfer like the old code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. The defer part is addressed in bc1b396 (defer builder.HandleUnusedViewHints()), and I added a follow-up TODO in 7bb7149 to refine multi-round warning handling so only winner-round unused view-hint warnings are emitted when buildRound > 1.

Copy link

@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: 1

♻️ Duplicate comments (1)
pkg/planner/optimize.go (1)

471-480: ⚠️ Potential issue | 🟡 Minor

Unused view-hint warnings lost on error paths.

If buildLogicalPlan or any subsequent check fails, builder.HandleUnusedViewHints() is never called, and user-visible warnings about unused qb_name view hints are silently dropped. The previous implementation used defer to ensure warnings were emitted even on failure.

Suggested fix: defer HandleUnusedViewHints
 func buildAndOptimizeLogicalPlanRound(
 	...
 ) (base.Plan, types.NameSlice, bool, error) {
 	builder := planBuilderPool.Get().(*core.PlanBuilder)
 	defer planBuilderPool.Put(builder.ResetForReuse())

 	builder.Init(sctx, is, hintProcessor)
+	defer builder.HandleUnusedViewHints()

 	// todo: you can customize each round's special builder (like semi join rewrite or not by signal)
 	p, err := buildLogicalPlan(ctx, sctx, node, builder)
 	if err != nil {
 		return nil, nil, false, err
 	}
 	...
-	// Handle the non-logical plan statement.
-	logic, isLogicalPlan := p.(base.LogicalPlan)
-	if !isLogicalPlan {
-		builder.HandleUnusedViewHints()
-		return p, names, true, nil
-	}
+	logic, isLogicalPlan := p.(base.LogicalPlan)
+	if !isLogicalPlan {
+		return p, names, true, nil
+	}
 	...
-	builder.HandleUnusedViewHints()

 	if *bestPlan == nil || cost < *bestCost {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` around lines 471 - 480, After acquiring the builder
from planBuilderPool, ensure builder.HandleUnusedViewHints() is deferred so
unused view-hint warnings are emitted on all error paths; to run it before the
builder is reset, keep the existing defer
planBuilderPool.Put(builder.ResetForReuse()) and then immediately add defer
builder.HandleUnusedViewHints() (place this before calling builder.Init and
before calling buildLogicalPlan), so HandleUnusedViewHints runs prior to
ResetForReuse even if buildLogicalPlan or subsequent checks return early.
🧹 Nitpick comments (1)
pkg/planner/optimize.go (1)

441-456: Incomplete state capture for multi-round restoration.

When buildRound > 1, restoreLogicalPlanBuildCtx will be used to restore the winning round's state. However, logicalPlanBuildCtx only captures stmtCtxState and plannerSelectBlockAsName, while buildLogicalPlan also populates SessionVars.MapScalarSubQ, SessionVars.MapHashCode2UniqueID4ExtendedCol, and SessionVars.RewritePhaseInfo. These fields will retain values from the last build round rather than the winning round.

This is latent today since buildRound = 1, but will cause incorrect session state when multi-round selection is enabled.

Suggested fix
 type logicalPlanBuildCtx struct {
 	stmtCtxState             stmtctx.LogicalPlanBuildState
 	plannerSelectBlockAsName *[]ast.HintTable
+	mapScalarSubQ            map[int]any
+	mapHashCode2UniqueID     map[string]int
+	rewritePhaseInfo         variable.RewritePhaseInfo
 }

 func saveLogicalPlanBuildCtx(sessVars *variable.SessionVars) logicalPlanBuildCtx {
 	return logicalPlanBuildCtx{
 		stmtCtxState:             sessVars.StmtCtx.SaveLogicalPlanBuildState(),
 		plannerSelectBlockAsName: sessVars.PlannerSelectBlockAsName.Load(),
+		mapScalarSubQ:            sessVars.MapScalarSubQ,
+		mapHashCode2UniqueID:     sessVars.MapHashCode2UniqueID4ExtendedCol,
+		rewritePhaseInfo:         sessVars.RewritePhaseInfo,
 	}
 }

 func restoreLogicalPlanBuildCtx(sessVars *variable.SessionVars, logicalPlanCtx logicalPlanBuildCtx) {
 	sessVars.StmtCtx.RestoreLogicalPlanBuildState(logicalPlanCtx.stmtCtxState)
 	sessVars.PlannerSelectBlockAsName.Store(logicalPlanCtx.plannerSelectBlockAsName)
+	sessVars.MapScalarSubQ = logicalPlanCtx.mapScalarSubQ
+	sessVars.MapHashCode2UniqueID4ExtendedCol = logicalPlanCtx.mapHashCode2UniqueID
+	sessVars.RewritePhaseInfo = logicalPlanCtx.rewritePhaseInfo
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` around lines 441 - 456, logicalPlanBuildCtx
currently only saves stmtCtxState and plannerSelectBlockAsName, so when
restoreLogicalPlanBuildCtx is used after multi-round builds it fails to revert
SessionVars.MapScalarSubQ, SessionVars.MapHashCode2UniqueID4ExtendedCol and
SessionVars.RewritePhaseInfo set by buildLogicalPlan; update logicalPlanBuildCtx
to include fields for MapScalarSubQ, MapHashCode2UniqueID4ExtendedCol and
RewritePhaseInfo, modify saveLogicalPlanBuildCtx to copy those SessionVars
values into the new struct, and modify restoreLogicalPlanBuildCtx to
Store/restore those three SessionVars from the saved logicalPlanBuildCtx so the
winning round fully restores session state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/planner/optimize.go`:
- Around line 590-593: The early return for nonLogical plans skips recording
optimizer timing; before returning in the nonLogical branch of optimize (the
block that currently returns p, names, 0, nil), set
sessVars.DurationOptimizer.Total to the elapsed optimization duration by
computing time.Since(<the optimization start time>) (or capture time.Now() at
function entry into a startTime variable if none exists) so the duration is
recorded for DDL / non-logical statements; keep the existing return values
unchanged.

---

Duplicate comments:
In `@pkg/planner/optimize.go`:
- Around line 471-480: After acquiring the builder from planBuilderPool, ensure
builder.HandleUnusedViewHints() is deferred so unused view-hint warnings are
emitted on all error paths; to run it before the builder is reset, keep the
existing defer planBuilderPool.Put(builder.ResetForReuse()) and then immediately
add defer builder.HandleUnusedViewHints() (place this before calling
builder.Init and before calling buildLogicalPlan), so HandleUnusedViewHints runs
prior to ResetForReuse even if buildLogicalPlan or subsequent checks return
early.

---

Nitpick comments:
In `@pkg/planner/optimize.go`:
- Around line 441-456: logicalPlanBuildCtx currently only saves stmtCtxState and
plannerSelectBlockAsName, so when restoreLogicalPlanBuildCtx is used after
multi-round builds it fails to revert SessionVars.MapScalarSubQ,
SessionVars.MapHashCode2UniqueID4ExtendedCol and SessionVars.RewritePhaseInfo
set by buildLogicalPlan; update logicalPlanBuildCtx to include fields for
MapScalarSubQ, MapHashCode2UniqueID4ExtendedCol and RewritePhaseInfo, modify
saveLogicalPlanBuildCtx to copy those SessionVars values into the new struct,
and modify restoreLogicalPlanBuildCtx to Store/restore those three SessionVars
from the saved logicalPlanBuildCtx so the winning round fully restores session
state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5d0d7496-a53f-4049-9b75-7490d09adf29

📥 Commits

Reviewing files that changed from the base of the PR and between 36d9021 and 2bd8678.

📒 Files selected for processing (1)
  • pkg/planner/optimize.go

Copy link

@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.

♻️ Duplicate comments (1)
pkg/planner/optimize.go (1)

556-559: ⚠️ Potential issue | 🟠 Major

Keep DurationOptimizer.Total scoped to the actual optimize phase.

Starting the timer before the hint walk and logical-plan build makes Optimize_time overlap Rewrite_time, and it also reports a non-zero optimize duration on the nonLogical path even though core.DoOptimize never ran. Please accumulate this metric around each core.DoOptimize call instead of around the whole round/build flow.

Also applies to: 561-565, 601-603

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` around lines 556 - 559, The timer for
sessVars.DurationOptimizer.Total is currently started too early (beginOpt)
covering hint walk and logical-plan build and thus overlaps Rewrite_time and
reports time on nonLogical paths; move the start/end timing to directly wrap
each core.DoOptimize invocation (create a new begin time immediately before
calling core.DoOptimize and add the elapsed duration to
sessVars.DurationOptimizer.Total instead of overwriting it), ensure the
nonLogical path does not touch DurationOptimizer, and apply the same change for
the other similar timing sites referenced around the core.DoOptimize calls (the
blocks at the other noted locations).
🧹 Nitpick comments (1)
pkg/planner/optimize.go (1)

482-482: Snapshot the winning round after unused view-hint warnings are materialized.

bestLogicalPlanCtx is saved before the deferred builder.HandleUnusedViewHints() runs. That is harmless with buildRound == 1, but as soon as a second round is enabled the final restoreLogicalPlanBuildCtx() will revive the winner without its unused qb_name/view-hint warnings. Either flush those warnings before saveLogicalPlanBuildCtx(), or persist them in the saved context.

Also applies to: 531-533, 609-610

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` at line 482, The saved bestLogicalPlanCtx is
captured before deferred builder.HandleUnusedViewHints() runs, causing the
restored winner to miss its qb_name/view-hint warnings when buildRound > 1; fix
by ensuring unused-view-hint warnings are materialized into the context before
snapshotting—either call builder.HandleUnusedViewHints() (or otherwise
flush/collect its warnings) prior to saveLogicalPlanBuildCtx(), or extend
saveLogicalPlanBuildCtx()/bestLogicalPlanCtx to persist the builder's
unused-view-hint warnings so restoreLogicalPlanBuildCtx() includes them; update
the same pattern around the other occurrences noted (lines referenced near
531-533 and 609-610).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/planner/optimize.go`:
- Around line 556-559: The timer for sessVars.DurationOptimizer.Total is
currently started too early (beginOpt) covering hint walk and logical-plan build
and thus overlaps Rewrite_time and reports time on nonLogical paths; move the
start/end timing to directly wrap each core.DoOptimize invocation (create a new
begin time immediately before calling core.DoOptimize and add the elapsed
duration to sessVars.DurationOptimizer.Total instead of overwriting it), ensure
the nonLogical path does not touch DurationOptimizer, and apply the same change
for the other similar timing sites referenced around the core.DoOptimize calls
(the blocks at the other noted locations).

---

Nitpick comments:
In `@pkg/planner/optimize.go`:
- Line 482: The saved bestLogicalPlanCtx is captured before deferred
builder.HandleUnusedViewHints() runs, causing the restored winner to miss its
qb_name/view-hint warnings when buildRound > 1; fix by ensuring unused-view-hint
warnings are materialized into the context before snapshotting—either call
builder.HandleUnusedViewHints() (or otherwise flush/collect its warnings) prior
to saveLogicalPlanBuildCtx(), or extend
saveLogicalPlanBuildCtx()/bestLogicalPlanCtx to persist the builder's
unused-view-hint warnings so restoreLogicalPlanBuildCtx() includes them; update
the same pattern around the other occurrences noted (lines referenced near
531-533 and 609-610).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8bc83890-e134-4cc9-bd3c-1d8f48ec0f89

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd8678 and bc1b396.

📒 Files selected for processing (1)
  • pkg/planner/optimize.go

Copy link

@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.

🧹 Nitpick comments (1)
pkg/planner/optimize.go (1)

482-534: Snapshot the winner context after unused view-hint warnings are applied.

Once buildRound is raised above 1, saveLogicalPlanBuildCtx() will capture StmtCtx before the deferred builder.HandleUnusedViewHints() runs. After restoreLogicalPlanBuildCtx(), the winning round comes back without its own unused qb_name/view-hint warnings. Please either snapshot after those warnings are appended, or delay emitting them until after the final winner is restored.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` around lines 482 - 534, The snapshot of the winning
round's StmtCtx is taken before deferred builder.HandleUnusedViewHints() runs,
so saved bestLogicalPlanCtx misses the winner's unused view-hint warnings;
ensure the winner context is captured after those warnings are applied by
invoking builder.HandleUnusedViewHints() (or moving the save) before calling
saveLogicalPlanBuildCtx() when updating *bestLogicalPlanCtx (references:
builder.HandleUnusedViewHints, saveLogicalPlanBuildCtx,
restoreLogicalPlanBuildCtx, and the section that updates
*bestPlan/*bestCost/*bestLogicalPlanCtx).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/planner/optimize.go`:
- Around line 482-534: The snapshot of the winning round's StmtCtx is taken
before deferred builder.HandleUnusedViewHints() runs, so saved
bestLogicalPlanCtx misses the winner's unused view-hint warnings; ensure the
winner context is captured after those warnings are applied by invoking
builder.HandleUnusedViewHints() (or moving the save) before calling
saveLogicalPlanBuildCtx() when updating *bestLogicalPlanCtx (references:
builder.HandleUnusedViewHints, saveLogicalPlanBuildCtx,
restoreLogicalPlanBuildCtx, and the section that updates
*bestPlan/*bestCost/*bestLogicalPlanCtx).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5989fe9c-c6c2-49dd-97ff-4575d440fce2

📥 Commits

Reviewing files that changed from the base of the PR and between bc1b396 and 7bb7149.

📒 Files selected for processing (1)
  • pkg/planner/optimize.go

Copy link

@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.

🧹 Nitpick comments (1)
pkg/planner/optimize.go (1)

467-481: Consider reducing parameter count for readability.

The function has 14 parameters, several of which are output pointers. While functional, this makes the signature hard to read and reason about. When multi-round selection is enabled, consider grouping related state into structs (e.g., a roundContext for timing/checked flags, a bestPlanState for winner tracking).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/optimize.go` around lines 467 - 481, The function
buildAndOptimizeLogicalPlanRound has too many parameters; refactor by creating
grouping structs to reduce the signature: introduce a roundContext (containing
checked *bool, optimizeStarted *bool, beginOpt *time.Time,
needRestoreLogicalPlanCtx bool) for per-round timing/flags and a bestPlanState
(containing bestPlan *base.PhysicalPlan, bestNames *types.NameSlice, bestCost
*float64, bestLogicalPlanCtx *logicalPlanBuildCtx) for winner tracking; replace
the corresponding parameters in buildAndOptimizeLogicalPlanRound with these two
structs (keeping ctx, sctx, node, is, hintProcessor as explicit inputs), update
all call sites to construct and pass the new structs, and ensure internal uses
reference the new struct fields instead of the previous standalone parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/planner/optimize.go`:
- Around line 467-481: The function buildAndOptimizeLogicalPlanRound has too
many parameters; refactor by creating grouping structs to reduce the signature:
introduce a roundContext (containing checked *bool, optimizeStarted *bool,
beginOpt *time.Time, needRestoreLogicalPlanCtx bool) for per-round timing/flags
and a bestPlanState (containing bestPlan *base.PhysicalPlan, bestNames
*types.NameSlice, bestCost *float64, bestLogicalPlanCtx *logicalPlanBuildCtx)
for winner tracking; replace the corresponding parameters in
buildAndOptimizeLogicalPlanRound with these two structs (keeping ctx, sctx,
node, is, hintProcessor as explicit inputs), update all call sites to construct
and pass the new structs, and ensure internal uses reference the new struct
fields instead of the previous standalone parameters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dc6904ef-4e95-4563-86e6-ba0fd87e8b29

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb7149 and f89fdd9.

📒 Files selected for processing (1)
  • pkg/planner/optimize.go

@ti-chi-bot
Copy link

ti-chi-bot bot commented Mar 10, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: guo-shaoge

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

The pull request process is described 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 Mar 10, 2026
@ti-chi-bot
Copy link

ti-chi-bot bot commented Mar 10, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-03-10 08:32:21.445315613 +0000 UTC m=+338972.957373274: ☑️ agreed by guo-shaoge.

@ti-chi-bot ti-chi-bot bot added the approved label Mar 10, 2026
@ti-chi-bot
Copy link

ti-chi-bot bot commented Mar 10, 2026

@AilinKid: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
idc-jenkins-ci-tidb/mysql-test f89fdd9 link true /test mysql-test

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. release-note-none Denotes a PR that doesn't merit a release note. sig/planner SIG: Planner size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants