2026-05-29
Neue DuckDB-Iceberg-Funktionen in v1.5.3
Tom Ebergen, Thijs Bruineman
Trotz der Arbeit an den Features für DuckLake v1.0 und Quack arbeitet das DuckLabs-Team weiter an der DuckDB-Iceberg-Erweiterung. In diesem Blogbeitrag zeigen wir einige der Funktionen, die in DuckDB v1.5.3 verfügbar sind. Viele davon waren in unserem letzten Iceberg-Beitrag „Writes in DuckDB-Iceberg“ für ein späteres Release vorgesehen – Sie können diesen Text als „Teil 2“ dazu lesen.
Einstieg
Um die neuen DuckDB-Iceberg-Funktionen auszuprobieren, verbinden Sie sich mit Ihrem bevorzugten Iceberg REST Catalog. Es gibt viele Wege dahin: Schauen Sie auf die Seite Connecting to REST Catalogs mit Anleitungen für Kataloge wie Apache Polaris und Lakekeeper. Für Amazon S3 Tables siehe Connecting to S3 Tables. Ihr ATTACH-Befehl sieht in etwa so aus:
ATTACH '⟨warehouse_name⟩' AS my_datalake ( TYPE iceberg, ⟨other options⟩);Unterstützung für MERGE INTO
DuckDBs Statement MERGE INTO ist der empfohlene Weg für Upserts, wenn die Zieltabelle keinen Primärschlüssel hat – das gilt für alle Lakehouse-Formate.
Ab v1.5.3 ist MERGE INTO voll gegen Iceberg-Tabellen unterstützt. Sie wenden ein Changeset in einem Statement an und entscheiden pro Zeile, ob eingefügt, aktualisiert oder gelöscht wird.
Nehmen wir diese Tabelle:
CREATE TABLE my_datalake.default.people ( id INTEGER, name VARCHAR, salary FLOAT);INSERT INTO my_datalake.default.people VALUES (1, 'John', 92_000.0), (2, 'Anna', 100_000.0);┌───────┬─────────┬──────────┐│ id │ name │ salary ││ int32 │ varchar │ float │├───────┼─────────┼──────────┤│ 1 │ John │ 92000.0 ││ 2 │ Anna │ 100000.0 │└───────┴─────────┴──────────┘Wir führen ein Update mit zwei Records aus: Person 1 bekommt eine Gehaltserhöhung, und eine neue Person mit id 3 kommt hinzu.
MERGE INTO my_datalake.default.people AS target USING ( FROM (VALUES (1, 'John', 105_000.0), (3, 'Sarah', 95_000.0) ) t(id, name, salary) ) AS upserts ON (upserts.id = target.id) WHEN MATCHED THEN UPDATE WHEN NOT MATCHED THEN INSERT;Die Abfrage des Ergebnisses liefert:
SELECT *FROM my_datalake.default.peopleORDER BY id;┌───────┬─────────┬──────────┐│ id │ name │ salary ││ int32 │ varchar │ float │├───────┼─────────┼──────────┤│ 1 │ John │ 105000.0 ││ 2 │ Anna │ 100000.0 ││ 3 │ Sarah │ 95000.0 │└───────┴─────────┴──────────┘Sie können matched- und unmatched-Zweige auch mit WHEN MATCHED THEN DELETE kombinieren, um ein Deleteset im selben Statement auszudrücken. Wie bei UPDATE und DELETE nutzt MERGE INTO Merge-on-Read-Semantik und schreibt positionale Deletes in die Iceberg-Tabelle.
Unterstützung für ALTER TABLE
In der Iceberg-Erweiterung von DuckDB v1.4 war fehlende Schemaevolution von Iceberg-Tabellen eine dokumentierte Einschränkung.
In v1.5.3 wird ALTER TABLE gegen Iceberg-Tabellen unterstützt und deckt die gängigsten Schemaevolutions-Operationen ab.
-- Create the tableCREATE TABLE my_datalake.default.simple_table AS FROM (VALUES (1, 'Andy'), (2, 'Bob'), (3, 'Claire'), (4, 'Mr. Duck')) t(col1, col2);
-- Rename the tableALTER TABLE my_datalake.default.simple_table RENAME TO renamed_table;
-- Add a columnALTER TABLE my_datalake.default.renamed_table ADD COLUMN col3 DOUBLE;
-- Rename a columnALTER TABLE my_datalake.default.renamed_table RENAME COLUMN col2 TO name;
-- Drop a columnALTER TABLE my_datalake.default.renamed_table DROP COLUMN col3;
-- Set the format-versionALTER TABLE my_datalake.default.renamed_table SET ('format-version' = 3);Fragen wir die Tabelle nach den Schemaänderungen ab, erhalten wir:
SELECT *FROM my_datalake.default.renamed_tableORDER BY col1;┌───────┬──────────┐│ col1 │ name ││ int32 │ varchar │├───────┼──────────┤│ 1 │ Andy ││ 2 │ Bob ││ 3 │ Claire ││ 4 │ Mr. Duck │└───────┴──────────┘Im Hintergrund aktualisiert jedes ALTER TABLE die current-schema-id der Iceberg-Tabelle. Die Änderungen sehen andere Iceberg-fähige Engines beim nächsten Aufruf des Endpoints LoadTableInformation. Iceberg-Schemaevolution ist rein metadatenbasiert, Datenfiles werden nicht umgeschrieben.
Unterstützung für truncate und bucket
Die Iceberg-Spezifikation definiert mehrere Partition Transforms, die festlegen, wie Datenfiles auf der Platte liegen. In v1.5.3 unterstützt DuckDB-Iceberg das Anlegen, Einfügen und Aktualisieren von Tabellen mit den Partition Transforms bucket und truncate.
Der Transform bucket(N, col) hasht den Spaltenwert in N Buckets, nützlich für stabile Partitionierung auf einer hochkardinalen Spalte. truncate(W, col) gruppiert Zeilen nach den ersten W Zeichen (oder, bei numerischen Spalten, nach dem auf ein Vielfaches von W abgerundeten Wert), nützlich für präfixbasierte Partitionierung.
CREATE TABLE my_datalake.default.events ( event_id BIGINT, user_id BIGINT, country VARCHAR, payload VARCHAR)PARTITIONED BY (bucket(16, user_id), truncate(2, country));
INSERT INTO my_datalake.default.events VALUES (1, 1001, 'United States', 'click'), (2, 1002, 'United Kingdom', 'view'), (3, 1003, 'Germany', 'click'), (4, 1004, 'Netherlands', 'view');Die entstandenen Datenfiles können Sie prüfen, um die Partitionierung zu verifizieren:
SELECT file_path, record_countFROM iceberg_metadata(my_datalake.default.events)WHERE content = 'EXISTING';Updates und Deletes gegen bucket- und truncate-partitionierte Tabellen sind ebenfalls unterstützt, mit positionalen Deletes unter Merge-on-Read-Semantik.
Iceberg Schema Properties
Iceberg-Kataloge erlauben beliebige Key-Value-Properties auf Schema-(Namespace-)Ebene. Typisch werden sie für Ownership, Beschreibungen, Default-Storage-Locations oder andere Metadaten genutzt, die für alle Tabellen eines Schemas gelten.
iceberg_schema_propertiesset_iceberg_schema_propertiesremove_iceberg_schema_properties
Verwendung:
-- to set schema propertiesCALL set_iceberg_schema_properties(my_datalake.default, { 'owner': 'analytics-team', 'description': 'Default analytics schema'});-- to read schema propertiesSELECT * FROM iceberg_schema_properties(my_datalake.default);┌─────────────┬──────────────────────────┐│ key │ value ││ varchar │ varchar │├─────────────┼──────────────────────────┤│ owner │ analytics-team ││ description │ Default analytics schema │└─────────────┴──────────────────────────┘-- to remove schema propertiesCALL remove_iceberg_schema_properties( my_datalake.default, ['description']);Schema Properties werden über den Iceberg REST Catalog geschrieben, jede andere Iceberg-fähige Engine am selben Katalog sieht die Updates sofort. Der Rückgabewert ist die Zahl der verbleibenden Schema Properties.
V3-Unterstützung
Die Iceberg-v3-Spezifikation führt mehrere neue Funktionen ein, die DuckDB-Iceberg jetzt für Reads und Writes unterstützt:
- die Datentypen
VARIANTundTIMESTAMP_NS - Schema-weite Default-Werte für Spalten
- binäre Deletion Vectors
- Row-Lineage-Tracking
Die größte praktische Änderung sind binäre Deletion Vectors. In v2-Tabellen schreibt DuckDB-Iceberg positionale Deletes als Parquet-Dateien; in v3-Tabellen wird dieselbe Information als deutlich kompakterer binärer Deletion Vector (Puffin-Datei) kodiert. DuckDB wählt das Format automatisch anhand der format-version der Tabelle.
Eine v3-Tabelle legen Sie an, indem Sie die Table Property format-version beim Anlegen setzen:
CREATE TABLE my_datalake.default.v3_tableWITH ('format-version' = 3) AS FROM (VALUES (1, {'kind': 'click', 'x': 10}::VARIANT, TIMESTAMP_NS '2026-05-20 12:00:00.123456789'), (2, {'kind': 'view'}::VARIANT, TIMESTAMP_NS '2026-05-20 12:00:00.987654321') ) t(id, payload, event_time);
-- Deletes against a v3 table are written as binary deletion vectorsDELETE FROM my_datalake.default.v3_tableWHERE id = 1;
SELECT * FROM my_datalake.default.v3_table;┌───────┬──────────────────┬───────────────────────────────┐│ id │ payload │ event_time ││ int32 │ variant │ timestamp_ns │├───────┼──────────────────┼───────────────────────────────┤│ 2 │ {"kind": "view"} │ 2026-05-20 12:00:00.987654321 │└───────┴──────────────────┴───────────────────────────────┘Ein Blick in die Metadaten bestätigt, dass der Delete als Deletion Vector geschrieben wurde, nicht als positional-delete-Parquet-Datei:
SELECT manifest_content, content, file_formatFROM iceberg_metadata(my_datalake.default.v3_table);┌──────────────────┬──────────────────┬─────────────┐│ manifest_content │ content │ file_format ││ varchar │ varchar │ varchar │├──────────────────┼──────────────────┼─────────────┤│ DATA │ EXISTING │ parquet ││ DELETE │ POSITION_DELETES │ puffin │└──────────────────┴──────────────────┴─────────────┘Die Typen Geography und Unknown sind in DuckDB-Iceberg noch nicht unterstützt; wir planen sie für DuckDB v2.0.0.
Fazit und Ausblick
Mit diesen Funktionen hat DuckDB-Iceberg viele der Lücken aus dem vorherigen Blogbeitrag geschlossen: partitionierte Writes, Schemaevolution, MERGE INTO und viele Iceberg-v3-Funktionen sind jetzt da. Es kommt noch mehr, und wie immer: Wenn Sie eine bestimmte Funktion priorisiert sehen möchten, schreiben Sie uns im DuckDB-Iceberg-GitHub-Repository oder melden Sie sich bei unseren Ingenieurinnen und Ingenieuren.