Skip to content

planner, meta: avoid unsafe index on virtual generated temporal columns#67985

Open
expxiaoli wants to merge 1 commit intopingcap:masterfrom
expxiaoli:fix_52520
Open

planner, meta: avoid unsafe index on virtual generated temporal columns#67985
expxiaoli wants to merge 1 commit intopingcap:masterfrom
expxiaoli:fix_52520

Conversation

@expxiaoli
Copy link
Copy Markdown
Contributor

@expxiaoli expxiaoli commented Apr 23, 2026

What problem does this PR solve?

Issue Number: close #52520

Problem Summary:

When a virtual generated temporal column is indexed, the value stored in the index can be computed under the sql_mode used when the row was written, while reading the virtual generated column from the table path recomputes it under the current sql_mode.

For example, a virtual generated DATE column based on varchar input may produce different results for invalid dates such as 2020-02-31 after switching sql_mode to ALLOW_INVALID_DATES. As a result, queries using the index and queries not using the index may return inconsistent results.

What changed and how does it work?

This PR prevents TiDB from using indexes that contain virtual generated temporal columns with a date part, including DATE, DATETIME, and TIMESTAMP.

  • Add ColumnInfo.IsVirtualGeneratedTemporalWithDateColumn() to identify virtual generated columns whose target type contains a date part.
  • Filter such indexes out of normal TiKV index access paths during plan building.
  • Preserve hint behavior by recognizing filtered indexes and returning an inapplicable-hint warning instead of treating the index as missing.
  • Add regression coverage for USE INDEX, optimizer USE_INDEX hints, and IGNORE INDEX behavior around issue inconsistent data and index for virtual generated column #52520.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test

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

Validation
Used Ready profile because this creates a local commit with code changes and PR-ready message. Commands run:

GOSUMDB=sum.golang.org GOPATH=/Users/xiaoli/go GOCACHE=/tmp/tidb-gocache ./tools/check/failpoint-go-test.sh pkg/planner/core/casetest/index -run TestInvisibleIndex -count=1
GOSUMDB=sum.golang.org GOPATH=/Users/xiaoli/go GOCACHE=/tmp/tidb-gocache make lint
git diff --check

Release note

Fix an issue that queries using indexes on virtual generated temporal columns could return inconsistent results after sql_mode changes.

Summary by CodeRabbit

  • Bug Fixes

    • Query planner now correctly excludes virtual generated temporal/date columns from being used as regular index targets and emits consistent warnings when such columns are referenced via index hints (USE/ FORCE/ IGNORE INDEX).
  • Tests

    • Added tests covering index-hint behavior and warning emission for generated temporal/date columns; updated test shard and build test settings.

@ti-chi-bot ti-chi-bot Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/planner SIG: Planner size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 23, 2026
@tiprow
Copy link
Copy Markdown

tiprow Bot commented Apr 23, 2026

Hi @expxiaoli. 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 Apr 23, 2026

📝 Walkthrough

Walkthrough

Introduces detection of virtual generated temporal-with-date columns and updates planner index-hint resolution to exclude indexes containing them from normal access paths; adds tests and small build config updates to ensure hinted matches produce warnings (not key-not-found errors) when such filtered indexes are referenced (unless ignored).

Changes

Cohort / File(s) Summary
Column Classification
pkg/meta/model/column.go
Add IsVirtualGeneratedTemporalWithDateColumn() to detect virtual/generated columns with types DATE, DATETIME, TIMESTAMP.
Hint Resolution & Index Handling
pkg/planner/core/planbuilder.go
Replace single-name resolver with resolveIndexHintByName and indexHintResolveStatus; split access paths to exclude indexes containing the flagged columns and record warnings (instead of key-not-found errors) when USE/FORCE INDEX references them; IGNORE INDEX bypasses the warning.
Regression & Planner Tests
pkg/planner/core/casetest/index/index_test.go
Add tests validating planner behavior and warnings when hinting indexes that include virtual generated temporal-with-date columns (including TiFlash/inverted-index case and behavior under ALLOW_INVALID_DATES).
Build/Test config
pkg/importsdk/BUILD.bazel, pkg/planner/core/casetest/index/BUILD.bazel
Add //pkg/parser/ast dependency to tests and adjust test shard count from 12 to 13.

Sequence Diagram(s)

(Skipped — changes are focused within planner hint-resolution logic and test coverage; no multi-component external runtime sequence diagram generated.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

approved, lgtm

Suggested reviewers

  • qw4990
  • hawkingrei
  • guo-shaoge

Poem

🐰 I hopped through indices and date-time vines,
Found virtual columns hiding strange signs.
I nudged the planner, gave warnings a say,
Now hinted paths skip the risky way —
A little carrot for consistent finds! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'planner, meta: avoid unsafe index on virtual generated temporal columns' accurately describes the main change: preventing unsafe index usage on virtual generated temporal columns.
Description check ✅ Passed The PR description comprehensively addresses all required template sections: linked issue number, problem summary, technical implementation details, test coverage (unit tests included), documentation impact, and release notes.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #52520 by adding ColumnInfo.IsVirtualGeneratedTemporalWithDateColumn() to identify unsafe columns, filtering such indexes from access paths, and adding comprehensive test coverage for index hint behavior.
Out of Scope Changes check ✅ Passed All code changes are directly related to addressing issue #52520: adding the temporal column predicate, filtering indexes during plan building, adjusting test configuration, and adding regression tests. No extraneous changes 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

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

Command failed


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.

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

🧹 Nitpick comments (1)
pkg/planner/core/casetest/index/index_test.go (1)

107-125: Move issue-52520 regression into its own test function.

This new block tests virtual generated temporal columns, which is unrelated to TestInvisibleIndex's scope (invisible-index visibility toggling). Embedding it here makes the regression harder to discover via test name, hurts targeted test runs (-run), and will conflate failures. Consider extracting it to a dedicated TestIssue52520VirtualGeneratedTemporalIndex (still using RunTestUnderCascades) in the same file.

As per coding guidelines, code SHOULD remain maintainable for future readers and SHOULD be self-documenting through clear naming and structure.

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

In `@pkg/planner/core/casetest/index/index_test.go` around lines 107 - 125,
Extract the virtual generated temporal column assertions (the entire block that
creates t_issue_52520, sets sql_mode, inserts the invalid date, then runs the
index/no-index queries and warning checks) out of the existing
TestInvisibleIndex test and place them into a new, self-contained test function
named TestIssue52520VirtualGeneratedTemporalIndex; keep the same setup/teardown
and use RunTestUnderCascades with the same testKit calls (MustExec,
MustNoIndexUsed, MustQuery, etc.) so behavior is identical but scoped to the new
test, ensuring the new test references RunTestUnderCascades and preserves the
same checks for "virtual generated temporal column".
🤖 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/planbuilder.go`:
- Around line 1197-1214: The current hint-resolution can pick a filtered unsafe
index from virtualGeneratedTemporalWithDateIndexes after publicPaths are
checked, causing USE INDEX(i) to wrongly resolve to a safe prefix; change the
resolution to consider both publicPaths and
virtualGeneratedTemporalWithDateIndexes together with exact-match precedence:
implement a single resolver (or extend
getVirtualGeneratedTemporalWithDateIndexByName) that accepts both lists, first
checks for exact matches in publicPaths then in
virtualGeneratedTemporalWithDateIndexes (and returns an explicit “inapplicable
filtered-unsafe” result if the exact match is only in the filtered set),
otherwise count prefix matches across both sets and return the one prefix match
only if the combined count == 1; update the code that forces a path (the branch
that currently inspects publicPaths then virtualGeneratedTemporalWithDateIndexes
around the USE INDEX handling) to use this combined resolver so exact matches
take precedence and inapplicable warnings are emitted correctly.

---

Nitpick comments:
In `@pkg/planner/core/casetest/index/index_test.go`:
- Around line 107-125: Extract the virtual generated temporal column assertions
(the entire block that creates t_issue_52520, sets sql_mode, inserts the invalid
date, then runs the index/no-index queries and warning checks) out of the
existing TestInvisibleIndex test and place them into a new, self-contained test
function named TestIssue52520VirtualGeneratedTemporalIndex; keep the same
setup/teardown and use RunTestUnderCascades with the same testKit calls
(MustExec, MustNoIndexUsed, MustQuery, etc.) so behavior is identical but scoped
to the new test, ensuring the new test references RunTestUnderCascades and
preserves the same checks for "virtual generated temporal column".
🪄 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: 8110ce2b-908d-4cb9-8d8e-4660a087d1c4

📥 Commits

Reviewing files that changed from the base of the PR and between 5260244 and 15173db.

📒 Files selected for processing (3)
  • pkg/meta/model/column.go
  • pkg/planner/core/casetest/index/index_test.go
  • pkg/planner/core/planbuilder.go

Comment thread pkg/planner/core/planbuilder.go Outdated
@ti-chi-bot ti-chi-bot Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 23, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 23, 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 qw4990, tiancaiamao 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

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

🧹 Nitpick comments (1)
pkg/planner/core/casetest/index/index_test.go (1)

142-144: Unused setup in t_issue_52520_prefix leaves coverage gaps.

The table defines idx_safe(c) (on a regular column) and idx_unsafe(b) (on the virtual generated date column), but the only query (line 144) references a non-existent index idx. As a result:

  • idx_unsafe is never exercised, so the assertion "indexes containing a virtual generated temporal column are filtered out when mixed with other indexes on the same table" is not actually covered here.
  • idx_safe is never exercised, so there's no regression check that safe indexes on the same table remain usable/hintable.
  • The table name suggests "prefix" but no prefix index is defined.

Consider either dropping the unused columns/indexes to match the intent (just verifying that the not-found error path still fires), or adding assertions against idx_safe (usable) and idx_unsafe (filtered with the virtual-generated-temporal warning) to justify the richer schema.

🧪 Suggested additional coverage
 		tk.MustExec("drop table if exists t_issue_52520_prefix")
 		tk.MustExec("create table t_issue_52520_prefix (a varchar(32), c int, b date as (a), key idx_safe(c), key idx_unsafe(b))")
 		tk.MustContainErrMsg("select /* issue:52520 */ c from t_issue_52520_prefix use index(idx) where c = 1", "Key 'idx' doesn't exist")
+		// idx_safe on a regular column should still be usable.
+		tk.MustUseIndex("select /* issue:52520 */ c from t_issue_52520_prefix use index(idx_safe) where c = 1", "idx_safe")
+		// idx_unsafe contains the virtual generated temporal column and must be filtered with a warning.
+		tk.MustNoIndexUsed("select /* issue:52520 */ b from t_issue_52520_prefix use index(idx_unsafe)")
+		tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/core/casetest/index/index_test.go` around lines 142 - 144, The
test setup for t_issue_52520_prefix creates two indexes (idx_safe on c and
idx_unsafe on virtual column b) but the assertion only references a non-existent
idx, leaving idx_safe/idx_unsafe untested; either simplify the CREATE TABLE to
remove the unused index/virtual column if you only want to assert the not-found
path, or add explicit assertions: call tk.MustQuery / tk.MustContainErrMsg with
use index(idx_safe) to confirm idx_safe is selectable/hintable, and a query
using use index(idx_unsafe) (or an optimizer path that would consider it) to
assert it is filtered out due to the virtual-generated temporal column (and that
the proper warning/message is produced); adjust the table definition to include
a real prefix index if you intended to test prefix behavior and update the
assertion accordingly.
🤖 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/planbuilder.go`:
- Around line 1389-1392: In checkTblIndexForPointPlan (in point_get_plan.go) add
the same unsafe-index filter used in getPossibleAccessPaths: when iterating
tbl.Indices and considering an index (the loop that checks idxInfo.Unique,
idxInfo.State and hint availability), call
indexContainsVirtualGeneratedTemporalWithDateColumn(tblInfo, index) and skip the
index if it returns true so the point-get planner rejects indexes that contain
virtual generated temporal-with-date columns.

---

Nitpick comments:
In `@pkg/planner/core/casetest/index/index_test.go`:
- Around line 142-144: The test setup for t_issue_52520_prefix creates two
indexes (idx_safe on c and idx_unsafe on virtual column b) but the assertion
only references a non-existent idx, leaving idx_safe/idx_unsafe untested; either
simplify the CREATE TABLE to remove the unused index/virtual column if you only
want to assert the not-found path, or add explicit assertions: call tk.MustQuery
/ tk.MustContainErrMsg with use index(idx_safe) to confirm idx_safe is
selectable/hintable, and a query using use index(idx_unsafe) (or an optimizer
path that would consider it) to assert it is filtered out due to the
virtual-generated temporal column (and that the proper warning/message is
produced); adjust the table definition to include a real prefix index if you
intended to test prefix behavior and update the assertion accordingly.
🪄 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: ca273f28-1870-4136-805d-3297a8c85ca8

📥 Commits

Reviewing files that changed from the base of the PR and between 15173db and fe004d8.

📒 Files selected for processing (5)
  • pkg/importsdk/BUILD.bazel
  • pkg/meta/model/column.go
  • pkg/planner/core/casetest/index/BUILD.bazel
  • pkg/planner/core/casetest/index/index_test.go
  • pkg/planner/core/planbuilder.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/planner/core/casetest/index/BUILD.bazel
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/meta/model/column.go

Comment thread pkg/planner/core/planbuilder.go
@expxiaoli
Copy link
Copy Markdown
Contributor Author

/retest-required

@tiprow
Copy link
Copy Markdown

tiprow Bot commented Apr 23, 2026

@expxiaoli: 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.

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 23, 2026

@expxiaoli: 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
pull-build-next-gen fe004d8 link true /test pull-build-next-gen
pull-unit-test-ddlv1 fe004d8 link true /test pull-unit-test-ddlv1
idc-jenkins-ci-tidb/build fe004d8 link true /test build
pull-unit-test-next-gen fe004d8 link true /test pull-unit-test-next-gen
idc-jenkins-ci-tidb/unit-test fe004d8 link true /test unit-test
idc-jenkins-ci-tidb/check_dev fe004d8 link true /test check-dev

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

release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/planner SIG: Planner size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inconsistent data and index for virtual generated column

1 participant