Introduce a new way to configure Iceberg catalogs using PostgreSQL's native CREATE SERVER and CREATE USER MAPPING infrastructure. Each catalog becomes a named server object with its own connection settings and credentials, replacing the current global GUC-based configuration, which has some limitations (can have only single REST catalog, multiple default location prefix definitions).
Proposal
A new configuration-only FDW (iceberg_catalog, no handler) is introduced. Each catalog is represented as a CREATE SERVER with a TYPE indicating the catalog kind (postgres, object_store, or rest). Credentials are stored securely in CREATE USER MAPPING. The catalog option on Iceberg tables changes from a type string ('postgres', 'rest') to a server name reference.
Development
Defining catalogs
-- Pre-created by the extension
CREATE FOREIGN DATA WRAPPER iceberg_catalog
VALIDATOR iceberg_catalog_validator;
CREATE SERVER postgres TYPE 'postgres'
FOREIGN DATA WRAPPER iceberg_catalog;
CREATE SERVER object_store TYPE 'object_store'
FOREIGN DATA WRAPPER iceberg_catalog
OPTIONS (location_prefix '...', internal_prefix 'frompg', external_prefix 'fromsf');
-- User-defined REST catalog (e.g., Polaris / OpenCatalog)
CREATE SERVER my_open_catalog TYPE 'rest'
FOREIGN DATA WRAPPER iceberg_catalog
OPTIONS (
rest_endpoint 'http://polaris:8181',
rest_auth_type 'default',
catalog_name 'default',
location_prefix 's3://datalake/iceberg',
scope 'PRINCIPAL_ROLE:ALL',
enable_vended_credentials 'true'
);
Credentials via USER MAPPING
Secrets are stored in USER MAPPING, which is access-controlled (unlike SERVER OPTIONS, which are publicly readable). This supports shared credentials via PUBLIC and per-user credentials.
-- Shared credentials
CREATE USER MAPPING FOR PUBLIC SERVER my_polaris
OPTIONS (client_id 'svc-account', client_secret 'secret');
-- Per-user credentials
CREATE USER MAPPING FOR analyst SERVER my_polaris
OPTIONS (client_id 'analyst-id', client_secret 'analyst-secret');
Creating tables
-- Default (postgres catalog, unchanged UX)
CREATE TABLE ice (id int) USING iceberg;
-- Write to a named REST catalog
CREATE TABLE events (id int, ts timestamptz) USING iceberg
WITH (catalog = 'my_polaris');
-- Read from a REST catalog
CREATE TABLE external_data () USING iceberg
WITH (catalog = 'my_polaris', read_only = 'true',
catalog_namespace = 'raw', catalog_table_name = 'events');
GUC migration
| Current GUC |
Status |
Destination |
New Option Name |
default_location_prefix |
Still needed as GUC |
-- |
-- |
default_catalog |
Still needed as GUC (now holds a server name) |
-- |
-- |
object_store_catalog_location_prefix |
Remains for backward compat |
-- |
-- |
internal_object_store_catalog_prefix |
Could move to server or be removed |
SERVER |
internal_prefix |
external_object_store_catalog_prefix |
Could move to server or be removed |
SERVER |
external_prefix |
rest_catalog_host |
Becomes server property |
SERVER |
rest_endpoint |
rest_catalog_auth_type |
Becomes server property |
SERVER |
rest_auth_type |
rest_catalog_oauth_host_path |
Becomes server property |
SERVER |
oauth_endpoint |
rest_catalog_scope |
Becomes server property |
SERVER |
scope |
rest_catalog_enable_vended_credentials |
Becomes server property |
SERVER |
enable_vended_credentials |
rest_catalog_client_id |
Becomes server property |
USER MAPPING |
client_id |
rest_catalog_client_secret |
Becomes server property |
USER MAPPING |
client_secret |
Task outline
Introduce a new way to configure Iceberg catalogs using PostgreSQL's native
CREATE SERVERandCREATE USER MAPPINGinfrastructure. Each catalog becomes a named server object with its own connection settings and credentials, replacing the current global GUC-based configuration, which has some limitations (can have only single REST catalog, multiple default location prefix definitions).Proposal
A new configuration-only FDW (
iceberg_catalog, no handler) is introduced. Each catalog is represented as aCREATE SERVERwith aTYPEindicating the catalog kind (postgres,object_store, orrest). Credentials are stored securely inCREATE USER MAPPING. Thecatalogoption on Iceberg tables changes from a type string ('postgres','rest') to a server name reference.Development
CREATE SERVERinfrastructure New Catalog Configuration via CREATE SERVER #242CREATE USER MAPPINGinfrastructure New Catalog Configuration Credentials #255Defining catalogs
Credentials via USER MAPPING
Secrets are stored in
USER MAPPING, which is access-controlled (unlikeSERVER OPTIONS, which are publicly readable). This supports shared credentials viaPUBLICand per-user credentials.Creating tables
GUC migration
default_location_prefixdefault_catalogobject_store_catalog_location_prefixinternal_object_store_catalog_prefixinternal_prefixexternal_object_store_catalog_prefixexternal_prefixrest_catalog_hostrest_endpointrest_catalog_auth_typerest_auth_typerest_catalog_oauth_host_pathoauth_endpointrest_catalog_scopescoperest_catalog_enable_vended_credentialsenable_vended_credentialsrest_catalog_client_idclient_idrest_catalog_client_secretclient_secretTask outline
iceberg_catalogFDW (validator only, no handler) with a validator that enforces allowed options per server TYPE and per context (SERVER vs USER MAPPING). Pre-createpostgresandobject_storeservers in the extension script.IcebergCatalogConfigstruct that merges SERVER OPTIONS (non-secret) with USER MAPPING OPTIONS (credentials). AGetIcebergCatalogConfig()function resolves the full configuration from a server name. Support current settings as defaults when not specified in server.RestCatalogHost,RestCatalogClientId, etc.) accept anIcebergCatalogConfiginstead. OAuth token caching becomes per-server.iceberg_catalogFDW and thepostgres/object_storeservers. Existing tables withcatalog='postgres'orcatalog='object_store'resolve seamlessly to these pre-created servers.GRANT USAGE ON FOREIGN DATA WRAPPER iceberg_catalog TO ...) and creates USER MAPPINGs for REST catalogs.location_prefixresolution hierarchy: per-tablelocation> catalog serverlocation_prefix> global GUC. Determine whether catalogs should enforce location constraints (e.g., all tables under a catalog must share the same bucket prefix).