Skip to content

planner: support table aliases in FOR UPDATE OF Clause#65532

Merged
ti-chi-bot[bot] merged 14 commits intopingcap:masterfrom
cryo-zd:fix/selectlock
Mar 18, 2026
Merged

planner: support table aliases in FOR UPDATE OF Clause#65532
ti-chi-bot[bot] merged 14 commits intopingcap:masterfrom
cryo-zd:fix/selectlock

Conversation

@cryo-zd
Copy link
Contributor

@cryo-zd cryo-zd commented Jan 12, 2026

What problem does this PR solve?

Issue Number: close #63035

Problem Summary: SELECT ... FOR UPDATE OF <table> currently fails to resolve table alias in the locking clause during preprocess, return "table not exists" for valid alias targets.

What changed and how does it work?

  • Track lock-clause table names per SELECT and skip their resolution during preprocess
  • Resolve lock targets against the FROM clause(including alias) after the SELECT subtree is processed, then bind the actual table metadata.
MySQL: locking tables across databases


# Test 1(FROM Order)
```SQL
-- sessionA
mysql> select database();
+----------------+
| database()     |
+----------------+
| db_lock_test_2 |
+----------------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from db_lock_test_1.t1, db_lock_test_2.t1 for update of t1;
+----+------+----+------+
| id | v    | id | v    |
+----+------+----+------+
|  1 |  100 |  1 |  999 |
|  2 |  200 |  1 |  999 |
+----+------+----+------+
2 rows in set (0.02 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
+----------------+-------------+------------+-----------+-----------+
| OBJECT_SCHEMA  | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE |
+----------------+-------------+------------+-----------+-----------+
| db_lock_test_1 | t1          | NULL       | TABLE     | IX        |
| db_lock_test_1 | t1          | PRIMARY    | RECORD    | X         |
| db_lock_test_1 | t1          | PRIMARY    | RECORD    | X         |
| db_lock_test_1 | t1          | PRIMARY    | RECORD    | X         |
+----------------+-------------+------------+-----------+-----------+
4 rows in set (0.01 sec)

-- session A
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
Empty set (0.01 sec)
```

# Test 2(FROM Order)
```SQL
-- session A
mysql> select version(), database();
+-----------+----------------+
| version() | database()     |
+-----------+----------------+
| 8.4.4     | db_lock_test_2 |
+-----------+----------------+
1 row in set (0.03 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from db_lock_test_2.t1, db_lock_test_1.t1 for update of t1;
+----+------+----+------+
| id | v    | id | v    |
+----+------+----+------+
|  1 |  999 |  1 |  100 |
|  1 |  999 |  2 |  200 |
+----+------+----+------+
2 rows in set (0.01 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
+----------------+-------------+------------+-----------+-----------+
| OBJECT_SCHEMA  | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE |
+----------------+-------------+------------+-----------+-----------+
| db_lock_test_2 | t1          | NULL       | TABLE     | IX        |
| db_lock_test_2 | t1          | PRIMARY    | RECORD    | X         |
| db_lock_test_2 | t1          | PRIMARY    | RECORD    | X         |
+----------------+-------------+------------+-----------+-----------+
3 rows in set (0.01 sec)

-- session A
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
Empty set (0.00 sec)
```

# Test 3(FROM Order)
```SQL
-- session A
Database changed
mysql> select version(), database();
+-----------+----------------+
| version() | database()     |
+-----------+----------------+
| 8.4.4     | db_lock_test_1 |
+-----------+----------------+
1 row in set (0.02 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from db2.t1, db_lock_test_1.t1, db_lock_test_2.t1 for update of t1;
+----+------+----+------+----+------+
| id | v    | id | v    | id | v    |
+----+------+----+------+----+------+
|  2 |  200 |  1 |  100 |  1 |  999 |
|  1 |  100 |  1 |  100 |  1 |  999 |
|  2 |  200 |  2 |  200 |  1 |  999 |
|  1 |  100 |  2 |  200 |  1 |  999 |
+----+------+----+------+----+------+
4 rows in set (0.01 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
+---------------+-------------+------------+-----------+-----------+
| OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE |
+---------------+-------------+------------+-----------+-----------+
| db2           | t1          | NULL       | TABLE     | IX        |
| db2           | t1          | PRIMARY    | RECORD    | X         |
| db2           | t1          | PRIMARY    | RECORD    | X         |
| db2           | t1          | PRIMARY    | RECORD    | X         |
+---------------+-------------+------------+-----------+-----------+
4 rows in set (0.01 sec)

-- session A
mysql> commit;
Query OK, 0 rows affected (0.03 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
Empty set (0.00 sec)
```

# Test 4(FROM Order)
```SQL
-- session A
mysql> select version(), database();
+-----------+------------+
| version() | database() |
+-----------+------------+
| 8.4.4     | db2        |
+-----------+------------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from db_lock_test_2.t1, db_lock_test_1.t1 for update of t1;
+----+------+----+------+
| id | v    | id | v    |
+----+------+----+------+
|  1 |  999 |  1 |  100 |
|  1 |  999 |  2 |  200 |
+----+------+----+------+
2 rows in set (0.00 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
+----------------+-------------+------------+-----------+-----------+
| OBJECT_SCHEMA  | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE |
+----------------+-------------+------------+-----------+-----------+
| db_lock_test_2 | t1          | NULL       | TABLE     | IX        |
| db_lock_test_2 | t1          | PRIMARY    | RECORD    | X         |
| db_lock_test_2 | t1          | PRIMARY    | RECORD    | X         |
+----------------+-------------+------------+-----------+-----------+
3 rows in set (0.00 sec)

-- session A
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

-- session B
mysql> SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks;
Empty set (0.00 sec)

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.

Fix alias resolution for `SELECT ... FOR UPDATE OF` to align TiDB behavior with MySQL and prevent incorrect “unknown table” errors.

Summary by CodeRabbit

  • Bug Fixes
    • More reliable FOR UPDATE / LOCK IN SHARE MODE handling: lock targets (including aliases and cross-database references) are now bound correctly, ambiguous targets emit compatibility warnings, and privilege checks use the correctly resolved database.
  • Tests
    • Added integration tests covering cross-database FOR UPDATE scenarios, aliasing, explicit lock targets, warning control, and error paths.

@ti-chi-bot ti-chi-bot bot added release-note-none Denotes a PR that doesn't merit a release note. contribution This PR is from a community contributor. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. labels Jan 12, 2026
@ti-chi-bot
Copy link

ti-chi-bot bot commented Jan 12, 2026

Hi @cryo-zd. Thanks for your PR.

I'm waiting for a pingcap member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

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 the sig/planner SIG: Planner label Jan 12, 2026
@tiprow
Copy link

tiprow bot commented Jan 12, 2026

Hi @cryo-zd. 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.

@lance6716
Copy link
Contributor

/ok-to-test

@ti-chi-bot ti-chi-bot bot added ok-to-test Indicates a PR is ready to be tested. and removed needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. labels Jan 13, 2026
@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

❌ Patch coverage is 95.23810% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.8590%. Comparing base (987e37f) to head (ba1970e).
⚠️ Report is 27 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #65532        +/-   ##
================================================
+ Coverage   77.7132%   79.8590%   +2.1458%     
================================================
  Files          2013       1963        -50     
  Lines        551161     548252      -2909     
================================================
+ Hits         428325     437829      +9504     
+ Misses       121105     108949     -12156     
+ Partials       1731       1474       -257     
Flag Coverage Δ
integration 47.7857% <94.0476%> (-0.3697%) ⬇️
unit 77.1084% <52.9761%> (+0.9003%) ⬆️

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

Components Coverage Δ
dumpling 57.2380% <ø> (+0.4405%) ⬆️
parser ∅ <ø> (∅)
br 65.8577% <ø> (+4.9908%) ⬆️
🚀 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.

@lance6716
Copy link
Contributor

/retest

1 similar comment
@lance6716
Copy link
Contributor

/retest

@cryo-zd
Copy link
Contributor Author

cryo-zd commented Feb 25, 2026

/retest

@cryo-zd cryo-zd changed the title planner: enhance locking clause handling in SELECT statements [WIP] planner: enhance locking clause handling in SELECT statements Mar 3, 2026
@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 Mar 3, 2026
@cryo-zd cryo-zd marked this pull request as draft March 3, 2026 03:56
@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. and removed release-note-none Denotes a PR that doesn't merit a release note. labels Mar 5, 2026
@ti-chi-bot ti-chi-bot bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 5, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds locking-clause awareness to the preprocessor: collects FROM-clause table refs per SELECT, defers and binds FOR UPDATE / LOCK IN SHARE MODE targets to aliases or qualified names with compatibility warnings, and prefers resolved DBInfo for privilege checks during plan building.

Changes

Cohort / File(s) Summary
Preprocessor: locking-clause handling
pkg/planner/core/preprocess.go
Adds lockRef and lockRefCollector types; new fields lockRefCollectorStack and lockTablesInSelectStack on preprocessor; pushes/pops collector and lock-target maps on SELECT Enter/Leave; collects FROM aliases; skips normal TableName resolution for locking-clause names; adds checkLockClauseTables to bind/validate lock targets and emit compatibility warnings.
Planner: privilege DB resolution
pkg/planner/core/logical_plan_builder.go
In SELECT build for FOR UPDATE, prefer tNameW.DBInfo.Name.L (when present) for privilege DB lookup, falling back to schema-based lookup to ensure correct DB is used for privilege checks.
Integration tests: FOR UPDATE scenarios
tests/integrationtest/t/planner/core/issuetest/planner_issue.test, tests/integrationtest/r/planner/core/issuetest/planner_issue.result
Adds multi-database and alias FOR UPDATE tests (cross-db refs, base-name vs alias ambiguity, CTE usage, warning suppression, error path), plus setup/cleanup sequences and expected warnings/errors.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Pre as Preprocessor
    participant Catalog as Catalog/DBInfo
    participant Planner as LogicalPlanner

    Client->>Pre: parse SELECT ... FOR UPDATE
    Pre->>Pre: push lockRefCollector & lockTables map
    Pre->>Pre: traverse FROM -> collect aliases into collector
    Pre->>Pre: mark locking-clause TableName nodes to skip normal resolution
    Pre->>Pre: leave SELECT -> checkLockClauseTables() binds targets to collected refs (emit warnings if needed)
    Pre->>Catalog: resolve bound targets' DBInfo (if required)
    Catalog-->>Pre: return DBInfo (may include Name.L)
    Pre->>Planner: pass resolved lock bindings
    Planner->>Catalog: use tNameW.DBInfo.Name.L (if present) for privilege checks
    Planner-->>Client: return plan with validated lock targets
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

size/XXL, lgtm

Suggested reviewers

  • winoros
  • qw4990

Poem

🐇
I hopped through FROMs and aliases light,
Gathered locks where names hide from sight.
I nudged DB names into their place,
Whispered warnings with polite grace.
A carrot cheer — ready for the race!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 title clearly and concisely describes the main change: adding support for table aliases in the FOR UPDATE OF clause, which directly addresses the linked issue.
Description check ✅ Passed The description follows the template with a linked issue, clear problem statement, detailed explanation of the solution, and appropriate checklist selections. It includes integration tests and release notes.
Linked Issues check ✅ Passed The PR directly addresses the coding requirements from issue #63035 by implementing alias resolution for FOR UPDATE OF targets against the FROM clause, matching MySQL's behavior and preventing incorrect 'table not exists' errors.
Out of Scope Changes check ✅ Passed All changes are focused on addressing the FOR UPDATE OF alias resolution issue: preprocessing updates to track and resolve lock targets, logical plan builder enhancements for database name resolution, and integration tests validating the fix. 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
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can validate your CodeRabbit configuration file in your editor.

If your editor has YAML language server, you can enable auto-completion and validation by adding # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json at the top of your CodeRabbit configuration file.

@cryo-zd cryo-zd changed the title [WIP] planner: enhance locking clause handling in SELECT statements planner: support table aliases in FOR UPDATE OF Clause Mar 5, 2026
@cryo-zd cryo-zd marked this pull request as ready for review March 5, 2026 13:30
@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 Mar 5, 2026
Copy link

@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 the current code and only fix it if needed.

Inline comments:
In `@pkg/planner/core/preprocess.go`:
- Around line 1938-1943: The qualifiedMap population currently overwrites
earlier refs for duplicate qualified keys
(qualifiedMap[src.Schema.L+"."+src.Name.L] = ref) causing inconsistent
resolution versus the unqualified orderedRefs which picks the first match;
change the logic in the block that handles src.Schema.L to only set qualifiedMap
entries if the key does not already exist (i.e., preserve the first-seen ref)
for both the schema.table key and the schema.alias key (x.AsName.L) so that
lookups for qualified names are deterministic and consistent with orderedRefs
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a1dd41f5-3472-4bde-abee-524795b3a78b

📥 Commits

Reviewing files that changed from the base of the PR and between 04965dd and 8c58375.

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

@ti-chi-bot ti-chi-bot bot added approved lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Mar 16, 2026
@ti-chi-bot
Copy link

ti-chi-bot bot commented Mar 16, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-03-10 06:44:36.297454353 +0000 UTC m=+332507.809512004: ☑️ agreed by you06.
  • 2026-03-16 13:13:02.370169896 +0000 UTC m=+189909.457827423: ☑️ agreed by winoros.

@ti-chi-bot ti-chi-bot bot added the needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. label Mar 16, 2026
@YangKeao
Copy link
Member

/retest

18 similar comments
@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@YangKeao
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@YangKeao
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@cryo-zd
Copy link
Contributor Author

cryo-zd commented Mar 18, 2026

/retest

@YangKeao
Copy link
Member

/retest

@YangKeao
Copy link
Member

/retest

@YangKeao
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@hawkingrei
Copy link
Member

/retest

@ti-chi-bot ti-chi-bot bot merged commit bab4993 into pingcap:master Mar 18, 2026
35 checks passed
ti-chi-bot pushed a commit to ti-chi-bot/tidb that referenced this pull request Mar 18, 2026
Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
@ti-chi-bot
Copy link
Member

In response to a cherrypick label: new pull request created to branch release-8.5: #67130.
But this PR has conflicts, please resolve them!

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

Labels

approved contribution This PR is from a community contributor. lgtm 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. 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.

Compatibility Issue with MySQL related to select for update of

7 participants