Setup
- Open Settings → Lake and find BigQuery connections.
- Give the connection a name - lowercase letters, numbers, and underscores, e.g.
my_bq. This becomes the catalog prefix in SQL. - Paste your service account JSON. The GCP project ID is filled in from the key.
roles/bigquery.dataViewer, or narrower table-level permissions. The key is stored encrypted and is never returned to the browser after saving.
Querying
Tables are reachable asconnection_name.dataset.table:
A BigQuery table runs on Bloom or DuckDB. The query router prefers Bloom for reads and uses DuckDB for writes or when Bloom isn’t available - leave
engine on auto. Polars and Spark can’t query connections.Importing into Iceberg
For anything larger than an ad hoc read, import the table. In the lake tree, hover a BigQuery table and choose Import into the lake. The import runs as a Spark job that reads one consistent snapshot of the table through the BigQuery Storage Read API, split into streams that executors read in parallel, and lands the data as an Iceberg table in your lake, where every engine can reach it.- The source is
dataset.tablein the connection’s project, orproject.dataset.tablefor another project the service account can read, such as a public dataset. - Logical views can’t be imported; import the tables they select from instead.
- Your BigQuery project is billed for the bytes read.
oleander.default.<table> - the sanitized source table name, with no connection prefix. OVERWRITE replaces it, APPEND adds to it.
An import does not return rows. It submits a job and returns a run_id you can monitor like any other run, and once it completes you can sample the destination with a normal query. Importing requires Describe on the connection and Execute on Spark.
Table syncing
A one-off import gives you a snapshot. Put the same import on a schedule and the Iceberg copy tracks the source instead. Schedules run hourly or daily, and are managed the same way as Postgres syncs: under Scheduled syncs in Settings → Lake, and under Transfers in the lake’s left panel.Scheduled syncs are incremental where the table allows it. A table with a timestamp cursor column (such as
updated_at) and a declared primary key (PRIMARY KEY (...) NOT ENFORCED) merges rows changed since the last run. A table without both is copied in full each run, and the reason is written to the run’s plan log. Rows deleted in BigQuery are not removed from the Iceberg copy.Cost attribution
BigQuery spend is pulled into the context graph alongside your own runs. Usebigquery_cost_get from an agent, or the cost views in observability, to see cost per table broken down by producing pipeline, query type, and full-scan detection.
From an agent
An agent connected over MCP reaches the same tables throughquery_run. It discovers them with catalogs_list and queries connection.dataset.table directly - no BigQuery-specific tool needed for live reads.
Two tools cover imports: