duckdb-lake-formation
June 28, 2026 · View on GitHub
lake_formation is a DuckDB community extension that enforces AWS Lake Formation row-level data filters when reading Glue-managed Apache Iceberg tables. It is a thin, AWS-specific layer on top of the core iceberg extension: all AWS SDK usage (Glue, Lake Formation, STS) lives here, and duckdb-iceberg carries no Lake Formation code.
How it works
The extension registers an access-delegation provider with the iceberg extension through a generic plugin registry that lives on DuckDB's database-wide object cache. When a catalog is attached in lake_formation mode, the provider participates in the Iceberg scan lifecycle for each table it reads:
- Policy discovery — fetches the caller's effective data-filter grant via Glue
GetUnfilteredTableMetadata. - Scoped credentials — vends short-lived, filter-scoped S3 credentials via Lake Formation
GetTemporaryGlue*Credentials, refreshed per partition during manifest walks. - Mandatory row filter — installs the Lake Formation row filter as a mandatory scan filter that user
WHEREpredicates cannot widen.
flowchart LR
subgraph iceberg [iceberg extension]
hooks[access-delegation hooks]
end
subgraph lf [lake_formation extension]
provider[LakeFormationProvider]
sdk[AWS SDK: Glue / LF / STS]
end
subgraph core [DuckDB core]
httpfs
aws
end
hooks --> provider
provider --> sdk
provider --> aws
provider --> httpfs
Usage
LOAD lake_formation auto-loads iceberg, httpfs, and aws, so you only need to load this extension. Always load it before attaching a catalog in Lake Formation mode.
INSTALL iceberg;
INSTALL lake_formation FROM community;
LOAD lake_formation; -- auto-loads iceberg, httpfs, aws
-- Base AWS identity (credential chain + STS AssumeRole) as a standard S3 secret:
CREATE SECRET glue_lf_secret (
TYPE S3,
PROVIDER credential_chain,
CHAIN 'sts',
ASSUME_ROLE_ARN 'arn:aws:iam::123456789012:role/my-lf-caller'
);
ATTACH '123456789012' AS lf (
TYPE ICEBERG,
ENDPOINT_TYPE 'GLUE',
ACCESS_DELEGATION_MODE 'lake_formation', -- handled by this extension
LF_SESSION_TAG 'my-app' -- parsed only by this extension
);
SELECT count(*) FROM lf.my_db.my_table;
ACCESS_DELEGATION_MODE 'lake_formation' is a generic key understood by iceberg; the LF_SESSION_TAG option is ignored by iceberg and validated by this extension.
Limitations
- Row-level filters only. Column-level and cell-level Lake Formation grants are rejected in v1.
- Glue catalogs only (
ENDPOINT_TYPE 'GLUE'). - Not available on WebAssembly (
wasm_*) orwindows_amd64_mingw.
Building
This extension compiles against the duckdb-iceberg public headers but does not link the iceberg binary — iceberg is loaded as its own extension at runtime (DONT_LINK). The AWS SDK (Glue, Lake Formation, STS) is pulled in through vcpkg.json and is the only place in the stack that needs those components.
git clone --recurse-submodules https://github.com/The-Alchemist/duckdb-lake-formation
cd duckdb-lake-formation
make
The build needs the iceberg headers that contain the access-delegation hooks. Until those hooks are merged and a pinned GIT_TAG is set in extension_config.cmake, point the build at a local duckdb-iceberg checkout that contains them:
make ICEBERG_SRC_DIR=/path/to/duckdb-iceberg
By default ICEBERG_SRC_DIR resolves to a sibling ../duckdb-iceberg checkout.
Known integration task:
extension_config.cmakestill has a placeholderGIT_TAG(REPLACE_WITH_PINNED_HOOK_COMMIT). Once the access-delegation hook PR lands induckdb-iceberg, pin that commit and the localICEBERG_SRC_DIRoverride becomes optional.
Testing
SQL logic tests live in test/sql/lakeformation. The cloud tests require a provisioned Lake Formation environment (see scripts/setup_lf_data_filters.py) and a set of ICEBERG_LF_* environment variables; they run in CI via .github/workflows/CloudTesting.yml on demand.
make test
License
Apache-2.0.