Iceberg에 쓰기
Iceberg에 쓰기 (Writing to Iceberg)
DuckDB iceberg 익스텐션은 Iceberg 카탈로그가 attach되어 있을 때 Iceberg 테이블에 쓰기를 지원해요. iceberg_scan으로 스토리지에서 직접 읽은 테이블에 쓰는 것은 지원되지 않아요: 새 테이블 메타데이터를 커밋하려면 카탈로그가 필요해요.
출처: 문서
본문
지원되는 연산 (Supported Operations)
iceberg 익스텐션은 REST Catalog가 attach된 상태에서 다음 연산을 지원해요:
CREATE/DROP SCHEMACREATE/DROP TABLEINSERT INTOUPDATEDELETEMERGE INTOALTER TABLESELECT
이 연산들이 지원되므로 다음도 동작해요:
COPY FROM DATABASE duckdb_db TO iceberg_datalake;
-- 또는
COPY FROM DATABASE iceberg_datalake TO duckdb_db;
이 기능은 Iceberg와 DuckDB 스토리지 사이에 딥 카피를 가능하게 해요.
테이블 만들기 (Creating Tables)
테이블은 표준 CREATE TABLE 문법으로 만들어요. Iceberg 테이블 속성은 WITH 절로 제공할 수 있어요:
CREATE TABLE test_create_table (a INTEGER)
WITH (
'format-version' = '2', -- 테이블을 만들 때 format-version으로 승격됨
'location' = 's3://path/to/data', -- 테이블을 만들 때 location으로 승격됨
'property1' = 'value1',
'property2' = 'value2'
);
테이블 속성은 생성 후에도 조회·수정할 수 있어요. Table Properties Functions를 참고해요.
MERGE INTO
MERGE INTO 문은 기본 키가 없는 Iceberg 테이블에 대한 upsert를 표현하는 권장 방법이에요. 변경 세트를 단일 문으로 적용하고, 행마다 삽입·업데이트·삭제 여부를 결정할 수 있어요:
MERGE INTO iceberg_catalog.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;
같은 문에서 WHEN MATCHED THEN DELETE를 사용해 삭제 세트를 표현할 수도 있어요. UPDATE와 DELETE처럼 MERGE INTO는 merge-on-read 시맨틱을 사용하고 Iceberg 테이블에 positional deletes를 써요.
ALTER TABLE과 스키마 발전
ALTER TABLE 문은 Iceberg 테이블에서 지원되며, 가장 흔한 스키마 발전 연산을 다뤄요:
-- 테이블 이름 바꾸기
ALTER TABLE iceberg_catalog.default.simple_table
RENAME TO renamed_table;
-- 열 추가
ALTER TABLE iceberg_catalog.default.renamed_table
ADD COLUMN col3 DOUBLE;
-- 열 이름 바꾸기
ALTER TABLE iceberg_catalog.default.renamed_table
RENAME COLUMN col2 TO name;
-- 열 삭제
ALTER TABLE iceberg_catalog.default.renamed_table
DROP COLUMN col3;
-- format-version 설정
ALTER TABLE iceberg_catalog.default.renamed_table
SET ('format-version' = 3);
각 ALTER TABLE 문은 Iceberg 테이블의 current-schema-id를 업데이트하며, 변경 사항은 다른 Iceberg 인식 엔진이 다음에 LoadTableInformation 엔드포인트를 조회할 때 보이게 돼요. Iceberg 스키마 발전은 메타데이터 전용이라 데이터 파일이 다시 쓰이지 않아요.
파티션 트랜스폼 (Partition Transforms)
identity 파티셔닝 외에도 DuckDB-Iceberg는 bucket과 truncate 파티션 트랜스폼을 사용하는 테이블의 생성·삽입·업데이트를 지원해요.
bucket(N, col) 트랜스폼은 열의 값을 N개 버킷으로 해시하는데, 고카디널리티 열에 안정적인 파티셔닝을 원할 때 유용해요. truncate(W, col)은 행을 첫 W개 문자(숫자 열의 경우 열 값을 W의 배수로 내림)로 그룹화하는데, prefix 기반 파티셔닝에 유용해요.
CREATE TABLE iceberg_catalog.default.events (
event_id BIGINT,
user_id BIGINT,
country VARCHAR,
payload VARCHAR
)
PARTITIONED BY (bucket(16, user_id), truncate(2, country));
bucket·truncate 파티셔닝 테이블에 대한 업데이트와 삭제도 지원되며, merge-on-read 시맨틱 하에 positional deletes를 사용해요.
Iceberg V3 지원
DuckDB-Iceberg는 읽기·쓰기 양쪽에서 다음 Iceberg v3 명세 기능을 지원해요:
VARIANT와TIMESTAMP_NS데이터 타입- 열의 스키마 수준 기본값
- 바이너리 삭제 벡터 (binary deletion vectors)
- 행 계보 추적 (row lineage tracking)
실제로 가장 큰 변화는 바이너리 삭제 벡터예요. v2 테이블에서 DuckDB-Iceberg는 positional deletes를 Parquet 파일로 써요. v3 테이블에서는 같은 정보를 훨씬 더 컴팩트한 바이너리 삭제 벡터(Puffin 파일)로 인코딩해요. DuckDB는 테이블의 format-version에 따라 올바른 형식을 자동으로 선택해요. 생성 시 format-version 테이블 속성을 설정해 v3 테이블을 만들 수 있어요:
CREATE TABLE iceberg_catalog.default.v3_table
WITH ('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);
경고
GEOGRAPHY와UNKNOWN타입은 아직 DuckDB-Iceberg에서 지원되지 않아요; DuckDB v2.0.0에 계획돼 있어요.
UPDATE와 DELETE의 제한 사항
UPDATE와 DELETE 연산에는 다음과 같은 제한 사항이 있어요:
- 정렬된(sorted) 테이블에서만 동작해요. 정렬된 테이블에서 이 연산을 시도하면 [실패]해요.
- DuckDB-Iceberg는 positional deletes만 써요(v3 테이블의 경우 바이너리 삭제 벡터로 인코딩). Copy-on-write 기능은 아직 지원되지 않아요.
- DuckDB-Iceberg는 merge-on-read 시맨틱만 지원해요. 테이블에
write.update.mode나write.delete.mode속성이merge-on-read가 아닌 다른 값으로 설정되어 있으면 연산이 실패해요.
미지원 연산 (Unsupported Operations)
DuckDB iceberg 익스텐션이 아직 지원하지 않는 것은:
GEOGRAPHY와UNKNOWN데이터 타입 (DuckDB v2.0.0 예정)UPDATE,DELETE,MERGE INTO에 대한 copy-on-write 시맨틱