Skip to content

planner: pre-refactor for join reorder conflict detection algorithm#68870

Open
guo-shaoge wants to merge 6 commits into
pingcap:release-8.5from
guo-shaoge:cp_pre_cdc_impl
Open

planner: pre-refactor for join reorder conflict detection algorithm#68870
guo-shaoge wants to merge 6 commits into
pingcap:release-8.5from
guo-shaoge:cp_pre_cdc_impl

Conversation

@guo-shaoge
Copy link
Copy Markdown
Collaborator

@guo-shaoge guo-shaoge commented Jun 2, 2026

What problem does this PR solve?

Issue Number: close #66088

Problem Summary: manually cherry pick #66087

What changed and how does it work?

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

Release Notes

  • New Features

    • Added support for nested tuple forms in join-ordering optimizer hints, enabling more granular control over query execution plans.
  • Improvements

    • Refactored join-reordering logic for improved code organization and maintainability.

Signed-off-by: guo-shaoge <shaoge1994@163.com>
Signed-off-by: guo-shaoge <shaoge1994@163.com>
Signed-off-by: guo-shaoge <shaoge1994@163.com>
Signed-off-by: guo-shaoge <shaoge1994@163.com>
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 2, 2026

This cherry pick PR is for a release branch and has not yet been approved by triage owners.
Adding the do-not-merge/cherry-pick-not-approved label.

To merge this cherry pick:

  1. It must be LGTMed and approved by the reviewers firstly.
  2. For pull requests to TiDB-x branches, it must have no failed tests.
  3. AFTER it has lgtm and approved labels, please wait for the cherry-pick merging approval from triage owners.
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.

@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 Jun 2, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces structured AST support for nested LEADING(...) optimizer hints, creates a shared joinorder utilities package for join-reorder logic, and refactors rule_join_reorder to use centralized hint handling. It also adds schema equality comparison and extracts common projection and cost helpers.

Changes

LEADING Hint Parsing and Propagation

Layer / File(s) Summary
Schema equality comparison
pkg/expression/schema.go
Adds Schema.Equal() method comparing two schemas by nil-ness, column count, and per-position column equality without considering keys.
AST structures for nested LEADING hints
pkg/parser/ast/misc.go, pkg/parser/ast/misc_test.go
Introduces LeadingList AST node, FlattenLeadingList helper to extract flat table list, RestoreWithQB for rendering nested LEADING with query-block awareness, and updates TableOptimizerHint.Restore to handle nested structures. Tests verify nested parenthesized LEADING forms and column/table identifiers.
Parser grammar for nested LEADING hints
pkg/parser/hintparser.y
Extends %union with leadingList and leadingElement payload fields; adds LeadingTableList and LeadingTableElement nonterminals to parse comma-separated and parenthesized nested table/list items; removes duplicate "LEADING" from hint-name enumeration to route through dedicated LEADING production.
Generated parser code and symbol tables
pkg/parser/hintparser.go
Regenerated parsing tables, symbol names, reductions, and parse automaton to support nested LEADING hint construction; updates symbol type to carry leadingElement interface{}; sets HintData to structured *ast.LeadingList and initializes flat Tables via FlattenLeadingList for compatibility.
Parser test cases for LEADING hints
pkg/parser/hintparser_test.go, pkg/parser/parser_test.go
Added test cases validating parsing of LEADING(a,(b,(c,d))) and similar nested forms; updated TestOptimizerHints to verify HintData as *ast.LeadingList with nested Items structure instead of flat Tables slice.
Hint propagation with structured LEADING data
pkg/util/hint/hint.go
Adds LeadingList field to PlanHints; updates ParsePlanHints to extract *ast.LeadingList from HintData when handling HintLeading and propagate it in returned PlanHints.

Join-order Utility Library and Rule Refactoring

Layer / File(s) Summary
Join-order utilities package
pkg/planner/core/BUILD.bazel, pkg/planner/core/joinorder/BUILD.bazel, pkg/planner/core/joinorder/util.go
Creates new public joinorder library providing CheckAndGenerateLeadingHint (validates single distinct hint), BuildLeadingTreeFromList (generic tree constructor), FindAndRemovePlanByAstHint (hint-to-plan matcher with DB/QB alias fallback), IsDerivedTableInLeadingHint (derived-table checking), and SetNewJoinWithHint (hint application to join nodes).
Logical projection injection helper
pkg/planner/core/operator/logicalop/logical_projection.go
Adds InjectExpr helper wrapping/creating LogicalProjection above plans and appending expressions with proper schema/child setup.
Cost model helper extraction
pkg/planner/core/plan_cost_ver2.go
Replaces local cols2Exprs helper with shared expression.Column2Exprs for join key cost calculation in PhysicalMergeJoin.GetPlanCostVer2.
Rule join reorder refactoring to use shared utilities
pkg/planner/core/rule_join_reorder.go
Updates rule_join_reorder to use joinorder.CheckAndGenerateLeadingHint, joinorder.SetNewJoinWithHint for hint handling; replaces local injectExpr with logicalop.InjectExpr for projections; simplifies schema reconciliation to p.Schema().Equal(originalSchema); removes local checkAndGenerateLeadingHint, joinMethodHint struct, and helper methods; updates basicJoinGroupInfo.joinMethodHintInfo type to map[int]*joinorder.JoinMethodHint.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pingcap/tidb#67305: Adds order-aware join reorder rule and OrderedLeadingChoice annotation; depends on the shared joinorder package infrastructure introduced in this PR.

Suggested labels

sig/planner, size/XL, ok-to-test, cherry-pick-approved, type/cherry-pick-for-release-8.5

Suggested reviewers

  • hawkingrei
  • AilinKid
  • qw4990

Poem

🐰 Twitch, twitch! A rabbit hops through nested LEADING hints,
Restructuring join orders with gleaming checklists,
Schema.Equal() whispers: both paths align,
Shared utilities bloom—join-reorder refines!
Binky!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.42% 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 PR title accurately describes the main change: a pre-refactor for join reorder conflict detection algorithm in the planner, aligning with the primary objective.
Description check ✅ Passed The PR description includes the issue number, problem summary, and checked test status. However, it lacks details in 'What changed and how does it work?' section.
Linked Issues check ✅ Passed The PR implements preparatory refactoring for join reorder conflict detection with support for nested LEADING hints, schema equality, and hint-related utilities. Changes align with the linked issue's objective.
Out of Scope Changes check ✅ Passed All changes are focused on supporting nested LEADING hints, schema comparison, and join reorder utilities. Minor changes to BUILD.bazel files and projection helpers are support-level changes necessary for the main refactoring.

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

@guo-shaoge guo-shaoge changed the title Cp pre cdc impl planner: pre-refactor for join reorder conflict detection algorithm Jun 2, 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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/util/hint/hint.go (1)

955-983: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear LeadingList when LEADING is invalidated.

This branch invalidates conflicting LEADING hints by emptying LeadingJoinOrder, but it still returns the parsed LeadingList. The new join-order helpers read PlanHints.LeadingList directly, so LEADING + STRAIGHT_JOIN or multiple LEADING hints can still influence join-order selection after being marked invalid here.

Suggested fix
 	if leadingHintCnt > 1 || (leadingHintCnt > 0 && straightJoinOrder) {
 		// If there are more leading hints or the straight_join hint existes, all leading hints will be invalid.
 		leadingJoinOrder = leadingJoinOrder[:0]
+		leadingList = nil
 		if leadingHintCnt > 1 {
 			warnHandler.SetHintWarning("We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid")
 		} else if straightJoinOrder {
 			warnHandler.SetHintWarning("We can only use the straight_join hint, when we use the leading hint and straight_join hint at the same time, all leading hints will be invalid")
 		}
 	}
🤖 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/util/hint/hint.go` around lines 955 - 983, The code invalidates LEADING
by clearing leadingJoinOrder but still returns LeadingList, so
PlanHints.LeadingList remains populated and can affect join-order; update the
branch that checks leadingHintCnt and straightJoinOrder (the block manipulating
leadingJoinOrder and warnHandler.SetHintWarning) to also clear leadingList (or
set PlanHints.LeadingList to empty) when LEADING is deemed invalidated so the
returned PlanHints has LeadingList cleared alongside LeadingJoinOrder and the
same warning behavior is preserved.
🧹 Nitpick comments (1)
pkg/expression/schema.go (1)

86-100: ⚡ Quick win

Clarify that Equal only compares ordered columns.

The implementation is narrower than the name/comment suggest: it ignores Keys and UniqueKeys. That is fine for the new join-reorder caller, but the exported contract should say so explicitly or use a more specific name to avoid future misuse.

As per coding guidelines, "Keep exported-symbol doc comments, and prefer semantic constraints over name restatement".

🤖 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/expression/schema.go` around lines 86 - 100, The Schema.Equal method
currently only compares ordered Columns and ignores Keys and UniqueKeys, which
is narrower than its name suggests; update the exported doc comment for
Schema.Equal to explicitly state it compares only the ordered Columns (and not
Keys or UniqueKeys) or rename the method to a more specific name (e.g.,
EqualColumnsOrdered) to avoid misuse; locate the method by the symbol
Schema.Equal and modify its comment to describe the exact contract (ordered
column-wise equality) and, if renaming, update all call sites to use the new
name.
🤖 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/parser/ast/misc.go`:
- Around line 3967-4005: The bug is that when the first LeadingList item is
itself a nested LeadingList, RestoreWithQB pushes the hint-level QB into the
child causing LEADING((`@qb` a, b), c) instead of LEADING(`@qb` (a, b), c); modify
LeadingList.RestoreWithQB so that in the case "case *LeadingList" if i == 0 and
currentQBName.L != "" and !qbOnTable you emit the "@<qb>"
(ctx.WriteKeyWord("@"); ctx.WriteName(currentQBName.String()); ctx.WritePlain("
")) before restoring the child and then call t.RestoreWithQB with an empty
model.CIStr for qbName (so the child does not receive the QB), afterwards clear
currentQBName; otherwise keep the existing behavior of passing currentQBName
into the child.

In `@pkg/parser/hintparser_test.go`:
- Around line 369-474: The LEADING hint test cases use ast.NewCIStr but NewCIStr
is defined in the model package; update the LEADING test block to replace all
ast.NewCIStr(...) occurrences with model.NewCIStr(...). Specifically modify the
HintName fields and all HintTable.TableName constructors in the
TableOptimizerHint / LeadingList test cases so they use model.NewCIStr, leaving
the surrounding structures (TableOptimizerHint, LeadingList, HintTable,
HintName, Tables) unchanged.

In `@pkg/planner/core/joinorder/util.go`:
- Around line 221-225: The fallback path in util.go currently computes dbMatch
as "astTbl.DBName.L == '' || astTbl.DBName.L == blockName.DBName.L", which drops
the "*" DB wildcard semantics; update the dbMatch logic used in the blockOffset
> 1 fallback so it treats "*" as a wildcard (e.g., accept when astTbl.DBName.L
== "*" or blockName.DBName.L == "*") in addition to the empty or exact-match
cases, keeping tableMatch unchanged; modify the dbMatch check near variables
blockOffset, queryBlockNames, blockName, astTbl, dbMatch so LEADING(*.alias)
still matches derived-table aliases in this fallback path.

---

Outside diff comments:
In `@pkg/util/hint/hint.go`:
- Around line 955-983: The code invalidates LEADING by clearing leadingJoinOrder
but still returns LeadingList, so PlanHints.LeadingList remains populated and
can affect join-order; update the branch that checks leadingHintCnt and
straightJoinOrder (the block manipulating leadingJoinOrder and
warnHandler.SetHintWarning) to also clear leadingList (or set
PlanHints.LeadingList to empty) when LEADING is deemed invalidated so the
returned PlanHints has LeadingList cleared alongside LeadingJoinOrder and the
same warning behavior is preserved.

---

Nitpick comments:
In `@pkg/expression/schema.go`:
- Around line 86-100: The Schema.Equal method currently only compares ordered
Columns and ignores Keys and UniqueKeys, which is narrower than its name
suggests; update the exported doc comment for Schema.Equal to explicitly state
it compares only the ordered Columns (and not Keys or UniqueKeys) or rename the
method to a more specific name (e.g., EqualColumnsOrdered) to avoid misuse;
locate the method by the symbol Schema.Equal and modify its comment to describe
the exact contract (ordered column-wise equality) and, if renaming, update all
call sites to use the new name.
🪄 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: 362404df-e24a-4cdb-ab10-6a67cd93d0ba

📥 Commits

Reviewing files that changed from the base of the PR and between e198e2d and 153d1cc.

📒 Files selected for processing (14)
  • pkg/expression/schema.go
  • pkg/parser/ast/misc.go
  • pkg/parser/ast/misc_test.go
  • pkg/parser/hintparser.go
  • pkg/parser/hintparser.y
  • pkg/parser/hintparser_test.go
  • pkg/parser/parser_test.go
  • pkg/planner/core/BUILD.bazel
  • pkg/planner/core/joinorder/BUILD.bazel
  • pkg/planner/core/joinorder/util.go
  • pkg/planner/core/operator/logicalop/logical_projection.go
  • pkg/planner/core/plan_cost_ver2.go
  • pkg/planner/core/rule_join_reorder.go
  • pkg/util/hint/hint.go

Comment thread pkg/parser/ast/misc.go
Comment on lines +3967 to +4005
func (lt *LeadingList) RestoreWithQB(ctx *format.RestoreCtx, qbName model.CIStr, needParen bool, isTop bool, qbOnTable bool) error {
if lt == nil || len(lt.Items) == 0 {
return nil
}
if needParen {
ctx.WritePlain("(")
}

currentQBName := qbName // hint level QBName

for i, item := range lt.Items {
if i > 0 {
ctx.WritePlain(", ")
}

switch t := item.(type) {
case *HintTable:
if i == 0 && currentQBName.L != "" && !qbOnTable {
ctx.WriteKeyWord("@")
ctx.WriteName(currentQBName.String())
ctx.WritePlain(" ")
t.Restore(ctx)
currentQBName = model.CIStr{}
} else {
t.Restore(ctx)
}
case *LeadingList:
if err := t.RestoreWithQB(ctx, currentQBName, true, false, qbOnTable); err != nil {
return err
}
currentQBName = model.CIStr{}
default:
return fmt.Errorf("unexpected type in LeadingList: %T", t)
}
}
if needParen {
ctx.WritePlain(")")
}
return nil
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

Preserve the hint-level QB before a parenthesized first LEADING group.

When the first LEADING element is nested, Line 3994 pushes QBName into the child list, so Restore emits LEADING((@QB a, b), c) instead of LEADING(@QB (a, b), c). That breaks restore/parse round-tripping for valid hints with a hint-level query block.

Suggested fix
 	case *LeadingList:
-		if err := t.RestoreWithQB(ctx, currentQBName, true, false, qbOnTable); err != nil {
+		childQBName := currentQBName
+		if i == 0 && childQBName.L != "" && !qbOnTable {
+			ctx.WriteKeyWord("@")
+			ctx.WriteName(childQBName.String())
+			ctx.WritePlain(" ")
+			childQBName = model.CIStr{}
+		}
+		if err := t.RestoreWithQB(ctx, childQBName, true, false, qbOnTable); err != nil {
 			return err
 		}
 		currentQBName = model.CIStr{}
🤖 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/parser/ast/misc.go` around lines 3967 - 4005, The bug is that when the
first LeadingList item is itself a nested LeadingList, RestoreWithQB pushes the
hint-level QB into the child causing LEADING((`@qb` a, b), c) instead of
LEADING(`@qb` (a, b), c); modify LeadingList.RestoreWithQB so that in the case
"case *LeadingList" if i == 0 and currentQBName.L != "" and !qbOnTable you emit
the "@<qb>" (ctx.WriteKeyWord("@"); ctx.WriteName(currentQBName.String());
ctx.WritePlain(" ")) before restoring the child and then call t.RestoreWithQB
with an empty model.CIStr for qbName (so the child does not receive the QB),
afterwards clear currentQBName; otherwise keep the existing behavior of passing
currentQBName into the child.

Comment on lines +369 to +474
{
input: "LEADING(a,(b,(c,d)))",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("b")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("c")},
&ast.HintTable{TableName: ast.NewCIStr("d")},
},
},
},
},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
{TableName: ast.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(a,b,c)",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.HintTable{TableName: ast.NewCIStr("b")},
&ast.HintTable{TableName: ast.NewCIStr("c")},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
},
},
},
},
{
input: "LEADING((a,b),(c,d))",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.HintTable{TableName: ast.NewCIStr("b")},
},
},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("c")},
&ast.HintTable{TableName: ast.NewCIStr("d")},
},
},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
{TableName: ast.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(x,(y,z),w)",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("x")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("y")},
&ast.HintTable{TableName: ast.NewCIStr("z")},
},
},
&ast.HintTable{TableName: ast.NewCIStr("w")},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("x")},
{TableName: ast.NewCIStr("y")},
{TableName: ast.NewCIStr("z")},
{TableName: ast.NewCIStr("w")},
},
},
},
},
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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking whether pkg/parser/ast defines or re-exports NewCIStr..."
rg -n '(^func NewCIStr\b|^var NewCIStr\b|type CIStr\b)' pkg/parser/ast pkg/parser/model

echo
echo "Showing NewCIStr call sites in the updated test..."
rg -n 'ast\.NewCIStr|model\.NewCIStr' pkg/parser/hintparser_test.go

Repository: pingcap/tidb

Length of output: 6357


Fix build break in LEADING hint assertions: use model.NewCIStr (not ast.NewCIStr)

pkg/parser/hintparser_test.go’s LEADING cases construct HintName: ast.NewCIStr(...) and ast.HintTable{TableName: ast.NewCIStr(...)}, but NewCIStr is defined in pkg/parser/model (not pkg/parser/ast). Replace all ast.NewCIStr(...) with model.NewCIStr(...) in this block.

Suggested fix
-					HintName: ast.NewCIStr("LEADING"),
+					HintName: model.NewCIStr("LEADING"),
...
-							&ast.HintTable{TableName: ast.NewCIStr("a")},
+							&ast.HintTable{TableName: model.NewCIStr("a")},
📝 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
{
input: "LEADING(a,(b,(c,d)))",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("b")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("c")},
&ast.HintTable{TableName: ast.NewCIStr("d")},
},
},
},
},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
{TableName: ast.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(a,b,c)",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.HintTable{TableName: ast.NewCIStr("b")},
&ast.HintTable{TableName: ast.NewCIStr("c")},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
},
},
},
},
{
input: "LEADING((a,b),(c,d))",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("a")},
&ast.HintTable{TableName: ast.NewCIStr("b")},
},
},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("c")},
&ast.HintTable{TableName: ast.NewCIStr("d")},
},
},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("a")},
{TableName: ast.NewCIStr("b")},
{TableName: ast.NewCIStr("c")},
{TableName: ast.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(x,(y,z),w)",
output: []*ast.TableOptimizerHint{
{
HintName: ast.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("x")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: ast.NewCIStr("y")},
&ast.HintTable{TableName: ast.NewCIStr("z")},
},
},
&ast.HintTable{TableName: ast.NewCIStr("w")},
},
},
Tables: []ast.HintTable{
{TableName: ast.NewCIStr("x")},
{TableName: ast.NewCIStr("y")},
{TableName: ast.NewCIStr("z")},
{TableName: ast.NewCIStr("w")},
},
},
},
},
{
input: "LEADING(a,(b,(c,d)))",
output: []*ast.TableOptimizerHint{
{
HintName: model.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("a")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("b")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("c")},
&ast.HintTable{TableName: model.NewCIStr("d")},
},
},
},
},
},
},
Tables: []ast.HintTable{
{TableName: model.NewCIStr("a")},
{TableName: model.NewCIStr("b")},
{TableName: model.NewCIStr("c")},
{TableName: model.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(a,b,c)",
output: []*ast.TableOptimizerHint{
{
HintName: model.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("a")},
&ast.HintTable{TableName: model.NewCIStr("b")},
&ast.HintTable{TableName: model.NewCIStr("c")},
},
},
Tables: []ast.HintTable{
{TableName: model.NewCIStr("a")},
{TableName: model.NewCIStr("b")},
{TableName: model.NewCIStr("c")},
},
},
},
},
{
input: "LEADING((a,b),(c,d))",
output: []*ast.TableOptimizerHint{
{
HintName: model.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("a")},
&ast.HintTable{TableName: model.NewCIStr("b")},
},
},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("c")},
&ast.HintTable{TableName: model.NewCIStr("d")},
},
},
},
},
Tables: []ast.HintTable{
{TableName: model.NewCIStr("a")},
{TableName: model.NewCIStr("b")},
{TableName: model.NewCIStr("c")},
{TableName: model.NewCIStr("d")},
},
},
},
},
{
input: "LEADING(x,(y,z),w)",
output: []*ast.TableOptimizerHint{
{
HintName: model.NewCIStr("LEADING"),
HintData: &ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("x")},
&ast.LeadingList{
Items: []interface{}{
&ast.HintTable{TableName: model.NewCIStr("y")},
&ast.HintTable{TableName: model.NewCIStr("z")},
},
},
&ast.HintTable{TableName: model.NewCIStr("w")},
},
},
Tables: []ast.HintTable{
{TableName: model.NewCIStr("x")},
{TableName: model.NewCIStr("y")},
{TableName: model.NewCIStr("z")},
{TableName: model.NewCIStr("w")},
},
},
},
},
🤖 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/parser/hintparser_test.go` around lines 369 - 474, The LEADING hint test
cases use ast.NewCIStr but NewCIStr is defined in the model package; update the
LEADING test block to replace all ast.NewCIStr(...) occurrences with
model.NewCIStr(...). Specifically modify the HintName fields and all
HintTable.TableName constructors in the TableOptimizerHint / LeadingList test
cases so they use model.NewCIStr, leaving the surrounding structures
(TableOptimizerHint, LeadingList, HintTable, HintName, Tables) unchanged.

Comment on lines +221 to +225
if blockOffset > 1 && blockOffset < len(queryBlockNames) {
blockName := queryBlockNames[blockOffset]
dbMatch := astTbl.DBName.L == "" || astTbl.DBName.L == blockName.DBName.L
tableMatch := astTbl.TableName.L == blockName.TableName.L
if dbMatch && tableMatch {
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

Preserve * DB wildcard in the subquery-alias fallback.

The fallback path drops the wildcard DB semantics that direct matching already supports. LEADING(*.alias) can match a derived-table alias in step 1, but the same binding stops matching in step 2 because dbMatch only accepts empty or exact DB names here.

Suggested fix
-			dbMatch := astTbl.DBName.L == "" || astTbl.DBName.L == blockName.DBName.L
+			dbMatch := astTbl.DBName.L == "" || astTbl.DBName.L == blockName.DBName.L || astTbl.DBName.L == "*"
🤖 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/joinorder/util.go` around lines 221 - 225, The fallback path
in util.go currently computes dbMatch as "astTbl.DBName.L == '' ||
astTbl.DBName.L == blockName.DBName.L", which drops the "*" DB wildcard
semantics; update the dbMatch logic used in the blockOffset > 1 fallback so it
treats "*" as a wildcard (e.g., accept when astTbl.DBName.L == "*" or
blockName.DBName.L == "*") in addition to the empty or exact-match cases,
keeping tableMatch unchanged; modify the dbMatch check near variables
blockOffset, queryBlockNames, blockName, astTbl, dbMatch so LEADING(*.alias)
still matches derived-table aliases in this fallback path.

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 2, 2026

@guo-shaoge: The following tests 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/build 153d1cc link true /test build
idc-jenkins-ci-tidb/unit-test 153d1cc link true /test unit-test
idc-jenkins-ci-tidb/check_dev_2 153d1cc link true /test check-dev2

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

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

Additional details and impacted files
@@               Coverage Diff                @@
##             release-8.5     #68870   +/-   ##
================================================
  Coverage               ?   42.2244%           
================================================
  Files                  ?       1535           
  Lines                  ?     428602           
  Branches               ?          0           
================================================
  Hits                   ?     180975           
  Misses                 ?     231545           
  Partials               ?      16082           
Flag Coverage Δ
integration 42.2244% <22.5663%> (?)

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

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

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 2, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: AilinKid
Once this PR has been reviewed and has the lgtm label, please assign d3hunter, terry1purcell, xuhuaiyu 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 2, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 2, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-06-02 08:00:52.95991249 +0000 UTC m=+255754.030229880: ☑️ agreed by AilinKid.

@tiprow
Copy link
Copy Markdown

tiprow Bot commented Jun 2, 2026

@guo-shaoge: 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
fast_test_tiprow_for_release 153d1cc link true /test fast_test_tiprow_for_release

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

do-not-merge/cherry-pick-not-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/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