Skip to content

planner: build multi alternative logical plan from shared AST#66677

Merged
ti-chi-bot[bot] merged 6 commits into
pingcap:masterfrom
AilinKid:ast-multi-logical-plan
Mar 6, 2026
Merged

planner: build multi alternative logical plan from shared AST#66677
ti-chi-bot[bot] merged 6 commits into
pingcap:masterfrom
AilinKid:ast-multi-logical-plan

Conversation

@AilinKid
Copy link
Copy Markdown
Contributor

@AilinKid AilinKid commented Mar 4, 2026

What problem does this PR solve?

Issue Number: ref #66676

Problem Summary:

This work is split into two steps for shared-AST multi logical plan build support.

This PR is Step 1 only: isolate planner build state so repeated builds from the same AST can be added safely in the follow-up step.

What changed and how does it work?

Main work in this PR (Step 1):

  • Audit remaining planner-held AST references and document the read-only ones.
  • Split QBHintHandler into:
    • shared AST-derived metadata,
    • per-build hint runtime state.
  • Add StatementContext logical-plan-build snapshot/restore support for mutable planning fields used by planner build/optimize paths (including warnings, plan-cache tracker state, and TableStats).

Not included in this PR:

  • The actual optimize-path behavior change for repeated logical-plan builds from the same AST.
  • Candidate winner-selection behavior changes for multi-build (planned in Step 2).

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.

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.

None

Summary by CodeRabbit

  • Refactor

    • Improved isolation and lifecycle of hint processing and logical-plan build state to thread per-build hint state through view resolution and reduce spurious warnings; moved unused-view-hint handling into the plan builder for consistent warning timing.
  • New Features

    • Exposed snapshot/restore for planner build state and plan-cache tracker; added accessors to inspect/handle per-build hint state; safer deep-copy of return types to avoid shared mutation.
  • Tests

    • Added tests for build-state restore and hint-handler build-state behavior.

@ti-chi-bot ti-chi-bot Bot added release-note-none Denotes a PR that doesn't merit a release note. do-not-merge/needs-triage-completed labels Mar 4, 2026
@pantheon-ai
Copy link
Copy Markdown

pantheon-ai Bot commented Mar 4, 2026

@AilinKid I've received your pull request and will start the review. I'll conduct a thorough review covering code quality, potential issues, and implementation details.

⏳ This process typically takes 10-30 minutes depending on the complexity of the changes.

ℹ️ Learn more details on Pantheon AI.

@ti-chi-bot ti-chi-bot Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. sig/planner SIG: Planner labels Mar 4, 2026
@tiprow
Copy link
Copy Markdown

tiprow Bot commented Mar 4, 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
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Threads a per-build QB hint state through planner/view resolution, moves transient hint bookkeeping out of handler fields into QBHintBuildState, adds snapshot/restore APIs for planner and plan-cache state, and updates tests and init flows to use the new state.

Changes

Cohort / File(s) Summary
Hint subsystem & per-build state
pkg/util/hint/hint_query_block.go
Introduce QBHintBuildState and NewBuildState(). Remove handler-level QBOffsetToHints/ViewQBNameUsed. Update GetCurrentStmtHints(..., state), HandleUnusedViewHints(state, ...), MarkViewQBNameUsed(), and add SetWarns() to operate on explicit build state.
Logical plan builder & hint threading
pkg/planner/core/logical_plan_builder.go, pkg/planner/core/planbuilder.go, pkg/planner/optimize.go
Add hintState to PlanBuilder with GetHintState() and HandleUnusedViewHints(). Thread per-view hintState through view resolution and pushTableHints; move defer for unused-view-hints handling to PlanBuilder.
StatementContext planner-state snapshot
pkg/sessionctx/stmtctx/stmtctx.go, pkg/sessionctx/stmtctx/stmtctx_test.go, pkg/sessionctx/stmtctx/BUILD.bazel
Add LogicalPlanBuildState plus SaveLogicalPlanBuildState() / RestoreLogicalPlanBuildState() to snapshot/restore planner build-time fields. Add tests (TestLogicalPlanBuildStateRestore, TestQBHintHandlerBuildState) and update test shard/deps.
Plan cache tracker snapshot
pkg/util/context/plancache.go
Add Save() and Restore() on PlanCacheTracker to snapshot and rehydrate plan-cache related flags/strings.
Misc (type safety / deep copies)
pkg/planner/core/planbuilder.go
Use DeepCopy() for returned types to avoid shared mutation during plan building.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PlanBuilder
  participant HintHandler
  participant ViewResolver
  participant StmtCtx

  Client->>PlanBuilder: Start plan build
  PlanBuilder->>HintHandler: NewBuildState() -> hintState
  PlanBuilder->>StmtCtx: SaveLogicalPlanBuildState()
  PlanBuilder->>ViewResolver: BuildDataSourceFromView(hintState)
  ViewResolver->>HintHandler: GetCurrentStmtHints(hints, level, hintState)
  ViewResolver->>HintHandler: MarkViewQBNameUsed(qbName, hintState)
  ViewResolver-->>PlanBuilder: View plan
  PlanBuilder->>HintHandler: HandleUnusedViewHints(hintState) (deferred)
  PlanBuilder->>StmtCtx: RestoreLogicalPlanBuildState() (if needed)
  PlanBuilder-->>Client: Complete plan build
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • qw4990
  • guo-shaoge
  • yudongusa

