Skip to content

planner: ignore hidden columns in natural/using join matching (#66069) - #67005

Closed
ti-chi-bot wants to merge 1 commit into
pingcap:release-8.5from
ti-chi-bot:cherry-pick-66069-to-release-8.5
Closed

planner: ignore hidden columns in natural/using join matching (#66069)#67005
ti-chi-bot wants to merge 1 commit into
pingcap:release-8.5from
ti-chi-bot:cherry-pick-66069-to-release-8.5

Conversation

@ti-chi-bot

@ti-chi-bot ti-chi-bot commented Mar 14, 2026

Copy link
Copy Markdown
Member

This is an automated cherry-pick of #66069

What problem does this PR solve?

Issue Number: close #65929

Problem Summary: NATURAL/USING join could incorrectly treat hidden columns (e.g. expression index backing columns) as common columns, producing wrong join conditions and empty results.

What changed and how does it work?

Skip hidden columns when matching common column names for NATURAL/USING joins, so only visible columns participate in the join condition.

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

  • Bug Fixes

    • Fixed NATURAL and USING joins to properly exclude internal system columns from column matching, preventing interference with join operations.
  • Tests

    • Added integration tests to verify NATURAL/USING join behavior with various table configurations.

Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
@ti-chi-bot ti-chi-bot added contribution This PR is from a community contributor. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. 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/M Denotes a PR that changes 30-99 lines, ignoring generated files. type/cherry-pick-for-release-8.5 This PR is cherry-picked to release-8.5 from a source PR. labels Mar 14, 2026
@ti-chi-bot

ti-chi-bot Bot commented Mar 14, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign qw4990 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

@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR fixes incorrect NATURAL/USING join results when expression indices are present. The fix enhances column matching in the logical plan builder by excluding internal hidden columns—such as _tidb_rowid, _tidb_commit_ts, and _tidb_phys_tbl_id—from common-column coalescing logic to prevent metadata columns from interfering with join-key detection.

Changes

Cohort / File(s) Summary
Join Column Matching Logic
pkg/planner/core/logical_plan_builder.go
Adds filters to skip hidden and system columns during NATURAL/USING join column matching; prevents internal metadata columns from participating in common-column coalescing and join condition construction.
Integration Tests
tests/integrationtest/t/planner/core/issuetest/planner_issue.test, tests/integrationtest/r/planner/core/issuetest/planner_issue.result
Adds test cases and expected results for NATURAL JOIN with expression indices; verifies correct join output when tables have expression-based indices.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

cherry-pick-approved

Suggested reviewers

  • winoros
  • qw4990
  • hawkingrei
  • guo-shaoge
  • AilinKid

Poem

🐰 With twitching nose and ears held high,
Hidden columns we now skip by!
Natural joins no longer stumble,
When indices make expressions humble—
Metadata shadows fade away,
Let correct results win the day! 🌾

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: ignoring hidden columns in natural/using join matching.
Description check ✅ Passed The description follows the template with all required sections: issue number, problem summary, and what changed. Integration test is checked and release note is provided.
Linked Issues check ✅ Passed The PR addresses issue #65929 by skipping hidden columns during NATURAL JOIN matching. Code changes in logical_plan_builder.go implement the required filtering of hidden columns to match only visible columns.
Out of Scope Changes check ✅ Passed All changes are scoped to the issue: logical planner modifications for hidden column filtering and corresponding integration tests. No unrelated changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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
📝 Coding Plan
  • Generate coding plan for human review comments

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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/integrationtest/t/planner/core/issuetest/planner_issue.test (1)

633-642: Consider adding a USING (a) variant to this regression block.
This change targets NATURAL/USING matching; adding JOIN ... USING (a) with the same expression-index setup would lock coverage for both entry points.

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

In `@tests/integrationtest/t/planner/core/issuetest/planner_issue.test` around
lines 633 - 642, Add a second query variant that tests the USING-clause path
alongside the existing NATURAL join: after the existing setup with tables t1 and
t2 and indexes idx on the expressions (a+1) and (a+2), add a query that performs
"JOIN ... USING (a)" between t1 and t2 (same sort/order conditions as the
NATURAL join) so the regression covers both NATURAL and USING matching paths;
reference the existing objects t1, t2 and the query "select * from t1 natural
join t2 order by t1.a" to place the new USING variant immediately alongside it.
🤖 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/core/logical_plan_builder.go`:
- Around line 874-881: The NATURAL/USING matching loop currently only skips
_tidb_rowid and therefore can wrongly include _tidb_commit_ts and
_tidb_phys_tbl_id in commonLen and equality predicates; add a shared predicate
(e.g., shouldIgnoreNaturalUsingCol) that returns true for col.IsHidden OR
name.ColName equal to model.ExtraHandleName, model.ExtraCommitTSName, or
model.ExtraPhysTblIDName, then replace the ad-hoc checks in the pre-scan, the
coalescing loop that iterates lColumns/rColumns/commonLen, and the USING
ambiguity check (checkAmbiguous) to reuse this helper so all passes consistently
exclude the same internal columns (also note addExtraPhysTblIDColumn4DS appends
phys_tbl_id without hidden flag).

---

Nitpick comments:
In `@tests/integrationtest/t/planner/core/issuetest/planner_issue.test`:
- Around line 633-642: Add a second query variant that tests the USING-clause
path alongside the existing NATURAL join: after the existing setup with tables
t1 and t2 and indexes idx on the expressions (a+1) and (a+2), add a query that
performs "JOIN ... USING (a)" between t1 and t2 (same sort/order conditions as
the NATURAL join) so the regression covers both NATURAL and USING matching
paths; reference the existing objects t1, t2 and the query "select * from t1
natural join t2 order by t1.a" to place the new USING variant immediately
alongside it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8be3272c-e849-4205-b9ad-00159a3863ca

📥 Commits

Reviewing files that changed from the base of the PR and between 0d53c99 and cf03eeb.

📒 Files selected for processing (3)
  • pkg/planner/core/logical_plan_builder.go
  • tests/integrationtest/r/planner/core/issuetest/planner_issue.result
  • tests/integrationtest/t/planner/core/issuetest/planner_issue.test

Comment on lines +874 to +881
// Hidden columns are internal-only and shuold not participate in NATURAL/USING column matching.
if lColumns[i].IsHidden {
continue
}
for j := commonLen; j < len(rNames); j++ {
if rColumns[j].IsHidden {
continue
}

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

Apply the full internal-column exclusion in the actual coalescing loop.

Line 870 still only skips _tidb_rowid. That means _tidb_commit_ts and _tidb_phys_tbl_id can still be pulled into commonLen and turned into equality predicates here, even though the earlier pre-scan now excludes them. _tidb_phys_tbl_id is especially reachable because addExtraPhysTblIDColumn4DS later in this file appends it without marking it hidden. Please use one shared predicate for all NATURAL/USING matching passes, and reuse it from the USING ambiguity check as well.

Possible fix
-		// Natural join should ignore _tidb_rowid
-		if lName.ColName.L == "_tidb_rowid" {
-			continue
-		}
-		// Hidden columns are internal-only and shuold not participate in NATURAL/USING column matching.
-		if lColumns[i].IsHidden {
+		if shouldIgnoreNaturalUsingCol(lName, lColumns[i]) {
 			continue
 		}
 		for j := commonLen; j < len(rNames); j++ {
-			if rColumns[j].IsHidden {
+			if shouldIgnoreNaturalUsingCol(rNames[j], rColumns[j]) {
 				continue
 			}
shouldIgnoreNaturalUsingCol := func(name *types.FieldName, col *expression.Column) bool {
	return col.IsHidden ||
		name.ColName.L == model.ExtraHandleName.L ||
		name.ColName.L == model.ExtraCommitTSName.L ||
		name.ColName.L == model.ExtraPhysTblIDName.L
}

Use the same helper in the earlier pre-scan and in checkAmbiguous so the exclusion list cannot drift again.

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

In `@pkg/planner/core/logical_plan_builder.go` around lines 874 - 881, The
NATURAL/USING matching loop currently only skips _tidb_rowid and therefore can
wrongly include _tidb_commit_ts and _tidb_phys_tbl_id in commonLen and equality
predicates; add a shared predicate (e.g., shouldIgnoreNaturalUsingCol) that
returns true for col.IsHidden OR name.ColName equal to model.ExtraHandleName,
model.ExtraCommitTSName, or model.ExtraPhysTblIDName, then replace the ad-hoc
checks in the pre-scan, the coalescing loop that iterates
lColumns/rColumns/commonLen, and the USING ambiguity check (checkAmbiguous) to
reuse this helper so all passes consistently exclude the same internal columns
(also note addExtraPhysTblIDColumn4DS appends phys_tbl_id without hidden flag).

@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 Mar 14, 2026
@EmmaDuDu

Copy link
Copy Markdown

/retest

1 similar comment
@EmmaDuDu

Copy link
Copy Markdown

/retest

@ti-chi-bot

ti-chi-bot Bot commented Mar 15, 2026

Copy link
Copy Markdown

@ti-chi-bot: 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/unit-test cf03eeb link true /test unit-test
idc-jenkins-ci-tidb/check_dev cf03eeb link true /test check-dev
idc-jenkins-ci-tidb/check_dev_2 cf03eeb link true /test check-dev2
idc-jenkins-ci-tidb/mysql-test cf03eeb 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.

@qw4990 qw4990 closed this Mar 16, 2026
@ti-chi-bot ti-chi-bot Bot added cherry-pick-approved Cherry pick PR approved by release team. and removed cherry-pick-approved Cherry pick PR approved by release team. labels Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cherry-pick-approved Cherry pick PR approved by release team. contribution This PR is from a community contributor. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. 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/M Denotes a PR that changes 30-99 lines, ignoring generated files. type/cherry-pick-for-release-8.5 This PR is cherry-picked to release-8.5 from a source PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants