Skip to content

fix(persistence): reject a rename that would move an entity across catalogs - #5305

Open
flyingImer wants to merge 2 commits into
apache:mainfrom
flyingImer:fix/rename-entity-cross-catalog-guard
Open

fix(persistence): reject a rename that would move an entity across catalogs#5305
flyingImer wants to merge 2 commits into
apache:mainfrom
flyingImer:fix/rename-entity-cross-catalog-guard

Conversation

@flyingImer

@flyingImer flyingImer commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Problem

renameEntity accepts a destination path, but it does not support moving an entity across catalogs. Before this change, nothing enforced that restriction.

If newCatalogPath is rooted in a different catalog, the rename can persist an inconsistent entity: its parentId is changed to a parent in the destination catalog, while its catalogId still points to the source catalog.

That entity is then no longer addressable by its expected path. A lookup under the old path uses the old parent and misses it, while a lookup under the new path uses the destination catalog id and misses it there as well.

The current REST table-rename surface cannot express this case because its source and destination are Iceberg TableIdentifiers, which contain a namespace and name but no catalog. However, renameEntity itself does not state or enforce that restriction, so a direct or future caller can create invalid persisted state.

Root cause

The rename implementations treat newCatalogPath as a re-parenting operation within the existing catalog.

When checking for a name collision at the destination, they derive the target catalog id from the first element of newCatalogPath. But when the rename is persisted, only the entity's parent is re-pointed; its existing catalogId is retained.

That is valid for moving an entity between namespaces in the same catalog, but not across catalogs.

A cross-catalog move would require more than changing the entity's parent. Descendants, grant records, and policy mappings can also retain references to the catalog the entity belongs to. renameEntity does not migrate that state, so accepting a cross-catalog destination would only partially perform the move.

The fix

Reject a rename when both the current and destination paths are present and their root catalog ids differ.

The validation is added consistently to: AtomicOperationMetaStoreManager, TransactionalMetaStoreManagerImpl, NoSqlMetaStoreManager

Same-catalog renames and re-parenting continue to use the existing behavior.

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

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

Good catch! Thanks, @flyingImer !

…talogs

renameEntity derives the target catalog id from the head of newCatalogPath
for its name-collision pre-check, then persists the entity with only parentId
re-pointed. A cross-catalog rename would store a row whose parent_id lives in
the new catalog while its catalog_id still names the old one, and by-name
lookups match that row from neither side.

Moving an entity between catalogs is not a one-field change: its children, its
grant records and its policy mappings all record the catalog it started in, and
renameEntity writes none of them. Reject the call rather than half-perform it.

No API can express a cross-catalog rename today, so the new test drives the
manager directly, from the shared metastore-manager fixture.
An empty catalogPath is the documented shape for a top-level entity, so the
previous check let any non-empty newCatalogPath through whenever the source path
was null or empty. That is the same inconsistency this change is about: the
entity keeps catalog id 0 while its parent_id points inside a catalog.

Require a non-empty catalogPath whose head matches the destination, and cover it
with a principal-role case in the shared fixture. Reverting only the predicate
makes that assertion fail.
@flyingImer
flyingImer force-pushed the fix/rename-entity-cross-catalog-guard branch from 666b858 to 1835475 Compare August 18, 2026 04:15
@flyingImer
flyingImer requested a review from dimas-b August 18, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants