BigQuery and Apache Iceberg
dbt supports materializing models in the Iceberg table format in two ways:
- Simplest: The model config
table_format = 'iceberg'instructs dbt to materialize this model as an Iceberg table in BigLake Metastore (managed catalog) - Extensible: Define an Iceberg catalog in
catalogs.ymland configure this model withcatalog_name
Creating Iceberg tables
dbt supports creating Iceberg tables for two of the BigQuery materializations:
Iceberg catalogs
BigQuery supports Iceberg tables through its built-in catalog BigLake Metastore today. No setup is needed to access the BigLake Metastore. However, you need to have a storage bucket and the required BigQuery roles configured prior to creating an Iceberg table.
BigQuery-specific configs for biglake_metastore catalogs
The following table outlines the configuration fields required to set up a catalog integration for BigLake Iceberg tables in BigQuery.
Supply and nest these additional configurations, unique to BigQuery, under config.bigquery (in the new catalog spec) or adapter_properties (in the old catalog spec).
| Loading table... |
base_location_root: Specifies the prefix of the base location path within the storage bucket where Iceberg table data is written.base_location_subpath: Specifies the suffix of the base location path within the storage bucket where Iceberg table data is written. This property can only be set in model configurations, not incatalogs.yml.storage_uri: Completely overrides the storage_uri, allowing you to specify the full path directly instead of using the catalog integration's external volume and base_location components.
Example
- Create a
catalogs.ymlat the top level of your dbt project.
- New spec
- Old spec
catalogs:
- name: my_biglake_catalog
type: biglake_metastore
table_format: iceberg
config:
bigquery:
external_volume: 'gs://mydbtbucket'
file_format: parquet
catalogs:
- name: my_biglake_catalog
active_write_integration: biglake_metastore
write_integrations:
- name: biglake_metastore
external_volume: 'gs://mydbtbucket'
table_format: iceberg
file_format: parquet
catalog_type: biglake_metastore
- Apply the catalog configuration at either the model, folder, or project level:
{{
config(
materialized='table',
catalog_name='my_biglake_catalog'
)
}}
select * from {{ ref('jaffle_shop_customers') }}
- Finally, run the model:
dbt run -s my_iceberg_model.
Limitations
BigQuery today doesn't support connecting to external Iceberg catalogs. In terms of SQL operations and table management features, refer to the BigQuery docs for more information.
Base location
BigQuery's DDL for creating Iceberg tables requires that a fully qualified storage_uri be provided, including the object path. Once the user has provided the bucket name as the external_volume in the catalog integration, dbt manages the storage_uri input. The default behavior in dbt is to provide an object path, referred to in dbt as the base_location, in the form: _dbt/{SCHEMA_NAME}/{MODEL_NAME}. We recommend using the default behavior, but if you need to customize the resulting base_location, you can configure base_location with the model configuration fields base_location_root and base_location_subpath.
- If no inputs are provided, dbt outputs for base_location
{{ external_volume }}/_dbt/{{ schema }}/{{ model_name }} - If base_location_root =
foo, dbt outputs{{ external_volume }}/foo/{{ schema }}/{{ model_name }} - If base_location_subpath =
bar, dbt outputs{{ external_volume }}/_dbt/{{ schema }}/{{ model_name }}/bar - If base_location_root =
fooand base_location_subpath =bar, dbt outputs{{ external_volume }}/foo/{{ schema }}/{{ model_name }}/bar
While you can customize paths with base_location_root and base_location_subpath, we don't recommend relying on them for environment isolation (such as separating development and production environments). Anyone with repository access can easily modify these configuration values. For true environment isolation, use separate external_volume values with infrastructure-level access controls.
You can also completely override the storage_uri with the model configuration field storage_uri. This overrides both the catalog integration path and the other model configuration fields to supply the entire storage_uri path directly.
An example model with a customized base_location:
{{
config(
materialized='table',
catalog_name='my_bigquery_iceberg_catalog',
base_location_root='foo',
base_location_subpath='bar',
)
}}
select * from {{ ref('jaffle_shop_customers') }}
- New spec
- Old spec
catalogs:
- name: my_bigquery_iceberg_catalog
type: biglake_metastore
table_format: iceberg
config:
bigquery:
external_volume: 'gs://mydbtbucket'
file_format: parquet
base_location_root: foo
catalogs:
- name: my_bigquery_iceberg_catalog
active_write_integration: biglake_metastore
write_integrations:
- name: biglake_metastore
external_volume: 'gs://mydbtbucket'
table_format: iceberg
file_format: parquet
catalog_type: biglake_metastore
adapter_properties:
base_location_root: foo
Rationale
By default, dbt manages the full storage_uri on behalf of users for ease of use. The base_location parameter specifies the location within the storage bucket where the data is written. Without guardrails (for example, if the user forgets to provide a base location root), it's possible for BigQuery to reuse the same path across multiple tables.
This behavior could result in future technical debt because it limits the ability to:
- Navigate the underlying object store
- Read Iceberg tables through an object-store integration
- Grant schema-specific access to tables through object store
- Use a crawler pointed at the tables within the external storage to build a new catalog with another tool
To maintain best practices, dbt enforces an input and, by default, writes your tables within a _dbt/{SCHEMA_NAME}/{TABLE_NAME} prefix to ensure easier object-store observability and auditability.
Was this page helpful?
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.