Skip to content

domain, privilege: add PLAN_REPLAYER_EXPLAIN_ADMIN dynamic privilege#67576

Open
cryo-zd wants to merge 8 commits into
pingcap:masterfrom
cryo-zd:plan_replay_admin
Open

domain, privilege: add PLAN_REPLAYER_EXPLAIN_ADMIN dynamic privilege#67576
cryo-zd wants to merge 8 commits into
pingcap:masterfrom
cryo-zd:plan_replay_admin

Conversation

@cryo-zd

@cryo-zd cryo-zd commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #67591

Problem Summary:
Cloud needs to run PLAN REPLAYER DUMP EXPLAIN, but the backend should not use a user with general SELECT privileges on user data. This PR adds a standalone dynamic privilege, PLAN_REPLAYER_EXPLAIN_ADMIN, for the plan replayer dump explain flow.

What changed and how does it work?

  • add the PLAN_REPLAYER_EXPLAIN_ADMIN dynamic privilege
  • retry internal EXPLAIN / SHOW CREATE helper SQLs with a narrow plan-replayer-only privilege bypass when the normal execution hits the expected privilege error
  • keep existing behavior unchanged for users who already have normal privileges

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.

Support the `PLAN_REPLAYER_EXPLAIN_ADMIN` dynamic privilege for `PLAN REPLAYER DUMP EXPLAIN`.

Summary by CodeRabbit

  • New Features

    • Added a dynamic privilege "PLAN_REPLAYER_EXPLAIN_ADMIN" and an option to use it to obtain richer EXPLAIN/SHOW output during plan replayer dumps when permitted.
  • Behavior

    • Plan replayer dump now retries EXPLAIN/SHOW operations under the privilege bypass for eligible statements to reduce permission-related errors.
  • Tests

    • Added tests covering privilege-bypass eligibility and end-to-end dump behavior with and without the new privilege.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue release-note Denotes a PR that will be considered when it comes time to generate release notes. labels Apr 6, 2026
@pantheon-ai

pantheon-ai Bot commented Apr 6, 2026

Copy link
Copy Markdown

Review Complete

Findings: 0 issues
Posted: 0
Duplicates/Skipped: 0

ℹ️ Learn more details on Pantheon AI.

@ti-chi-bot

ti-chi-bot Bot commented Apr 6, 2026

Copy link
Copy Markdown

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 contribution This PR is from a community contributor. needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Apr 6, 2026
@tiprow

tiprow Bot commented Apr 6, 2026

Copy link
Copy Markdown

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.

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

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 a new dynamic privilege PLAN_REPLAYER_EXPLAIN_ADMIN and implements a conditional privilege-bypass flow for "plan replayer dump explain": task-aware dumping, AST eligibility checks, session-context bypass flag, planner integration to skip standard verification when allowed, tests, and build updates.

Changes

Cohort / File(s) Summary
Privilege Definition
pkg/privilege/privileges/privileges.go, tests/integrationtest/r/executor/executor.result
Introduce PLAN_REPLAYER_EXPLAIN_ADMIN as a dynamic privilege and expose it in privilege listing.
Domain Plan Replayer
pkg/domain/plan_replayer.go, pkg/domain/plan_replayer_dump.go, pkg/domain/plan_replayer_test.go, pkg/domain/BUILD.bazel
Add useExplainAdminBypass to task struct, thread task through schema/explain dumping, implement AST-based eligibility checks, classification of privilege errors, conditional retry under bypass, and test double; update Bazel deps and shard_count.
Planner Privilege Integration
pkg/planner/core/optimizer.go, pkg/planner/core/logical_plan_builder.go, pkg/planner/core/plan_cache.go, pkg/planner/core/point_get_plan.go, pkg/planner/core/plan_cache.go, pkg/planner/optimize.go
Add canBypassPlanReplayerPrivilegeCheck(sctx, pm), change CheckPrivilege to accept session context, and skip/request verification conditionally when bypass is active; adjust call sites accordingly.
Session Context
pkg/sessionctx/context.go
Add PlanReplayerPrivilegeBypass context key and string representation for temporary retry marker.
Executor Tests / Build
pkg/executor/test/planreplayer/BUILD.bazel, pkg/executor/test/planreplayer/plan_replayer_test.go
Increase test shard_count, add parser/auth dep, and add end-to-end tests verifying behavior for users with/without the new dynamic privilege.

Sequence Diagram(s)

sequenceDiagram
    participant Exec as Executor
    participant Domain as PlanReplayerDumper
    participant Planner
    participant PrivMgr as PrivilegeManager
    participant Sctx as SessionCtx

    Exec->>Domain: dumpSchemasWithTask(ctx, task)
    Domain->>Domain: shouldUsePlanReplayerExplainAdminBypass(sctx, task)
    Domain->>PrivMgr: RequestDynamicVerification("PLAN_REPLAYER_EXPLAIN_ADMIN")
    PrivMgr-->>Domain: allow/deny

    Domain->>Planner: dumpExplain(ctx, task) (first attempt)
    Planner->>PrivMgr: CheckPrivilege(...) (normal verification)
    alt Privilege denied and domain allowed bypass
        Planner-->>Domain: privilege error
        Domain->>Sctx: set PlanReplayerPrivilegeBypass=true
        Domain->>Planner: dumpExplain(ctx, task) (retry)
        Planner->>PrivMgr: canBypassPlanReplayerPrivilegeCheck(sctx, pm)
        PrivMgr-->>Planner: true
        Planner-->>Domain: explain result
        Domain->>Sctx: restore PlanReplayerPrivilegeBypass=false
    else No bypass needed
        Planner-->>Domain: explain result
    end

    Domain->>Exec: write artifacts (zip)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

size/XXL

Suggested reviewers

  • tiancaiamao
  • fzzf678
  • wjhuang2016

Poem

🐰 I hopped through code both near and far,
A tiny flag became a privilege star.
Explain dumps now may quietly bypass,
With careful checks — no needless trespass.
Hooray for tidy hops across the grass! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% 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 summarizes the primary change: adding a new dynamic privilege PLAN_REPLAYER_EXPLAIN_ADMIN for plan replayer functionality.
Description check ✅ Passed The description follows the template structure with issue number (close #67591), problem summary, changes explained, and includes test and release note sections.
Linked Issues check ✅ Passed The PR successfully implements the PLAN_REPLAYER_EXPLAIN_ADMIN dynamic privilege for plan replayer dump explain flow as required by issue #67591, including privilege bypass logic for internal helper SQLs.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the PLAN_REPLAYER_EXPLAIN_ADMIN privilege bypass feature; no out-of-scope modifications detected in the changeset.

✏️ 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.

@pantheon-ai pantheon-ai 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.

✅ Code looks good. No issues found.

@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)
pkg/executor/test/planreplayer/plan_replayer_test.go (1)

347-407: Add a negative control that proves the bypass stays scoped.

This only checks successful dumps, so it would still pass if plain EXPLAIN / SHOW CREATE were accidentally widened or if a user without both grants could use the command. Please add a user with neither SELECT nor PLAN_REPLAYER_EXPLAIN_ADMIN, and assert pr_admin is still denied on plain EXPLAIN / SHOW CREATE outside PLAN REPLAYER—ideally in an integration-style SQL test as well.

Based on learnings: Applies to pkg/executor/** : For SQL behavior changes in executor, perform targeted unit test plus relevant integration test.

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

In `@pkg/executor/test/planreplayer/plan_replayer_test.go` around lines 347 - 407,
Add a negative control to TestPlanReplayerExplainAdminPrivilege: create a third
user (e.g., 'pr_none'@'localhost') with no SELECT and no
PLAN_REPLAYER_EXPLAIN_ADMIN grants, authenticate a TestKit as that user and
assert that "plan replayer dump explain ..." fails/does not produce privileged
output the same way as a normal user, and also explicitly assert that pr_admin
(authenticated via adminTk) is denied for plain EXPLAIN and SHOW CREATE outside
of the plan replayer (e.g., run adminTk.MustQueryToErr("EXPLAIN SELECT ...") and
adminTk.MustQueryToErr("SHOW CREATE VIEW v_plan_replayer_priv")). Locate changes
in the TestPlanReplayerExplainAdminPrivilege function and reuse the
readZipEntryNames helper to check outputs; ensure you create/grant users with
rootTk.MustExec and authenticate them with TestKit.Session().Auth like
pr_normal/pr_admin so the test proves the bypass is scoped.
🤖 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/privilege/privileges/privileges.go`:
- Around line 217-220: The current branch returns early when
mysqlPriv.RequestVerification(...) fails but canBypassPlanReplayerPrivilege(...)
is true, skipping any extension veto from authPluginRequestVerification; update
the control flow so that even if canBypassPlanReplayerPrivilege(activeRoles) is
true you still invoke authPluginRequestVerification(p.user, p.host, activeRoles,
db, table, column, priv) and only allow the bypass if the auth plugin allows it
(i.e., call authPluginRequestVerification when present and combine its boolean
result with canBypassPlanReplayerPrivilege), keeping references to
mysqlPriv.RequestVerification, canBypassPlanReplayerPrivilege, and
authPluginRequestVerification to locate the change.

---

Nitpick comments:
In `@pkg/executor/test/planreplayer/plan_replayer_test.go`:
- Around line 347-407: Add a negative control to
TestPlanReplayerExplainAdminPrivilege: create a third user (e.g.,
'pr_none'@'localhost') with no SELECT and no PLAN_REPLAYER_EXPLAIN_ADMIN grants,
authenticate a TestKit as that user and assert that "plan replayer dump explain
..." fails/does not produce privileged output the same way as a normal user, and
also explicitly assert that pr_admin (authenticated via adminTk) is denied for
plain EXPLAIN and SHOW CREATE outside of the plan replayer (e.g., run
adminTk.MustQueryToErr("EXPLAIN SELECT ...") and adminTk.MustQueryToErr("SHOW
CREATE VIEW v_plan_replayer_priv")). Locate changes in the
TestPlanReplayerExplainAdminPrivilege function and reuse the readZipEntryNames
helper to check outputs; ensure you create/grant users with rootTk.MustExec and
authenticate them with TestKit.Session().Auth like pr_normal/pr_admin so the
test proves the bypass is scoped.
🪄 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: 0119bb8d-4e4e-4d2b-bc80-92389dcd2821

📥 Commits

Reviewing files that changed from the base of the PR and between 3cebd11 and f99f3cc.

📒 Files selected for processing (11)
  • pkg/domain/BUILD.bazel
  • pkg/domain/plan_replayer_dump.go
  • pkg/domain/plan_replayer_test.go
  • pkg/executor/test/planreplayer/BUILD.bazel
  • pkg/executor/test/planreplayer/plan_replayer_test.go
  • pkg/privilege/privilege.go
  • pkg/privilege/privileges/privileges.go
  • pkg/sessionctx/variable/BUILD.bazel
  • pkg/sessionctx/variable/plan_replayer.go
  • pkg/sessionctx/variable/session.go
  • tests/integrationtest/r/executor/executor.result

Comment thread pkg/privilege/privileges/privileges.go Outdated
Comment on lines 217 to 220
if !mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column, priv) {
return false
return p.canBypassPlanReplayerPrivilege(activeRoles)
}
return p.authPluginRequestVerification == nil || p.authPluginRequestVerification(p.user, p.host, activeRoles, db, table, column, priv)

@coderabbitai coderabbitai Bot Apr 6, 2026

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

Keep extension-auth vetoes on the bypass path.

When the built-in MySQL check fails and canBypassPlanReplayerPrivilege(...) returns true, this returns before authPluginRequestVerification runs. That means a deployment using an extension auth plugin loses its plugin-side static privilege veto for these helper SQLs.

Possible fix
  mysqlPriv := p.Handle.Get()
- if !mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column, priv) {
- 	return p.canBypassPlanReplayerPrivilege(activeRoles)
- }
- return p.authPluginRequestVerification == nil || p.authPluginRequestVerification(p.user, p.host, activeRoles, db, table, column, priv)
+ granted := mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column, priv)
+ if !granted {
+ 	granted = p.canBypassPlanReplayerPrivilege(activeRoles)
+ }
+ if !granted {
+ 	return false
+ }
+ return p.authPluginRequestVerification == nil || p.authPluginRequestVerification(p.user, p.host, activeRoles, db, table, column, priv)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/privilege/privileges/privileges.go` around lines 217 - 220, The current
branch returns early when mysqlPriv.RequestVerification(...) fails but
canBypassPlanReplayerPrivilege(...) is true, skipping any extension veto from
authPluginRequestVerification; update the control flow so that even if
canBypassPlanReplayerPrivilege(activeRoles) is true you still invoke
authPluginRequestVerification(p.user, p.host, activeRoles, db, table, column,
priv) and only allow the bypass if the auth plugin allows it (i.e., call
authPluginRequestVerification when present and combine its boolean result with
canBypassPlanReplayerPrivilege), keeping references to
mysqlPriv.RequestVerification, canBypassPlanReplayerPrivilege, and
authPluginRequestVerification to locate the change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Plugin enforcement is still preserved on that path via RequestDynamicVerification("PLAN_REPLAYER_EXPLAIN_ADMIN", ...) , which in turn invokes authPluginRequestDynamicVerification(). So the plugin can still veto the helper SQLs through the dynamic privilege hook ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@cryo-zd, you raise a valid and well-reasoned point. RequestDynamicVerification does invoke authPluginRequestDynamicVerification before returning, so an extension auth plugin can veto the bypass by returning false for PLAN_REPLAYER_EXPLAIN_ADMIN. The dynamic hook is not skipped.

There is, however, a narrow residual gap worth being aware of:

The skipped hook is specifically authPluginRequestVerification — the static privilege check (table-level SELECT, etc.). A plugin that only implements authPluginRequestVerification (e.g., to enforce fine-grained, per-table access rules) but has not implemented authPluginRequestDynamicVerification would have its static veto bypassed. Because authPluginRequestDynamicVerification is nil, RequestDynamicVerification falls through to true, enabling the bypass even if the plugin's static rules would have denied access.

In practice this means:

  • Plugins that implement the dynamic hook → fully protected (your point is correct).
  • Plugins that rely solely on the static hook without a corresponding dynamic hook for PLAN_REPLAYER_EXPLAIN_ADMIN → their per-table static veto is silently skipped on the bypass path.

Since PLAN_REPLAYER_EXPLAIN_ADMIN is a new privilege, existing plugins that predate this PR would not have implemented a dynamic handler for it, leaving them in the second category. Whether that's acceptable depends on whether TiDB's extension plugin contract requires plugins to opt into new dynamic privileges explicitly. If so, my original comment may be overstated and your reasoning holds. But it's a nuance worth documenting.


🧠 Learnings used
Learnt from: cryo-zd
Repo: pingcap/tidb PR: 65532
File: pkg/planner/core/preprocess.go:1887-1898
Timestamp: 2026-03-12T08:41:44.509Z
Learning: In pkg/planner/core/preprocess.go (pingcap/tidb PR `#65532`), the `checkLockClauseTables` function intentionally accepts `FOR UPDATE OF <base_table>` even when the FROM item was introduced with an alias (`matched.alias.L != "" && !matchedByAlias`). Rather than returning an error (strict MySQL behavior), TiDB emits a warning to guide users toward the alias form. This backward-compatibility fallback is a deliberate acceptance criterion for the change — do not flag it as a MySQL-incompatibility bug in future reviews.

Learnt from: cryo-zd
Repo: pingcap/tidb PR: 65532
File: pkg/planner/core/preprocess.go:1906-1910
Timestamp: 2026-03-12T08:03:56.097Z
Learning: In pingcap/tidb (PR `#65532`), `buildSelect` in `pkg/planner/core/logical_plan_builder.go` derives the privilege-check DB for `FOR UPDATE OF` lock targets using `getLowerDB(tName.Schema, ...)`. For alias or unqualified lock targets whose `TableName.Schema` is empty after preprocessing, this falls back to `currentDB` and authorizes against the wrong database. The correct fix is to use `tNameW.DBInfo.Name.L` (with a nil guard) directly, since `tNameW` is already fully resolved by `checkLockClauseTables` in preprocess.go. This is consistent with how privilege checks for INSERT/DELETE use `tnW.DBInfo.Name.L` elsewhere in the codebase.

Learnt from: joechenrh
Repo: pingcap/tidb PR: 66878
File: pkg/planner/core/logical_plan_builder.go:6455-6472
Timestamp: 2026-03-11T06:29:00.122Z
Learning: Ensure code reviews verify the VirtualAssignmentsOffset semantics: the planner sets Update.VirtualAssignmentsOffset = len(update.List). The executor should only apply OrderedList[:VirtualAssignmentsOffset] when composing new rows, and only after the 'changed' check should it group/evaluate OrderedList[VirtualAssignmentsOffset:] per table. This pattern applies to files under pkg/planner/core and pkg/executor (e.g., common_plans.go and update.go). Reviewers should check that updates respect slicing behavior, that the offset is consistently derived from the planner, and that downstream code does not bypass the offset when creating new rows. Add tests validating both branches: the slice before the offset for new rows, and the per-table handling of the slice after the offset.

@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)
pkg/domain/plan_replayer_dump.go (1)

525-536: Precompute the task-level bypass decision once.

shouldUsePlanReplayerExplainAdminBypass depends only on task and sctx, but the schema path recomputes it inside each SHOW CREATE retry. Caching that boolean in dumpSchemasWithTask and passing it down would make this security-sensitive flow easier to audit and avoid rewalking every statement for each extracted object.

As per coding guidelines, code SHOULD remain maintainable for future readers with basic TiDB familiarity, including readers who are not experts in the specific subsystem/feature.

Also applies to: 573-590, 943-955

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

In `@pkg/domain/plan_replayer_dump.go` around lines 525 - 536, Precompute the
result of shouldUsePlanReplayerExplainAdminBypass once in dumpSchemasWithTask
and pass that boolean into getShowCreateTable (and any other helpers that
currently call shouldUsePlanReplayerExplainAdminBypass internally) instead of
recomputing it for every SHOW CREATE retry; update the signature of
getShowCreateTable (and mirrored call sites noted around the other ranges) to
accept the cached bypass flag, use it inside the retry logic, and keep
dumpSchemaMeta unchanged—this avoids repeated security decision recomputation
and makes the flow easier to audit.
🤖 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/domain/plan_replayer_dump.go`:
- Around line 593-596: The file-based (deferred) dump path can mutate the user's
shared SessionVars via runWithPlanReplayerSQLPrivilegeType, creating a
concurrency/security hole; change the deferred path so it does not mutate the
caller's session: update
DumpPlanReplayerInfo/handleSpecialQuery/PlanReplayerExec.Next to either accept a
PlanReplayerInternalSQLType parameter and use that instead of touching
SessionVars, or create a dedicated session/session-vars clone for the deferred
worker and set the privilege on that clone (e.g., obtain a new session via
createSessions() or clone sessionctx.GetSessionVars(), set
PlanReplayerSQLPrivilegeType there) before running the async dump, and remove
the temporary SetPlanReplayerSQLPrivilegeType calls on the original user session
in runWithPlanReplayerSQLPrivilegeType for the deferred path.

---

Nitpick comments:
In `@pkg/domain/plan_replayer_dump.go`:
- Around line 525-536: Precompute the result of
shouldUsePlanReplayerExplainAdminBypass once in dumpSchemasWithTask and pass
that boolean into getShowCreateTable (and any other helpers that currently call
shouldUsePlanReplayerExplainAdminBypass internally) instead of recomputing it
for every SHOW CREATE retry; update the signature of getShowCreateTable (and
mirrored call sites noted around the other ranges) to accept the cached bypass
flag, use it inside the retry logic, and keep dumpSchemaMeta unchanged—this
avoids repeated security decision recomputation and makes the flow easier to
audit.
🪄 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: 056935e2-2732-4ea0-a54f-45bedd4ed0ab

📥 Commits

Reviewing files that changed from the base of the PR and between f99f3cc and 8e96708.

📒 Files selected for processing (1)
  • pkg/domain/plan_replayer_dump.go

Comment thread pkg/domain/plan_replayer_dump.go Outdated
@YangKeao

YangKeao commented Apr 7, 2026

Copy link
Copy Markdown
Member

/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 Apr 7, 2026
@YangKeao

YangKeao commented Apr 7, 2026

Copy link
Copy Markdown
Member

Please create an issue and modify close #xxx.

The issue should have label type/enhancement.

@codecov

codecov Bot commented Apr 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.07339% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.5305%. Comparing base (3cebd11) to head (b55b209).
⚠️ Report is 305 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #67576        +/-   ##
================================================
+ Coverage   77.6380%   79.5305%   +1.8925%     
================================================
  Files          1961       1991        +30     
  Lines        543853     550983      +7130     
================================================
+ Hits         422237     438200     +15963     
+ Misses       120806     111322      -9484     
- Partials        810       1461       +651     
Flag Coverage Δ
integration 46.8808% <44.3396%> (+12.5503%) ⬆️
unit 76.7657% <88.0733%> (+0.3832%) ⬆️

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

Components Coverage Δ
dumpling 61.5065% <ø> (ø)
parser ∅ <ø> (∅)
br 66.6416% <ø> (+6.3037%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hawkingrei

Copy link
Copy Markdown
Contributor

/retest

Comment thread pkg/privilege/privileges/privileges.go Outdated
mysqlPriv := p.Handle.Get()
if !mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column, priv) {
return false
return p.canBypassPlanReplayerPrivilege(activeRoles)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please do not hack the privilege in this package. I prefer to skip the visitInfo of the privilege in planner when the user has the requested dynamic privilege.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gave it a try, PTAL.

PlanReplayerInternalSQLTypeNone PlanReplayerInternalSQLType = iota
PlanReplayerInternalSQLTypeExplain
PlanReplayerInternalSQLTypeShowCreateTable
PlanReplayerInternalSQLTypeShowCreateView

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we have different behavior for these three flags? Maybe it's fine to just use a bool value to represent whether the current statement is executed inside a plan replayer statement.

Comment thread pkg/sessionctx/variable/session.go Outdated
InPlanReplayer bool

// planReplayerSQLPrivilegeType limits the temporary privilege bypass to one helper SQL shape.
planReplayerSQLPrivilegeType PlanReplayerInternalSQLType

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add this thing to the SessionVars is not a good idea, though I know many strange states are already added to the SessionVars 🤦 .

As it's not widely used, I'd suggest to add this state though SetValue and Value.

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

🧹 Nitpick comments (2)
pkg/privilege/privileges/privileges.go (2)

217-219: Consider gating the fallback by priv too.

Right now any failed MySQL static privilege check under these helper SQL modes can fall through to PLAN_REPLAYER_EXPLAIN_ADMIN. Passing priv into canBypassPlanReplayerPrivilege and whitelisting only the expected read-only checks would keep later helper changes from silently widening the bypass surface.

Also applies to: 223-238

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

In `@pkg/privilege/privileges/privileges.go` around lines 217 - 219, The fallback
to canBypassPlanReplayerPrivilege currently ignores which privilege was checked,
allowing any failed mysqlPriv.RequestVerification to fall through; update
canBypassPlanReplayerPrivilege to accept the priv parameter and change call
sites (e.g., the branch in privileges.go where
mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column,
priv) is used) to pass priv through, then implement a whitelist inside
canBypassPlanReplayerPrivilege that only allows specific read-only privileges
(e.g., SELECT/SHOW/EXPLAIN) to bypass PLAN_REPLAYER_EXPLAIN_ADMIN so other
privileges cannot be silently widened.

81-82: Document the exact bypass contract.

PLAN_REPLAYER_EXPLAIN_ADMIN reads like a SELECT-only escape hatch, but the actual allowlist here is session-scoped EXPLAIN, SHOW CREATE TABLE, and SHOW CREATE VIEW. Please spell that out near the privilege registration or helper; this path is security-sensitive and the current wording leaves too much implicit.

As per coding guidelines "Comments SHOULD explain non-obvious intent, constraints, invariants, concurrency guarantees, SQL/compatibility contracts, or important performance trade-offs, and SHOULD NOT restate what the code already makes clear."

Also applies to: 223-238

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

In `@pkg/privilege/privileges/privileges.go` around lines 81 - 82, Update the
documentation near the PLAN_REPLAYER_EXPLAIN_ADMIN privilege registration and
the related helper at lines referenced so it explicitly states the precise
bypass contract: that this privilege allows session-scoped execution of EXPLAIN,
SHOW CREATE TABLE, and SHOW CREATE VIEW only (and does not grant general SELECT
or other table-read rights), including any constraints (e.g., whether it applies
to all schemas/tables or only those in the current session) and any
security/invariant notes; mention the exact symbols PLAN_REPLAYER_EXPLAIN_ADMIN
and the helper/registration function used in this file so reviewers can find the
comment easily and ensure it covers both the initial declaration and the block
around lines 223-238.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/privilege/privileges/privileges.go`:
- Around line 217-219: The fallback to canBypassPlanReplayerPrivilege currently
ignores which privilege was checked, allowing any failed
mysqlPriv.RequestVerification to fall through; update
canBypassPlanReplayerPrivilege to accept the priv parameter and change call
sites (e.g., the branch in privileges.go where
mysqlPriv.RequestVerification(activeRoles, p.user, p.host, db, table, column,
priv) is used) to pass priv through, then implement a whitelist inside
canBypassPlanReplayerPrivilege that only allows specific read-only privileges
(e.g., SELECT/SHOW/EXPLAIN) to bypass PLAN_REPLAYER_EXPLAIN_ADMIN so other
privileges cannot be silently widened.
- Around line 81-82: Update the documentation near the
PLAN_REPLAYER_EXPLAIN_ADMIN privilege registration and the related helper at
lines referenced so it explicitly states the precise bypass contract: that this
privilege allows session-scoped execution of EXPLAIN, SHOW CREATE TABLE, and
SHOW CREATE VIEW only (and does not grant general SELECT or other table-read
rights), including any constraints (e.g., whether it applies to all
schemas/tables or only those in the current session) and any security/invariant
notes; mention the exact symbols PLAN_REPLAYER_EXPLAIN_ADMIN and the
helper/registration function used in this file so reviewers can find the comment
easily and ensure it covers both the initial declaration and the block around
lines 223-238.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6c045304-9673-4636-b1d3-a31f1c4b796b

📥 Commits

Reviewing files that changed from the base of the PR and between 8e96708 and 4c37edb.

📒 Files selected for processing (1)
  • pkg/privilege/privileges/privileges.go

@ti-chi-bot ti-chi-bot Bot added the sig/planner SIG: Planner label Apr 7, 2026
@ti-chi-bot

ti-chi-bot Bot commented Apr 7, 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 elsa0520, 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

@tiprow

tiprow Bot commented Apr 7, 2026

Copy link
Copy Markdown

@cryo-zd: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
fast_test_tiprow b5f7435 link true /test fast_test_tiprow

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.

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

🧹 Nitpick comments (1)
pkg/domain/plan_replayer.go (1)

585-585: Consider documenting the privilege-bypass field's intent and constraints.

The useExplainAdminBypass field controls security-sensitive behavior (enabling privilege bypass for EXPLAIN/SHOW CREATE operations). A brief inline comment would help future readers understand:

  • When this field should be set to true
  • That it requires the caller to hold PLAN_REPLAYER_EXPLAIN_ADMIN privilege

This is especially important since the field is unexported and its purpose isn't immediately obvious from the name alone.

📝 Suggested documentation
 	ExecStmts             []ast.StmtNode
 	Analyze               bool
+	// useExplainAdminBypass, when true, allows internal EXPLAIN/SHOW CREATE
+	// operations to bypass standard privilege checks. Only set when the
+	// invoking session holds PLAN_REPLAYER_EXPLAIN_ADMIN dynamic privilege.
 	useExplainAdminBypass bool
 	HistoricalStatsTS     uint64

As per coding guidelines: "Comments SHOULD explain non-obvious intent, constraints, invariants, concurrency guarantees, SQL/compatibility contracts."

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

In `@pkg/domain/plan_replayer.go` at line 585, Add an inline comment above the
unexported boolean field useExplainAdminBypass in plan_replayer.go explaining
its intent and constraints: note that it enables privilege bypass for
EXPLAIN/SHOW CREATE operations, must only be set to true when the caller holds
the PLAN_REPLAYER_EXPLAIN_ADMIN privilege, and that it is security-sensitive
(describe any invariants such as not changing at runtime or who is responsible
for validation). Keep the comment brief and precise so future readers understand
when and by whom useExplainAdminBypass may be enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/domain/plan_replayer.go`:
- Line 585: Add an inline comment above the unexported boolean field
useExplainAdminBypass in plan_replayer.go explaining its intent and constraints:
note that it enables privilege bypass for EXPLAIN/SHOW CREATE operations, must
only be set to true when the caller holds the PLAN_REPLAYER_EXPLAIN_ADMIN
privilege, and that it is security-sensitive (describe any invariants such as
not changing at runtime or who is responsible for validation). Keep the comment
brief and precise so future readers understand when and by whom
useExplainAdminBypass may be enabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: afb1d679-35ee-4603-8a9b-ee0b22894e3e

📥 Commits

Reviewing files that changed from the base of the PR and between b5f7435 and 3f9ea31.

📒 Files selected for processing (2)
  • pkg/domain/plan_replayer.go
  • pkg/domain/plan_replayer_dump.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/domain/plan_replayer_dump.go

@YangKeao

YangKeao commented Apr 8, 2026

Copy link
Copy Markdown
Member

/retest

2 similar comments
@cryo-zd

cryo-zd commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@cryo-zd

cryo-zd commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@YangKeao YangKeao requested a review from hawkingrei April 9, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Support PLAN_REPLAYER_EXPLAIN_ADMIN for PLAN REPLAYER DUMP EXPLAIN

3 participants