Poem

🐰 I hopped through code, a careful sprite,

Collected hints and tucked them tight.
Each build gets its own tiny chest,
Saved and restored — the planner rests.
A tidy burrow, neat and bright.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% 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 'planner: build multi alternative logical plan from shared AST' clearly and specifically describes the main objective of this PR — enabling the building of multiple alternative logical plans from a single shared AST.
Description check ✅ Passed The PR description covers all required template sections: Issue Number is provided (ref #66676), Problem Summary explains the work is Step 1 of a two-step effort to isolate planner build state, What Changed section details the main work (auditing AST references, splitting QBHintHandler, adding StatementContext snapshot/restore), and the Checklist is completed with Unit test marked.

✏️ 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

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.

@pantheon-ai
Copy link
Copy Markdown

pantheon-ai Bot commented Mar 4, 2026

Review Complete

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

ℹ️ Learn more details on Pantheon AI.

@ti-chi-bot ti-chi-bot Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Mar 4, 2026
@AilinKid AilinKid closed this Mar 4, 2026
@AilinKid AilinKid reopened this Mar 4, 2026
@AilinKid AilinKid changed the title planner: isolate logical plan rebuild state for shared AST planner: build multi alternative logical plan from shared AST Mar 4, 2026
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: 2

🧹 Nitpick comments (1)
pkg/planner/core/planbuilder.go (1)

477-479: Defensively clear/guard hintState to avoid stale state reuse.

If Init(...) is called with processor == nil after a prior initialized run, b.hintState can remain stale. HandleUnusedViewHints() also assumes non-nil state when processor exists. A small defensive reset/guard avoids accidental leakage paths.

Suggested patch
 func (b *PlanBuilder) Init(sctx base.PlanContext, is infoschema.InfoSchema, processor *hint.QBHintHandler) (*PlanBuilder, []ast.HintTable) {
@@
 	b.ctx = sctx
 	b.is = is
 	b.hintProcessor = processor
+	b.hintState = nil
 	if processor != nil {
 		b.hintState = processor.NewBuildState()
 	}
@@
 func (b *PlanBuilder) HandleUnusedViewHints() {
-	if b.hintProcessor == nil {
+	if b.hintProcessor == nil || b.hintState == nil {
 		return
 	}
 	b.hintProcessor.SetWarns(b.hintProcessor.HandleUnusedViewHints(b.hintState, nil))
 }

Also applies to: 521-527

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

In `@pkg/planner/core/planbuilder.go` around lines 477 - 479, Init currently only
sets b.hintState when processor != nil, risking stale state if Init is later
called with processor == nil; ensure b.hintState is defensively cleared when
processor is nil by setting b.hintState = nil in the Init path where processor
== nil, and similarly guard or clear before/after any code that uses hintState
(e.g., HandleUnusedViewHints) so callers cannot assume non-nil state; reference
the Init method, b.hintState field, processor.NewBuildState(), and
HandleUnusedViewHints() when making the change.
🤖 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 555-560: The selection logic only updates winner when cost <
bestCost, which can leave bestPlan nil if the first successful candidate has a
non-finite/edge cost; change the update condition in the blocks that assign
bestCost/bestPlan/bestNames/bestState (the code that currently sets bestCost =
cost; bestPlan = finalPlan; bestNames = names; bestState =
captureLogicalPlanBuildBaseline(sessVars)) to always take the first successful
candidate or when cost < bestCost — e.g., if bestPlan == nil || cost < bestCost
then assign bestCost, bestPlan, bestNames and call
captureLogicalPlanBuildBaseline(sessVars); apply the same change to the other
similar block that currently uses only cost < bestCost so a valid plan is
selected even if its cost is non-finite.

In `@pkg/util/hint/hint_query_block.go`:
- Around line 269-273: The SetWarns method on QBHintHandler can panic if
p.warnHandler is nil; update SetWarns to guard before calling
p.warnHandler.SetHintWarning by checking if p.warnHandler == nil and returning
early (or skip warnings) when nil, so iterate warns only when warnHandler is
non-nil; reference QBHintHandler.SetWarns and the warnHandler field and
SetHintWarning method when making the change.

---

Nitpick comments:
In `@pkg/planner/core/planbuilder.go`:
- Around line 477-479: Init currently only sets b.hintState when processor !=
nil, risking stale state if Init is later called with processor == nil; ensure
b.hintState is defensively cleared when processor is nil by setting b.hintState
= nil in the Init path where processor == nil, and similarly guard or clear
before/after any code that uses hintState (e.g., HandleUnusedViewHints) so
callers cannot assume non-nil state; reference the Init method, b.hintState
field, processor.NewBuildState(), and HandleUnusedViewHints() when making the
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2a11a716-ee81-4aed-bb8c-5dc8e3495185

📥 Commits

Reviewing files that changed from the base of the PR and between a833b14 and 918214e.

📒 Files selected for processing (8)
  • pkg/planner/core/logical_plan_builder.go
  • pkg/planner/core/planbuilder.go
  • pkg/planner/optimize.go
  • pkg/sessionctx/stmtctx/BUILD.bazel
  • pkg/sessionctx/stmtctx/stmtctx.go
  • pkg/sessionctx/stmtctx/stmtctx_test.go
  • pkg/util/context/plancache.go
  • pkg/util/hint/hint_query_block.go

Comment thread pkg/planner/optimize.go Outdated
Comment thread pkg/util/hint/hint_query_block.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 4, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.6809%. Comparing base (21b5bb6) to head (82d97da).
⚠️ Report is 13 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #66677        +/-   ##
================================================
+ Coverage   77.6714%   77.6809%   +0.0094%     
================================================
  Files          2008       1929        -79     
  Lines        549230     538988     -10242     
================================================
- Hits         426595     418691      -7904     
+ Misses       120964     120285       -679     
+ Partials       1671         12      -1659     
Flag Coverage Δ
integration 41.5614% <50.4000%> (-6.6316%) ⬇️
unit 76.7992% <85.3846%> (+0.4812%) ⬆️

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

Components Coverage Δ
dumpling 56.7974% <ø> (ø)
parser ∅ <ø> (∅)
br 48.8279% <ø> (-12.0576%) ⬇️
🚀 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.

Comment thread pkg/planner/optimize.go Outdated
@AilinKid
Copy link
Copy Markdown
Contributor Author

AilinKid commented Mar 4, 2026

/retest-required

@tiprow
Copy link
Copy Markdown

tiprow Bot commented Mar 4, 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.

Comment thread pkg/util/context/plancache.go Outdated
@AilinKid AilinKid force-pushed the ast-multi-logical-plan branch from 918214e to d7b27b5 Compare March 5, 2026 08:11
@pantheon-ai
Copy link
Copy Markdown

pantheon-ai Bot commented Mar 5, 2026

Review Complete

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

ℹ️ Learn more details on Pantheon AI.

@pantheon-ai
Copy link
Copy Markdown

pantheon-ai Bot commented Mar 5, 2026

Review Complete

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

ℹ️ Learn more details on Pantheon AI.

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

♻️ Duplicate comments (1)
pkg/util/hint/hint_query_block.go (1)

269-273: ⚠️ Potential issue | 🟠 Major

Guard SetWarns against nil receiver/warn handler to avoid panic.

At Line 272, p.warnHandler.SetHintWarning(one) can panic when p == nil or p.warnHandler == nil.

Suggested fix
func (p *QBHintHandler) SetWarns(warns []string) {
+	if p == nil || p.warnHandler == nil || len(warns) == 0 {
+		return
+	}
 	for _, one := range warns {
 		p.warnHandler.SetHintWarning(one)
 	}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/util/hint/hint_query_block.go` around lines 269 - 273, Guard SetWarns
against a nil receiver or missing warn handler: in QBHintHandler.SetWarns check
if p == nil or p.warnHandler == nil and return early before iterating, then
proceed to loop calling p.warnHandler.SetHintWarning(one); this prevents panics
from dereferencing a nil QBHintHandler or its warnHandler.
🤖 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/sessionctx/stmtctx/stmtctx.go`:
- Line 608: The current use of maps.Clone(sc.TableStats) in
SaveLogicalPlanBuildState does a shallow copy and leaves pointers to mutable
*statistics.Table objects that can be modified later; replace the shallow clone
with a deep-copy that iterates sc.TableStats and calls
statistics.Table.CopyAs(...) for each value (using an appropriate CopyIntent) so
the saved tableStats snapshot contains independent copies; ensure the new map
stores the copied *statistics.Table instances instead of the original pointers.

---

Duplicate comments:
In `@pkg/util/hint/hint_query_block.go`:
- Around line 269-273: Guard SetWarns against a nil receiver or missing warn
handler: in QBHintHandler.SetWarns check if p == nil or p.warnHandler == nil and
return early before iterating, then proceed to loop calling
p.warnHandler.SetHintWarning(one); this prevents panics from dereferencing a nil
QBHintHandler or its warnHandler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cc763ff8-7f42-4e8b-b72d-abb32adebb46

📥 Commits

Reviewing files that changed from the base of the PR and between 918214e and d7b27b5.

📒 Files selected for processing (7)
  • pkg/planner/core/logical_plan_builder.go
  • pkg/planner/core/planbuilder.go
  • pkg/sessionctx/stmtctx/BUILD.bazel
  • pkg/sessionctx/stmtctx/stmtctx.go
  • pkg/sessionctx/stmtctx/stmtctx_test.go
  • pkg/util/context/plancache.go
  • pkg/util/hint/hint_query_block.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/sessionctx/stmtctx/stmtctx_test.go
  • pkg/planner/core/planbuilder.go
  • pkg/util/context/plancache.go

Comment thread pkg/sessionctx/stmtctx/stmtctx.go
Comment thread pkg/planner/optimize.go
@ti-chi-bot ti-chi-bot Bot added approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Mar 5, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Mar 5, 2026

[APPROVALNOTIFIER] This PR is APPROVED

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

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 lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Mar 5, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Mar 5, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-03-05 10:21:24.50760541 +0000 UTC m=+439929.085684604: ☑️ agreed by qw4990.
  • 2026-03-05 11:51:17.654404605 +0000 UTC m=+445322.232483799: ☑️ agreed by guo-shaoge.

@AilinKid
Copy link
Copy Markdown
Contributor Author

AilinKid commented Mar 6, 2026

/ok-to-test

@ti-chi-bot ti-chi-bot Bot added the ok-to-test Indicates a PR is ready to be tested. label Mar 6, 2026
@AilinKid
Copy link
Copy Markdown
Contributor Author

AilinKid commented Mar 6, 2026

/retest-required

1 similar comment
@AilinKid
Copy link
Copy Markdown
Contributor Author

AilinKid commented Mar 6, 2026

/retest-required

@ti-chi-bot ti-chi-bot Bot merged commit d59e531 into pingcap:master Mar 6, 2026
31 checks passed
@ti-chi-bot ti-chi-bot Bot added the needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. label Jun 2, 2026
@ti-chi-bot
Copy link
Copy Markdown
Member

In response to a cherrypick label: new pull request created to branch release-8.5: #68859.
But this PR has conflicts, please resolve them!

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

Labels

approved lgtm needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. ok-to-test Indicates a PR is ready to be tested. release-note-none Denotes a PR that doesn't merit a release note. sig/planner SIG: Planner size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants