Skip to content

Conversation

@mjonss
Copy link
Contributor

@mjonss mjonss commented Jan 1, 2026

What problem does this PR solve?

Issue Number: close #65289

Problem Summary:
Non-clustered partitioned table's non-unique Global Index key format is not unique per index entry if _tidb_rowid is not unique across all partitons, which they may not be after EXCHANGE PARTITION.

What changed and how does it work?

Create a new version of Global Index, which also includes the partition id in the key, while still keeping the partition id in the value part of the index entry, to not extend the scope to also affect the reading part of the index (to be followed by another PR which adds yet another global index version, that only has the partition id in the key, and not in the value).

The implementation consists of two different changes:

  • Find out when needed, i.e. only for non-unique global indexes on non-clustered tables. This needed a bit more code, since in BuildIndexInfo we need to know if tblInfo.HasClusteredIndex(), and that might not be set correctly yet, if the primary key was not handled yet in BuildTableInfo, so we needed to do a pre-scan of the constraints to set it properly first, before actually handling all indexes
  • Write the partition id as part of the key (for GlobalIndexVersionV1), including fixing the index backfill function fetchRowColVals() to properly set the handle as a PartitionHandle.

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.

Non-clustered partitioned table's non-unique Global Index could miss entries due to duplicate _tidb_rowid across partitions, after EXCHANGE PARTITION. Starting in 8.5.4 non-unique global indexes is allowed, and those indexes can be checked by 'admin check table' and those indexes are recommended to be re-created. In 8.5.4 creating a UNIQUE GLOBAL INDEX instead is a valid workaround (easily created by adding the rest of the PK columns).

mjonss and others added 3 commits December 29, 2025 11:14
…s compatibility

Add GlobalIndexVersion field to IndexInfo to enable backwards compatible
evolution of global index key formats. Only non-clustered tables with
non-unique global indexes use version 2, which encodes partition ID in
the key using PartitionIDFlag to prevent duplicate handle collisions
after EXCHANGE PARTITION.

Clustered tables and unique indexes are not affected by the duplicate
handle issue and continue using version 0 (legacy format). Clustered
tables have unique primary keys that include the partitioning column,
so no collisions are possible.

Version validation ensures unsupported future versions are rejected with
clear error messages. This approach allows gradual migration while
prioritizing correctness for affected index types.

Related to pingcap#65289.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@ti-chi-bot ti-chi-bot bot added the release-note Denotes a PR that will be considered when it comes time to generate release notes. label Jan 1, 2026
@ti-chi-bot
Copy link

ti-chi-bot bot commented Jan 1, 2026

[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 gmhdbjd 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 size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jan 1, 2026
@tiprow
Copy link

tiprow bot commented Jan 1, 2026

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

@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 Jan 1, 2026
@codecov
Copy link

codecov bot commented Jan 1, 2026

Codecov Report

❌ Patch coverage is 92.59259% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.4342%. Comparing base (258332a) to head (c1045f7).
⚠️ Report is 18 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #65380        +/-   ##
================================================
+ Coverage   77.7820%   79.4342%   +1.6521%     
================================================
  Files          2001       1949        -52     
  Lines        545618     536275      -9343     
================================================
+ Hits         424393     425986      +1593     
+ Misses       119563     108825     -10738     
+ Partials       1662       1464       -198     
Flag Coverage Δ
integration 47.7388% <84.2592%> (-0.4211%) ⬇️
unit 76.6922% <89.8148%> (+0.2727%) ⬆️

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

Components Coverage Δ
dumpling 56.7974% <ø> (ø)
parser ∅ <ø> (∅)
br 66.1993% <ø> (+5.2252%) ⬆️
🚀 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.

@mjonss mjonss marked this pull request as draft January 1, 2026 15:02
@ti-chi-bot ti-chi-bot bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 1, 2026
@mjonss
Copy link
Contributor Author

mjonss commented Jan 1, 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 Jan 1, 2026
@mjonss
Copy link
Contributor Author

mjonss commented Jan 1, 2026

/test all

@mjonss mjonss marked this pull request as ready for review January 1, 2026 21:03
@ti-chi-bot ti-chi-bot bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 1, 2026
mjonss and others added 5 commits January 1, 2026 22:28
…MARY KEY

Problem:
When creating a partitioned table with an inline PRIMARY KEY (e.g.,
`a INT PRIMARY KEY`) followed by global indexes in the same CREATE TABLE
statement, the global index was incorrectly assigned GlobalIndexVersion V1
even when the table was clustered.

This happened because inline PRIMARY KEY definitions are converted to
constraints and appended to the end of the constraints list (after table-
level constraints). When BuildTableInfo processes constraints in order,
global indexes are built before the PRIMARY KEY constraint is processed,
so HasClusteredIndex() returns false, causing incorrect version assignment.

Root Cause:
In buildColumnsAndConstraints (create_table.go:1063), inline PRIMARY KEY
constraints from column definitions are appended after table constraints:
  constraints = append(constraints, cts...)

So for `CREATE TABLE t (a INT PRIMARY KEY, key idx(b) global)`, constraints
are processed as:
  1. key idx(b) global  <- built first, sees HasClusteredIndex()=false
  2. primary key(a)     <- processed last, sets PKIsHandle=true

Solution:
Added a pre-scan loop before constraint processing in BuildTableInfo that:
1. Scans all constraints to find PRIMARY KEY
2. Determines if table will be clustered (PKIsHandle/IsCommonHandle)
3. Sets clustering flags before any indexes are built

This ensures HasClusteredIndex() returns correct value when building global
indexes, so:
- Clustered tables get GlobalIndexVersion = 0 (correct)
- Non-clustered tables with non-unique global indexes get V1 (correct)

The pre-scan handles expression-based PKs correctly without building hidden
columns (expression PKs can't be PKIsHandle, so they become IsCommonHandle).

Impact:
- Preserves constraint ordering (no SHOW CREATE TABLE changes)
- Fixes TestGlobalIndexVersion1 which was failing
- No performance impact (lightweight pre-scan)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@mjonss mjonss requested a review from Copilot January 2, 2026 00:51
Copilot AI review requested due to automatic review settings January 19, 2026 11:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Copilot AI review requested due to automatic review settings January 21, 2026 11:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

pkg/ddl/create_table.go:1409

  • The clustering index information (PKIsHandle, IsCommonHandle, CommonHandleVersion) is being set twice - once in the pre-scan (lines 1332-1348) and again in the main loop (lines 1403-1409). This duplication could lead to maintenance issues. While this appears intentional to ensure the information is available early for BuildIndexInfo calls, the duplicate logic increases the risk of inconsistencies if one location is updated without the other. Consider adding a comment in the main loop referencing the pre-scan or refactoring to eliminate the duplication by extracting the logic into a shared function.
			if ShouldBuildClusteredIndex(ctx.GetClusteredIndexDefMode(), constr.Option, isSingleIntPK) {
				if isSingleIntPK {
					tbInfo.PKIsHandle = true
				} else {
					tbInfo.IsCommonHandle = true
					tbInfo.CommonHandleVersion = 1
				}

@mjonss
Copy link
Contributor Author

mjonss commented Jan 22, 2026

/retest

1 similar comment
@mjonss
Copy link
Contributor Author

mjonss commented Jan 23, 2026

/retest

Copilot AI review requested due to automatic review settings January 23, 2026 15:39
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

@mjonss
Copy link
Contributor Author

mjonss commented Jan 23, 2026

/retest

Copilot AI review requested due to automatic review settings January 26, 2026 09:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

@mjonss
Copy link
Contributor Author

mjonss commented Jan 30, 2026

/retest

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

Labels

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 Denotes a PR that will be considered when it comes time to generate release notes. 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.

Exchange partition then create global index may cause data/index inconsistency

4 participants