Skip to content

fix(frontend): keep DDL ownership on matched role#24970

Merged
mergify[bot] merged 11 commits into
matrixorigin:4.0-devfrom
ouyuanning:fix-ownership-bug-4.0-dev
Jun 16, 2026
Merged

fix(frontend): keep DDL ownership on matched role#24970
mergify[bot] merged 11 commits into
matrixorigin:4.0-devfrom
ouyuanning:fix-ownership-bug-4.0-dev

Conversation

@ouyuanning

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:

issue #24956

What this PR does / why we need it:

This PR fixes DDL ownership handling when privileges are satisfied by secondary roles or inherited roles.

  • Track the matched privilege role during authorization and use it as the DDL owner for CREATE DATABASE / CREATE TABLE.
  • Map inherited privilege matches back to the active root role so ownership follows the active granted role, not the inherited leaf role.
  • Re-authenticate prepared CREATE DATABASE / CREATE TABLE during EXECUTE so prepared DDL also sets the owner role correctly.
  • Revoke implicit ownership privileges from the actual object owner role when dropping databases/tables, including multi-table drops.
  • Add unit tests and BVT coverage for secondary-role DDL ownership and prepared DDL ownership cleanup.

Validation:

  • go test -mod=mod ./pkg/defines -count=1
  • go test -mod=mod ./pkg/frontend -run 'TestCreateDatabaseOwnerRoleFollowsActiveInheritedRoleRoot|TestCreateTableOwnerRoleFollowsPrivilegeRole|Test_determineUserHasPrivilegeSet' -count=1 was blocked locally by missing xxhash.h before running tests.

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

@ouyuanning ouyuanning changed the title fix(frontend): keep DDL ownership on matched active role fix(frontend): keep DDL ownership on matche role Jun 14, 2026

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

Requesting changes for a drop-path regression.

After this PR, DROP TABLE / DROP DATABASE first resolve the current object owner role name and now propagate that lookup error. But DROP ROLE still deletes roles unconditionally, so an object can legitimately keep an owner role ID that no longer exists.

In that state, getRoleNameByIDWithBackgroundExec() returns there is no role id ..., doRevokePrivilegeImplicitly() returns the error, and the DROP aborts before execution. That means deleting the owner role can make the owned table/database undeletable by normal DDL.

Please either treat a missing owner role as a no-op during implicit revoke, or block DROP ROLE while the role still owns objects. I think this needs a regression test too.

@XuPeng-SH

Copy link
Copy Markdown
Contributor

A more actionable suggestion:

  1. Short-term / safe fix in this PR: make implicit ownership cleanup best-effort when the owner role row is already gone. In other words, if the object exists but owner points to a deleted role, skip the implicit revoke and continue the DROP TABLE / DROP DATABASE.

  2. Long-term consistency fix: prevent DROP ROLE from deleting a role that still owns databases/tables, or clean up/reassign ownership first.

The reason I think the short-term behavior matters is that implicit revoke is cleanup logic, not a correctness prerequisite for DROP itself. Right now a stale owner-role reference becomes a hard failure before execution, which can brick normal DDL on orphan-owned objects.

A regression test for this would be very helpful too:

  • create role r
  • create table/db owned by r
  • drop role r
  • drop table/db should still succeed (or, if you choose the other design, drop role r should be rejected while ownership still exists)

@ouyuanning

Copy link
Copy Markdown
Contributor Author

@XuPeng-SH Addressed this with the short-term safe behavior you suggested.

The implicit ownership cleanup is now best-effort when the object owner role row is already gone: object owner lookup returns an empty role name for a missing mo_role row, so doRevokePrivilegeImplicitly() skips the implicit revoke and continues the DROP TABLE / DROP DATABASE. Other lookup/parse errors are still propagated, and the stricter getRoleNameByIDWithBackgroundExec() behavior remains unchanged for callers that need it.

I also added regression coverage:

  • unit tests for database/table owners pointing to a deleted role, plus the existing-owner path
  • a BVT case in tenant/privilege/owner.sql: create an owner role, create an owned database/table, drop the owner role, then drop the orphan-owned table/database with a normal dropper role

Verified locally:
CGO_CFLAGS=-I/Users/ouyuanning/workspace/go/src/matrixone/thirdparties/install/include CGO_CXXFLAGS=-I/Users/ouyuanning/workspace/go/src/matrixone/thirdparties/install/include CGO_LDFLAGS=-L/Users/ouyuanning/workspace/go/src/matrixone/thirdparties/install/lib go test -mod=mod ./pkg/frontend -run "TestGetObjectOwnerRoleName|Test_shouldSkipImplicitOwnershipRevoke" -count=1 -v

For the long-term consistency point, I agree DROP ROLE should eventually reject roles that still own databases/tables or transfer/cleanup ownership first. I kept this PR scoped to preventing the DROP path from being bricked by stale owner-role references.

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

Looks good to me now. The deleted-owner-role case is handled safely by treating the missing owner-role lookup as non-fatal for implicit revoke, and the added BVT covers the orphan-owner drop path directly.

@mergify

mergify Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-06-16 11:31 UTC · Rule: release-4.0
  • Checks passed · in-place
  • Merged2026-06-16 12:31 UTC · at dfedc5b0dcd7664b6b9a6a2acb78bd1ac18ce2c5 · squash

This pull request spent 1 hour 10 seconds in the queue, including 59 minutes 51 seconds running CI.

Required conditions to merge
  • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
  • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
  • github-review-decision = APPROVED [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-neutral = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-skipped = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / SCA Test on Ubuntu/x86
    • check-neutral = Matrixone CI / SCA Test on Ubuntu/x86
    • check-skipped = Matrixone CI / SCA Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-neutral = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-skipped = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Utils CI / Coverage
    • check-neutral = Matrixone Utils CI / Coverage
    • check-skipped = Matrixone Utils CI / Coverage
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / UT Test on Ubuntu/x86
    • check-neutral = Matrixone CI / UT Test on Ubuntu/x86
    • check-skipped = Matrixone CI / UT Test on Ubuntu/x86

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/test-ci size/XL Denotes a PR that changes [1000, 1999] lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants