Skip to content

[4.2-dev] fix: make account restore repeatable and rebind grants - #29431

Merged
XuPeng-SH merged 13 commits into
matrixorigin:4.2-devfrom
gouhongshen:agent/issue-29399-4.2
Oct 2, 2026
Merged

XuPeng-SH merged 13 commits into
matrixorigin:4.2-devfrom
gouhongshen:agent/issue-29399-4.2

Conversation

@gouhongshen

@gouhongshen gouhongshen commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • API-change
  • BUG
  • Improvement
  • Documentation
  • Feature
  • Test and CI
  • Code Refactoring

Which issue(s) this PR fixes:

Fixes #29399 on 4.2-dev.

What this PR does / why we need it:

Repeated account restore misinterprets stored CLONE provenance as CREATE TABLE metadata. Copying historical object IDs also leaves restored grants attached to the wrong objects, while retained connections can reuse obsolete user and role identities.

  • Restore grants through one coordinator inside the existing restore transaction, after reconstructed tables, views and deferred subscriptions exist. Rebind account/database/name/kind to current object IDs; preserve wildcard sentinels and grant options. Snapshot, PITR, cross-account and cluster restore share this path.
  • Full account restore reconstructs principal catalogs. Partial restore preserves surviving principal IDs and current grants. Validate ownership only for objects actually reconstructed, preserving legitimate role renames and existing parent databases while rejecting missing table owners and reused identities.
  • Preserve CHECK constraints and malformed-metadata rejection while distinguishing CLONE provenance and views. Propagate restore/publication errors and retain rollback and cancellation ownership.
  • Reuse the existing session privilege cache when storage-owned catalog revisions, subscription/replay continuity and the current principal prove its validity at the statement's authorization snapshot. Misses use an independent read-only SQL transaction pinned to that snapshot. Warm admission creates no transaction or allocation. Cold fills are sealed only after successful transaction cleanup and matching catalog captures.
  • Keep cached grants under one reachable role, matching the existing compound-privilege SQL rule. Bound retained scopes and names; clear unproven or partially filled state. Validate current identity, role membership and account retirement version for retained sessions and protected native commands.
  • Avoid repeating the full parser for single-statement sanitized SQL records by reusing the existing record helper’s one-record-per-fragment contract. Preserve multi-statement filtering, SQL-mode handling, malformed-SQL errors and secret redaction.
  • For binary prepared execution, record the existing protocol-generated EXECUTE envelope directly. Its fixed grammar contains only a numeric statement name, never prepared SQL or bound values. This removes redundant parsing/sanitization while preserving downstream statement handling and parameter redaction.
  • Preserve SQL-task definer identity at the existing internal-executor session boundary. Reuse login-name parsing for bare/qualified names, bind the authorization username and resolve the tenant/selected-role names from the final numeric IDs, reject malformed or missing identities before SQL and close the allocated session on failure. SQL-task metadata and filters take account/user/role IDs from the same authenticated TenantInfo, including fresh native-only connections. SET ROLE does not reinterpret the role embedded in the original login name. Catalog ID/name/role checks are not bypassed.
  • Align the existing BVT sessions with that identity contract: the old target admin must reject after cross-account restore, while a fresh restored-source login reads the data; protected-database case-fold checks reuse the actual live tenant session rather than a cached connection belonging to a deleted SYS user.
  • Retain immutable prepared authorization requirements on PrepareStmt, tied to its existing plan replacement/Close lifecycle. Remove the mutable session requirement slot and matched-role field. Ordinary, binary/text prepared and executable EXPLAIN wrappers use the shared evaluators. Preserve EXPLAIN output handling across compile reuse.

Visibility follows the existing statement RC snapshot and logtail frontier. The TN serial pre-prepare/prepare ordering establishes the inclusive snapshot boundary; a real-store regression and a fixed-clock SQL/publication oracle protect this invariant.

Validation

Rebased the original PR branch onto 4.2-dev at 28f1f02a89339def1b2572dbe07e98144a203167.

Current local checks use Linux amd64 / Go 1.26.4 and the repository CGo wrapper:

  • Full frontend race after the final record-generation optimization: 29.316 s. Current three-CN restore/revocation/identity/EXPLAIN/role-provenance race suite: 41.954 s.
  • Real TAE transaction-store tests with a frozen production HLC cover database-only DDL and row insert/delete timestamp ordering. Independent compiled SQL at the identical snapshot checks authorization before, exactly at, and after a real grant deletion published after the cached capture. The warm check performs one capture and opens zero authorization transactions.
  • Ordinary, text/binary prepared, active/inherited roles, cross-CN REVOKE/REGRANT, retained identities, old user transactions, protected native commands and denied-write effects are covered. Failed/reused prepared-plan boundaries also pass targeted race tests.
  • Actual KILL QUERY during account restore checks rollback of data, logical IDs, grants and retained access, followed by successful retry. Existing injected read/delete/batch failures cover cleanup and error propagation. Prior full issues/embed race results remain evidence for unchanged restore and fixture paths.
  • Final incremental SCA across nine affected package roots: 0 issues; go vet: passed. The final cleanup regression passes -race (1.888 s) and 46 repeated same-process runs (32.724 s). The TN ordering regression passes 100 repetitions (1.740 s); its full owning package race passes (2.256 s). The complete mo-service binary builds with matching prebuilt native libraries. The final binary-envelope delta also passes frontend incremental SCA (0 issues) and go vet.

Performance

The final warm binary COUNT/SELECT experiment passes all four predeclared zero-margin nonregression gates. Sixteen balanced ABBA/BAAB blocks plus four A/A control pairs completed 72/72 successful processes. Each variant in each block ran both workload orders; each workload had 2,000 warm-up and 30,000 measured queries.

Candidate-minus-base latency (negative is faster):

Endpoint Mean paired block change Simultaneous upper confidence bound
COUNT median −3.792 µs −0.916 µs
COUNT p95 −3.829 µs −1.887 µs
SELECT median −2.694 µs −1.537 µs
SELECT p95 −2.543 µs −0.915 µs

Each endpoint uses a one-sided 98.75% t bound across 16 block contrasts (simultaneous 95% coverage for four endpoints, assuming independent block errors). Acceptance required every upper bound ≤ 0; no positive tolerance was allowed. Matched native inputs, frozen binaries, CPU affinity, GOMAXPROCS and raw per-query samples were retained. An earlier candidate failed this gate; profiling identified redundant binary record parsing, which the final candidate removes.

A longer four-connection prepared UPDATE check used 200 warm-up and 10,000 measured updates per worker (40,000 per process), with disjoint primary keys and affected-row assertions. All six AA+ABBA runs passed:

Run Median (µs) p95 (µs) QPS
A/A base 1 1193.526 7017.611 1746.50
A/A base 2 1184.847 6422.173 1881.05
ABBA base 1 1179.861 6387.552 1857.86
ABBA candidate 1 1145.901 6293.253 1812.76
ABBA candidate 2 1155.701 6264.292 1839.98
ABBA base 2 1145.972 6388.430 1481.35

This longer write diagnostic reports all observed values; six processes do not establish a statistical nonregression bound for UPDATE. No tolerance was introduced from the A/A variability.

These measurements support the stated warm workloads, not universal workload parity. Catalog changes and other uncertified cache states still require SQL validation. Earlier ordinary/admin/public, text prepared and JOIN probes provide supporting coverage but are not a statistical whole-system throughput guarantee.

Change size

The following original implementation counts exclude the final license-header, behavior-neutral analyzer-cleanup and internal-executor/BVT follow-ups.

  • production: 30 files, +2029/−600 (net +1429).
  • tests_results: 42 files, +4401/−564 (net +3837).
  • shared_fixtures: 2 files, +5/−0 (net +5).

Most new implementation is restore grant rebinding and a bounded certificate over the existing privilege cache. Storage revisions stay with replay state; authorization stays with the existing evaluator. Prepared requirements reuse PrepareStmt lifecycle. Tests cover distinct public SQL, distributed publication, restore rollback, and ownership boundaries; benchmark probes and measurement artifacts are not shipped.

Review / CI

The requested Astra xhigh design and independent gpt-6.1-sol xhigh review are recorded with exact source and test artifacts. Delivered implementation commit: ac0f58d322729557896418cce3c8abdb0d4d32b6; current head: 8f27fa7ff0fcee217feef013e476be1495ced552 (license/analyzer cleanup followed by scoped internal-executor and BVT identity fixes). Original implementation independent gpt-6.1-sol / xhigh review: APPROVE for source tree db83b2d966c790a6a0c0280165ad7dd43febd001, with no unresolved material blocker. The reviewer independently checked all 72 read-process raw samples and the six UPDATE runs, and reconciled prior source, ownership, failure-path and test-quality reviews. UPDATE variability and unmeasured-workload limits remain explicit above.

Implementation-head SCA job 110829631877 failed the configured license header check, identifying authorization_cache_test.go, prepared_authorization.go, and explain_authorization_test.go. Commit 6dee99854aed55b661db220767c4c68703d8de51 completes their standard Apache headers only; all non-header bytes in that delta are unchanged. Local license-eye v0.4.0 checks pass for the three files and all tracked configured source extensions (4,614 checked; 0 invalid). Whitespace checks and independent local header-delta review pass. The earlier implementation/runtime and benchmark evidence above is unchanged-source evidence, not a fresh whole-suite run for that comment-only commit.

The next SCA job 110843254669 passes header/dependency checks, then reports three analyzer findings: an unreachable user-role SQL helper/constant and inconsistent receiver naming on PrepareStmt. Follow-up head 59f364619cf30816a7be4629db15a4070a6a1137 removes only that dead helper/constant and aligns the receiver name. A concurrent local candidate has the identical complete source-tree hash 8c7a50c7db5305733cb365b74f7a4846f5a7015f; the remote commit is preserved, with no duplicate push or force push. On this exact source tree, full frontend configured lint reports 0 issues, full frontend -race passes (23.825 s), and independent read-only local cleanup review approves. No authorization/cache/prepared-plan semantics, tests, workflows or linter policy are changed.

Current CI-regression follow-up

Completed prior-head run 37014090595 passes Ubuntu UT, arm64 SCA, shared build and UT Coverage, but does not pass BVT or its aggregates. PROXY 110863242559 has six failures: two reads through the obsolete cross-account target-admin connection and four protected-database operations through a reused session ID that still belongs to a deleted SYS user. PESSIMISTIC 110863242558 has 58 failures in the two SQL-task files because the definer username was not propagated into the internal authorization principal. Downstream Coverage and CI Required fail; no changed-code coverage percentage is claimed for that run.

Commit 377869d30673d2d48aaec0ba421acdb9c2222f47 fixes the existing internal-executor identity boundary and the two stale BVT session assumptions. The SQL-task golden results and authorization checks stay unchanged; the old-admin denial is retained alongside the legitimate restored-login positive control. No workflow, tester, metadata suppression, protection policy, native build input or generated transfer file is changed.

Fresh monitor-local proof on macOS ARM64 / Go 1.26.4 / Java 20:

  • Full frontend/taskservice -race passes (31.987 s / 43.591 s); strengthened actual DefinerOpts and active-role identity controls pass ten repetitions under race (2.946 s). Bare/qualified/encoded/label names, nonzero option IDs, root zero IDs, and malformed command/base overrides are covered. Final configured frontend lint: 0 issues; production binary builds.
  • On the rebuilt patched binary, strict JDBC snapshot BVT is 215/215, and SQL-task/procedure BVT is 170/170, including repeated runs. The local CN uses the source default Pessimistic transaction mode; this is not the remote multi-CN Linux environment.
  • Protected-database strict BVT is 160/164: four unchanged column labels expect @@protected_databases but return @@global.protected_databases. Pristine prior-head control is 156/164, with the same four labels plus the four identity failures addressed here. Protected row/error semantic validation is 164/164 using the existing local nometa option; full strict metadata validation is not passed. No label expectations or tester configuration are committed. This local prior-head control does not establish a clean-upstream/platform root cause.
  • Independent read-only local reviews approve the scoped fixture and internal-executor deltas. Named fixture cleanup is zero, and task-owned services/tests have stopped. Earlier whole-PR/performance evidence remains explicitly unchanged-path historical evidence, not a fresh whole-suite proof for this commit.

Prior-head ALL CI 37023454288 for 377869d3 subsequently completes successfully: actual Ubuntu UT, SCA, UT Coverage, shared build, PROXY/PESSIMISTIC BVT, Coverage and CI Required succeed. Mongo E2E succeeds. Iceberg 37023457426 / 110892062786 fails at 15:02:48 UTC while pulling MinIO (unauthorized), before integration SQL; the persistent setup blocker is not blindly rerun. None of these prior-head successes replaces the new-head checks below. The previously confirmed 4.2-dev protection rule requires five historical context names while producers use execution names; the existing administrator request is separate from code fixes.

SQL-task principal follow-up (prior head 465d94dcb1)

SQL-task follow-up head: 465d94dcb1db8aced132a0ea9b60bf361b2cb6ae. XuPeng's new P2 4167312881 identifies a valid in-scope producer/consumer mismatch: non-SYS task sessions have user/role IDs but lack tenant/role policy names, so legitimate accountadmin REVOKE is rejected. A fresh authenticated three-CN reproduction on 377869d3 passes direct administrator REVOKE and INSERT task, but fails actual REVOKE task, SET ROLE task and bare-name/explicit-ID positive controls (18.020 s).

  • After both existing override layers, one internal-session binder obtains the account name from its effective ID and reuses the existing selected-role lookup by ID. Parsed usernames and numeric IDs are retained; malformed names, missing account/role and lookup errors stop before user SQL. Background and allocated session cleanup remain owned by their existing Close paths. Unnamed bootstrap sessions and ordinary public warm authorization paths are unchanged; named internal commands incur these catalog name reads, with no new task-path performance claim.
  • SET ROLE testing also exposes fresh native-only task sessions reading the plan-populated account field as zero. Task creation and all task/run metadata filters now uniformly use authenticated TenantInfo, the existing source of user/selected-role IDs. No role is recovered from the stale Creator login string.
  • Public SQL asserts actual grant removal, original reader login versus selected accountadmin ID, bare username/explicit IDs, ordinary-role denial, login-admin/selected-reader denial, mismatched username/user-ID denial and revoked membership. No identity or membership check is relaxed. SQL-task golden results, workflows, tester and generated transfer files are not changed.

Monitor-local macOS ARM64 / Go 1.26.4 proof on the 465d94dcb1 source:

  • Full frontend/taskservice -race passes (24.760 s / 44.518 s); final IE/SQL-task handler controls pass 10 repetitions under race (2.763 s). The first missing-ID mock omitted its empty result-set shape and failed the test harness; corrected empty sets pass without production fallback.
  • Complete authenticated TestIssue29399* family, including the final public task controls plus retained identity, snapshot/PITR and failed-restore authorization, passes (22.993 s). Test-only atomic [no statements] is not production statement coverage. The earlier intermediate candidate passed administrator/bare controls but still failed SET ROLE because metadata stored account 0; the final canonical-account correction closes that failure without a preparatory SELECT.
  • Full configured frontend lint: 0 issues; production binary build and whitespace checks pass. Independent read-only local review APPROVE for this four-file follow-up, not a new whole-PR benchmark or GitHub approval. Explicit injected catalog-read failures/Close-call counters remain a test-coverage gap; error cleanup is statically reviewed.

The remote CI and local proofs for this prior 465d94dcb1 head are historical; see the exact-current-head CI status below. Earlier warm-workload measurements remain unchanged-path evidence, not a universal performance or merge-protection guarantee.

Current SCA Error-Contract Follow-up

Current head: 8f27fa7ff0fcee217feef013e476be1495ced552. Prior-head SCA 110928686599 fails the repository Makefile err-check at 16:42:08 UTC: the new role-error fmt.Errorf wrapper is prohibited. This is introduced by the SQL-task follow-up, not an infrastructure failure. The previous golangci-lint success did not exercise this separate policy.

  • The existing role lookup error is now returned directly, preserving its original *moerr.Error and error code. Tenant/user/selected-role binding, fail-before-SQL, membership rejection and background/session cleanup are unchanged. No substitute exception, new error wrapper, fallback, linter suppression, workflow or tester change.
  • Both missing-account/selected-role Exec and Query controls assert moerr.ErrInternal in addition to their messages. The original role wrapper deterministically fails this strengthened contract (1.524 s); the repository's IsMoErrCode intentionally requires a direct *moerr.Error.

Fresh local proof of this two-file follow-up (macOS ARM64 / Go 1.26.4 with real absolute CGo inputs/rpaths):

  • Repository-wide make err-check: PASS, after a recorded pre-fix rejection.
  • Final IE/SQL-task handler controls: 10 repetitions under race PASS (2.949 s), including missing-account/role typed errors on both entry points.
  • Complete authenticated TestIssue29399* family: PASS (25.966 s), including actual accountadmin REVOKE, fresh SET ROLE, bare IDs, ordinary-role and revoked-membership denial plus retained restore/PITR/identity controls. Test-only atomic [no statements] is not production coverage.
  • Full configured frontend golangci-lint: 0 issues; compilation and gofmt/diff checks pass. Independent read-only local review APPROVE for the exact two-file delta. No fresh full BVT, whole-repository SCA, production binary/version build or performance measurement is claimed; explicit injected catalog-read/Close-counter coverage remains a gap.

