Skip to content

Add support for ALTER TABLE path changes on external Iceberg tables#314

Open
cbrianpace wants to merge 2 commits into
Snowflake-Labs:mainfrom
cbrianpace:main
Open

Add support for ALTER TABLE path changes on external Iceberg tables#314
cbrianpace wants to merge 2 commits into
Snowflake-Labs:mainfrom
cbrianpace:main

Conversation

@cbrianpace
Copy link
Copy Markdown

Description

External Iceberg foreign tables (created via SERVER pg_lake OPTIONS (path '...metadata.json')) were previously immutable — to point to a newer snapshot you had to DROP and re-CREATE the table, losing any dependent views, permissions, or references.

This change adds a narrow ALTER TABLE ... OPTIONS (SET path '...') path for these read-only external tables, allowing callers to redirect to a different metadata snapshot in-place.

Changes
pg_lake_table/src/ddl/alter_table.c: Added a branch in ProcessAlterTable() that detects NONE_CATALOG external Iceberg tables and permits only the path option to be changed, rejecting all other option modifications via ErrorIfUnsupportedTableOptionChange.


Checklist

  • I have tested my changes and added tests if necessary
  • I updated documentation if needed
  • I confirm that all my commits are signed off (DCO)

DCO Reminder (important)

This project uses the Developer Certificate of Origin (DCO).
DCO is a simple way for you to confirm that you wrote your code and that you have the right to contribute it.

If the DCO check fails, please sign off your commits.

How to sign off

For your last commit:
git commit --amend -s
git push --force

For multiple commits:
git rebase --signoff main
git push --force

More info: https://developercertificate.org/

Signed-off-by: brian.pace <brian.pace@outlook.com>
Copy link
Copy Markdown
Collaborator

@sfc-gh-okalaci sfc-gh-okalaci left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, Brian! It's a useful feature to have. I'm happy to take over from here if you prefer, or we can iterate together, you choose!

I dug through the history and this was actually already supported — we unintentionally broke it in commit b447e29 ("Refactor read-only iceberg table errors") when we broadened the ProcessAlterTable() guard to use IsIcebergTable(), which pulled these tables into scope where they hit ErrorIfReadOnlyIcebergTable(). So your PR is really a fix, not a new feature.

That said, there are a few things we should address before merging:

Schema validation

The biggest concern is schema drift. If the new metadata.json has a different schema than what PostgreSQL has in pg_attribute (from the original CREATE FOREIGN TABLE), we'd get silent data corruption or crashes.

We already have ErrorIfSchemasDoNotMatch() in snapshot.c — it compares the Iceberg metadata schema against the Postgres column definitions. It's currently called for REST_CATALOG_READ_ONLY and OBJECT_STORE_READ_ONLY, but not for NONE_CATALOG (the path-based case this PR targets):

if (catalogType == REST_CATALOG_READ_ONLY ||
    catalogType == OBJECT_STORE_READ_ONLY)
    ErrorIfSchemasDoNotMatch(relationId, metadata);

We should extend this condition to also cover NONE_CATALOG external Iceberg tables. For consistency with the other catalog types, let's add the check in the same place (the read path in snapshot.c) rather than in alter_table.c. This way all external Iceberg types are validated uniformly.

Permission check

The FDW validator (pg_lake_table_validator) calls CheckURLReadAccess() on both CREATE and ALTER, so I believe this is already covered. But it would be good to verify with a test: create a user without the lake_read role, and confirm that ALTER FOREIGN TABLE ... OPTIONS (SET path '...') is denied.

Tests

We'll need tests covering:

  1. Happy path: Create an internal Iceberg table (CREATE TABLE ... USING iceberg), create an external foreign table pointing to its metadata, insert data into the internal table, then ALTER FOREIGN TABLE ... OPTIONS (SET path '<new_metadata.json>') to point to the updated snapshot — and verify the external table sees the new data.

  2. Schema mismatch: Same setup, but after the ALTER, the new metadata has a different schema (e.g., an added/dropped column on the internal table). Verify we get the ErrorIfSchemasDoNotMatch() error.

  3. Permission denied: A user without lake_read attempting the ALTER should be denied.

  4. Dependent objects: Create views and materialized views on top of the external table, change the underlying path, and verify everything still works (or fails gracefully if schema changed). This is the real-world use case you're solving for, so it's important we cover it to prevent future regressions.

For writing the tests, Claude is great at generating pytest scenarios — you should mostly need to describe what you want, and it can come up with a good test structure. Look at test_data_file_pruning.py for examples of how we create external Iceberg tables in tests (the create_external_iceberg_table() helper function).

Let me know how you'd like to proceed!

sfc-gh-mslot
sfc-gh-mslot previously approved these changes May 14, 2026
@sfc-gh-mslot sfc-gh-mslot dismissed their stale review May 14, 2026 11:21

meant to comment

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.

3 participants