Skip to content

iceberg: add lake_iceberg.relocate_table() to rebuild a table at a new location - #718

Draft
sfc-gh-dachristensen wants to merge 4 commits into
mainfrom
dachristensen/relocate-table
Draft

sfc-gh-dachristensen wants to merge 4 commits into
mainfrom
dachristensen/relocate-table

Conversation

@sfc-gh-dachristensen

@sfc-gh-dachristensen sfc-gh-dachristensen commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

There is no way to move a pg_lake Iceberg table to a different storage location. Copying the existing files and rewriting metadata paths would carry over position delete files and the full snapshot history, and it would leave the foreign table location option and pg_lake's file catalog pointing at the old paths.

Solution

lake_iceberg.relocate_table(table_name regclass, new_location text) rebuilds the table at the new location from its visible rows. It creates a new Iceberg table there and fills it with INSERT ... SELECT from the source. This is the same thing CTAS does internally, and reading the source applies its deletes. The new table starts with one snapshot and no delete files. The source is then dropped and the new table takes its name, so the normal create, insert and drop hooks keep the pg_lake catalogs correct.

Plain CTAS would lose NOT NULL, defaults and generated expressions, so the column definitions come from the source catalog. Table options, owner, table and column privileges, comments and serial sequence ownership also move to the new table. Tables with triggers, policies, inheritance, extended statistics, publications or identity columns are refused, since DROP TABLE would remove those silently. Objects that depend on the table, such as views, make the drop fail, which rolls the whole call back.

Only tables in the postgres catalog are supported. The function lives in pg_lake_table, which owns CTAS and the catalog hooks.

-- Rebuild the table under a new prefix; returns the new location.
SELECT lake_iceberg.relocate_table('public.events', 's3://new-bucket/events');

Test plan

  • A table with a position delete relocates with the deleted row still absent, no delete files in the new metadata, and the new location option
  • Columns, defaults, generated columns, comments, grants, role dependencies, options and the serial sequence are identical after relocation, and the sequence keeps counting
  • A table with a trigger is refused
  • make check-indent

🤖 Generated with Claude Code

sfc-gh-dachristensen and others added 3 commits October 8, 2026 15:59
…ocation

Copies all data files to the new prefix via parallel DuckDB copy_file,
rewrites manifests/manifest-lists with updated paths, writes new
metadata.json, and updates the catalog pointer. History is preserved
across all snapshots.

Generated with [Snowflake CoCo](https://docs.snowflake.com/en/user-guide/cortex-code/cortex-code)

Co-authored-by: Snowflake CoCo <noreply@snowflake.com>
MaxParallelFileUploads is defined in pg_lake_engine.so and not
available at pg_lake_iceberg.so load time, causing an undefined
symbol error in CI.
* regenerated with rewritten paths in the next pass.
*/
static HTAB *
RelocateDataFiles(IcebergTableMetadata * metadata,

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.

what does this do with the position delete files

StringInfoData command;

initStringInfo(&command);
appendStringInfo(&command, "SELECT * FROM pg_lake_copy_file(%s,%s);",

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.

given the way things are implemented, it might actually be a lot faster to do a CTAS type operation to maximize parallelization and throw away stuff we don't really need

pg_lake_copy_file does not parallelize, though there could be a possible optimization of using the S3 copy future

Copying the existing data and delete files to the new location carried
position deletes and the old snapshot history along, and left the
foreign table location and pg_lake's file catalog pointing at the old
paths.

Instead, create a new Iceberg table at the target location and fill it
with INSERT ... SELECT from the source, which is what CTAS does
internally and applies deletes on read. The column definitions come
from the source catalog, since CTAS would drop NOT NULL, defaults and
generated expressions. The owner, table and column privileges, comments
and serial sequence ownership move to the new table before the source
is dropped and the new table takes its name. Tables with properties that
DROP TABLE would silently remove (triggers, policies, inheritance,
extended statistics, publications, identity columns) are refused.

The function moves to pg_lake_table, which owns CTAS and the catalog
hooks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
@sfc-gh-dachristensen sfc-gh-dachristensen changed the title iceberg: add lake_iceberg.relocate_table() to move a table to a new location iceberg: add lake_iceberg.relocate_table() to rebuild a table at a new location Oct 12, 2026

This branch has not been deployed

No deployments
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