Connect DuckDB to the Iceberg REST catalog
DuckDB connects to the Iceberg REST service through its iceberg
extension, with no Gravitino engine connector to install. One ATTACH makes a Gravitino catalog
appear as a DuckDB database, and DuckDB can then read, create, insert into, update and delete from
its tables. Storage access comes from vended credentials, so DuckDB needs no keys of its own for the
catalog's bucket.
Quick start
The example signs you in with your own token from the identity provider and attaches the Gravitino
catalog sales_lake. Gravitino authorizes every request as you.
INSTALL iceberg;
LOAD iceberg;
INSTALL httpfs;
LOAD httpfs;
CREATE SECRET gravitino (TYPE iceberg, TOKEN '{token}');
ATTACH 'sales_lake' AS sales (
TYPE iceberg,
ENDPOINT 'https://{iceberg_host}/iceberg',
SECRET gravitino
);
SHOW ALL TABLES;
SELECT * FROM sales.q4.leads LIMIT 10;
The schemas of the catalog appear as schemas of the sales database, so a table is
sales.{schema}.{table}.
Prerequisites
- DuckDB 1.5 or later, with the
icebergandhttpfsextensions. The steps on this page were run with DuckDB 1.5. - The Iceberg REST service published and reachable from where DuckDB runs, at the hostname set in
ingress.iceberg.host. See Client access. - An identity provider configured for the server, since DuckDB authenticates with OAuth tokens only; see Authentication.
- For storage access, credential vending configured on the catalog; see Credential vending.
Select the catalog
The first argument to ATTACH is the name of the Gravitino catalog, and the AS name is what it is
called in DuckDB. DuckDB sends the catalog name to the service as the warehouse and receives the
catalog's prefix in return, so every later request goes to that catalog. Attach several catalogs
under different AS names to work across them in one session.
ATTACH 'sales_lake' AS sales (TYPE iceberg, ENDPOINT 'https://{iceberg_host}/iceberg', SECRET gravitino);
ATTACH 'finance_lake' AS finance (TYPE iceberg, ENDPOINT 'https://{iceberg_host}/iceberg', SECRET gravitino);
An empty first argument, ATTACH '', names no warehouse, and the service then serves its default
catalog, if one is set. Naming the catalog is the safer habit.
Authentication
DuckDB authenticates with OAuth only. It sends one bearer token on every request, taken from the
iceberg secret the catalog is attached with: either a shared identity, from a client ID and secret
or a pre-issued token, or a person's own token. A secret with a person's token needs no client ID or
secret. See How to authenticate for the Gravitino side.
DuckDB has no option for Basic credentials on the catalog connection, so a local user cannot connect DuckDB directly. Everyone who uses DuckDB needs an identity in the identity provider. Every request is authorized as that identity, with the privileges listed under Authentication and access control.
Shared identity for DuckDB
Use a shared identity for scripts and jobs that run as a service account. Every session that signs in with it is authorized as the same account, so grant that account in Gravitino only what those jobs need.
Client ID and client secret
DuckDB fetches a token from the identity provider's token endpoint and sends it to the service.
CREATE SECRET gravitino (
TYPE iceberg,
CLIENT_ID '{client_id}',
CLIENT_SECRET '{client_secret}',
OAUTH2_SERVER_URI '{token_endpoint_uri}',
OAUTH2_SCOPE '{scope}'
);
DuckDB sends the client ID and secret to the token endpoint as an HTTP Authorization: Basic header
rather than in the request body, so the client in the identity provider must accept client
authentication by Basic header. Keycloak accepts it for any confidential client.
Pre-issued token
A token issued to a service account, set in the secret's TOKEN option the same way as a
personal token. Nothing renews it, so requests fail once it expires.
Per-user identity
A person gets a token from the identity provider, outside DuckDB, and puts it in the secret, as in the Quick start. DuckDB sends it as the bearer token on every request, and Gravitino authorizes each request as that person.
CREATE SECRET gravitino (TYPE iceberg, TOKEN '{token}');
The token must carry the audience configured for Gravitino and a principal claim naming a member of the metalake. Nothing renews it, so requests fail once it expires; create the secret again with a new token.
Storage access
DuckDB asks the service for vended credentials when it loads a table, and the service returns
credentials scoped to that table along with the storage settings DuckDB needs to use them, such as
the S3 endpoint, region and path-style access for an S3-compatible service. No CREATE SECRET for
S3, GCS or Azure is needed for tables in an attached catalog.
DuckDB requests vended credentials by default. Add ACCESS_DELEGATION_MODE 'vended_credentials' to
the ATTACH options to state it explicitly. If the catalog has no credential provider, DuckDB
receives no credentials, and reads of table data fail unless DuckDB has storage credentials of its
own; see Credential vending.
Examples
CREATE SCHEMA sales.q4;
CREATE TABLE sales.q4.orders (id BIGINT, amount DECIMAL(10, 2));
INSERT INTO sales.q4.orders VALUES (1, 9.99), (2, 20.00);
UPDATE sales.q4.orders SET amount = 21.00 WHERE id = 2;
DELETE FROM sales.q4.orders WHERE id = 1;
SELECT * FROM sales.q4.orders;
Each statement that changes data commits a new snapshot through the service, so the change is visible to every other engine on the catalog as soon as the statement returns.
Known issues
iceberg_snapshots() and iceberg_metadata() fail on an attached table. These table functions
read the table's metadata files with DuckDB's own storage settings rather than the vended
credentials, so they fail with a 404 or 403 from the storage service. Querying the table itself is
unaffected. The behavior is in DuckDB's iceberg extension, not in the Iceberg REST service.