Normal push follows a fresh awaited/read OPEN/head/body guard. XuPeng-SH subsequently submitted an actual APPROVED review on this exact 8f27fa7f head at 2026-10-02 17:17:31 UTC; no redundant review request or self-resolution is made.

Current-head ALL CI run 37036619505, attempt 1, completes SUCCESS at 17:40 UTC: actual Ubuntu UT, arm64 SCA, shared build, UT Coverage, PROXY/PESSIMISTIC multi-CN BVT, Coverage and CI Required all succeed. The CI Required log confirms full scope, generation 37036619505-1, all five dependency groups successful and coverage_ready=true / coverage_status=passed. The Coverage log selects UT plus complementary BVT profiles from that same generation and publishes the merged result. The current SCA log confirms the prohibited-error check, license headers (4,642 files; 0 invalid) and configured analyzer result (0 issues) pass. Mongo E2E also succeeds. Skipped checks are not tests passed.

The actual current-head Iceberg job remains FAILURE before SQL: MinIO image pull is unauthorized. Its workflow-level SUCCESS comes from the existing continue-on-error, not a successful integration test. No additional rerun is requested without recovery evidence. The PR remains OPEN / Ready / MERGEABLE / BLOCKED / APPROVED. The five historical Required contexts were last confirmed still expected by the reviewer's read-only protection inspection at 16:49 UTC; this monitor cannot revalidate that configuration through its unreadable protection API or unsigned-in browser. Completed execution jobs and CI Required do not establish that all merge protections are satisfied. Three code discussion threads remain unresolved; their code fixes and current-head approval are separate from discussion status. No protection, Ready/base, approval or merge policy is changed.

@mergify
mergify Bot requested a review from XuPeng-SH September 28, 2026 05:55
@mergify mergify Bot added the kind/bug Something isn't working label Sep 28, 2026
@matrix-meow matrix-meow added the size/XL Denotes a PR that changes [1000, 1999] lines label Sep 28, 2026
@gouhongshen
gouhongshen marked this pull request as ready for review September 28, 2026 07:45
@gouhongshen
gouhongshen requested a review from aunjgr as a code owner September 28, 2026 07:45
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@gouhongshen

Copy link
Copy Markdown
Contributor Author

@XuPeng-SH CI rollout blocker on 4.2-dev: the actual tests passed on head fbf3df901ce55a6699abe3f13a657a151444468d, but the merge gate has not passed. The PR UI still shows these five required contexts as Expected - Waiting for status to be reported:

Required context still expected Successful emitted context
Matrixone CI / SCA Test on Linux/arm64 Matrixone CI execution / SCA Test on Linux/arm64
Matrixone CI / UT Test on Ubuntu/x86 Matrixone CI execution / UT Test on Ubuntu/x86
Matrixone Compose CI / multi cn e2e bvt test docker compose(PROXY) Matrixone Compose CI execution / multi cn e2e bvt test docker compose(PROXY)
Matrixone Standlone CI / multi CN e2e BVT Test on Linux/x64(COMPOSE, PESSIMISTIC) Matrixone Standlone CI execution / multi CN e2e BVT Test on Linux/x64(COMPOSE, PESSIMISTIC)
Matrixone Utils CI / Coverage Matrixone Coverage execution / Coverage

Run 36650587281 used trusted workflow SHA a36da7f72d78030d8822ac310a9d20089db4c90f and full scope. UT, SCA, both multi-CN BVT groups, UT coverage producer, final Coverage (206/273 = 75.46%), and CI Required succeeded.

#28547 removed the old aggregate names and documented migrating required contexts to the GitHub Actions CI Required check. Could the repository administrator complete/reconcile that rollout for 4.2-dev, preserving strict/up-to-date, code-owner review, and unrelated protections? Reruns produce the same new names and cannot satisfy the old contexts. This restore PR does not change CI workflows or branch protection.

Correction to the earlier status: the aggregate job passed, not all required merge checks. The merge gate remains blocked by the missing contexts and required review.

@XuPeng-SH XuPeng-SH left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep review: REQUEST_CHANGES

审阅 head 37fbfd16dca22168ae5fd9218af6a396845702bb,base/merge-base 28f1f02a89339def1b2572dbe07e98144a203167。31 个文件的实现、测试、SQL/golden 均已审阅;未开启 subagent。

统一 grants 恢复入口、复用恢复事务、按 account/database/name/kind 重绑对象 ID、复用 DATA BRANCH 授权的方向合理。两个公开 SQL 反例说明授权/ownership 的边界尚未闭环,详见两条 inline comments:

  1. P1:跨账号恢复后,旧目标连接继承另一个身份的 SELECT 权限。 恢复前拒绝,恢复后同一物理连接读到 42;旧用户的新连接却因用户已不存在被拒绝。清 grants cache 没有使旧 UserID/DefaultRoleID 失效。
  2. P2:合法 TABLE RESTORE 被未重建数据库的历史创建者阻断。 表创建者仍有效;删除数据库创建者前恢复成功,删除后对同一快照恢复报错,数据仍保留但恢复无法完成。

验证与工程要求

  • 使用该 head 的 CI 共享构建产物(BUILD_INFO SHA 匹配,-cover),在独立端口、UUID、gossip seed 和 MinIO 前缀的实例复现;GOMAXPROCS=2、GOMEMLIMIT=1GiB、各配置 memory cache 8MB,表只有一两行。两项反例及正向对照均有断言,夹具/实例已清理。
  • 复用 exact-head CI 的 UT、SCA、PROXY/PESSIMISTIC 多 CN BVT、Coverage 和 CI Required 成功证据。它们未覆盖上述反例,不能据此认定授权修复完整。
  • 优先扩展现有 authenticated restore/PITR fixture,覆盖同 ID 不同身份的旧连接、跨 CN、prepared EXECUTE、失败恢复,以及已有数据库/实际重建数据库的区别。保留真实表 owner 缺失时的拒绝和回滚,不增加重复集群/状态机。

性能与评论闭环

每语句清权限缓存取消了跨语句 warm hit,让普通查询/prepared EXECUTE 重新进入 background executor、授权事务和角色/目录查询。PR 已说明未 benchmark;本次覆盖率构建和共享机器反例也不是匹配 base/head 的性能基准,性能尚不能给 PASS。优化应保留跨连接/CN 的立即 REVOKE/RESTORE 生效,由已有权限目录与会话生命周期统一提供有效性依据。

作者提出的 required-context 名称迁移 仍未闭环:本次只读 branch protection 仍要求五个旧状态名,实际成功 jobs 使用 execution 名称,PR 为 BLOCKED/REVIEW_REQUIRED。CI Required 成功不等于所有保护要求成功。

Iceberg E2E 日志显示 MinIO 镜像拉取 unauthorized、createbuckets 中断,尚未进入 SQL 测试;没有证据将其归因于本 PR,也不能当作 Iceberg 功能通过。

Comment thread pkg/frontend/mysql_cmd_executor.go Outdated

// Cache grants only within one statement. A session-local cache cannot
// observe REVOKE or RESTORE committed by another connection or another CN.
if cache := ses.GetPrivilegeCache(); cache != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] 清 grants cache 没有使恢复前的已认证身份失效,旧连接可以继承另一个角色的权限。

已在该 head 的实际构建产物用公开 SQL 复现,权限检查开启:

  1. 新建 source/target 两个账号,各有 app.secret(id int) 和一行 42。source 的第一普通角色/user 为 secret_reader/source_user,role_id=3、user_id=3,授予 CONNECT 和 SELECT。target 的第一普通角色/user 为 guest/target_user,也是 role_id=3、user_id=3,只授予 CONNECT 和 INSERT。
  2. 保留 target#target_user#guest 的物理连接。SELECT * FROM app.secret 被拒绝。
  3. SYS 执行源账号快照和恢复:
    CREATE SNAPSHOT s FOR ACCOUNT source;
    RESTORE ACCOUNT source {snapshot='s'} TO ACCOUNT target;
  4. 同一个旧 target 连接的 SELECT 返回 42,current_user(),current_role() 仍为 target_user@localhost,guest。此时用旧用户名/角色重新登录却报 there is no user target_user。恢复后的 target#source_user#secret_reader 新连接能读 42,作为恢复成功的正向对照。

这里的 cache.invalidate() 只让 grants 重新查目录;登录时 TenantInfo 保存的 UserID/DefaultRoleID 没有被刷新或失效。authenticate.go 的权限搜索 直接使用旧 DefaultRoleID。恢复后数字 3 属于另一个角色,且 grants 已正确重绑到新表,于是旧身份获得新角色的 SELECT。数字 ID 相同不能证明身份相同。

请将身份目录替换与已有账号/会话生命周期衔接,复用账号版本/AccountRoutineManager/跨 CN 会话失效职责:成功恢复后旧身份在下一次授权前失效或重新验证;失败回滚不提前使有效连接失效。仅提交后异步 kill 仍有生效前窗口。普通查询、text/binary EXECUTE 和跨 CN 应共享这个边界,并在现有认证 fixture 中保留同 ID 不同名称的旧连接进行断言。

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.

The fix in ac0f58d, confirmed by your12:18UTC APPROVED review, is retained in current59f36461. Authorization now proves the current account/user ID plus authenticated username and role relations against the statement catalog snapshot; numeric-ID coincidence alone cannot authorize an old connection. Existing cache validity, replay and session/account lifecycle owners supply that boundary for ordinary, text/binary prepared and protected native commands. The shared authenticated regressions cover cross-CN retained identities, positive replacement-login controls and failed-restore retained access. This heartbeat's latest delta is limited to license headers and behavior-neutral analyzer cleanup; the exact59f source tree has fresh frontend-race PASS23.825s and full frontend configuredlint0issues. Latest remote CI remains unfinished; prior review/proofs are not called current-head CI passes. Please confirm the thread closure together with the final follow-up; no self-resolution.

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.

CI follow-up in 377869d preserves this identity boundary. The actual 59f BVT failures exposed an internal-executor mismatch: DefinerOpts supplied the creator and numeric IDs, but applyOverride changed only the protocol username, leaving TenantInfo.User as internal/empty. The shared override now parses the existing canonical identity once, propagates malformed-name errors before SQL and closes allocated sessions on failure; ID/name authorization is not relaxed. Tests cover real taskservice.DefinerOpts, qualified names, explicit IDs, root ID zero and malformed base/command overrides. The snapshot fixture now requires the retained acc02:admin connection to be denied and checks 50/11 rows through a fresh restored acc02:test_user login. The protected-database fixture reuses its real tenant connection, not a cached physical SYS connection. Fresh local full frontend/taskservice race and frontend configured lint pass; strict task BVT is 170/170 and snapshot BVT 215/215. Protected-database strict metadata is 160/164, with the same four header differences on pristine 59f; its separate nometa semantic comparison is 164/164, not a strict pass. Latest remote CI is unfinished. Please review this final identity/fixture follow-up; the original retained-session rejection remains enforced.

}
}
owners := make(map[restoreObjectName]restoreOwner, len(dbs)+len(tables))
for _, row := range append(dbs, tables...) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] TABLE RESTORE 将未重建数据库的历史 creator/owner 也作为必需身份,错误拒绝合法表恢复。

已在该 head 复现:普通用户 db_builder 创建 app 数据库;账号管理员创建 app.t(id int) 并插入 1,做账号快照。目录里数据库 creator=3(db_builder),表 creator=2(仍存在的 admin)。

INSERT INTO app.t VALUES (2);
RESTORE TABLE app.t {snapshot='s'}; -- 成功,结果恢复为 1,正向对照
INSERT INTO app.t VALUES (2);
DROP USER db_builder;
RESTORE TABLE app.t {snapshot='s'};
-- 20101: cannot restore app.: creator no longer exists

失败后表仍为 1、2:没有破坏数据,但合法恢复无法完成。表自身的 creator/owner 仍有效,目标 app 数据库也未被重建。

table 模式仍读取历史数据库 ownership,这个循环将 dbs/tables 一起做 principal 映射并要求数据库 creator 存在。snapshot.go 对已有数据库只是 CREATE DATABASE IF NOT EXISTS,无需安装历史数据库身份。

请复用实际恢复目标/对象是否重建的决定,按实际将重建的对象验证 ownership;已有数据库的表恢复应保留当前数据库身份。不要用 SYS fallback 放宽实际表 owner 缺失的拒绝。同一 helper 的 PITR 表级路径也要同步核对。最小回归应覆盖数据库创建者缺失但表身份有效时成功、实际表 creator/owner 缺失时失败、数据库真正重建时仍检查历史数据库身份,以及 snapshot/PITR 的对应边界。

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.

The actual recreation-set fix in ac0f58d, confirmed by your latest APPROVED review, is preserved in current59f36461. Snapshot/PITR table restore retains the existing parent DB identity and does not require its historical creator/owner; a truly recreated database/table still requires the applicable historical principals before destructive DDL. The existing shared authenticated fixture covers retained DB success and missing actual-table-owner rejection/rollback, without SYS fallback. Current follow-up removes only an unreachable SQL helper/constant and aligns the prepared receiver name; full frontend-race PASS23.825s and configured frontendlint0issues on the identical current source tree. No ownership behavior change, new restore branch or weakened rejection. Please confirm the discussion can close; remote checks/protection are still separate.

Rebind restored grants to current logical objects while preserving surviving
principals and parent owners. Validate cached privileges against statement
snapshots and storage-owned catalog revisions, with role provenance and
failure-safe cache sealing.

Reuse immutable prepared requirements, unify executable EXPLAIN checks, and
remove redundant SQL record parsing from trusted binary execute envelopes.
Cover retained identities, cross-CN revocation, publication ordering, rollback,
cancellation, prepared reuse and role boundaries.

@XuPeng-SH XuPeng-SH left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep review: APPROVE

审阅 head ac0f58d322729557896418cce3c8abdb0d4d32b6,base/merge-base 28f1f02a89339def1b2572dbe07e98144a203167。本轮直接审查,未开 subagent。未发现新的阻塞性代码问题。

之前的代码评论

  • P1:旧连接身份串权,已解决。 授权验证当前账号、用户 ID/已认证用户名及角色关系;覆盖跨 CN、普通查询、text/binary prepared、旧管理员原生入口,以及失败恢复后保留合法访问。对应 discussion_r4162050879。
  • P2:表恢复误检查父数据库历史创建者,已解决。 表恢复复用现存数据库时保留当前父库身份;实际重建对象仍检查 creator/owner,缺失身份继续拒绝。Snapshot/PITR 共用该边界。对应 discussion_r4162050881。

设计、失败路径与工程质量

  • grants 重绑仍由原恢复事务负责;授权复用现有 privilege cache/evaluator,目录版本由 logtail/replay 负责,prepared requirements 绑定现有 AST/plan 生命周期。没有新增并行权限存储、后台同步任务或重复恢复事务。
  • 删除 Session.priv、可变 matched-role 字段、重复权限范围查询及失效注释路径。恢复失败回滚,授权失败清除未证实的部分缓存;取消/关闭能终止快照等待,缓存受 1024 scopes/256 KiB 名称限制。
  • 测试覆盖公开 SQL、跨 CN、实际 KILL 回滚、同一快照的独立 SQL 对照、prepared 重用和拒绝写入后的数据断言。实现净增 1,429 行,测试/预期净增 3,837 行,共享夹具净增 5 行;规模较大,但对应不同正确性与生命周期边界,未发现阻塞性的重复框架。
  • 授权契约是语句快照下的目录视图。单次缓存校验依赖 TN 现有时间戳分配顺序,该依赖已有说明及真实存储回归保护。

验证与性能范围

本轮核对并复用同一代码树 db83b2d966c790a6a0c0280165ad7dd43febd001 的已有验证,没有重复运行测试:frontend -race、增量 SCA、vet、完整构建,以及分布式 SQL/有界压力验证通过。

72 个读性能进程、16 个配对块的四项零回退门槛通过;COUNT/SELECT 中位延迟分别改善约 3.79/2.69 µs,p95 也改善。六轮较长并发 UPDATE 未支持明显退化结论;基线波动较大,不能据此保证所有写负载性能不变。

尚未完成的仓库配置项

issuecomment-5907964607 的 required-context 名称迁移仍未完成:最新读取的 4.2-dev 保护配置仍要求五个旧名称。这是独立的合并配置问题;本次代码 approve 不表示保护条件已满足,也不表示最新 CI 已通过。未等待或 rerun CI,未修改仓库保护配置。

@XuPeng-SH XuPeng-SH left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep review: REQUEST_CHANGES

审阅 head 377869d30673d2d48aaec0ba421acdb9c2222f47,base/merge-base 28f1f02a89339def1b2572dbe07e98144a203167。本轮未开 subagent。

已确认的阻塞问题

P2:SQL task 的 accountadmin 身份只绑定了用户名,没有完整绑定租户/角色名称,导致合法管理员语句被拒绝。 详见 inline comment。

真实公开 SQL 验证使用现有三 CN authenticated fixture,一个账号、一张两行表、一个 reader role,无授权/catalog stub:

-- qa_task_admin#admin#accountadmin 连接;app.t 和 reader 已存在
revoke select on table app.t from reader; -- 成功:直接执行的正向对照
grant select on table app.t to reader;
create task qa_task_insert as begin insert into app.t values(2); end;
execute task qa_task_insert;             -- 成功:表 count=2
create task qa_task_revoke as begin revoke select on table app.t from reader; end;
execute task qa_task_revoke;             -- 20101: do not have privilege
-- reader 的 SELECT grant 仍有 1 条,未完成 REVOKE

黑盒测试 16.921s,确定性失败。另一个白盒挑战使用真实 DefinerOpts、newCmdSession 和 authenticateUserCanExecuteStatement:SYS admin 与完整命名的同一数字身份通过,实际非 SYS task principal 被拒绝。既有 IE focused suite 通过(0.044s),说明当前字段/格式断言没有覆盖该行为。测试串行,GOMAXPROCS=2、GOMEMLIMIT=2GiB;创建账号已清理,测试进程已退出。

修复应归属现有 internal session 身份绑定入口,保证数字 IDs 与所有现有 policy 使用的身份信息一致,保留真实 membership 校验。不能简单复制 Creator 中的角色:SET ROLE 后 Creator 登录名与 CreatorRoleID 可能不同。建议扩展已有 fixture,覆盖合法 tenant-admin REVOKE 的实际 grant 变化、普通角色拒绝、SET ROLE 和 bare username/显式 ID 边界;不要另加权限缓存或绕过管理员检查。

整体设计、旧评论和工程质量

  • 完整变更图为 79 文件:实现 31 文件 +2063/-620,测试/夹具 39 文件 +3967/-550,结果 9 文件 +522/-19,无 tracked 文档增量。复用 此前完整评审 的未变化部分,并重新审阅相对 ac0f58d 的全部 9 文件 +130/-38 及共享身份/授权闭包;没有把另一份未交付 staged 实现当作当前证据。
  • 原恢复事务仍负责 grants 重绑与回滚,现有 privilege cache/evaluator、storage replay/subscription 版本、PrepareStmt 生命周期各自持有职责。整体方向合理,没有发现另建事务、存储或后台状态机的必要。
  • cache 有 1024 scopes/256KiB 名称上限;失败/取消清理未证实缓存;internal override 解析失败关闭新 session。dead helper/constant 已清理。F1 暴露的是身份生产者与现有 policy 消费者不一致,应统一关闭这个边界。
  • discussion_r4162050879 旧连接身份串权、discussion_r4162050881 表恢复误查未重建父库 creator:代码问题已解决,当前 delta 未破坏原修复;两条 GitHub threads 尚未 resolved。
  • required-context 名称迁移 仍未闭环:最新读取的 4.2-dev 保护要求旧名称,实际 producer 使用 execution 名称。这是管理员配置项,独立于 F1。

验证与性能范围

当前 head CI run 37023454288 的 SCA 和 UT Coverage 已成功;评审抓取时实际 UT、PROXY/PESSIMISTIC BVT 尚在运行,未等待 CI,也未将 pending 计为通过。Iceberg 镜像 unauthorized 在进入 SQL 前失败,不能计为功能通过或归因为本 PR。

普通/text/binary prepared 未变化路径复用先前 72 个读性能进程、16 个配对块的证据;本轮没有重新跑 benchmark。读结论只覆盖已测 warm COUNT/SELECT,六轮 UPDATE 诊断不能保证所有写负载无回退。既有 task/procedure BVT 通过也没有覆盖本轮复现的管理员 task 形状。

本轮只有一条已确认代码 blocker;不把尚未证明公开可达的理论越权路径作为发现。

return err
}
if acc := sess.GetTenantInfo(); acc != nil {
acc.SetUser(principal.GetUser())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] 完整绑定 SQL-task definer 的身份,避免合法 accountadmin REVOKE 被拒绝

这里仅 SetUser,非 SYS 的 newCmdSession 原本只从 context 填数字 IDs,Tenant/DefaultRole 名称仍为空。SQL-task 的实际 producer 使用 AttachAccount(AccountID, CreatorUserID, CreatorRoleID),因此即使 ID 对应 accountadmin,IsAccountAdminRole/IsAdminRole 的名称判断仍为 false。我已用真实三 CN 公开 SQL 复现:同一管理员直接 REVOKE 成功、INSERT task 成功,但 REVOKE task 报 20101 do not have privilege,SELECT grant 仍保留。完整命名的同一数字身份可通过实际授权函数的正向对照。

请在现有身份绑定入口统一保证 principal 与 name-based policy 一致,并保留当前 membership 校验;同时覆盖 SET ROLE 后 Creator 登录字符串与实际 CreatorRoleID 不同的场景,不能直接把登录字符串中的角色当作实际选中角色。

@gouhongshen gouhongshen Oct 2, 2026 •

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.

Fixed in 465d94d. I reproduced the actual grant-changing accountadmin task failure on377869d3 (direct REVOKE/INSERT task positive controls), plus SET ROLE and bare-name/explicit-ID failures. The existing internal-session boundary now resolves tenant and selected-role names by final numeric IDs after both overrides, reusing the role lookup; username/ID and current-membership checks remain enforced. SET ROLE also exposed fresh native-only task metadata taking account0 from a plan-populated field. Task creation and every task/run filter now use the authenticated TenantInfo IDs consistently; Creator retains its original reader login while CreatorRoleID selects accountadmin. Final public authenticated regression asserts actual grant removal, selected-role provenance, bare-ID positive, ordinary/selected-reader/mismatched-user-ID denial, and revoked-membership denial; the complete TestIssue29399 family passes22.993s. Full frontend/taskservice race passes24.760/44.518s, final IE/task-handler10x race2.763s, frontend lint0issues, binary build and independent local four-file review APPROVE. No preparatory SELECT, fallback, role/membership bypass, workflow/tester/golden change or cache framework. Current-head CI is newly triggered, not passed; old377 ALL CI success is historical. Please re-review this delivered fix; I have not self-resolved or approved the thread.

SCA follow-up in 8f27fa7: the separate Makefile err-check rejects the fmt.Errorf role-error wrapper (job110928686599). Returning the existing lookup error directly also preserves *moerr.Error, which IsMoErrCode does not unwrap. Both missing-account/role Exec and Query now assert the original error code; the old wrapper fails this strengthened control. Fresh make err-check PASS, IE/task-handler 10x race PASS2.949s, complete authenticated issue family PASS25.966s, configured frontend lint0issues and independent two-file local review APPROVE. Selected-role binding, membership rejection and cleanup are unchanged; no workflow/tester/policy suppression. Latest-head CI remains unfinished; please include this error-contract follow-up in the re-review.

@XuPeng-SH XuPeng-SH left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep review: APPROVE

审阅 head 465d94dcb1db8aced132a0ea9b60bf361b2cb6ae,base 28f1f02a89339def1b2572dbe07e98144a203167。本轮直接评审和独立 QA 挑战,未开 subagent。未确认新的 P1/P2 代码问题。

评论闭环

  • 4162050879:恢复后旧连接身份串权,代码已解决。 当前账号/用户 ID、已认证用户名及角色校验仍覆盖普通查询、text/binary prepared 和受保护 native 入口;新 task 改动保留该边界。
  • 4162050881:表恢复误检查已有父数据库的历史 creator,代码已解决。 Snapshot/PITR 共用实际重建对象的 ownership 边界;已有父库保留当前身份,实际重建表的缺失 creator/owner 仍拒绝。
  • 4167312881:SQL-task definer 身份不完整,代码已解决。 在两层 override 后按最终数字 IDs 绑定 tenant/selected-role 名称,复用现有用户名解析和角色查询。SET ROLE 后不从原始登录字符串恢复旧角色。Task 创建、管理和 run filters 统一使用 authenticated TenantInfo。真实 SQL 验证 accountadmin REVOKE、SET ROLE、bare-name/explicit-ID 正向控制及拒绝路径通过。
  • 5907964607:required CI context 名称迁移,仍未完成。 最新读取的 4.2-dev protection 仍要求五个历史名称,属于独立管理员配置事项。三个代码 review threads 目前也仍未 resolve;代码 closure 与 GitHub discussion 状态应区分。

设计、失败路径和工程质量

Grants 重绑仍由现有恢复事务统一负责,授权复用现有 evaluator/cache,目录版本归 replay/subscription owner,prepared requirements 归 PrepareStmt replacement/Close。未发现阻塞性的重复状态机、并行权限存储或额外恢复事务。旧 Session.priv、matched-role 状态、重复范围查询及无用 SQL helper 已清理。

审查维度 结论
资源归属 Binder 的 background executor 由 defer 关闭;绑定失败关闭已分配 session。恢复回滚和 prepared 清理保留原 owner。
等待终止 Catalog SQL 使用现有 caller/session timeout;无新增后台重试或等待循环。ReadSnapshot 在等待前释放 client lock,并保留 caller cancel/client close 边界。
有界增长 Privilege cache 保持 1024 scopes/256 KiB 名称限制;certificate 只保留标量版本,prepared 状态跟随 plan 生命周期。

当前 head 本地验证

Linux amd64 / Go 1.26.4,使用仓库 CGo wrapper:

  • 定向 internal-executor/SQL-task-handler -race:PASS,1.989 s。
  • 真实 authenticated TestIssue29399SQLTaskDefinerAuthorization -race:PASS,19.381 s。断言实际 grant 删除、SET ROLE provenance、普通/选中 reader/mismatched-user-ID 拒绝及撤销普通角色 membership 后拒绝。
  • 独立外置 Go overlay probe 12 场景 -race:PASS,1.224 s:Exec/Query × account/role lookup × ordinary/canceled/deadline error。错误链保留,后续读取及用户 SQL 停止,background Close 恰好一次,session proc/tenant 清理。Probe 不随 PR 交付;该证据验证注入错误返回的传播和清理,不声称证明任意阻塞 transport 的实时取消或 durable rollback。
  • 当前增量 configured SCA(frontend/issues,自 ac0f58d):0 issues;完整 PR diff whitespace 检查通过。

未变化的 restore/replay/snapshot/prepared/SQL-record 路径复用先前精确源码验证;未重复运行或宣称当前 head 全量测试/构建通过。

性能和增量

Public warm binary COUNT/SELECT 热路径未被最新提交改变;原有 72-process/16-block 测量四项零回退上界均通过,中位延迟变化分别约 −3.792/−2.694 µs。六轮较长 UPDATE 不构成统计上的写负载不回退保证,也不证明所有 workload 无下降。

具名 internal session 每条语句增加一次 selected-role query,非 SYS 再增加一次 account-name query。此 correctness 成本没有 task-path 对比测量,不能保证 SQL task 性能不下降。 未具名 bootstrap 和 public warm 路径不承担这两次名称查询。

完整当前 diff 分别审查:

类别 文件数 新增 删除 净增
生产代码 32 2115 627 +1488
测试/结果 46 4634 569 +4065
共享夹具 2 5 0 +5
仓库文档 0 0 0 0

增量较大;新增 task 回归复用现有 authenticated cluster,验证真实数据/目录效果并包含拒绝对照;mapping/error mocks 与 public SQL、真实 snapshot/timestamp、KILL rollback 各证明不同边界。未发现阻塞性的重复测试框架,未因测试占比高而免审。

此 APPROVE 是代码评审结论,不表示配置评论已完成、当前 CI 已通过或 merge gate 已满足。本轮未等待/rerun CI,未更新源码 commit 或 PR body。

This branch was successfully deployed

1 active deployment
ci — 8f27fa7f Deployed Oct 2, 2026 by XuPeng-SH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bug Something isn't working kind/enhancement kind/refactor Code refactor size/XXL Denotes a PR that changes 2000+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants