Skip to content

[Iceberg 1.11] feat: add RegisterTable overwrite support; preserve correct auth for overwrites - #3719

Merged
flyrain merged 11 commits into
apache:feature/iceberg-1.11from
sririshindra:feature/iceberg-1.11-issues-2896
Mar 9, 2026
Merged

[Iceberg 1.11] feat: add RegisterTable overwrite support; preserve correct auth for overwrites#3719
flyrain merged 11 commits into
apache:feature/iceberg-1.11from
sririshindra:feature/iceberg-1.11-issues-2896

Conversation

@sririshindra

@sririshindra sririshindra commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Add support for the new overwrite boolean on RegisterTableRequest (default: false) so clients can register a metadata location that replaces an existing table pointer. Implement register-table overwrite semantics:

  • If overwrite=false (default): preserve existing behavior
  • attempting to register a table that already exists returns a conflict/error. If overwrite=true:
  • If the table does not exist: create it (normal register).
  • If the table exists: update the table's metadata-location to the provided value (do not throw AlreadyExists).

Ensure safety/backward-compatibility:

  • overwrite defaults to false so existing clients are unaffected. Validate provided metadata location where possible before committing to avoid corrupting catalog state.

Details / rationale:
The change implements the REST Catalog spec extension that adds an overwrite flag to RegisterTableRequest. This enables clients to atomically point an existing table identifier at a new metadata file (useful for moving or restoring table metadata).

Tests added/updated:

  • Integration / behavior tests (integration-tests module):
    • Register new table with overwrite=false → success.
    • Register existing table with overwrite=false → conflict / exception as before.
    • Register existing table with overwrite=true → success and the table's metadata-location is updated atomically.
  • Authorization tests: updated/added unit tests to assert (TABLE_WRITE_PROPERTIES) is enforced for overwrite against existing tables.

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)

@github-project-automation github-project-automation Bot moved this to PRs In Progress in Basic Kanban Board Feb 10, 2026
@sririshindra sririshindra changed the title feat: add RegisterTable overwrite support; preserve correct auth for overwrites [DRAFT] feat: add RegisterTable overwrite support; preserve correct auth for overwrites Feb 10, 2026
@sririshindra sririshindra changed the title [DRAFT] feat: add RegisterTable overwrite support; preserve correct auth for overwrites feat: add RegisterTable overwrite support; preserve correct auth for overwrites Feb 10, 2026
@sririshindra

Copy link
Copy Markdown
Contributor Author

cc: @adutra @flyrain

@sririshindra
sririshindra marked this pull request as draft February 10, 2026 05:01
@adutra adutra changed the title feat: add RegisterTable overwrite support; preserve correct auth for overwrites [Iceberg 1.11] feat: add RegisterTable overwrite support; preserve correct auth for overwrites Feb 10, 2026
@sririshindra
sririshindra force-pushed the feature/iceberg-1.11-issues-2896 branch from a3bcdb3 to 3ccadc8 Compare February 10, 2026 20:09
@sririshindra
sririshindra marked this pull request as ready for review February 10, 2026 22:34
@snazy

snazy commented Feb 12, 2026

Copy link
Copy Markdown
Member

Honestly, I think this 'overwrite` flag can be dangerous, because it allows users to change an existing table in a breaking way. The ability to replace an existing table should be guarded by a separate privilege. We should also think about what happens to the existing, old (meta)data files and how changes to the base-location and write-(meta)data properties are handled.

I do not mind adding this to the feature branch, but it would be really good to have a separate discussion about the behavior before this change goes into the main branch.

The safest approach IMO would be to initially just error out when overwrite == true.

@flyrain

flyrain commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

It's an operation users need to be careful with. Adding an addition privilege sounds a good solution to me. In which case, the operation is blocked by default. It is only possible when a special privilege is granted.

@sririshindra

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback @snazy and @flyrain. I understand the concern about safety.

  1. On Auth:
  • I agree TABLE_WRITE_PROPERTIES might be too loose. Would you prefer I restrict this to CATALOG_MANAGE_CONTENT (catalog admins), or should we introduce a specific TABLE_REGISTER_OVERWRITE privilege?
  1. On Behavior:
  • Currently, this simply performs a pointer swap. Old metadata/data files are left untouched (not deleted) to prevent accidental data loss. Since the old metadata location is not actually deleted, users can always use that to restore back to the previous state in critical cases. We can log the previous metadata location if necessary for data recovery purposes.
  • Regarding base-location: My current implementation leaves the base-location unchanged (pointing to the original table path). However, if the new metadata file lives in a different location (e.g. during a restore from a backup path), should I update the entity's base-location to match the new metadata's parent directory?

As a next step, would you prefer I update the PR to throw an error when overwrite=true (disabling it for now) so we can merge the API changes first, or should I implement the stricter auth checks in this PR? My preference would be implement the auth checks in this PR itself, but I am open to suggestions.

@flyrain

flyrain commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

I agree TABLE_WRITE_PROPERTIES might be too loose. Would you prefer I restrict this to CATALOG_MANAGE_CONTENT (catalog admins), or should we introduce a specific TABLE_REGISTER_OVERWRITE privilege?

We will need a new operation in enum PolarisAuthorizableOperation, otherwise, external PDP won't work. For built-in RBAC, I think TABLE_FULL_METADATA should be good to use, as it covers both TABLE_CREATE and TABLE_DROP.

@sririshindra

Copy link
Copy Markdown
Contributor Author

@flyrain , @snazy Could you please take another look at this PR when you get a chance. I have updated the PR.

@snazy

snazy commented Feb 20, 2026

Copy link
Copy Markdown
Member

I do not mind having this in the feature branch, but I do think the overwrite behavior deserves more thoughts wrt authZ and the behavior when table-attributes like the base-location or the write-(meta)data-locations are different.

@flyrain

flyrain commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

The new operation(REGISTER_TABLE_OVERWRITE(TABLE_FULL_METADATA)) looks good to me.

The location concern is also valid, I think it should be similar to location checking in updateTable, more details could be find here

.

@sririshindra

Copy link
Copy Markdown
Contributor Author

I do not mind having this in the feature branch, but I do think the overwrite behavior deserves more thoughts wrt authZ and the behavior when table-attributes like the base-location or the write-(meta)data-locations are different.

Totally agree this deserves careful thought — a few notes on what this PR covers:

  • AuthZ: We settled on REGISTER_TABLE_OVERWRITE mapped to TABLE_FULL_METADATA. The rationale is that replacing a table's metadata pointer is semantically equivalent to simultaneously dropping the old pointer and registering a new one, so it warrants the strictest table-level privilege rather than just TABLE_WRITE_PROPERTIES (which covers normal metadata updates). TABLE_FULL_METADATA also encompasses both TABLE_CREATE and TABLE_DROP individually. Happy to revisit if there's a more appropriate granularity in mind.

  • Location changes: The implementation mirrors what doCommit does when the table location changes. It calls StorageUtil.getLocationsUsedByTable(metadata.location(), metadata.properties()) to collect all locations referenced by the new metadata — including write.data.path and write.metadata.path if set — and then runs them through CatalogUtils.validateLocationsForTableLike (checks against the resolved storage config) and validateNoLocationOverlap (checks for conflicts with existing tables). So a metadata file pointing to a different base location or write paths will be validated and rejected if those fall outside the allowed storage configuration.

The new operation(REGISTER_TABLE_OVERWRITE(TABLE_FULL_METADATA)) looks good to me.

The location concern is also valid, I think it should be similar to location checking in updateTable, more details could be find here

.

agreed it should mirror updateTable. This is addressed in the latest commit: overwriteRegisteredTable now performs the same three checks that doCommit does when the table location changes:

  1. CatalogUtils.validateLocationsForTableLike — validates all data locations against the resolved storage configuration
  2. validateNoLocationOverlap — ensures the new metadata location doesn't overlap with an existing table
  3. validateMetadataFileInTableDir — ensures the metadata file is within the table's directory structure

The resolved entity used for storage context is now obtained via getPassthroughResolvedPath on the table itself (rather than getResolvedPath on the namespace), which also aligns with how doCommit resolves storage credentials for an existing table.

@sririshindra

sririshindra commented Feb 25, 2026

Copy link
Copy Markdown
Contributor Author

@adutra There is a gradle check failing in this PR. I am not sure if that failure has anything do with the changes in this PR. I tried debugging it but I didn't quite get it. Could you help lead me in the right direction please.
Edit: I realized my branch is a bit behind and missing some commits that might be causing the gradle failure. I rebased and pushed the latest branch. Hopefully that will resolve this issue.

@snazy @flyrain Could you please take another look at this PR when you get a chance.

@sririshindra
sririshindra force-pushed the feature/iceberg-1.11-issues-2896 branch from d6be3e7 to d389cbd Compare February 26, 2026 00:32
return;
}

// Table doesn't exist, fall back to standard register-table authorization.

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.

I'm wondering how important this fallback is. If the table already exists, then eventually the caller needs REGISTER_TABLE_OVERWRITE role, but it turns out only when they attempted the operation. I'd expect anyone, who calls this endpoint to have REGISTER_TABLE_OVERWRITE, regardless of the 'state of the world'. What do you think?

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.

Agreed with @nandorKollar, I think a more consistent behavior here is still to use operation REGISTER_TABLE_OVERWRITE.

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.

We can also consolidate the code by

var target = getResolvedPath()
if (target == null) {
    target = resolutionManifest.getResolvedPath(identifier.namespace(), true);
}

 authorizer()
          .authorizeOrThrow(
              polarisPrincipal(),
              resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(),
              PolarisAuthorizableOperation.REGISTER_TABLE_OVERWRITE,
              target,
              null);

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.

I agree with this. I simplified the logic so overwrite=true always requires REGISTER_TABLE_OVERWRITE; the fallback path was removed. That makes auth deterministic and independent of runtime table-existence state.

* exists
* @return the registered table
*/
public Table registerTable(

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.

This method is not part of Catalog interface in Iceberg, in fact, there's no such method which corresponds to the register table overwrite semantic. Would it make sense to extend Iceberg Catalog interface with a corresponding method (registerTable(boolean overwrite))?

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.

I agree, I will raise a separate PR in the Iceberg repo for this. As the support for overwrite is added in the rest interface https://github.com/apache/iceberg/pull/15248/changes it is probably a good idea to add the same in the core Iceberg as well.

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.

Published apache/iceberg#15525 in Iceberg repo.

@sririshindra sririshindra left a comment

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.

Addressed all outstanding review items from this round: removed fallback auth logic for overwrite, clarified federated-catalog limitation in code/docs, and kept unsupported paths fail-fast with a clear error. cc: @nandorKollar @flyrain @snazy

* exists
* @return the registered table
*/
public Table registerTable(

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.

I agree, I will raise a separate PR in the Iceberg repo for this. As the support for overwrite is added in the rest interface https://github.com/apache/iceberg/pull/15248/changes it is probably a good idea to add the same in the core Iceberg as well.

"Overwriting registered table for identifier={}, metadataFileLocation={}",
identifier,
metadataFileLocation);
return overwriteRegisteredTable(identifier, metadataFileLocation, locationDir);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

IIUC, this path comes from overwrite=true and tableExists=true, in such case, would additional authz be required? i.e., privileges on the existing table entity, not just the namespace.

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.

The authZ usually happens in the class IcebergCatalogHandler.

…LE_FULL_METADATA

Enforce stricter permissions for RegisterTable with overwrite=true by requiring
TABLE_FULL_METADATA privilege (or CATALOG_MANAGE_CONTENT) instead of the previously
insufficient TABLE_WRITE_PROPERTIES. This aligns with the security requirement that
overwriting a table's metadata pointer is a destructive operation requiring both
create and drop capabilities.

Changes:
1. PolarisAuthorizableOperation.java
   - Added new enum value: REGISTER_TABLE_OVERWRITE(TABLE_FULL_METADATA)
   - Added static import for TABLE_FULL_METADATA privilege
   - Positioned after REGISTER_TABLE for semantic grouping

2. IcebergCatalogHandler.java
   - Updated authorizeUpdateTableOverwriteOrThrow() to use REGISTER_TABLE_OVERWRITE
   - Updated documentation explaining why TABLE_FULL_METADATA is required
   - Preserved fallback to REGISTER_TABLE when table doesn't exist

3. AbstractIcebergCatalogHandlerAuthzTest.java
   - Updated testRegisterTableOverwriteSufficientPrivileges():
     * Removed TABLE_WRITE_PROPERTIES from sufficient privileges
     * Now only requires TABLE_FULL_METADATA or CATALOG_MANAGE_CONTENT
     * Added detailed documentation of the privilege requirement
   - Updated testRegisterTableOverwriteInsufficientPermissions():
     * Added TABLE_WRITE_PROPERTIES to insufficient privileges list
     * Documented why TABLE_CREATE or TABLE_DROP alone are insufficient
     * Clarified that TABLE_FULL_METADATA is the minimum required

Security Properties:
✓ TABLE_FULL_METADATA: succeeds (contains both CREATE and DROP)
✓ CATALOG_MANAGE_CONTENT: succeeds (super-privilege for content management)
✗ TABLE_WRITE_PROPERTIES: fails (insufficient, no create/drop authority)
✗ TABLE_CREATE: fails individually (can't drop existing table pointer)
✗ TABLE_DROP: fails individually (can't create new entry)
✗ All read-only privileges: fail as expected

Rationale:
Table overwrite is semantically: (1) invalidate old table reference + (2) create
new one. Both operations require TABLE_FULL_METADATA. This prevents privilege
escalation where a user with only write-properties could replace another
principal's table.
@sririshindra
sririshindra force-pushed the feature/iceberg-1.11-issues-2896 branch from daf6274 to c3e546f Compare March 6, 2026 18:43

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

LGTM. Thanks @sririshindra ! Left a few minor comments.

"Overwriting registered table for identifier={}, metadataFileLocation={}",
identifier,
metadataFileLocation);
return overwriteRegisteredTable(identifier, metadataFileLocation, locationDir);

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.

The authZ usually happens in the class IcebergCatalogHandler.

Comment on lines +391 to +392
Set<String> tableLocations =
StorageUtil.getLocationsUsedByTable(metadata.location(), metadata.properties());

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.

Nit: fit into one line

var tableLocations = StorageUtil.getLocationsUsedByTable(metadata);

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.

@flyrain Addressed it in the latest commit.

@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Mar 8, 2026
@sririshindra

Copy link
Copy Markdown
Contributor Author

LGTM. Thanks @sririshindra ! Left a few minor comments.

Thanks @flyrain , I addressed your remaining comments as well in the latest commits.

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

LGTM

@flyrain
flyrain merged commit 445ffee into apache:feature/iceberg-1.11 Mar 9, 2026
@github-project-automation github-project-automation Bot moved this from Ready to merge to Done in Basic Kanban Board Mar 9, 2026
@flyrain

flyrain commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Thanks @sririshindra for the change. Thanks @snazy @flyingImer @nandorKollar for the review!

adutra added a commit that referenced this pull request Mar 9, 2026
adutra pushed a commit to adutra/polaris that referenced this pull request May 20, 2026
adutra pushed a commit to adutra/polaris that referenced this pull request May 26, 2026
adutra pushed a commit to adutra/polaris that referenced this pull request May 27, 2026
adutra pushed a commit to adutra/polaris that referenced this pull request May 27, 2026
adutra added a commit that referenced this pull request Jun 2, 2026
---------

Co-authored-by: Rishi <sririshindra@gmail.com>
MonkeyCanCode pushed a commit to MonkeyCanCode/polaris that referenced this pull request Jun 4, 2026
apache#4506)

---------

Co-authored-by: Rishi <sririshindra@gmail.com>
MonkeyCanCode pushed a commit to MonkeyCanCode/polaris that referenced this pull request Jun 4, 2026
apache#4506)

---------

Co-authored-by: Rishi <sririshindra@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants