Skip to content

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

Merged
ti-chi-bot[bot] merged 9 commits into
pingcap:release-8.5from
guo-shaoge:cp_pre_cdc_impl
Jun 4, 2026
Merged

planner: pre-refactor for join reorder conflict detection algorithm#68870
ti-chi-bot[bot] merged 9 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
there is no logic change in this PR, only new uilt functions are added, which will be used in the next PR: #68878

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

  • Refactor
    • Improved query join-order optimization infrastructure with better support for SQL LEADING hints, enabling more efficient query plan generation.
    • Reorganized join-planning logic into a dedicated module for improved code maintainability.
    • Enhanced schema comparison utilities to support more robust query optimization operations.

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 ti-chi-bot Bot added do-not-merge/invalid-title do-not-merge/cherry-pick-not-approved release-note-none Denotes a PR that doesn't merit a release note. 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 extracts join-order hint handling logic into a new joinorder package and integrates it into the planner, removing local duplicate implementations. It adds a schema equality comparison utility, creates joinorder utilities for leading-hint validation and plan matching, and updates the join-reorder rule to use the new shared types and functions.

Changes

Join-order utility extraction and planner integration

Layer / File(s) Summary
Schema equality comparison utility
pkg/expression/schema.go
Adds Schema.Equal method to compare two schemas by nil-ness, column count, and per-position column equality without checking keys.
New joinorder package with hint utilities
pkg/planner/core/joinorder/BUILD.bazel, pkg/planner/core/joinorder/util.go
Creates joinorder go_library with CheckAndGenerateLeadingHint for validating one leading hint, BuildLeadingTreeFromList for recursive join-tree construction from AST, FindAndRemovePlanByAstHint for plan matching by hint table with fallback to derived alias mapping, IsDerivedTableInLeadingHint for checking derived-table containment, and SetNewJoinWithHint for applying per-vertex join-method preferences to LogicalJoin nodes.
Planner integration of joinorder utilities
pkg/planner/core/BUILD.bazel, pkg/planner/core/rule_join_reorder.go
Updates core package dependency on joinorder; refactors rule_join_reorder.go to import and use joinorder.JoinMethodHint, CheckAndGenerateLeadingHint, and SetNewJoinWithHint, replacing local join-hint struct and helper function.
Supporting utility refactoring
pkg/planner/core/operator/logicalop/logical_projection.go, pkg/planner/core/plan_cost_ver2.go
Adds InjectExpr helper to wrap/create LogicalProjection and append expressions; consolidates join-key conversion by replacing local cols2Exprs helper with shared expression.Column2Exprs in PhysicalMergeJoin cost calculation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

size/L, cherry-pick-approved

Suggested reviewers

  • hawkingrei
  • AilinKid
  • qw4990

🐰 Join hints now have a home,
A package of their own to roam,
No more scattered helpers here and there—
The planner's cleaner, lighter, fair!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR description includes issue number and references related PRs, but lacks a detailed explanation of what changed and how it works. Provide a more detailed explanation in the 'What changed and how does it work?' section describing the utility functions added and their purpose in the join-order handling refactor.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change as a pre-refactor for join reorder conflict detection in the planner, which aligns with the changeset.
Linked Issues check ✅ Passed The changeset implements a refactor of join-order logic with new utility functions and schema comparison methods that align with the planner pre-refactor objective in issue #66088.
Out of Scope Changes check ✅ Passed All changes are focused on planner refactoring: schema equality checking, join-order utilities, and buildout of joinorder package infrastructure. No unrelated modifications detected.

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

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

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 thread pkg/parser/hintparser_test.go
Comment thread pkg/planner/core/joinorder/util.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

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

Additional details and impacted files
@@               Coverage Diff                @@
##             release-8.5     #68870   +/-   ##
================================================
  Coverage               ?   55.0895%           
================================================
  Files                  ?       1826           
  Lines                  ?     657991           
  Branches               ?          0           
================================================
  Hits                   ?     362484           
  Misses                 ?     268482           
  Partials               ?      27025           
Flag Coverage Δ
integration 38.3171% <15.7407%> (?)
unit 65.0316% <15.7407%> (?)

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

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

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jun 2, 2026
…_impl

Signed-off-by: guo-shaoge <shaoge1994@163.com>
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

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

Inline comments:
In `@pkg/planner/core/rule_join_reorder.go`:
- Around line 449-474: Remove the dead duplicate function
checkAndGenerateLeadingHint from this file: the project already uses
joinorder.CheckAndGenerateLeadingHint(joinOrderHintInfo) (see usage at the other
call site), so delete the local checkAndGenerateLeadingHint definition to avoid
duplicate/unreferenced code; ensure no other local references to
checkAndGenerateLeadingHint remain and run tests/compile to confirm nothing else
relied on this duplicate.
🪄 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: 7a9ba0bb-3c35-4ac1-913e-5b9d4467fa69

📥 Commits

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

📒 Files selected for processing (4)
  • pkg/parser/hintparser.y
  • pkg/parser/parser_test.go
  • pkg/planner/core/BUILD.bazel
  • pkg/planner/core/rule_join_reorder.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/planner/core/BUILD.bazel
  • pkg/parser/parser_test.go

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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

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

Inline comments:
In `@pkg/planner/core/rule_join_reorder.go`:
- Around line 449-474: Remove the dead duplicate function
checkAndGenerateLeadingHint from this file: the project already uses
joinorder.CheckAndGenerateLeadingHint(joinOrderHintInfo) (see usage at the other
call site), so delete the local checkAndGenerateLeadingHint definition to avoid
duplicate/unreferenced code; ensure no other local references to
checkAndGenerateLeadingHint remain and run tests/compile to confirm nothing else
relied on this duplicate.
🪄 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: 7a9ba0bb-3c35-4ac1-913e-5b9d4467fa69

📥 Commits

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

📒 Files selected for processing (4)
  • pkg/parser/hintparser.y
  • pkg/parser/parser_test.go
  • pkg/planner/core/BUILD.bazel
  • pkg/planner/core/rule_join_reorder.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/planner/core/BUILD.bazel
  • pkg/parser/parser_test.go
🛑 Comments failed to post (1)
pkg/planner/core/rule_join_reorder.go (1)

449-474: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dead code: local checkAndGenerateLeadingHint duplicates joinorder.CheckAndGenerateLeadingHint and is never called.

Line 342 uses joinorder.CheckAndGenerateLeadingHint(joinOrderHintInfo), while this local function has an identical implementation and signature but is never invoked.

🧹 Remove dead code
-// checkAndGenerateLeadingHint used to check and generate the valid leading hint.
-// We are allowed to use at most one leading hint in a join group. When more than one,
-// all leading hints in the current join group will be invalid.
-// For example: select /*+ leading(t3) */ * from (select /*+ leading(t1) */ t2.b from t1 join t2 on t1.a=t2.a) t4 join t3 on t4.b=t3.b
-// The Join Group {t1, t2, t3} contains two leading hints includes leading(t3) and leading(t1).
-// Although they are in different query blocks, they are conflicting.
-// In addition, the table alias 't4' cannot be recognized because of the join group.
-func checkAndGenerateLeadingHint(hintInfo []*h.PlanHints) (*h.PlanHints, bool) {
-	leadingHintNum := len(hintInfo)
-	var leadingHintInfo *h.PlanHints
-	hasDiffLeadingHint := false
-	if leadingHintNum > 0 {
-		leadingHintInfo = hintInfo[0]
-		// One join group has one leading hint at most. Check whether there are different join order hints.
-		for i := 1; i < leadingHintNum; i++ {
-			if hintInfo[i] != hintInfo[i-1] {
-				hasDiffLeadingHint = true
-				break
-			}
-		}
-		if hasDiffLeadingHint {
-			leadingHintInfo = nil
-		}
-	}
-	return leadingHintInfo, hasDiffLeadingHint
-}
📝 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.


🤖 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/rule_join_reorder.go` around lines 449 - 474, Remove the
dead duplicate function checkAndGenerateLeadingHint from this file: the project
already uses joinorder.CheckAndGenerateLeadingHint(joinOrderHintInfo) (see usage
at the other call site), so delete the local checkAndGenerateLeadingHint
definition to avoid duplicate/unreferenced code; ensure no other local
references to checkAndGenerateLeadingHint remain and run tests/compile to
confirm nothing else relied on this duplicate.

…_impl

Signed-off-by: guo-shaoge <shaoge1994@163.com>
Signed-off-by: guo-shaoge <shaoge1994@163.com>
@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 Jun 4, 2026
@guo-shaoge
Copy link
Copy Markdown
Collaborator Author

/retest

Copy link
Copy Markdown
Contributor

@windtalker windtalker left a comment

Choose a reason for hiding this comment

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

lgtm

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 4, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: AilinKid, windtalker

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 removed the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jun 4, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Jun 4, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-06-02 08:00:52.95991249 +0000 UTC m=+255754.030229880: ☑️ agreed by AilinKid.
  • 2026-06-04 11:28:12.291704449 +0000 UTC m=+440993.362021839: ☑️ agreed by windtalker.

@ti-chi-bot ti-chi-bot Bot added cherry-pick-approved Cherry pick PR approved by release team. and removed do-not-merge/cherry-pick-not-approved labels Jun 4, 2026
@ti-chi-bot ti-chi-bot Bot merged commit 87f39ce into pingcap:release-8.5 Jun 4, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved cherry-pick-approved Cherry pick PR approved by release team. lgtm 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.

3 participants